diff --git a/README.md b/README.md index dae72dcc3..a9e0bbf9f 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ sudo rm -rf /usr/local/share/meevax | Target Name | Description |:-------------------|:-- -| `all` (default) | Build shared-library `libmeevax.0.4.40.so` and executable `meevax`. +| `all` (default) | Build shared-library `libmeevax.0.4.64.so` and executable `meevax`. | `test` | Test executable `meevax`. -| `package` | Generate debian package `meevax_0.4.40_amd64.deb`. +| `package` | Generate debian package `meevax_0.4.64_amd64.deb`. | `install` | Copy files into `/usr/local` __(1)__. | `install.deb` | `all` + `package` + `sudo apt install .deb` | `safe-install.deb` | `all` + `test` + `package` + `sudo apt install .deb` @@ -120,7 +120,7 @@ __(1)__ Meevax installed by `make install` cannot be uninstalled by the system's ## Usage ``` -Meevax Lisp System, version 0.4.40 +Meevax Lisp System, version 0.4.64 Usage: meevax [OPTION...] [FILE...] diff --git a/VERSION b/VERSION index 12fd0b492..96b9b226d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.40 +0.4.64 diff --git a/basis/r4rs-essential.ss b/basis/r4rs-essential.ss index 2dfbc860a..92ea6560d 100644 --- a/basis/r4rs-essential.ss +++ b/basis/r4rs-essential.ss @@ -221,8 +221,8 @@ (if x #f #t)) (define (boolean? x) - (or (eqv? x #t) - (eqv? x #f))) + (or (eq? x #t) + (eq? x #f))) (define (equal? x y) (if (and (pair? x) diff --git a/include/meevax/kernel/boolean.hpp b/include/meevax/kernel/boolean.hpp index 798df6273..75d4829f3 100644 --- a/include/meevax/kernel/boolean.hpp +++ b/include/meevax/kernel/boolean.hpp @@ -23,24 +23,6 @@ namespace meevax { inline namespace kernel { - struct boolean - { - using value_type = bool; - - const value_type value; - - constexpr boolean(value_type const value) - : value { value } - {} - - constexpr operator value_type() const noexcept - { - return value; - } - }; - - auto operator <<(std::ostream &, boolean const&) -> std::ostream &; - let extern const t; let extern const f; diff --git a/include/meevax/kernel/configurator.hpp b/include/meevax/kernel/configurator.hpp index 35ebc1759..6d58d3de6 100644 --- a/include/meevax/kernel/configurator.hpp +++ b/include/meevax/kernel/configurator.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -33,133 +34,161 @@ inline namespace kernel { friend environment; + explicit configurator() + {} + IMPORT(environment, evaluate, NIL); IMPORT(environment, load, NIL); - IMPORT(environment, print, const); IMPORT(environment, read, NIL); + USING_STATIC(environment, print); + template using dispatcher = std::unordered_map; - const dispatcher short_options, short_options_with_arguments; + public: + static inline auto batch = false; + static inline auto debug = false; + static inline auto interactive = true; + static inline auto trace = false; + static inline auto verbose = false; - const dispatcher long_options, long_options_with_arguments; + static auto display_version() -> void + { + print("Meevax Lisp ", version()); + } - public: - bool batch = false; - bool debug = false; - bool interactive = false; - bool trace = false; - bool verbose = false; + static auto display_help() -> void + { + display_version(); + print(); + print("Usage: meevax [OPTION...] [FILE...]"); + print(); + print("Options:"); + print(" -b, --batch Suppress any system output."); + print(" -d, --debug Display detailed informations for developers."); + print(" -e, --evaluate=STRING Read and evaluate given STRING at configuration step."); + print(" -h, --help Display this help text and exit."); + print(" -i, --interactive Take over control of root environment."); + print(" -l, --load=FILENAME Same as -e '(load FILENAME)'"); + print(" -t, --trace Display stacks of virtual machine for each steps."); + print(" -v, --version Display version information and exit."); + print(" --verbose Display detailed informations."); + print(" -w, --write=OBJECT Same as -e '(write OBJECT)'"); + } - explicit configurator() - : short_options - { - std::make_pair('b', [this](auto&&...) - { - return make(batch = true); - }), + private: + static inline const dispatcher short_options + { + std::make_pair('b', [](auto&&...) + { + return make(batch = true); + }), - std::make_pair('d', [this](auto&&...) - { - return make(debug = true); - }), + std::make_pair('d', [](auto&&...) + { + return make(debug = true); + }), - std::make_pair('h', [this](auto&&...) -> lvalue - { - display_help(); - throw exit_status::success; - }), + std::make_pair('h', [](auto&&...) -> lvalue + { + configurator::display_help(); + throw exit_status::success; + }), - std::make_pair('i', [this](auto&&...) - { - return make(interactive = true); - }), + std::make_pair('i', [](auto&&...) + { + return make(interactive = true); + }), - std::make_pair('v', [this](auto&&...) -> lvalue - { - display_version(); - throw exit_status::success; - }), - } + std::make_pair('v', [](auto&&...) -> lvalue + { + configurator::display_version(); + throw exit_status::success; + }), + }; - , short_options_with_arguments - { - std::make_pair('e', [this](const_reference x, auto&&...) - { - return print(evaluate(x)), unspecified_object; - }), + static inline const dispatcher short_options_with_arguments + { + std::make_pair('e', [](const_reference x, auto&&...) + { + print(interaction_environment().as().evaluate(x)); + return unspecified_object; + }), - std::make_pair('l', [this](const_reference x, auto&&...) - { - return load(x.as_const()); - }), + std::make_pair('l', [](const_reference x, auto&&...) + { + return interaction_environment().as().load(x.as_const()); + }), - std::make_pair('w', [this](const_reference x, auto&&...) - { - return print(x), unspecified_object; - }), - } + std::make_pair('w', [](const_reference x, auto&&...) + { + print(x); + return unspecified_object; + }), + }; - , long_options - { - std::make_pair("batch", [this](auto&&...) - { - return make(batch = true); - }), + static inline const dispatcher long_options + { + std::make_pair("batch", [](auto&&...) + { + return make(batch = true); + }), - std::make_pair("debug", [this](auto&&...) - { - return make(debug = true); - }), + std::make_pair("debug", [](auto&&...) + { + return make(debug = true); + }), - std::make_pair("help", [this](auto&&...) -> lvalue - { - display_help(); - throw exit_status::success; - }), + std::make_pair("help", [](auto&&...) -> lvalue + { + display_help(); + throw exit_status::success; + }), - std::make_pair("interactive", [this](auto&&...) - { - return make(interactive = true); - }), + std::make_pair("interactive", [](auto&&...) + { + return make(interactive = true); + }), - std::make_pair("trace", [this](auto&&...) - { - return make(trace = true); - }), + std::make_pair("trace", [](auto&&...) + { + return make(trace = true); + }), - std::make_pair("verbose", [this](auto&&...) - { - return make(verbose = true); - }), + std::make_pair("verbose", [](auto&&...) + { + return make(verbose = true); + }), - std::make_pair("version", [this](auto&&...) -> lvalue - { - display_version(); - throw exit_status::success; - }), - } + std::make_pair("version", [](auto&&...) -> lvalue + { + display_version(); + throw exit_status::success; + }), + }; - , long_options_with_arguments - { - std::make_pair("evaluate", [this](const_reference x, auto&&...) - { - return print(evaluate(x)), unspecified_object; - }), + static inline const dispatcher long_options_with_arguments + { + std::make_pair("evaluate", [](const_reference x, auto&&...) + { + print(interaction_environment().as().evaluate(x)); + return unspecified_object; + }), - std::make_pair("load", [this](const_reference x, auto&&...) - { - return load(x.as_const()); - }), + std::make_pair("load", [](const_reference x, auto&&...) + { + return interaction_environment().as().load(x.as_const()); + }), - std::make_pair("write", [this](const_reference x, auto&&...) - { - return print(x), unspecified_object; - }), - } - {} + std::make_pair("write", [](const_reference x, auto&&...) + { + print(x); + return unspecified_object; + }), + }; + public: auto configure(const int argc, char const* const* const argv) { return configure({ argv + 1, argv + argc }); @@ -169,11 +198,7 @@ inline namespace kernel { static std::regex const pattern { R"(--(\w[-\w]+)(=(.*))?|-([\w]+))" }; - if (std::empty(args)) - { - interactive = true; - } - else for (auto current_option = std::begin(args); current_option != std::end(args); ++current_option) [&]() + for (auto current_option = std::begin(args); current_option != std::end(args); ++current_option) [&]() { std::smatch analysis {}; @@ -185,7 +210,7 @@ inline namespace kernel // std::cout << header("") << "analysis[3] = " << analysis[3] << std::endl; // std::cout << header("") << "analysis[4] = " << analysis[4] << std::endl; - if (auto const current_short_options = analysis.str(4); not current_short_options.empty()) + if (auto const& current_short_options = analysis.str(4); not current_short_options.empty()) { for (auto current_short_option = std::cbegin(current_short_options); current_short_option != std::cend(current_short_options); ++current_short_option) { @@ -210,7 +235,8 @@ inline namespace kernel } else { - throw error(make(cat, "unknown short-option -", *current_short_option)); + throw error(make("unknown short-option"), + make(*current_short_option)); } } } @@ -237,41 +263,19 @@ inline namespace kernel } else { - throw error(make(cat, "unknown long-option: ", *current_option)); + throw error(make("unknown long-option"), + make(*current_option)); } } else { + interactive = false; return load(*current_option); } return unspecified_object; }(); } - - auto display_version() const -> void - { - print("Meevax Lisp ", version()); - } - - auto display_help() const -> void - { - display_version(); - print(); - print("Usage: meevax [OPTION...] [FILE...]"); - print(); - print("Options:"); - print(" -b, --batch Suppress any system output."); - print(" -d, --debug Display detailed informations for developers."); - print(" -e, --evaluate=STRING Read and evaluate given STRING at configuration step."); - print(" -h, --help Display this help text and exit."); - print(" -i, --interactive Take over control of root environment."); - print(" -l, --load=FILENAME Same as -e '(load FILENAME)'"); - print(" -t, --trace Display stacks of virtual machine for each steps."); - print(" -v, --version Display version information and exit."); - print(" --verbose Display detailed informations."); - print(" -w, --write=OBJECT Same as -e '(write OBJECT)'"); - } }; } // namespace kernel } // namespace meevax diff --git a/include/meevax/kernel/context.hpp b/include/meevax/kernel/context.hpp index 98c633a42..0ee2d9540 100644 --- a/include/meevax/kernel/context.hpp +++ b/include/meevax/kernel/context.hpp @@ -23,25 +23,10 @@ inline namespace kernel { struct context { - enum value_type - { - none, - outermost = (1 << 0), - tail = (1 << 1), - size, - } - const value; - - template - constexpr context(T const value) noexcept - : value { static_cast(value) } - {} - - constexpr operator value_type() const noexcept - { - return value; - } + bool is_tail = false; }; + + constexpr auto in_a_tail_context = context { true }; } // namespace kernel } // namespace meevax diff --git a/include/meevax/kernel/environment.hpp b/include/meevax/kernel/environment.hpp index f57e8ab41..70142d36d 100644 --- a/include/meevax/kernel/environment.hpp +++ b/include/meevax/kernel/environment.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -28,9 +29,10 @@ inline namespace kernel { class environment : public virtual pair , public configurator - , public machine - , public reader - , public writer + , public machine + , public optimizer + , public reader + , public writer { using pair::pair; @@ -55,11 +57,11 @@ inline namespace kernel { (import(xs), ...); - define("set-batch!", [this](let const& xs, auto&&...) { return batch = select(car(xs)); }); - define("set-debug!", [this](let const& xs, auto&&...) { return debug = select(car(xs)); }); - define("set-interactive!", [this](let const& xs, auto&&...) { return interactive = select(car(xs)); }); - define("set-trace!", [this](let const& xs, auto&&...) { return trace = select(car(xs)); }); - define("set-verbose!", [this](let const& xs, auto&&...) { return verbose = select(car(xs)); }); + // define("set-batch!", [this](let const& xs, auto&&...) { return batch = select(car(xs)); }); + // define("set-debug!", [this](let const& xs, auto&&...) { return debug = select(car(xs)); }); + // define("set-interactive!", [this](let const& xs, auto&&...) { return interactive = select(car(xs)); }); + // define("set-trace!", [this](let const& xs, auto&&...) { return trace = select(car(xs)); }); + // define("set-verbose!", [this](let const& xs, auto&&...) { return verbose = select(car(xs)); }); } auto operator [](const_reference) -> const_reference; diff --git a/include/meevax/kernel/interaction_environment.hpp b/include/meevax/kernel/interaction_environment.hpp new file mode 100644 index 000000000..cec72e3df --- /dev/null +++ b/include/meevax/kernel/interaction_environment.hpp @@ -0,0 +1,30 @@ +/* + Copyright 2018-2022 Tatsuya Yamasaki. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef INCLUDED_MEEVAX_KERNEL_INTERACTION_ENVIRONMENT_HPP +#define INCLUDED_MEEVAX_KERNEL_INTERACTION_ENVIRONMENT_HPP + +#include + +namespace meevax +{ +inline namespace kernel +{ + auto interaction_environment() -> const_reference; +} // namespace kernel +} // namespace meevax + +#endif // INCLUDED_MEEVAX_KERNEL_INTERACTION_ENVIRONMENT_HPP diff --git a/include/meevax/kernel/library.hpp b/include/meevax/kernel/library.hpp index 45a455887..5c2919261 100644 --- a/include/meevax/kernel/library.hpp +++ b/include/meevax/kernel/library.hpp @@ -24,8 +24,6 @@ namespace meevax { inline namespace kernel { - auto interaction_environment() -> const_reference; - struct library : public environment { std::vector export_specs; diff --git a/include/meevax/kernel/machine.hpp b/include/meevax/kernel/machine.hpp index 7543211f0..afddfdad7 100644 --- a/include/meevax/kernel/machine.hpp +++ b/include/meevax/kernel/machine.hpp @@ -23,22 +23,23 @@ #include #include #include -#include #include namespace meevax { inline namespace kernel { - template + template class machine // TR-SECD machine. { - friend environment; + friend Environment; machine() {} - IMPORT(environment, fork, const); + IMPORT(Environment, fork, const); + + using environment = Environment; protected: let s, // stack (holding intermediate results and return address) @@ -183,8 +184,8 @@ inline namespace kernel * * ---------------------------------------------------------------------- */ static auto compile( - context const current_context, - environment & current_environment, + context current_context, + environment & current_environment, const_reference current_expression, const_reference current_scope = unit, const_reference current_continuation = list(make(mnemonic::stop))) -> lvalue @@ -253,7 +254,7 @@ inline namespace kernel { assert(id.as().load().is_also()); - return compile(context::none, + return compile(current_context, current_environment, id.as().load().as().expand(current_expression, current_environment.fork(current_scope)), current_scope, @@ -269,7 +270,7 @@ inline namespace kernel } else if (applicant.is_also()) { - return compile(context::none, + return compile(current_context, current_environment, applicant.as().expand(current_expression, current_environment.fork(current_scope)), @@ -313,24 +314,24 @@ inline namespace kernel * * ------------------------------------------------------------------ */ { - return operand(context::none, + return operand(context(), current_environment, cdr(current_expression), current_scope, - compile(context::none, + compile(context(), current_environment, car(current_expression), current_scope, - cons(make(current_context & context::tail ? mnemonic::tail_call : mnemonic::call), + cons(make(current_context.is_tail ? mnemonic::tail_call : mnemonic::call), current_continuation))); } } - template + template inline auto execute() -> lvalue { decode: - if constexpr (Option & option::trace) + if constexpr (trace) { std::cerr << faint("; s = ") << s << "\n" << faint("; e = ") << e << "\n" @@ -338,6 +339,11 @@ inline namespace kernel << faint("; d = ") << d << "\n" << std::endl; } + if constexpr (profiler::count_instruction_fetch) + { + current_profiler().instruction_fetchs[car(c).template as()]++; + } + switch (car(c).template as()) { case mnemonic::load_absolute: /* ----------------------------------------- @@ -370,10 +376,6 @@ inline namespace kernel * * ------------------------------------------------------------------- */ s = cons(cadr(c).template as().load(e), s); - if (car(s).template is()) - { - std::cout << "; warning: " << cadr(c) << " is unbound." << std::endl; - } c = cddr(c); goto decode; @@ -467,28 +469,35 @@ inline namespace kernel * s e (%let-syntax . c) d => s e c' d * * ------------------------------------------------------------------- */ - [&]() + [this]() { - for (let const& keyword_ : car(cadr(c).template as().scope())) + auto [current_expression, current_scope] = unpair(cadr(c)); + + let const syntactic_environment = fork(cdr(current_scope)); + + let const c_ = c; + + for (let const& keyword_ : car(current_scope)) { let & binding = keyword_.as().load(); - let const& f = environment(static_cast(*this)).execute(binding); + c = binding; - binding = make(f, fork(cdr(cadr(c).template as().scope()))); + binding = make(execute(), syntactic_environment); } - }(); - - e = cons(unit, e); // dummy environment - std::swap(c.as(), - body(context::none, - static_cast(*this), - cadr(c).template as().expression(), - cadr(c).template as().scope(), - cddr(c) - ).template as()); + c = c_; + std::swap(c.as(), + compile(context(), + static_cast(*this), + cons(cons(make("lambda", lambda), + car(current_scope), // + current_expression), // + car(current_scope)), + cdr(current_scope), + cddr(c)).template as()); + }(); goto decode; case mnemonic::letrec_syntax: /* ----------------------------------------- @@ -496,90 +505,68 @@ inline namespace kernel * s e (%letrec-syntax . c) d => s e c' d * * ------------------------------------------------------------------- */ - [&]() // DIRTY HACK!!! + [this]() // DIRTY HACK!!! { - let const expander = fork(cadr(c).template as().scope()); + auto && [current_expression, current_scope] = unpair(cadr(c)); - auto const [transformer_specs, body] = unpair(cadr(c).template as().expression()); + auto && [transformer_specs, body] = unpair(current_expression); + + let const syntactic_environment = fork(current_scope); for (let const& transformer_spec : transformer_specs) { - expander.as().execute(compile(context::outermost, - expander.as(), - cons(make("define-syntax", define_syntax), transformer_spec), - cadr(c).template as().scope())); + let const c = compile(context(), + syntactic_environment.as(), + cons(make("define-syntax", define_syntax), transformer_spec), + current_scope); + + syntactic_environment.as().execute(c); } std::swap(c.as(), - machine::body(context::outermost, - expander.as(), - body, - cadr(c).template as().scope(), - cddr(c) - ).template as()); + compile(context(), + syntactic_environment.as(), + cons(cons(make("lambda", lambda), unit, body), unit), // (let () ) + current_scope, + cddr(c) + ).template as()); }(); goto decode; - case mnemonic::call: + case mnemonic::tail_call: if (let const& callee = car(s); callee.is()) /* --------------- * - * ( xs . s) e (%call . c) d => () (xs . e') c' (s e c . d) + * ( xs . s) e (%tail-call . c) d => () (xs . e') c' d * * where = (c' . e') * * ------------------------------------------------------------------- */ { - d = cons(cddr(s), e, cdr(c), d); c = callee.as().c(); e = cons(cadr(s), callee.as().e()); s = unit; + goto decode; } - else if (callee.is_also()) /* ------------------------------- - * - * ( xs . s) e (%call . c) d => (x . s) e c d - * - * where x = procedure(xs) - * - * ------------------------------------------------------------------- */ - { - s = cons(callee.as().call(cadr(s)), cddr(s)); - c = cdr(c); - } - else if (callee.is()) /* --------------------------------- - * - * ( xs . s) e (%call . c) d => (xs . s') e' c' d' - * - * where = (s' e' c' . 'd) - * - * ------------------------------------------------------------------- */ - { - s = cons(caadr(s), callee.as().s()); - e = callee.as().e(); - c = callee.as().c(); - d = callee.as().d(); - } - else - { - throw error(make("not applicable"), callee); - } - goto decode; + [[fallthrough]]; // This is inefficient because the type check occurs twice, but currently the performance difference caused by this is too small. - case mnemonic::tail_call: + case mnemonic::call: if (let const& callee = car(s); callee.is()) /* --------------- * - * ( xs . s) e (%tail-call . c) d => () (xs . e') c' d + * ( xs . s) e (%call . c) d => () (xs . e') c' (s e c . d) * * where = (c' . e') * * ------------------------------------------------------------------- */ { + d = cons(cddr(s), e, cdr(c), d); c = callee.as().c(); e = cons(cadr(s), callee.as().e()); s = unit; + goto decode; } else if (callee.is_also()) /* ------------------------------- * - * ( xs . s) e (%tail-call . c) d => (x . s) e c d + * ( xs . s) e (%call . c) d => (x . s) e c d * * where x = procedure(xs) * @@ -587,10 +574,11 @@ inline namespace kernel { s = cons(callee.as().call(cadr(s)), cddr(s)); c = cdr(c); + goto decode; } else if (callee.is()) /* --------------------------------- * - * ( xs . s) e (%tail-call . c) d => (xs . s') e' c' d' + * ( xs . s) e (%call . c) d => (xs . s') e' c' d' * * where = (s' e' c' . 'd) * @@ -600,12 +588,12 @@ inline namespace kernel e = callee.as().e(); c = callee.as().c(); d = callee.as().d(); + goto decode; } else { throw error(make("not applicable"), callee); } - goto decode; case mnemonic::dummy: /* ------------------------------------------------- * @@ -693,8 +681,13 @@ inline namespace kernel return [this]() { assert(cdr(s).template is()); + assert(cdr(c).template is()); + let const x = car(s); - s = unit; + + s = cdr(s); + c = cdr(c); + return x; }(); } @@ -734,6 +727,7 @@ inline namespace kernel return variable.is() ? variable.as().identify_with_offset(scope) : f; } + [[deprecated]] inline auto reset() -> void { s = unit; @@ -757,7 +751,7 @@ inline namespace kernel { let const& id = current_environment.identify(car(current_expression), current_scope); - return compile(context::none, + return compile(context(), current_environment, cadr(current_expression), current_scope, @@ -818,7 +812,7 @@ inline namespace kernel */ if (cdr(current_expression).is()) // is tail-sequence { - return compile(current_context | context::tail, + return compile(in_a_tail_context, current_environment, car(current_expression), current_scope, @@ -895,67 +889,60 @@ inline namespace kernel * * ----------------------------------------------------------------------- */ { - if (current_context & context::tail) + if (current_context.is_tail) { - auto consequent = - compile(context::tail, - current_environment, - cadr(current_expression), - current_scope, - list(make(mnemonic::return_))); - - auto alternate = - cddr(current_expression) - ? compile(context::tail, - current_environment, - caddr(current_expression), - current_scope, - list(make(mnemonic::return_))) - : list(make(mnemonic::load_constant), unspecified_object, - make(mnemonic::return_)); - - return compile(context::none, + assert(lexical_cast(current_continuation) == "(return)"); + + return compile(context(), current_environment, car(current_expression), // current_scope, - cons(make(mnemonic::tail_select), consequent, alternate, - cdr(current_continuation))); + list(make(mnemonic::tail_select), + compile(current_context, + current_environment, + cadr(current_expression), + current_scope, + current_continuation), + cddr(current_expression) + ? compile(current_context, + current_environment, + caddr(current_expression), + current_scope, + current_continuation) + : list(make(mnemonic::load_constant), unspecified_object, + make(mnemonic::return_)))); } else { - auto consequent = - compile(context::none, - current_environment, - cadr(current_expression), - current_scope, - list(make(mnemonic::join))); - - auto alternate = - cddr(current_expression) - ? compile(context::none, - current_environment, - caddr(current_expression), - current_scope, - list(make(mnemonic::join))) - : list(make(mnemonic::load_constant), unspecified_object, - make(mnemonic::join)); - - return compile(context::none, + return compile(context(), current_environment, car(current_expression), // current_scope, - cons(make(mnemonic::select), consequent, alternate, + cons(make(mnemonic::select), + compile(context(), + current_environment, + cadr(current_expression), + current_scope, + list(make(mnemonic::join))), + cddr(current_expression) + ? compile(context(), + current_environment, + caddr(current_expression), + current_scope, + list(make(mnemonic::join))) + : list(make(mnemonic::load_constant), unspecified_object, + make(mnemonic::join)), current_continuation)); } } static SYNTAX(cons_) { - return compile(context::none, + return compile(context(), current_environment, cadr(current_expression), current_scope, - compile(context::none, + compile(context(), current_environment, car(current_expression), current_scope, @@ -987,11 +974,11 @@ inline namespace kernel * * ----------------------------------------------------------------------- */ { - if (current_scope.is() or (current_context & context::outermost)) + if (current_scope.is()) { if (car(current_expression).is()) // (define (f . ) ) { - return compile(context::none, + return compile(context(), current_environment, cons(make("lambda", lambda), cdar(current_expression), cdr(current_expression)), current_scope, @@ -1000,7 +987,7 @@ inline namespace kernel } else // (define x ...) { - return compile(context::none, + return compile(context(), current_environment, cdr(current_expression) ? cadr(current_expression) : unspecified_object, current_scope, @@ -1068,7 +1055,7 @@ inline namespace kernel * * ----------------------------------------------------------------------- */ { - return compile(context::none, + return compile(context(), current_environment, cdr(current_expression) ? cadr(current_expression) : undefined, current_scope, @@ -1132,7 +1119,7 @@ inline namespace kernel auto make_keyword = [&](let const& binding) { return make(car(binding), - compile(context::outermost, + compile(context(), current_environment, cadr(binding), current_scope)); @@ -1213,11 +1200,11 @@ inline namespace kernel auto const& [variables, inits] = unzip2(car(current_expression)); return cons(make(mnemonic::dummy), - operand(context::none, + operand(context(), current_environment, inits, cons(variables, current_scope), - lambda(context::none, + lambda(current_context, current_environment, cons(variables, cdr(current_expression)), // ( ) current_scope, @@ -1268,11 +1255,11 @@ inline namespace kernel { if (current_expression.is()) { - return operand(context::none, + return operand(context(), current_environment, cdr(current_expression), current_scope, - compile(context::none, + compile(context(), current_environment, car(current_expression), current_scope, @@ -1281,7 +1268,7 @@ inline namespace kernel } else { - return compile(context::none, + return compile(context(), current_environment, current_expression, current_scope, @@ -1315,53 +1302,26 @@ inline namespace kernel * * ---------------------------------------------------------------------- */ { - if (current_context & context::outermost) + if (cdr(current_expression).is()) // is tail sequence { - if (cdr(current_expression).is()) - { - return compile(current_context, - current_environment, - car(current_expression), - current_scope, - current_continuation); - } - else - { - return compile(context::outermost, - current_environment, - car(current_expression), - current_scope, - cons(make(mnemonic::drop), - begin(context::outermost, - current_environment, - cdr(current_expression), - current_scope, - current_continuation))); - } + return compile(current_context, + current_environment, + car(current_expression), + current_scope, + current_continuation); } else { - if (cdr(current_expression).is()) // is tail sequence - { - return compile(current_context, - current_environment, - car(current_expression), - current_scope, - current_continuation); - } - else - { - return compile(context::none, - current_environment, - car(current_expression), // head expression - current_scope, - cons(make(mnemonic::drop), // pop result of head expression - begin(context::none, - current_environment, - cdr(current_expression), // rest expressions - current_scope, - current_continuation))); - } + return compile(context(), + current_environment, + car(current_expression), // head expression + current_scope, + cons(make(mnemonic::drop), // pop result of head expression + begin(current_context, + current_environment, + cdr(current_expression), // rest expressions + current_scope, + current_continuation))); } } }; diff --git a/include/meevax/kernel/miscellaneous.hpp b/include/meevax/kernel/miscellaneous.hpp index 34754bdb7..77c9cfad0 100644 --- a/include/meevax/kernel/miscellaneous.hpp +++ b/include/meevax/kernel/miscellaneous.hpp @@ -29,13 +29,6 @@ inline namespace kernel let extern const eof_object; auto operator <<(std::ostream &, eof const&) -> std::ostream &; - - struct eos - {}; - - let extern const eos_object; - - auto operator <<(std::ostream &, eos const&) -> std::ostream &; } // namespace kernel } // namespace meevax diff --git a/include/meevax/kernel/object.hpp b/include/meevax/kernel/object.hpp index 44dbfcc0a..958698de4 100644 --- a/include/meevax/kernel/object.hpp +++ b/include/meevax/kernel/object.hpp @@ -50,13 +50,13 @@ inline namespace kernel }; template - constexpr auto make(Ts&&... xs) + auto make(Ts&&... xs) { return lvalue::allocate(std::forward(xs)...); // NOTE: This leaks memory if exception thrown from T's constructor. } template - constexpr auto make(T&& x) + auto make(T&& x) { return lvalue::allocate::type>(std::forward(x)); } diff --git a/include/meevax/kernel/optimizer.hpp b/include/meevax/kernel/optimizer.hpp new file mode 100644 index 000000000..f10466acb --- /dev/null +++ b/include/meevax/kernel/optimizer.hpp @@ -0,0 +1,168 @@ +/* + Copyright 2018-2022 Tatsuya Yamasaki. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef INCLUDED_MEEVAX_KERNEL_OPTIMIZER_HPP +#define INCLUDED_MEEVAX_KERNEL_OPTIMIZER_HPP + +#include + +namespace meevax +{ +inline namespace kernel +{ + struct optimizer + { + static inline auto fmerge_constants = true; + + static auto merge_constants(const_reference c) -> lvalue + { + if (not c.is()) + { + return c; + } + else switch (car(c).template as()) + { + case mnemonic::call: + case mnemonic::cons: + case mnemonic::drop: + case mnemonic::dummy: + case mnemonic::join: + case mnemonic::letrec: + case mnemonic::return_: + case mnemonic::stop: + case mnemonic::tail_call: + return [&]() + { + if (let const& continuation = merge_constants(cdr(c)); continuation == cdr(c)) + { + return c; + } + else + { + return cons(car(c), continuation); + } + }(); + + case mnemonic::define: + case mnemonic::define_syntax: + case mnemonic::let_syntax: + case mnemonic::letrec_syntax: + case mnemonic::load_absolute: + case mnemonic::load_relative: + case mnemonic::load_variadic: + case mnemonic::store_absolute: + case mnemonic::store_relative: + case mnemonic::store_variadic: + return [&]() + { + if (let const& continuation = merge_constants(cddr(c)); continuation == cddr(c)) + { + return c; + } + else + { + return cons(car(c), cadr(c), continuation); + } + }(); + + case mnemonic::load_closure: + case mnemonic::load_continuation: + return [&]() + { + if (let const& branch = merge_constants(cadr(c)), + continuation = merge_constants(cddr(c)); + branch == cadr(c) and continuation == cddr(c)) + { + return c; + } + else + { + return cons(car(c), branch, continuation); + } + }(); + + case mnemonic::select: + case mnemonic::tail_select: + return [&]() + { + if (let const& consequent = merge_constants(cadr(c)), + alternate = merge_constants(caddr(c)), + continuation = merge_constants(cdddr(c)); + consequent == cadr(c) and alternate == caddr(c) and continuation == cdddr(c)) + { + return c; + } + else + { + return cons(car(c), consequent, alternate, continuation); + } + }(); + + + case mnemonic::load_constant: /* ----------------------------------------- + * + * (load-constant x + * load-constant y + * cons + * ...) + * + * => (load-constant (x . y) + * ...) + * + * --------------------------------------------------------------------- */ + if (5 <= length(c) and + list_ref(c, 0).is() and + list_ref(c, 0).as() == mnemonic::load_constant and + list_ref(c, 2).is() and + list_ref(c, 2).as() == mnemonic::load_constant and + list_ref(c, 4).is() and + list_ref(c, 4).as() == mnemonic::cons) + { + return merge_constants(cons(list_ref(c, 0), cons(list_ref(c, 3), + list_ref(c, 1)), + merge_constants(list_tail(c, 5)))); + } + else if (let const& continuation = merge_constants(cddr(c)); continuation == cddr(c)) + { + return c; + } + else + { + return cons(car(c), cadr(c), continuation); + } + + default: + assert(false); + return c; + } + } + + static auto optimize(const_reference c) + { + let code = c; + + if (fmerge_constants) + { + code = merge_constants(code); + } + + return code; + } + }; +} // namespace kernel +} // namespace meevax + +#endif // INCLUDED_MEEVAX_KERNEL_OPTIMIZER_HPP diff --git a/include/meevax/kernel/profiler.hpp b/include/meevax/kernel/profiler.hpp index e03e6a71f..f7a1f40f2 100644 --- a/include/meevax/kernel/profiler.hpp +++ b/include/meevax/kernel/profiler.hpp @@ -20,16 +20,21 @@ #include #include +#include + namespace meevax { inline namespace kernel { struct profiler { - static constexpr auto count_allocations = false; + static constexpr auto count_allocations = false; + static constexpr auto count_instruction_fetch = false; std::unordered_map allocation_counts; + std::unordered_map instruction_fetchs; + ~profiler(); }; diff --git a/include/meevax/kernel/syntax.hpp b/include/meevax/kernel/syntax.hpp index 11b99e8a3..d18713421 100644 --- a/include/meevax/kernel/syntax.hpp +++ b/include/meevax/kernel/syntax.hpp @@ -23,7 +23,7 @@ #define SYNTAX(NAME) \ auto NAME( \ - [[maybe_unused]] context const current_context, \ + [[maybe_unused]] context current_context, \ [[maybe_unused]] environment & current_environment, \ [[maybe_unused]] const_reference current_expression, \ [[maybe_unused]] const_reference current_scope, \ diff --git a/include/meevax/kernel/writer.hpp b/include/meevax/kernel/writer.hpp index 5baecdc38..ed1efa393 100644 --- a/include/meevax/kernel/writer.hpp +++ b/include/meevax/kernel/writer.hpp @@ -33,38 +33,38 @@ inline namespace kernel public: template - auto write(std::ostream & os, Ts&&... xs) const -> std::ostream & + static auto write(std::ostream & os, Ts&&... xs) -> std::ostream & { return (os << ... << xs); } template - auto write(const_reference x, Ts&&... xs) const -> decltype(auto) + static auto write(const_reference x, Ts&&... xs) -> decltype(auto) { return write(x.as(), std::forward(xs)...); } template - auto print(Ts&&... xs) const -> decltype(auto) + static auto print(Ts&&... xs) -> decltype(auto) { return write(standard_output, std::forward(xs)..., '\n'); } public: - auto null_port() const -> const_reference + static auto null_port() -> const_reference { let static port = make("/dev/null"); return port; } - auto verbose_port() const -> const_reference + static auto verbose_port() -> const_reference { - return static_cast(*this).verbose ? standard_output : null_port(); + return Environment::verbose ? standard_output : null_port(); } - auto debug_port() const -> const_reference + static auto debug_port() -> const_reference { - return static_cast(*this).debug ? standard_error : null_port(); + return Environment::debug ? standard_error : null_port(); } }; } // namespace kernel diff --git a/include/meevax/memory/nan_boxing_pointer.hpp b/include/meevax/memory/nan_boxing_pointer.hpp index e47a8f34d..99436d382 100644 --- a/include/meevax/memory/nan_boxing_pointer.hpp +++ b/include/meevax/memory/nan_boxing_pointer.hpp @@ -18,6 +18,7 @@ #define INCLUDED_MEEVAX_MEMORY_NAN_BOXING_POINTER_HPP #include +#include #include #include #include @@ -71,12 +72,12 @@ inline namespace memory auto operator =(nan_boxing_pointer const&) -> nan_boxing_pointer & = default; - constexpr nan_boxing_pointer(std::nullptr_t = nullptr) + nan_boxing_pointer(std::nullptr_t = nullptr) : nan_boxing_pointer { static_cast(nullptr) } {} #define DEFINE(TYPE) \ - constexpr nan_boxing_pointer(TYPE const& value) noexcept \ + nan_boxing_pointer(TYPE const& value) noexcept \ : data { reinterpret_cast( \ signature_##TYPE | bit_cast>(value)) } \ {} \ @@ -108,17 +109,17 @@ inline namespace memory #undef DEFINE - constexpr auto operator ->() const + auto operator ->() const { return get(); } - constexpr auto operator *() const -> decltype(auto) + auto operator *() const -> decltype(auto) { return *get(); } - constexpr explicit operator bool() const noexcept + explicit operator bool() const noexcept { return get() != nullptr; } @@ -138,17 +139,17 @@ inline namespace memory } } - constexpr auto dereferenceable() const noexcept + auto dereferenceable() const noexcept { return signature() == signature_pointer; } - constexpr auto equivalent_to(nan_boxing_pointer const& nbp) const noexcept + auto equivalent_to(nan_boxing_pointer const& nbp) const noexcept { return data == nbp.data; } - constexpr auto get() const noexcept -> pointer + auto get() const noexcept -> pointer { return dereferenceable() ? reinterpret_cast(reinterpret_cast(data) & mask_payload) : nullptr; } @@ -159,12 +160,12 @@ inline namespace memory return type() == typeid(typename std::decay::type); } - constexpr auto signature() const noexcept + auto signature() const noexcept { return reinterpret_cast(data) & mask_signature; } - constexpr auto type() const noexcept -> decltype(auto) + auto type() const noexcept -> decltype(auto) { switch (signature()) { @@ -193,7 +194,15 @@ inline namespace memory { #define DEFINE(TYPE) \ case signature_##TYPE: \ - return os << yellow(as()) + if constexpr (std::is_same_v) \ + { \ + return os << std::boolalpha << yellow('#', as()); \ + } \ + else \ + { \ + return os << yellow(as()); \ + } \ + static_assert(true) DEFINE(pointer); DEFINE(T_0b010); diff --git a/include/meevax/utility/module.hpp b/include/meevax/utility/module.hpp index 6df353ad8..fea3b9c6c 100644 --- a/include/meevax/utility/module.hpp +++ b/include/meevax/utility/module.hpp @@ -30,4 +30,12 @@ static_assert(true) #define EXPORT(M, SYMBOL) using M::SYMBOL +#define USING_STATIC(TYPE, FUNCTION) \ +template \ +static auto FUNCTION(Ts&&... xs) -> decltype(auto) \ +{ \ + return TYPE::FUNCTION(std::forward(xs)...); \ +} \ +static_assert(true) + #endif // INCLUDED_MEEVAX_UTILITY_MODULE_HPP diff --git a/src/kernel/boolean.cpp b/src/kernel/boolean.cpp index 31de59fdc..ca7a53f33 100644 --- a/src/kernel/boolean.cpp +++ b/src/kernel/boolean.cpp @@ -21,17 +21,12 @@ namespace meevax { inline namespace kernel { - auto operator <<(std::ostream & os, boolean const& datum) -> std::ostream & - { - return os << cyan("#", std::boolalpha, datum.value); - } - - let const t = make(true); - let const f = make(false); + let const t = make(true); + let const f = make(false); auto select(const_reference x) -> bool { - return not eq(x, f) or not eqv(x, f); + return not eq(x, f); } } // namespace kernel } // namespace meevax diff --git a/src/kernel/environment.cpp b/src/kernel/environment.cpp index 600e3fdf4..60b22e459 100644 --- a/src/kernel/environment.cpp +++ b/src/kernel/environment.cpp @@ -33,6 +33,8 @@ inline namespace kernel auto environment::apply(const_reference f, const_reference xs) -> lvalue { + assert(f.is() or f.is() or f.is()); + auto dump = std::make_tuple(std::exchange(s, list(f, xs)), std::exchange(e, unit), std::exchange(c, list(make(mnemonic::call), @@ -90,11 +92,6 @@ inline namespace kernel define(binding.as().symbol(), binding.as().load()); } - - if (interactive) - { - print(faint("; ", length(bindings), " identifiers imported.")); - } } auto environment::define(const_reference name, const_reference value) -> void @@ -129,10 +126,14 @@ inline namespace kernel } else { - auto dump = std::make_tuple(std::exchange(s, unit), - std::exchange(e, unit), - std::exchange(c, compile(context::none, *this, expression, scope())), - std::exchange(d, unit)); + assert(s.is()); + assert(e.is()); + assert(c.is()); + assert(d.is()); + + c = compile(context(), *this, expression, scope()); + + c = optimize(c); if (debug) { @@ -141,10 +142,10 @@ inline namespace kernel let const result = execute(); - s = std::get<0>(dump); - e = std::get<1>(dump); - c = std::get<2>(dump); - d = std::get<3>(dump); + assert(s.is()); + assert(e.is()); + assert(c.is()); + assert(d.is()); return result; } @@ -152,14 +153,7 @@ inline namespace kernel auto environment::execute() -> lvalue { - if (trace) - { - return machine::execute(); - } - else - { - return machine::execute(); - } + return trace ? machine::execute() : machine::execute(); } auto environment::execute(const_reference code) -> lvalue diff --git a/src/kernel/identity.cpp b/src/kernel/identity.cpp index a824472bf..fcde5f6e2 100644 --- a/src/kernel/identity.cpp +++ b/src/kernel/identity.cpp @@ -31,7 +31,7 @@ inline namespace kernel auto identity::symbol() const -> const_reference { - assert(first.is()); + assert(first.is_also()); return first; } diff --git a/include/meevax/kernel/option.hpp b/src/kernel/interaction_environment.cpp similarity index 60% rename from include/meevax/kernel/option.hpp rename to src/kernel/interaction_environment.cpp index a48b6a7d7..34a79151a 100644 --- a/include/meevax/kernel/option.hpp +++ b/src/kernel/interaction_environment.cpp @@ -14,34 +14,17 @@ limitations under the License. */ -#ifndef INCLUDED_MEEVAX_KERNEL_OPTION_HPP -#define INCLUDED_MEEVAX_KERNEL_OPTION_HPP +#include +#include namespace meevax { inline namespace kernel { - struct option + auto interaction_environment() -> const_reference { - enum value_type - { - none, - trace = (1 << 0), - size, - } - const value; - - template - constexpr option(T const value) noexcept - : value { static_cast(value) } - {} - - constexpr operator value_type() const noexcept - { - return value; - } - }; + let static const interaction_environment = make(); + return interaction_environment; + } } // namespace kernel } // namespace meevax - -#endif // INCLUDED_MEEVAX_KERNEL_OPTION_HPP diff --git a/src/kernel/library.cpp b/src/kernel/library.cpp index 5bc1a4e94..aff410047 100644 --- a/src/kernel/library.cpp +++ b/src/kernel/library.cpp @@ -15,18 +15,13 @@ */ #include +#include #include namespace meevax { inline namespace kernel { - auto interaction_environment() -> const_reference - { - let static const interaction_environment = make(); - return interaction_environment; - } - library::library(syntax_library_t) { define("begin", machine::begin); @@ -973,7 +968,7 @@ inline namespace kernel library::library(write_library_t) { - define("%write-simple", [this](let const& xs) + define("%write-simple", [](let const& xs) { write(cadr(xs), car(xs)); return unspecified_object; @@ -1084,7 +1079,7 @@ inline namespace kernel throw exit_status::success; case 1: - if (let const& x = car(xs); x.is()) + if (let const& x = car(xs); x.is()) { throw select(x) ? exit_status::success : exit_status::failure; } diff --git a/src/kernel/miscellaneous.cpp b/src/kernel/miscellaneous.cpp index e9f189474..8a59d763a 100644 --- a/src/kernel/miscellaneous.cpp +++ b/src/kernel/miscellaneous.cpp @@ -26,12 +26,5 @@ inline namespace kernel { return os << magenta("#,(") << green("eof-object") << magenta(")"); } - - let const eos_object = make(); - - auto operator <<(std::ostream & os, eos const&) -> std::ostream & - { - return os << magenta("#,(") << green("eos-object") << magenta(")"); - } } // namespace kernel } // namespace meevax diff --git a/src/kernel/profiler.cpp b/src/kernel/profiler.cpp index 0d53fda6d..5dc0c3e86 100644 --- a/src/kernel/profiler.cpp +++ b/src/kernel/profiler.cpp @@ -52,6 +52,22 @@ inline namespace kernel | echo \"TYPENAME\tALLOCATION COUNT\n$(cat -)\" \ | column -t -s'\t'"); } + + if (auto ss = std::stringstream(); not std::empty(instruction_fetchs)) + { + for (auto&& [mnemonic, count] : instruction_fetchs) + { + ss << mnemonic << "\t" << count << "\n"; + } + + sh("echo \"" + ss.str() + "\" | sed 's/meevax::kernel:://g' \ + | sort --field-separator='\t' \ + --key=2 \ + --numeric-sort \ + --reverse \ + | echo \"MNEMONIC\tFETCH COUNT\n$(cat -)\" \ + | column -t -s'\t'"); + } } auto current_profiler() -> profiler & diff --git a/src/kernel/syntax.cpp b/src/kernel/syntax.cpp index 2c3e9464b..6b9f7a763 100644 --- a/src/kernel/syntax.cpp +++ b/src/kernel/syntax.cpp @@ -22,7 +22,7 @@ inline namespace kernel { syntax::syntax(std::string const& name, function_type const& compile) : description { name } - , compile { compile } + , compile { compile } {} auto operator <<(std::ostream & os, syntax const& datum) -> std::ostream & diff --git a/src/main.cpp b/src/main.cpp index c065cde12..0aca95e72 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,7 @@ limitations under the License. */ +#include #include #include diff --git a/test/environment.cpp b/test/environment.cpp index ceb52a956..e37e87981 100644 --- a/test/environment.cpp +++ b/test/environment.cpp @@ -1,13 +1,14 @@ #undef NDEBUG #include +#include #include auto main() -> int { using namespace meevax; - const auto specials_count = 12; + const auto specials_count = 9; { assert(standard_error.is()); assert(standard_input.is()); @@ -15,9 +16,6 @@ auto main() -> int assert(e0.is()); assert(e1.is()); assert(eof_object.is()); - assert(eos_object.is()); - assert(f.is()); - assert(t.is()); assert(undefined.is()); assert(unspecified_object.is()); assert(interaction_environment().is()); diff --git a/test/transformer.ss b/test/transformer.ss index 0b8e0e827..0a68ee9e9 100644 --- a/test/transformer.ss +++ b/test/transformer.ss @@ -32,7 +32,7 @@ (check (cons x y) => (2 . 1)) -; ; ------------------------------------------------------------------------------ +; ------------------------------------------------------------------------------ (define-syntax swap! (sc-macro-transformer