Skip to content

Commit

Permalink
fix macro.
Browse files Browse the repository at this point in the history
  • Loading branch information
hhorikawa committed Sep 14, 2024
1 parent afbe43e commit 4b7d57d
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 41 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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)`

Expand Down
3 changes: 2 additions & 1 deletion environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) );
}


Expand Down
1 change: 1 addition & 0 deletions environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
48 changes: 26 additions & 22 deletions evaluation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,8 @@ static Trampoline do_function(std::shared_ptr<cons> form, EnvPtr env)
}


// マクロの外側でも使える
Trampoline do_quasiquote(std::shared_ptr<cons> form, EnvPtr env)
static value_t do_quasiquote_sub(ListPtr tmpl, EnvPtr env)
{
ListPtr tmpl = OBJECT_CAST<class list>(form->at(1));
if (!tmpl || tmpl->empty() )
return form->at(1); // シンボルもそのまま返せばよい

// `,x の形 tmpl = (unquote x)
std::shared_ptr<cons> unq = starts_with(tmpl->at(0), "UNQUOTE"); // ","
if (unq != nullptr) {
Expand Down Expand Up @@ -305,8 +300,8 @@ Trampoline do_quasiquote(std::shared_ptr<cons> 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
Expand All @@ -315,9 +310,20 @@ Trampoline do_quasiquote(std::shared_ptr<cons> form, EnvPtr env)

// `() => NIL
if (ret->empty())
return Trampoline(nilValue);
return nilValue;
else
return Trampoline(ret);
return ret;
}

// マクロの外側でも使える
Trampoline do_quasiquote(std::shared_ptr<cons> form, EnvPtr env)
{
ListPtr tmpl = OBJECT_CAST<class list>(form->at(1));
if (!tmpl || tmpl->empty() )
return form->at(1); // シンボルもそのまま返せばよい

value_t ret = do_quasiquote_sub(OBJECT_CAST<list>(tmpl), env);
return ret;
}


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<class list>(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<Environment>();
exp_env->set_value("FORM", ast, false);
ast = macroExpand(exp_env); // 実引数を評価せずに渡す
// TODO: 実引数の個数の事前検査?
exp_env.reset();
#endif

list = OBJECT_CAST<class list>(ast);
if ( !list || list->empty() )
return eval_atom(ast, env);
Expand Down
12 changes: 12 additions & 0 deletions macros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*
//
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 $@


Expand Down
7 changes: 5 additions & 2 deletions test/evaluation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
49 changes: 40 additions & 9 deletions test/macro_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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::Environment>();

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::Environment>();

// まず、展開してみる
// 展開してみる
my::value_t arg1 = my::read_from_string("(mac1 3 4)");
my::FuncPtr p = env->find_function("MACROEXPAND-1");
std::shared_ptr<my::cons> args = std::make_shared<my::cons>();
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::Environment>();
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;
}
5 changes: 5 additions & 0 deletions test/reader_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
#include <iostream>
#include <fstream>

// Multiline comments
void test_comment()
{
// TODO: impl.
}

int main(int argc, char** argv)
{
Expand Down

0 comments on commit 4b7d57d

Please sign in to comment.