/* * Copyright (C) 2023 Riyyi * * SPDX-License-Identifier: MIT */ #include // sd::advance, std::next, std::prev #include #include // std::static_pointer_cast #include // std::span #include #include "ast.h" #include "environment.h" #include "error.h" #include "eval.h" #include "ruc/meta/assert.h" #include "types.h" namespace blaze { Eval::Eval(ASTNodePtr ast, EnvironmentPtr env) : m_ast(ast) , m_env(env) { } void Eval::eval() { m_ast = evalImpl(m_ast, m_env); } ASTNodePtr Eval::evalImpl(ASTNodePtr ast, EnvironmentPtr env) { if (ast == nullptr || env == nullptr) { return nullptr; } if (!is(ast.get())) { return evalAst(ast, env); } auto list = std::static_pointer_cast(ast); if (list->empty()) { return ast; } // Environment auto nodes = list->nodes(); if (is(nodes.front().get())) { auto symbol = std::static_pointer_cast(nodes.front())->symbol(); nodes.pop_front(); if (symbol == "def!") { return evalDef(nodes, env); } if (symbol == "let*") { return evalLet(nodes, env); } if (symbol == "do") { return evalDo(nodes, env); } } // Function call return apply(std::static_pointer_cast(evalAst(ast, env))); } ASTNodePtr Eval::evalAst(ASTNodePtr ast, EnvironmentPtr env) { if (ast == nullptr || env == nullptr) { return nullptr; } ASTNode* ast_raw_ptr = ast.get(); if (is(ast_raw_ptr)) { auto result = env->get(std::static_pointer_cast(ast)->symbol()); if (!result) { Error::the().addError(format("'{}' not found", ast)); return nullptr; } return result; } else if (is(ast_raw_ptr)) { auto result = makePtr(); auto nodes = std::static_pointer_cast(ast)->nodes(); for (auto node : nodes) { ASTNodePtr eval_node = evalImpl(node, env); if (eval_node == nullptr) { return nullptr; } result->addNode(eval_node); } return result; } else if (is(ast_raw_ptr)) { auto result = makePtr(); auto nodes = std::static_pointer_cast(ast)->nodes(); for (auto node : nodes) { ASTNodePtr eval_node = evalImpl(node, env); if (eval_node == nullptr) { return nullptr; } result->addNode(eval_node); } return result; } else if (is(ast_raw_ptr)) { auto result = makePtr(); auto elements = std::static_pointer_cast(ast)->elements(); for (auto& element : elements) { ASTNodePtr element_node = evalImpl(element.second, env); if (element_node == nullptr) { return nullptr; } result->addElement(element.first, element_node); } return result; } return ast; } ASTNodePtr Eval::evalDef(const std::list& nodes, EnvironmentPtr env) { if (nodes.size() != 2) { Error::the().addError(format("wrong number of arguments: def!, {}", nodes.size())); return nullptr; } auto first_argument = *nodes.begin(); auto second_argument = *std::next(nodes.begin()); // First element needs to be a Symbol if (!is(first_argument.get())) { Error::the().addError(format("wrong type argument: symbol, {}", first_argument)); return nullptr; } std::string symbol = std::static_pointer_cast(first_argument)->symbol(); ASTNodePtr value = evalImpl(second_argument, env); // Dont overwrite symbols after an error if (Error::the().hasAnyError()) { return nullptr; } // Modify existing environment return env->set(symbol, value); } ASTNodePtr Eval::evalLet(const std::list& nodes, EnvironmentPtr env) { if (nodes.size() != 2) { Error::the().addError(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()) && !is(first_argument.get())) { Error::the().addError(format("wrong argument type: list, '{}'", first_argument)); return nullptr; } // Get the nodes out of the List or Vector std::list binding_nodes; if (is(first_argument.get())) { auto bindings = std::static_pointer_cast(first_argument); binding_nodes = bindings->nodes(); } else { 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().addError(format("wrong number of arguments: {}, {}", "let* bindings", count)); return nullptr; } // Create new environment auto let_env = makePtr(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().addError(format("wrong argument type: symbol, '{}'", *it)); return nullptr; } std::string key = std::static_pointer_cast(*it)->symbol(); ASTNodePtr value = evalImpl(*std::next(it), let_env); let_env->set(key, value); } // TODO: Remove limitation of 3 arguments // Eval all values in this new env, return last sexp of the result return evalImpl(second_argument, let_env); } ASTNodePtr Eval::evalDo(const std::list& nodes, EnvironmentPtr env) { if (nodes.size() == 0) { Error::the().addError(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) { evalImpl(*it, env); } // Eval and return last node return evalImpl(nodes.back(), env); } ASTNodePtr Eval::apply(std::shared_ptr evaluated_list) { if (evaluated_list == nullptr) { return nullptr; } auto nodes = evaluated_list->nodes(); if (!is(nodes.front().get())) { Error::the().addError(format("invalid function: {}", nodes.front())); return nullptr; } // car auto lambda = std::static_pointer_cast(nodes.front())->lambda(); // cdr nodes.pop_front(); return lambda(nodes); } } // namespace blaze