From 4b7d57d812afb68b0aaf906149d12da71bf59e38 Mon Sep 17 00:00:00 2001 From: Hisashi Horikawa Date: Sat, 14 Sep 2024 21:50:17 +0900 Subject: [PATCH] fix macro. --- README.md | 18 ++++++++++----- environment.cpp | 3 ++- environment.h | 1 + evaluation.cpp | 48 +++++++++++++++++++++------------------ macros.cpp | 12 ++++++++++ test/Makefile | 2 +- test/evaluation_test.cpp | 7 ++++-- test/macro_test.cpp | 49 ++++++++++++++++++++++++++++++++-------- test/reader_test.cpp | 5 ++++ 9 files changed, 104 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 5b436fe..cb6f0a0 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,15 @@ C++17 - REPL Read-Eval-Print-Loop + ✅ GNU Readline はライセンスが GPLv3 なので使えない。libedit-devel を利用 + + `repl()`, `main()` - Reader, Print -- ファイルからの読み込み `READ()` 関数, S式の構築、画面への表示 `PRINT()` 関数. 文法をユーザが拡張できるので, bison/flex は使えない。手書き。 + ✅ Standard Macro Characters + + ✅ dotted pair notation + reader macro + Sharpsign (dispatching macro character) - `simple-vector` + + 複数行コメント - Environment + ✅ 変数定義. `SETQ` @@ -31,25 +34,28 @@ C++17 + ✅ 関数呼び出し. Function `FUNCALL` + ✅ atom の評価 + `lambda` 式からクロージャをつくる - + ユーザ定義関数 + + ユーザ関数の定義 `DEFUN` + ✅ Special operator `IF` + macro `DO` + ✅ Tail Call Optimization (TCO) - Files - - Quoting `QUOTE` + - ✅ Quoting `QUOTE` - Macros - + Backquote `quasiquote`, Comma `unquote`, `,@` `unquote-splicing` - + Function `MACROEXPAND` + + ✅ Backquote `quasiquote`, Comma `unquote`, `,@` `unquote-splicing` + + ✅ Function `MACROEXPAND-1`. マクロの入れ子は未了 + + ✅ マクロの評価 - Try (Conditions) + `tagbody` & 無条件ジャンプ `go` -- 実装しない - + `block` & `return-from` + + `block` & `return-from` -- lexical non-local exit facility + `catch` & `throw` -- ほかのプログラミング言語の例外処理とは異なる。実装しない + `handler-bind` or `handler-case` -- これが例外処理 - + + - 構造体 + - 若干のライブラリ + type `hash-table`. `(make-hash-table)` diff --git a/environment.cpp b/environment.cpp index 4b8f9e9..02eadcf 100644 --- a/environment.cpp +++ b/environment.cpp @@ -74,6 +74,7 @@ value_t Environment::find_value(const UnicodeString& symbol) throw std::invalid_argument(symbol.toUTF8String(u) ); } +// @return 見つからなかったときは nullptr FuncPtr Environment::find_function(const UnicodeString& symbol) { FuncMap::const_iterator it; @@ -88,7 +89,7 @@ FuncPtr Environment::find_function(const UnicodeString& symbol) return it->second; std::string u; - throw std::invalid_argument(symbol.toUTF8String(u) ); + return nullptr; // std::invalid_argument(symbol.toUTF8String(u) ); } diff --git a/environment.h b/environment.h index b9bf11b..b20b201 100644 --- a/environment.h +++ b/environment.h @@ -133,6 +133,7 @@ class Environment //: public RefCounted value_t find_value(const icu::UnicodeString& symbol); // ローカルと, global environment から探す + // @return 見つからなかったときは nullptr FuncPtr find_function(const icu::UnicodeString& symbol); private: diff --git a/evaluation.cpp b/evaluation.cpp index d1728d3..a48c1d0 100644 --- a/evaluation.cpp +++ b/evaluation.cpp @@ -266,13 +266,8 @@ static Trampoline do_function(std::shared_ptr form, EnvPtr env) } -// マクロの外側でも使える -Trampoline do_quasiquote(std::shared_ptr form, EnvPtr env) +static value_t do_quasiquote_sub(ListPtr tmpl, EnvPtr env) { - ListPtr tmpl = OBJECT_CAST(form->at(1)); - if (!tmpl || tmpl->empty() ) - return form->at(1); // シンボルもそのまま返せばよい - // `,x の形 tmpl = (unquote x) std::shared_ptr unq = starts_with(tmpl->at(0), "UNQUOTE"); // "," if (unq != nullptr) { @@ -305,8 +300,8 @@ Trampoline do_quasiquote(std::shared_ptr form, EnvPtr env) ret->append_range(lst); } else { - Trampoline r = do_quasiquote(sub, env); // 再帰 - ret->append(r.value); + value_t r = do_quasiquote_sub(sub, env); + ret->append(r); } } else @@ -315,9 +310,20 @@ Trampoline do_quasiquote(std::shared_ptr form, EnvPtr env) // `() => NIL if (ret->empty()) - return Trampoline(nilValue); + return nilValue; else - return Trampoline(ret); + return ret; +} + +// マクロの外側でも使える +Trampoline do_quasiquote(std::shared_ptr form, EnvPtr env) +{ + ListPtr tmpl = OBJECT_CAST(form->at(1)); + if (!tmpl || tmpl->empty() ) + return form->at(1); // シンボルもそのまま返せばよい + + value_t ret = do_quasiquote_sub(OBJECT_CAST(tmpl), env); + return ret; } @@ -368,16 +374,6 @@ static const SpecialForm specialForms[] = { }; -/** - * repeatedly expands form until it is no longer a macro form. - * @return 展開した AST を返す - */ -value_t macroExpand(const value_t& ast, EnvPtr env) { - // TODO: impl. とりあえずそのまま返す - return ast; -} - - static ListPtr eval_args(ListPtr args, EnvPtr env) { std::cout << __func__ << ": "; PRINT(args, std::cout); std::cout << "\n"; // DEBUG @@ -406,18 +402,26 @@ static ListPtr eval_args(ListPtr args, EnvPtr env) 4. invoke the most specific method. */ +extern value_t macroExpand(EnvPtr args); + value_t EVAL(value_t ast, EnvPtr env) { while (true) { - std::cout << "EVAL() loop: "; PRINT(ast, std::cout); std::cout << "\n"; // DEBUG + //std::cout << "EVAL() loop: "; PRINT(ast, std::cout); std::cout << "\n"; // DEBUG ListPtr list = OBJECT_CAST(ast); if (!list || list->empty() ) return eval_atom(ast, env); +#ifndef DISABLE_MACRO // CL: special form と同名の関数は禁止. - ast = macroExpand(ast, env); // 実引数を評価せずに渡す + EnvPtr exp_env = std::make_shared(); + exp_env->set_value("FORM", ast, false); + ast = macroExpand(exp_env); // 実引数を評価せずに渡す // TODO: 実引数の個数の事前検査? + exp_env.reset(); +#endif + list = OBJECT_CAST(ast); if ( !list || list->empty() ) return eval_atom(ast, env); diff --git a/macros.cpp b/macros.cpp index cd321d2..0fe5951 100644 --- a/macros.cpp +++ b/macros.cpp @@ -6,6 +6,7 @@ namespace my { extern value_t EVAL(value_t ast, EnvPtr env); extern value_t eval_atom(const value_t& atom, EnvPtr env); + // Macro DEFMACRO // defmacro name lambda-list [[declaration* | documentation]] form* // @@ -66,4 +67,15 @@ my::value_t macroExpand1(my::EnvPtr args) return tmpl; // 評価せず戻す } +/** + * repeatedly expands form until it is no longer a macro form. + * @return 展開した AST を返す + */ +value_t macroExpand(EnvPtr args) +{ + // TODO: impl. + return macroExpand1(args); +} + + } // namespace my diff --git a/test/Makefile b/test/Makefile index f45b68c..fa3fdb1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -46,7 +46,7 @@ environment_test: environment_test.o ../environment.o ../object_print.o ../reade macro_test: macro_test.o ../macros.o ../environment.o ../object_print.o ../reader.o ../evaluation.o $(CXX) $^ $(LDFLAGS) $(LDLIBS) -lstdc++ -licuuc -licuio -ledit -o $@ -evaluation_test: evaluation_test.o ../evaluation.o ../environment.o ../object_print.o ../reader.o +evaluation_test: evaluation_test.o ../evaluation.o ../environment.o ../object_print.o ../reader.o ../macros.o $(CXX) $^ $(LDFLAGS) $(LDLIBS) -lstdc++ -licuuc -licuio -ledit -o $@ diff --git a/test/evaluation_test.cpp b/test/evaluation_test.cpp index e0ac453..67ed132 100644 --- a/test/evaluation_test.cpp +++ b/test/evaluation_test.cpp @@ -47,10 +47,13 @@ my::value_t func2(my::EnvPtr args) { return x; } - +// * (defun times-elm (n xs) +// (mapcar (lambda (x) (* x n)) xs)) n, xs をレキシカルに外側へ探す +// * (times-elm 3 '(1 2 3)) +// (3 6 9) void test_lambda() { - // TODO: ● lambda expr のテスト + // lambda expr のテスト } int main() diff --git a/test/macro_test.cpp b/test/macro_test.cpp index a61b0cb..f4693e7 100644 --- a/test/macro_test.cpp +++ b/test/macro_test.cpp @@ -34,28 +34,59 @@ value_t do_multiply(EnvPtr args) } // namespace my -int main() +// quasiquote の呼び出し +// `(+ ,a (* ,b 3)) => (+ 10 (* 30 3)) +void test_quasiquote() { - define_function("MACROEXPAND-1", "(form)", my::macroExpand1); - define_function("+", "(x y)", my::do_add); - define_function("*", "(x y)", my::do_multiply); + my::EnvPtr env = std::make_shared(); - my::ListPtr list = my::VALUE_CAST_CHECKED(class my::list, my::read_from_string("`(+ ,a (* ,b 3))")); - my::define_macro("MAC1", "(a b)", list); + my::value_t form; + my::value_t res; + form = my::read_from_string("(setq a 10)"); + res = my::EVAL(form, env); + + form = my::read_from_string("(setq b 30)"); + res = my::EVAL(form, env); + + form = my::VALUE_CAST_CHECKED(class my::list, my::read_from_string("`(+ ,a (* ,b 3))")); + my::PRINT(form, std::cout); + res = my::EVAL(form, env); + my::PRINT(res, std::cout); +} + +void test_expand() +{ + std::cout << __func__ << " ======================================= \n"; my::EnvPtr env = std::make_shared(); - // まず、展開してみる + // 展開してみる my::value_t arg1 = my::read_from_string("(mac1 3 4)"); my::FuncPtr p = env->find_function("MACROEXPAND-1"); std::shared_ptr args = std::make_shared(); args->append(arg1); my::value_t result = p->apply(args); my::PRINT(result, std::cout); // => (+ 3 (* 4 3)) +} + +int main() +{ + define_function("MACROEXPAND-1", "(form)", my::macroExpand1); + define_function("+", "(x y)", my::do_add); + define_function("*", "(x y)", my::do_multiply); + + test_quasiquote(); + + my::ListPtr list = my::VALUE_CAST_CHECKED(class my::list, my::read_from_string("`(+ ,a (* ,b 3))")); + my::define_macro("MAC1", "(a b)", list); + + test_expand(); // 評価する - //result = my::EVAL(arg1, env); - //my::PRINT(result, std::cout); + my::EnvPtr env = std::make_shared(); + my::value_t arg1 = my::read_from_string("(mac1 3 4)"); + my::value_t result = my::EVAL(arg1, env); + my::PRINT(result, std::cout); return 0; } diff --git a/test/reader_test.cpp b/test/reader_test.cpp index e43b001..ac658f6 100644 --- a/test/reader_test.cpp +++ b/test/reader_test.cpp @@ -6,6 +6,11 @@ #include #include +// Multiline comments +void test_comment() +{ + // TODO: impl. +} int main(int argc, char** argv) {