From 4b400d61469fb42a1372ae358aac5341d5ed312d Mon Sep 17 00:00:00 2001 From: Rain Mark Date: Mon, 21 Feb 2022 21:34:14 +0800 Subject: [PATCH] add some utils func Signed-off-by: Rain Mark --- core.h | 30 ++++----- example/macro.lisp | 5 ++ example/quote.lisp | 4 +- lispoo.cpp | 24 ++++++- lispoo.h | 163 ++++++++++++++++++++++++++------------------- 5 files changed, 133 insertions(+), 93 deletions(-) create mode 100644 example/macro.lisp diff --git a/core.h b/core.h index 2c52091..3c942cc 100644 --- a/core.h +++ b/core.h @@ -5,32 +5,26 @@ namespace lispoo { inline std::shared_ptr sum(const std::shared_ptr& args) { - len_eq(args, 2); + assert_len(args, 2); auto a = args->value()[0], b = args->value()[1]; if (a->type() == Type::Float && b->type() == Type::Float) { - return std::make_shared(std::static_pointer_cast(a)->value() + - std::static_pointer_cast(b)->value()); + return std::make_shared(get_value(a) + get_value(b)); } if (a->type() == Type::Integer && b->type() == Type::Integer) { - return std::make_shared(std::static_pointer_cast(a)->value() + - std::static_pointer_cast(b)->value()); + return std::make_shared(get_value(a) + get_value(b)); } if (a->type() == Type::Float) { - return std::make_shared(std::static_pointer_cast(a)->value() + - std::static_pointer_cast(b)->value()); + return std::make_shared(get_value(a) + get_value(b)); } if (b->type() == Type::Float) { - return std::make_shared(std::static_pointer_cast(a)->value() + - std::static_pointer_cast(b)->value()); + return std::make_shared(get_value(a) + get_value(b)); } + return nil; } inline std::shared_ptr message(const std::shared_ptr& expr) { - if (!expr) { - return null_expr; - } auto type = expr->type(); switch (type) { case Type::Null: { @@ -38,23 +32,23 @@ inline std::shared_ptr message(const std::shared_ptr& expr) { break; } case Type::Integer: { - std::cout << std::static_pointer_cast(expr)->value(); + std::cout << get_value(expr); break; } case Type::Float: { - std::cout << std::static_pointer_cast(expr)->value(); + std::cout << get_value(expr); break; } case Type::Symbol: { - std::cout << std::static_pointer_cast(expr)->value(); + std::cout << get_value(expr); break; } case Type::Callable: { - std::cout << ": " << expr.get(); + std::cout << ": " << expr.get(); break; } case Type::List: { - auto& value = std::static_pointer_cast(expr)->value(); + auto value = get_value(expr); std::cout << "("; for (auto i = 0; i < value.size(); ++i) { if (i > 0) { @@ -67,7 +61,7 @@ inline std::shared_ptr message(const std::shared_ptr& expr) { default: break; } - return null_expr; + return nil; } } // namespace lispoo diff --git a/example/macro.lisp b/example/macro.lisp new file mode 100644 index 0000000..2cefa80 --- /dev/null +++ b/example/macro.lisp @@ -0,0 +1,5 @@ +(prog + (message (quote (name args))) + (def echo message) + (echo 1) + ) diff --git a/example/quote.lisp b/example/quote.lisp index 4037143..96a6700 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 true)) + (message (quote false)) ) (message (quote (+ 1 2))) ) diff --git a/lispoo.cpp b/lispoo.cpp index 7149b31..87030af 100644 --- a/lispoo.cpp +++ b/lispoo.cpp @@ -5,7 +5,7 @@ int main(int argc, char* argv[]) { if (argc != 2) { - lispoo::oops("lispoo ./xxx.lisp"); + lispoo::oops("error: no input files"); } std::ifstream ifs(argv[1]); std::stringstream ss; @@ -18,8 +18,26 @@ int main(int argc, char* argv[]) { unsigned long cursor = 0; auto expr = lispoo::parse(tokens, cursor); - lispoo::register_symbol("+", [](auto args) { return lispoo::sum(args); }); - lispoo::register_symbol("message", [](auto args) { return lispoo::message(args); }); + 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 28078ad..ae44a3f 100644 --- a/lispoo.h +++ b/lispoo.h @@ -13,7 +13,7 @@ namespace lispoo { inline void oops(const std::string& err) { std::cerr << err << std::endl; - exit(-1); + std::exit(EXIT_FAILURE); } enum class Type { @@ -65,12 +65,12 @@ class Null: public Expr { public: Type type() override { return Type::Null; } }; -static const std::shared_ptr null_expr = std::make_shared(); +static const std::shared_ptr nil = std::make_shared(); class List: public Expr { public: Type type() override { return Type::List; } - const std::vector>& value() const { return value_; } + std::vector>& value() { return value_; } void append(const std::shared_ptr& expr) { value_.emplace_back(expr); } private: @@ -91,66 +91,87 @@ private: Fn lambda_; }; +// environment + class Env { public: Env(const std::shared_ptr& env) : env_(env) {} - std::shared_ptr get(const std::shared_ptr& symbol) const { - // std::cout << "get: " << symbol->value() << std::endl; - auto it = expr_map_.find(symbol->value()); + std::shared_ptr get(const std::string& symbol) const { + auto it = expr_map_.find(symbol); if (it != expr_map_.end()) { return it->second; } if (env_) { return env_->get(symbol); } - return null_expr; + return nil; } - void put(const std::shared_ptr& symbol, const std::shared_ptr& expr) { - // std::cout << "put: " << symbol->value() << std::endl; - expr_map_[symbol->value()] = expr; + void put(const std::string& symbol, const std::shared_ptr& expr) { + expr_map_[symbol] = expr; } private: std::unordered_map> expr_map_; std::shared_ptr env_; }; - static std::shared_ptr global = std::make_shared(std::shared_ptr()); - -inline void register_symbol(const std::string& symbol, Callable::Fn&& lambda) { - global->put(std::make_shared(symbol), std::make_shared(std::forward(lambda))); +inline void putenv(const std::string& symbol, Callable::Fn&& lambda) { + global->put(symbol, std::make_shared(std::forward(lambda))); } +// type utils + inline bool is_par(char ch) { return ch == '(' || ch == ')'; } -inline bool is_number(const Type& type) { - return type == Type::Integer || type == Type::Float; +template +inline decltype(auto) get_value(const std::shared_ptr& expr) { + return std::static_pointer_cast(expr)->value(); } -inline bool is_atom(const Type& type) { - return type < Type::Atom; +template +inline bool is_type(const std::shared_ptr& expr) { + return expr && expr->type() == t; +} +inline bool is_nil(const std::shared_ptr& expr) { + return is_type(expr); +} +inline bool is_number(const std::shared_ptr& expr) { + return is_type(expr) || is_type(expr); +} +inline bool is_symbol(const std::shared_ptr& expr) { + return is_type(expr); } inline bool is_true(const std::shared_ptr& expr) { - if (!is_number(expr->type())) { + if (!is_number(expr)) { return false; } - if (expr->type() == Type::Integer) { - return std::static_pointer_cast(expr)->value(); + if (is_type(expr)) { + return get_value(expr); } - return std::static_pointer_cast(expr)->value() != 0.0; + return get_value(expr); } -inline void len_eq(const std::shared_ptr& expr, unsigned long expect) { - if (expr->type() != Type::List) { - oops("len_eq() can't check non List type"); + +// asserts + +template +inline void assert_type(const std::shared_ptr& expr) { + if (!is_type(expr)) { + oops("syntax error"); + } +} +inline void assert_len(const std::shared_ptr& expr, unsigned long expect) { + if (!is_type(expr)) { + oops("assert_len() failed, not List type"); } - auto list = std::static_pointer_cast(expr); - if (list->value().size() != expect) { - auto symbol = std::static_pointer_cast(list->value()[0]); - oops("symbol: " + symbol->value() + " length not eq: " + std::to_string(expect)); + auto value = get_value(expr); + if (value.size() != expect) { + oops("assert_len() failed, expect: " + std::to_string(expect)); } } +// parse & evaluate + inline void tokenize(const std::string& str, std::vector& tokens) { for (auto i = 0; i < str.size(); ++i) { if (std::isspace(str[i])) { @@ -186,21 +207,22 @@ inline std::shared_ptr parse_atom(const std::vector& tokens, } } switch (type) { - case Type::Symbol: - return std::make_shared(token); - case Type::Integer: - return std::make_shared(std::stol(token)); - case Type::Float: - return std::make_shared(std::stod(token)); + case Type::Symbol: + return std::make_shared(token); + case Type::Integer: + return std::make_shared(std::stol(token)); + case Type::Float: + return std::make_shared(std::stod(token)); + default: + break; } - return std::shared_ptr(); + return nil; } inline std::shared_ptr parse(const std::vector& tokens, unsigned long& cursor) { if (cursor >= tokens.size()) { oops("parse error"); } - // std::cout << "parse: " << tokens[cursor] << std::endl; if (tokens[cursor] == "(") { auto list = std::make_shared(); while (tokens[++cursor] != ")") { @@ -214,49 +236,48 @@ inline std::shared_ptr parse(const std::vector& tokens, unsig inline std::shared_ptr eval(const std::shared_ptr& expr, const std::shared_ptr& env) { if (!expr) { oops("syntax error"); - return null_expr; + return nil; } - auto type = expr->type(); - if (is_number(type)) { + if (is_number(expr)) { return expr; } - if (type == Type::Symbol) { - return env->get(std::static_pointer_cast(expr)); - } - if (type != Type::List) { - oops("syntax error"); + if (is_symbol(expr)) { + return env->get(get_value(expr)); } - auto& value = std::static_pointer_cast(expr)->value(); - std::shared_ptr _ = std::static_pointer_cast(value[0]); - auto& name = _->value(); + assert_type(expr); + auto value = get_value(expr); + assert_type(value[0]); + auto name = get_value(value[0]); if (name == "quote") { - len_eq(expr, 2); + assert_len(expr, 2); return value[1]; } if (name == "def") { - len_eq(expr, 3); - auto symbol = std::static_pointer_cast(value[1]); - if (env->get(symbol)->type() != Type::Null) { - oops("symbol defined: " + symbol->value()); + 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 null_expr; + return nil; } if (name == "set!") { - len_eq(expr, 3); - auto symbol = std::static_pointer_cast(value[1]); + assert_len(expr, 3); + assert_type(value[1]); + auto symbol = get_value(value[1]); env->put(symbol, eval(value[2], env)); - return null_expr; + return nil; } if (name == "prog") { for (auto i = 1; i < value.size(); ++i) { eval(value[i], env); } - return null_expr; + return nil; } if (name == "if") { // (if (cond) (then body) (else body)) - len_eq(expr, 4); + assert_len(expr, 4); if (is_true(eval(value[1], env))) { return eval(value[2], env); } else { @@ -265,23 +286,25 @@ inline std::shared_ptr eval(const std::shared_ptr& expr, const std:: } if (name == "while") { // (while (cond) (loop body)) - len_eq(expr, 3); + assert_len(expr, 3); while (is_true(eval(value[1], env))) { eval(value[2], env); } - return null_expr; + return nil; } if (name == "lambda") { // (lambda (args) (body)) - len_eq(expr, 3); + assert_len(expr, 3); auto lambda = [expr, parent = env](const std::shared_ptr& args) { - auto& value = std::static_pointer_cast(expr)->value(); - auto symbols = std::static_pointer_cast(value[1]); + auto value = get_value(expr); + auto symbols = get_value(value[1]); // arguments bind - len_eq(symbols, args->value().size()); + if (symbols.size() != args->value().size()) { + oops("arguments error"); + } auto env = std::make_shared(parent); - for (auto i = 0; i < symbols->value().size(); ++i) { - auto symbol = std::static_pointer_cast(symbols->value()[i]); + for (auto i = 0; i < symbols.size(); ++i) { + auto symbol = get_value(symbols[i]); env->put(symbol, args->value()[i]); } // eval body @@ -291,18 +314,18 @@ inline std::shared_ptr eval(const std::shared_ptr& expr, const std:: } // function/lambda call // (symbol arg1 arg2 arg3 ... ) - auto callable = env->get(_); + auto callable = env->get(name); if (!callable) { oops("unknow symbol: " + name); } - if (callable->type() != Type::Callable) { + 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 std::static_pointer_cast(callable)->value()(args); + return get_value(callable)(args); } } // namespace lispoo