#include "interpreter.hpp" #include "parser.hpp" #include using namespace peglib; using namespace std; struct Eval { static Value eval(const Ast& ast, shared_ptr env) { switch (ast.tag) { case Statements: return eval_statements(ast, env); case While: return eval_while(ast, env); case If: return eval_if(ast, env); case Function: return eval_function(ast, env); case FunctionCall: return eval_function_call(ast, env); case Assignment: return eval_assignment(ast, env); case LogicalOr: return eval_logical_or(ast, env); case LogicalAnd: return eval_logical_and(ast, env); case Condition: return eval_condition(ast, env); case UnaryPlus: return eval_unary_plus(ast, env); case UnaryMinus: return eval_unary_minus(ast, env); case UnaryNot: return eval_unary_not(ast, env); case BinExpresion: return eval_bin_expression(ast, env); case Identifier: return eval_identifier(ast, env); case Number: return eval_number(ast, env); case Boolean: return eval_bool(ast, env); case InterpolatedString: return eval_interpolated_string(ast, env); } if (ast.is_token) { return Value(string(ast.token)); } // NOTREACHED throw logic_error("invalid Ast type"); } private: static Value eval_statements(const Ast& ast, shared_ptr env) { if (ast.is_token) { return eval(ast, env); } else if (ast.nodes.empty()) { return Value(); } auto it = ast.nodes.begin(); while (it != ast.nodes.end() - 1) { eval(**it, env); ++it; } return eval(**it, env); } static Value eval_while(const Ast& ast, shared_ptr env) { for (;;) { auto cond = eval(*ast.nodes[0], env); if (!cond.to_bool()) { break; } eval(*ast.nodes[1], env); } return Value(); } static Value eval_if(const Ast& ast, shared_ptr env) { const auto& nodes = ast.nodes; for (auto i = 0u; i < nodes.size(); i += 2) { if (i + 1 == nodes.size()) { return eval(*nodes[i], env); } else { auto cond = eval(*nodes[i], env); if (cond.to_bool()) { return eval(*nodes[i + 1], env); } } } return Value(); } static Value eval_function(const Ast& ast, shared_ptr env) { vector params; for (auto node: ast.nodes[0]->nodes) { auto mut = node->nodes[0]->token == "mut"; const auto& name = node->nodes[1]->token; params.push_back({ name, mut }); } auto body = ast.nodes[1]; return Value(Value::FunctionValue { params, [=](shared_ptr callEnv) { callEnv->set_outer(env); return eval(*body, callEnv); } }); }; static Value eval_function_call(const Ast& ast, shared_ptr env) { const auto& var = ast.nodes[0]->token; const auto& args = ast.nodes[1]->nodes; const auto& f = env->get(var); const auto& fv = f.to_function(); if (fv.params.size() <= args.size()) { auto callEnv = make_shared(); callEnv->initialize("self", f, false); for (auto i = 0u; i < fv.params.size(); i++) { auto param = fv.params[i]; auto arg = args[i]; auto val = eval(*arg, env); callEnv->initialize(param.name, val, param.mut); } return fv.eval(callEnv); } string msg = "arguments error in '" + var + "'..."; throw runtime_error(msg); } static Value eval_logical_or(const Ast& ast, shared_ptr env) { if (ast.nodes.size() == 1) { return eval(*ast.nodes[0], env); } else { Value ret; for (auto node: ast.nodes) { ret = eval(*node, env); if (ret.to_bool()) { return ret; } } return ret; } } static Value eval_logical_and(const Ast& ast, shared_ptr env) { Value ret; for (auto node: ast.nodes) { ret = eval(*node, env); if (!ret.to_bool()) { return ret; } } return ret; } static Value eval_condition(const Ast& ast, shared_ptr env) { if (ast.nodes.size() == 1) { return eval(*ast.nodes[0], env); } else { auto lhs = eval(*ast.nodes[0], env); auto ope = eval(*ast.nodes[1], env).to_string(); auto rhs = eval(*ast.nodes[2], env); if (ope == "==") { return Value(lhs == rhs); } else if (ope == "!=") { return Value(lhs != rhs); } else if (ope == "<=") { return Value(lhs <= rhs); } else if (ope == "<") { return Value(lhs < rhs); } else if (ope == ">=") { return Value(lhs >= rhs); } else if (ope == ">") { return Value(lhs > rhs); } else { throw std::logic_error("invalid internal condition."); } } } static Value eval_unary_plus(const Ast& ast, shared_ptr env) { if (ast.nodes.size() == 1) { return eval(*ast.nodes[0], env); } else { return eval(*ast.nodes[1], env); } } static Value eval_unary_minus(const Ast& ast, shared_ptr env) { if (ast.nodes.size() == 1) { return eval(*ast.nodes[0], env); } else { return Value(eval(*ast.nodes[1], env).to_long() * -1); } } static Value eval_unary_not(const Ast& ast, shared_ptr env) { if (ast.nodes.size() == 1) { return eval(*ast.nodes[0], env); } else { return Value(!eval(*ast.nodes[1], env).to_bool()); } } static Value eval_bin_expression(const Ast& ast, shared_ptr env) { auto ret = eval(*ast.nodes[0], env).to_long(); for (auto i = 1u; i < ast.nodes.size(); i += 2) { auto val = eval(*ast.nodes[i + 1], env).to_long(); auto ope = eval(*ast.nodes[i], env).to_string()[0]; switch (ope) { case '+': ret += val; break; case '-': ret -= val; break; case '*': ret *= val; break; case '/': ret /= val; break; case '%': ret %= val; break; } } return Value(ret); } static Value eval_assignment(const Ast& ast, shared_ptr env) { const auto& mut = ast.nodes[0]->token; const auto& var = ast.nodes[1]->token; auto val = eval(*ast.nodes[2], env); if (env->has(var)) { env->assign(var, val); } else { env->initialize(var, val, mut == "mut"); } return val; }; static Value eval_identifier(const Ast& ast, shared_ptr env) { const auto& var = ast.token; return env->get(var); }; static Value eval_number(const Ast& ast, shared_ptr env) { return Value(stol(ast.token)); }; static Value eval_bool(const Ast& ast, shared_ptr env) { return Value(ast.token == "true"); }; static Value eval_interpolated_string(const Ast& ast, shared_ptr env) { string s; for (auto node: ast.nodes) { const auto& val = eval(*node, env); s += val.str(); } return Value(std::move(s)); }; }; bool run(const string& path, shared_ptr env, const char* expr, size_t len, Value& val, std::string& msg, bool print_ast) { try { shared_ptr ast; auto& parser = get_parser(); parser.log = [&](size_t ln, size_t col, const string& err_msg) { stringstream ss; ss << path << ":" << ln << ":" << col << ": " << err_msg << endl; msg = ss.str(); }; if (parser.parse_n(expr, len, ast)) { if (print_ast) { ast->print(); } val = Eval::eval(*ast, env); return true; } } catch (runtime_error& e) { msg = e.what(); } return false; } // vim: et ts=4 sw=4 cin cino={1s ff=unix