#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 Call: return eval_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 Object: return eval_object(ast, env); case Array: return eval_array(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) { std::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]; auto f = FunctionValue( params, [=](shared_ptr callEnv) { callEnv->append_outer(env); return eval(*body, callEnv); } ); return Value(std::move(f)); }; static Value eval_call(const Ast& ast, shared_ptr env) { Value val = eval(*ast.nodes[0], env); for (auto i = 1u; i < ast.nodes.size(); i++) { const auto& n = *ast.nodes[i]; if (n.original_tag == AstTag::Arguments) { // Function call const auto& f = val.to_function(); const auto& params = f.data->params; const auto& args = n.nodes; if (params.size() <= args.size()) { auto callEnv = make_shared(); callEnv->initialize("self", val, false); for (auto iprm = 0u; iprm < params.size(); iprm++) { auto param = params[iprm]; auto arg = args[iprm]; auto val = eval(*arg, env); callEnv->initialize(param.name, val, param.mut); } callEnv->initialize("__LINE__", Value((long)ast.line), false); callEnv->initialize("__COLUMN__", Value((long)ast.column), false); val = f.data->eval(callEnv); } else { string msg = "arguments error..."; throw runtime_error(msg); } } else if (n.original_tag == AstTag::Index) { // Array reference const auto& arr = val.to_array(); auto idx = eval(n, env).to_long(); if (0 <= idx && idx < static_cast(arr.values->size())) { val = arr.values->at(idx); } } else if (n.original_tag == AstTag::Dot) { // Property auto name = n.token; auto prop = val.get_property(name); if (prop.get_type() == Value::Function) { const auto& pf = prop.to_function(); auto f = FunctionValue( pf.data->params, [=](shared_ptr callEnv) { callEnv->initialize("this", val, false); if (val.get_type() == Value::Object) { callEnv->set_object(val.to_object()); } return pf.data->eval(callEnv); } ); val = Value(std::move(f)); } else { val = prop; } } else { throw std::logic_error("invalid internal condition."); } } return val; } 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_object(const Ast& ast, shared_ptr env) { ObjectValue obj; for (auto i = 0u; i < ast.nodes.size(); i++) { const auto& prop = *ast.nodes[i]; const auto& name = prop.nodes[0]->token; auto val = eval(*prop.nodes[1], env); obj.properties->emplace(name, val); } return Value(std::move(obj)); } static Value eval_array(const Ast& ast, shared_ptr env) { ArrayValue arr; for (auto i = 0u; i < ast.nodes.size(); i++) { auto expr = ast.nodes[i]; auto val = eval(*expr, env); arr.values->push_back(val); } return Value(std::move(arr)); } 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, 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