From 21f0d8fbd89cfee5f65c20a89f5606411ddd2b1f Mon Sep 17 00:00:00 2001 From: Riyyi Date: Mon, 3 Apr 2023 23:17:24 +0200 Subject: [PATCH] Eval: Move logic from defines back into functions --- src/eval.cpp | 206 ++++++++++++++++++++++++++------------------------- src/eval.h | 3 + 2 files changed, 107 insertions(+), 102 deletions(-) diff --git a/src/eval.cpp b/src/eval.cpp index 1d8de65..b01d3aa 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -27,105 +27,6 @@ Eval::Eval(ASTNodePtr ast, EnvironmentPtr env) // ----------------------------------------- -#define EVAL_LET(ast, nodes, env) \ - { \ - if (nodes.size() != 2) { \ - Error::the().add(format("wrong number of arguments: let*, {}", nodes.size())); \ - return nullptr; \ - } \ - \ - auto first_argument = *nodes.begin(); \ - auto second_argument = *std::next(nodes.begin()); \ - \ - /* First argument needs to be a List or Vector */ \ - if (!is(first_argument.get())) { \ - Error::the().add(format("wrong argument type: collection, '{}'", first_argument)); \ - return nullptr; \ - } \ - \ - /* Get the nodes out of the List or Vector */ \ - std::list binding_nodes; \ - auto bindings = std::static_pointer_cast(first_argument); \ - binding_nodes = bindings->nodes(); \ - \ - /* List or Vector needs to have an even number of elements */ \ - size_t count = binding_nodes.size(); \ - if (count % 2 != 0) { \ - Error::the().add(format("wrong number of arguments: {}, {}", "let* bindings", count)); \ - return nullptr; \ - } \ - \ - /* Create new environment */ \ - auto let_env = Environment::create(env); \ - \ - for (auto it = binding_nodes.begin(); it != binding_nodes.end(); std::advance(it, 2)) { \ - /* First element needs to be a Symbol */ \ - if (!is(*it->get())) { \ - Error::the().add(format("wrong argument type: symbol, '{}'", *it)); \ - return nullptr; \ - } \ - \ - std::string key = std::static_pointer_cast(*it)->symbol(); \ - m_ast_stack.push(*std::next(it)); \ - m_env_stack.push(let_env); \ - ASTNodePtr value = evalImpl(); \ - let_env->set(key, value); \ - } \ - \ - /* TODO: Remove limitation of 3 arguments */ \ - /* Eval all values in this new env, return last sexp of the result */ \ - m_ast_stack.push(second_argument); \ - m_env_stack.push(let_env); \ - continue; /* TCO */ \ - } - -#define EVAL_DO(ast, nodes, env) \ - { \ - if (nodes.size() == 0) { \ - Error::the().add(format("wrong number of arguments: do, {}", nodes.size())); \ - return nullptr; \ - } \ - \ - /* Evaluate all nodes except the last */ \ - for (auto it = nodes.begin(); it != std::prev(nodes.end(), 1); ++it) { \ - m_ast_stack.push(*it); \ - m_env_stack.push(env); \ - evalImpl(); \ - } \ - \ - /* Eval last node */ \ - m_ast_stack.push(nodes.back()); \ - m_env_stack.push(env); \ - continue; /* TCO */ \ - } - -#define EVAL_IF(ast, nodes, env) \ - { \ - if (nodes.size() != 2 && nodes.size() != 3) { \ - Error::the().add(format("wrong number of arguments: if, {}", nodes.size())); \ - return nullptr; \ - } \ - \ - auto first_argument = *nodes.begin(); \ - auto second_argument = *std::next(nodes.begin()); \ - auto third_argument = (nodes.size() == 3) ? *std::next(std::next(nodes.begin())) : makePtr(Value::Nil); \ - \ - m_ast_stack.push(first_argument); \ - m_env_stack.push(env); \ - auto first_evaluated = evalImpl(); \ - if (!is(first_evaluated.get()) \ - || std::static_pointer_cast(first_evaluated)->state() == Value::True) { \ - m_ast_stack.push(second_argument); \ - m_env_stack.push(env); \ - continue; /* TCO */ \ - } \ - else { \ - m_ast_stack.push(third_argument); \ - m_env_stack.push(env); \ - continue; /* TCO */ \ - } \ - } - void Eval::eval() { m_ast_stack = std::stack(); @@ -175,13 +76,16 @@ ASTNodePtr Eval::evalImpl() return evalDef(nodes, env); } if (symbol == "let*") { - EVAL_LET(ast, nodes, env); + evalLet(nodes, env); + continue; // TCO } if (symbol == "do") { - EVAL_DO(ast, nodes, env); + evalDo(nodes, env); + continue; // TCO } if (symbol == "if") { - EVAL_IF(ast, nodes, env); + evalIf(nodes, env); + continue; // TCO } if (symbol == "fn*") { return evalFn(nodes, env); @@ -291,6 +195,104 @@ ASTNodePtr Eval::evalDef(const std::list& nodes, EnvironmentPtr env) return env->set(symbol, value); } +void Eval::evalLet(const std::list& nodes, EnvironmentPtr env) +{ + if (nodes.size() != 2) { + Error::the().add(format("wrong number of arguments: let*, {}", nodes.size())); + return; + } + + auto first_argument = *nodes.begin(); + auto second_argument = *std::next(nodes.begin()); + + // First argument needs to be a List or Vector + if (!is(first_argument.get())) { + Error::the().add(format("wrong argument type: collection, '{}'", first_argument)); + return; + } + + // Get the nodes out of the List or Vector + std::list binding_nodes; + auto bindings = std::static_pointer_cast(first_argument); + binding_nodes = bindings->nodes(); + + // List or Vector needs to have an even number of elements + size_t count = binding_nodes.size(); + if (count % 2 != 0) { + Error::the().add(format("wrong number of arguments: {}, {}", "let* bindings", count)); + return; + } + + // Create new environment + auto let_env = Environment::create(env); + + for (auto it = binding_nodes.begin(); it != binding_nodes.end(); std::advance(it, 2)) { + // First element needs to be a Symbol + if (!is(*it->get())) { + Error::the().add(format("wrong argument type: symbol, '{}'", *it)); + return; + } + + std::string key = std::static_pointer_cast(*it)->symbol(); + m_ast_stack.push(*std::next(it)); + m_env_stack.push(let_env); + ASTNodePtr value = evalImpl(); + let_env->set(key, value); + } + + // TODO: Remove limitation of 3 arguments + // Eval all values in this new env, return last sexp of the result + m_ast_stack.push(second_argument); + m_env_stack.push(let_env); + return; // TCO +} + +void Eval::evalDo(const std::list& nodes, EnvironmentPtr env) +{ + if (nodes.size() == 0) { + Error::the().add(format("wrong number of arguments: do, {}", nodes.size())); + return; + } + + // Evaluate all nodes except the last + for (auto it = nodes.begin(); it != std::prev(nodes.end(), 1); ++it) { + m_ast_stack.push(*it); + m_env_stack.push(env); + evalImpl(); + } + + // Eval last node + m_ast_stack.push(nodes.back()); + m_env_stack.push(env); + return; // TCO +} + +void Eval::evalIf(const std::list& nodes, EnvironmentPtr env) +{ + if (nodes.size() != 2 && nodes.size() != 3) { + Error::the().add(format("wrong number of arguments: if, {}", nodes.size())); + return; + } + + auto first_argument = *nodes.begin(); + auto second_argument = *std::next(nodes.begin()); + auto third_argument = (nodes.size() == 3) ? *std::next(std::next(nodes.begin())) : makePtr(Value::Nil); + + m_ast_stack.push(first_argument); + m_env_stack.push(env); + auto first_evaluated = evalImpl(); + if (!is(first_evaluated.get()) + || std::static_pointer_cast(first_evaluated)->state() == Value::True) { + m_ast_stack.push(second_argument); + m_env_stack.push(env); + return; // TCO + } + + m_ast_stack.push(third_argument); + m_env_stack.push(env); + return; // TCO +} + #define ARG_COUNT_CHECK(name, comparison, size) \ if (comparison) { \ Error::the().add(format("wrong number of arguments: {}, {}", name, size)); \ diff --git a/src/eval.h b/src/eval.h index 873107b..2fd3838 100644 --- a/src/eval.h +++ b/src/eval.h @@ -29,6 +29,9 @@ private: ASTNodePtr evalImpl(); ASTNodePtr evalAst(ASTNodePtr ast, EnvironmentPtr env); ASTNodePtr evalDef(const std::list& nodes, EnvironmentPtr env); + void evalLet(const std::list& nodes, EnvironmentPtr env); + void evalDo(const std::list& nodes, EnvironmentPtr env); + void evalIf(const std::list& nodes, EnvironmentPtr env); ASTNodePtr evalFn(const std::list& nodes, EnvironmentPtr env); ASTNodePtr apply(std::shared_ptr evaluated_list);