diff --git a/README.md b/README.md index 5ba8b10..9516c06 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,40 @@ $ ./build.sh # example -![lispoo.png](./lispoo.png) +> lambda.lisp + +```lisp +(progn + (define abc (lambda (a b c) (+ a (+ b c)))) + (set! x (abc 1 2 3)) + (message x) + (progn (+ 1.2 3.9) (define plus (lambda (a b) (+ a b))) (message (plus 9 9))) + ) +``` ```sh $ ./lispoo example/lambda.lisp -(6)(18) +8 +18 +``` -$ ./lispoo example/prog.lisp -(3)(2)(1)(2)(1) +> quote.lisp + +```lisp +(progn + (define test (lambda (x) x)) + (if (test (quote (+ 1 -1))) + (message (quote true)) + (message (quote false)) + ) + (message (quote (+ 1 2))) + ) +``` +```sh $ ./lispoo example/quote.lisp -("false")((+ 1 2)) -``` \ No newline at end of file +false +(+ 1 2) +``` + +[More Examples](./example) diff --git a/core.h b/core.h index 3c942cc..1f9f2ff 100644 --- a/core.h +++ b/core.h @@ -4,9 +4,8 @@ namespace lispoo { -inline std::shared_ptr sum(const std::shared_ptr& args) { - assert_len(args, 2); - auto a = args->value()[0], b = args->value()[1]; +inline std::shared_ptr sum(const std::shared_ptr& a, + const std::shared_ptr& b) { if (a->type() == Type::Float && b->type() == Type::Float) { return std::make_shared(get_value(a) + get_value(b)); @@ -64,4 +63,105 @@ inline std::shared_ptr message(const std::shared_ptr& expr) { return nil; } +inline void init() { + // syntax + putenv("quote", + [](auto expr, auto env) { + assert_len(expr, 2); + auto value = get_value(expr); + return value[1]; + }); + putenv("define", + [](auto expr, auto env) { + assert_len(expr, 3); + auto value = get_value(expr); + assert_type(value[1]); + auto symbol = get_value(value[1]); + if (!is_nil(env->get(symbol))) { + oops("symbol defined: " + symbol); + } + env->put(symbol, eval(value[2], env)); + return nil; + }); + putenv("set!", + [](auto expr, auto env) { + assert_len(expr, 3); + auto value = get_value(expr); + assert_type(value[1]); + auto symbol = get_value(value[1]); + env->put(symbol, eval(value[2], env)); + return nil; + }); + putenv("progn", + [](auto expr, auto env) { + auto val = nil; + auto value = get_value(expr); + for (auto i = 1; i < value.size(); ++i) { + val = eval(value[i], env); + } + return val; + }); + putenv("if", + [](auto expr, auto env) { + // (if (cond) (then body) (else body)) + assert_len(expr, 4); + auto value = get_value(expr); + if (is_true(eval(value[1], env))) { + return eval(value[2], env); + } else { + return eval(value[3], env); + } + }); + putenv("while", + [](auto expr, auto env) { + // (while (cond) (loop body)) + assert_len(expr, 3); + auto value = get_value(expr); + while (is_true(eval(value[1], env))) { + eval(value[2], env); + } + return nil; + }); + putenv("lambda", + [](auto expr, auto env) { + // (lambda (args) (body)) + assert_len(expr, 3); + auto lambda = + [expr, parent = env](auto args, auto) { + auto value = get_value(expr); + auto symbols = get_value(value[1]); + // arguments bind + auto args_value = get_value(args); + if (symbols.size() != args_value.size() - 1) { + oops("arguments error"); + } + auto env = std::make_shared(parent); + for (auto i = 0; i < symbols.size(); ++i) { + auto symbol = get_value(symbols[i]); + env->put(symbol, eval(args_value[i + 1], parent)); + } + // eval body + return eval(value[2], env); + }; + return std::make_shared(std::move(lambda)); + }); + + // normal builtin function + putenv("+", + [](auto expr, auto env) { + assert_len(expr, 3); + auto value = get_value(expr); + return sum(eval(value[1], env), eval(value[2], env)); + }); + putenv("message", + [](auto expr, auto env) { + auto value = get_value(expr); + for (auto i = 1; i < value.size(); ++i) { + message(eval(value[i], env)); + std::cout << std::endl; + } + return nil; + }); +} + } // namespace lispoo diff --git a/example/lambda.lisp b/example/lambda.lisp index 30835d9..b898221 100644 --- a/example/lambda.lisp +++ b/example/lambda.lisp @@ -1,6 +1,6 @@ -(prog - (def abc (lambda (a b c) (+ a (+ b c)))) - (set! x (abc 1 2 3)) - (message x) - (prog (+ 1.2 3.9) (def plus (lambda (a b) (+ a b))) (message (plus 9 9))) - ) +(progn + (define abc (lambda (a b c) (+ a (+ b c)))) + (set! x (abc 1 2 3)) + (message x) + (progn (+ 1.2 3.9) (define plus (lambda (a b) (+ a b))) (message (plus 9 9))) + ) diff --git a/example/macro.lisp b/example/macro.lisp index 2cefa80..98552ce 100644 --- a/example/macro.lisp +++ b/example/macro.lisp @@ -1,5 +1,5 @@ -(prog - (message (quote (name args))) - (def echo message) - (echo 1) - ) +(progn + (message (quote (name args))) + (define echo message) + (echo 1) + ) diff --git a/example/prog.lisp b/example/prog.lisp index 848fd58..91ef6e2 100644 --- a/example/prog.lisp +++ b/example/prog.lisp @@ -1,6 +1,7 @@ -(prog - (set! i 3) - (while i (prog (message i) (set! i (+ i -1)))) - (prog (set! x 0) (message (if x 1 2))) - (prog (set! x 1) (message x)) - ) +(progn + (set! i 3) + (while i (progn (message i) (set! i (+ i -1)))) + (progn (set! x 0) (message (if x 1 2))) + (progn (set! x 1) (message x)) + (message (progn (+ 1 1) (+ 2 2))) + ) diff --git a/example/quote.lisp b/example/quote.lisp index 96a6700..982f723 100644 --- a/example/quote.lisp +++ b/example/quote.lisp @@ -1,8 +1,8 @@ -(prog - (def test (lambda (x) x)) - (if (test (quote (+ 1 -1))) - (message (quote true)) - (message (quote false)) - ) - (message (quote (+ 1 2))) - ) +(progn + (define test (lambda (x) x)) + (if (test (quote (+ 1 -1))) + (message (quote true)) + (message (quote false)) + ) + (message (quote (+ 1 2))) + ) diff --git a/example/sum.lisp b/example/sum.lisp index 99168c4..8b482b5 100644 --- a/example/sum.lisp +++ b/example/sum.lisp @@ -1,6 +1,6 @@ -(prog - (message (+ - (+ 2 1.1) - 1)) - (message (+ 1 2) (+ 1.1 1.1) (+ 2 1.1) (+ 1.9 -1)) - ) +(progn + (message (+ + (+ 2 1.1) + 1)) + (message (+ 1 2) (+ 1.1 1.1) (+ 2 1.1) (+ 1.9 -1)) + ) diff --git a/lispoo.cpp b/lispoo.cpp index 87030af..05ddcae 100644 --- a/lispoo.cpp +++ b/lispoo.cpp @@ -13,31 +13,12 @@ int main(int argc, char* argv[]) { lispoo::oops("can't open: " + std::string(argv[1])); } ss << ifs.rdbuf(); + + lispoo::init(); + unsigned long cursor = 0; std::vector tokens; lispoo::tokenize(ss.str(), tokens); - unsigned long cursor = 0; auto expr = lispoo::parse(tokens, cursor); - - lispoo::putenv("+", [](auto args) { - return lispoo::sum(args); - }); - lispoo::putenv("car", [](auto args) { - return args->value()[0]; - }); - lispoo::putenv("cdr", [](auto args) { - auto value = args->value(); - auto cdr = std::make_shared(); - cdr->value().assign(value.begin() + 1, value.end()); - return cdr; - }); - lispoo::putenv("message", [](auto args) { - auto value = lispoo::get_value(args); - for (auto& v : value) { - lispoo::message(v); - std::cout << std::endl; - } - return lispoo::nil; - }); lispoo::eval(expr, lispoo::global); return 0; } diff --git a/lispoo.h b/lispoo.h index ae44a3f..d7334fb 100644 --- a/lispoo.h +++ b/lispoo.h @@ -77,9 +77,11 @@ private: std::vector> value_; }; +class Env; class Callable: public Expr { public: - using Fn = std::function(const std::shared_ptr&)>; + using Fn = std::function(const std::shared_ptr&, + const std::shared_ptr& env)>; explicit Callable(Fn&& lambda) : lambda_(lambda) {} Type type() override { return Type::Callable; } @@ -248,70 +250,6 @@ inline std::shared_ptr eval(const std::shared_ptr& expr, const std:: auto value = get_value(expr); assert_type(value[0]); auto name = get_value(value[0]); - if (name == "quote") { - assert_len(expr, 2); - return value[1]; - } - if (name == "def") { - assert_len(expr, 3); - assert_type(value[1]); - auto symbol = get_value(value[1]); - if (!is_nil(env->get(symbol))) { - oops("symbol defined: " + symbol); - } - env->put(symbol, eval(value[2], env)); - return nil; - } - if (name == "set!") { - assert_len(expr, 3); - assert_type(value[1]); - auto symbol = get_value(value[1]); - env->put(symbol, eval(value[2], env)); - return nil; - } - if (name == "prog") { - for (auto i = 1; i < value.size(); ++i) { - eval(value[i], env); - } - return nil; - } - if (name == "if") { - // (if (cond) (then body) (else body)) - assert_len(expr, 4); - if (is_true(eval(value[1], env))) { - return eval(value[2], env); - } else { - return eval(value[3], env); - } - } - if (name == "while") { - // (while (cond) (loop body)) - assert_len(expr, 3); - while (is_true(eval(value[1], env))) { - eval(value[2], env); - } - return nil; - } - if (name == "lambda") { - // (lambda (args) (body)) - assert_len(expr, 3); - auto lambda = [expr, parent = env](const std::shared_ptr& args) { - auto value = get_value(expr); - auto symbols = get_value(value[1]); - // arguments bind - if (symbols.size() != args->value().size()) { - oops("arguments error"); - } - auto env = std::make_shared(parent); - for (auto i = 0; i < symbols.size(); ++i) { - auto symbol = get_value(symbols[i]); - env->put(symbol, args->value()[i]); - } - // eval body - return eval(value[2], env); - }; - return std::make_shared(std::move(lambda)); - } // function/lambda call // (symbol arg1 arg2 arg3 ... ) auto callable = env->get(name); @@ -321,11 +259,7 @@ inline std::shared_ptr eval(const std::shared_ptr& expr, const std:: if (!is_type(callable)) { oops("can't call symbol: " + name); } - auto args = std::make_shared(); - for (auto i = 1; i < value.size(); ++i) { - args->append(eval(value[i], env)); - } - return get_value(callable)(args); + return get_value(callable)(expr, env); } } // namespace lispoo diff --git a/lispoo.png b/lispoo.png deleted file mode 100644 index 255c2aa..0000000 Binary files a/lispoo.png and /dev/null differ