diff --git a/language/interpreter.cc b/language/interpreter.cc index 68900ae..cc8f522 100755 --- a/language/interpreter.cc +++ b/language/interpreter.cc @@ -75,9 +75,11 @@ private: } static Value eval_function(const Ast& ast, shared_ptr env) { - vector params; + vector params; for (auto node: ast.nodes[0]->nodes) { - params.push_back(node->token); + auto mut = node->nodes[0]->token == "mut"; + const auto& name = node->nodes[1]->token; + params.push_back({ name, mut }); } auto body = ast.nodes[1]; @@ -95,19 +97,19 @@ private: const auto& var = ast.nodes[0]->token; const auto& args = ast.nodes[1]->nodes; - const auto& f = dereference_identirier(env, var); + const auto& f = env->get(var); const auto& fv = f.to_function(); if (fv.params.size() <= args.size()) { auto callEnv = make_shared(); - callEnv->set("self", f); + callEnv->declare("self", f, false); for (auto i = 0u; i < fv.params.size(); i++) { - auto var = fv.params[i]; + auto param = fv.params[i]; auto arg = args[i]; auto val = eval(*arg, env); - callEnv->set(var, val); + callEnv->declare(param.name, val, param.mut); } return fv.eval(callEnv); @@ -159,15 +161,20 @@ private: } static Value eval_assignment(const Ast& ast, shared_ptr env) { - const auto& var = ast.nodes[0]->token; - auto val = eval(*ast.nodes[1], env); - env->set(var, val); + 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->declare(var, val, mut == "mut"); + } return val; }; static Value eval_identifier(const Ast& ast, shared_ptr env) { const auto& var = ast.token; - return dereference_identirier(env, var); + return env->get(var); }; static Value eval_number(const Ast& ast, shared_ptr env) { @@ -186,14 +193,6 @@ private: } return Value(std::move(s)); }; - - static Value dereference_identirier(shared_ptr env, const string& var) { - if (!env->has(var)) { - string msg = "undefined variable '" + var + "'..."; - throw runtime_error(msg); - } - return env->get(var); - }; }; bool run(const string& path, shared_ptr env, const char* expr, size_t len, Value& val, std::string& msg, bool print_ast) diff --git a/language/interpreter.hpp b/language/interpreter.hpp index 71fde03..cb2b3de 100755 --- a/language/interpreter.hpp +++ b/language/interpreter.hpp @@ -13,7 +13,11 @@ struct Value }; struct FunctionValue { - std::vector params; + struct Parameter { + std::string name; + bool mut; + }; + std::vector params; std::function env)> eval; }; @@ -189,40 +193,58 @@ struct Environment Value get(const std::string& s) const { if (dic_.find(s) != dic_.end()) { - return dic_.at(s); + return dic_.at(s).val; } if (outer_) { return outer_->get(s); } - // NOTREACHED - throw std::logic_error("invalid internal condition."); + std::string msg = "undefined variable '" + s + "'..."; + throw std::runtime_error(msg); } - void set(const std::string& s, const Value& val) { + void assign(const std::string& s, const Value& val) { + assert(has(s)); if (dic_.find(s) != dic_.end()) { - dic_[s] = val; + auto& sym = dic_[s]; + if (!sym.mut) { + std::string msg = "immutable variable '" + s + "'..."; + throw std::runtime_error(msg); + } + sym.val = val; return; } if (outer_ && outer_->has(s)) { - outer_->set(s, val); + outer_->assign(s, val); return; } - dic_[s] = val; + } + + void declare(const std::string& s, const Value& val, bool mut) { + assert(!has(s)); + dic_[s] = Symbol{val, mut}; } void setup_built_in_functions() { - set("pp", Value(Value::FunctionValue { - { "arg" }, - [](std::shared_ptr env) { - std::cout << env->get("arg").str() << std::endl; - return Value(); - } - })); + declare( + "pp", + Value(Value::FunctionValue { + { {"arg", true} }, + [](std::shared_ptr env) { + std::cout << env->get("arg").str() << std::endl; + return Value(); + } + }), + false); } private: + struct Symbol { + Value val; + bool mut; + }; + std::shared_ptr outer_; - std::map dic_; + std::map dic_; }; bool run(const std::string& path, std::shared_ptr env, const char* expr, size_t len, Value& val, std::string& msg, bool print_ast); diff --git a/language/parser.cc b/language/parser.cc index 80f9871..bfd21a3 100755 --- a/language/parser.cc +++ b/language/parser.cc @@ -9,11 +9,12 @@ static auto g_grammar = R"( STATEMENTS <- EXPRESSION* EXPRESSION <- ASSIGNMENT / PRIMARY - ASSIGNMENT <- IDENTIFIER '=' _ EXPRESSION + ASSIGNMENT <- MUTABLE IDENTIFIER '=' _ EXPRESSION WHILE <- 'while' _ EXPRESSION BLOCK IF <- 'if' _ EXPRESSION BLOCK ('else' _ 'if' _ EXPRESSION BLOCK)* ('else' _ BLOCK)? - FUNCTION <- 'fun' _ PARAMETERS BLOCK - PARAMETERS <- '(' _ (IDENTIFIER (',' _ IDENTIFIER)*)? ')' _ + FUNCTION <- 'fn' _ PARAMETERS BLOCK + PARAMETERS <- '(' _ (PARAMETER (',' _ PARAMETER)*)? ')' _ + PARAMETER <- MUTABLE IDENTIFIER FUNCTION_CALL <- IDENTIFIER ARGUMENTS ARGUMENTS <- '(' _ (EXPRESSION (', ' _ EXPRESSION)*)? ')' _ @@ -36,6 +37,8 @@ static auto g_grammar = R"( INTERPOLATED_STRING <- '"' ('{' _ EXPRESSION '}' / INTERPOLATED_CONTENT)* '"' _ INTERPOLATED_CONTENT <- (!["{] .) (!["{] .)* + MUTABLE <- < 'mut'? > _ + ~_ <- (Space / EndOfLine / Comment)* Space <- ' ' / '\t' EndOfLine <- '\r\n' / '\n' / '\r' @@ -81,6 +84,7 @@ peg& get_parser() { peg::AstNodeType::Token, "IDENTIFIER", Identifier }, { peg::AstNodeType::Regular, "INTERPOLATED_STRING", InterpolatedString }, { peg::AstNodeType::Token, "INTERPOLATED_CONTENT", Undefined }, + { peg::AstNodeType::Token, "MUTABLE", Undefined }, }, Undefined); }