From 778b175ab09bf0bd4972dabef960d58839721e4d Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 25 Sep 2015 15:20:55 -0400 Subject: [PATCH] Added lexical scope support. --- language/culebra/culebra.h | 55 ++++++++++++++++++------------- language/culebra/samples/test.cul | 31 +++++++++++++++++ 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/language/culebra/culebra.h b/language/culebra/culebra.h index 3871f30..00a6f83 100644 --- a/language/culebra/culebra.h +++ b/language/culebra/culebra.h @@ -9,14 +9,15 @@ const auto grammar_ = R"( PROGRAM <- _ STATEMENTS _ STATEMENTS <- (STATEMENT (_sp_ (';' / _nl_) (_ STATEMENT)?)*)? - STATEMENT <- DEBUGGER / RETURN / EXPRESSION + STATEMENT <- DEBUGGER / RETURN / LEXICAL_SCOPE / EXPRESSION DEBUGGER <- debugger RETURN <- return (_sp_ !_nl_ EXPRESSION)? + LEXICAL_SCOPE <- BLOCK EXPRESSION <- ASSIGNMENT / LOGICAL_OR - ASSIGNMENT <- MUTABLE _ PRIMARY (_ (ARGUMENTS / INDEX / DOT))* _ '=' _ EXPRESSION + ASSIGNMENT <- LET _ MUTABLE _ PRIMARY (_ (ARGUMENTS / INDEX / DOT))* _ '=' _ EXPRESSION LOGICAL_OR <- LOGICAL_AND (_ '||' _ LOGICAL_AND)* LOGICAL_AND <- CONDITION (_ '&&' _ CONDITION)* @@ -50,6 +51,7 @@ const auto grammar_ = R"( UNARY_NOT_OPERATOR <- '!' MULTIPLICATIVE_OPERATOR <- [*/%] + LET <- < ('let' _wd_)? > MUTABLE <- < ('mut' _wd_)? > IDENTIFIER <- < IdentInitChar IdentChar* > @@ -558,7 +560,7 @@ struct Interpreter case "IF"_: return eval_if(ast, env); case "FUNCTION"_: return eval_function(ast, env); case "CALL"_: return eval_call(ast, env); - case "BLOCK"_: return eval_block(ast, env); + case "LEXICAL_SCOPE"_: return eval_lexical_scope(ast, env); case "ASSIGNMENT"_: return eval_assignment(ast, env); case "LOGICAL_OR"_: return eval_logical_or(ast, env); case "LOGICAL_AND"_: return eval_logical_and(ast, env); @@ -726,7 +728,12 @@ private: return val; } - Value eval_block(const peg::Ast& ast, std::shared_ptr env) { + Value eval_lexical_scope(const peg::Ast& ast, std::shared_ptr env) { + auto scopeEnv = std::make_shared(); + scopeEnv->append_outer(env); + for (auto node: ast.nodes) { + eval(*node, scopeEnv); + } return Value(); } @@ -806,28 +813,30 @@ private: } Value eval_assignment(const peg::Ast& ast, std::shared_ptr env) { - auto end = ast.nodes.size() - 1; + auto lvaloff = 2; + auto lvalcnt = ast.nodes.size() - 3; - auto mut = ast.nodes[0]->token == "mut"; - auto val = eval(*ast.nodes[end], env); + auto let = ast.nodes[0]->token == "let"; + auto mut = ast.nodes[1]->token == "mut"; + auto rval = eval(*ast.nodes.back(), env); - if (ast.nodes.size() == 3) { - const auto& ident = ast.nodes[1]->token; - if (env->has(ident)) { - env->assign(ident, val); + if (lvalcnt == 1) { + const auto& ident = ast.nodes[lvaloff]->token; + if (!let && env->has(ident)) { + env->assign(ident, rval); } else if (is_keyword(ident)) { throw std::runtime_error("left-hand side is invalid variable name."); } else { - env->initialize(ident, val, mut); + env->initialize(ident, rval, mut); } - return val; + return rval; } else { using peg::operator"" _; - Value lval = eval(*ast.nodes[1], env); + Value lval = eval(*ast.nodes[lvaloff], env); - auto end = ast.nodes.size() - 2; - for (auto i = 2u; i < end; i++) { + auto end = lvaloff + lvalcnt - 1; + for (auto i = lvaloff + 1; i < end; i++) { const auto& postfix = *ast.nodes[i]; switch (postfix.original_tag) { @@ -845,21 +854,21 @@ private: const auto& arr = lval.to_array(); auto idx = eval(postfix, env).to_long(); if (0 <= idx && idx < static_cast(arr.values->size())) { - arr.values->at(idx) = val; + arr.values->at(idx) = rval; } else { - // TODO: error? or set 'undefined'? + throw std::logic_error("index out of range."); } - return val; + return rval; } case "DOT"_: { auto& obj = lval.to_object(); auto name = postfix.token; if (obj.has(name)) { - obj.assign(name, val); + obj.assign(name, rval); } else { - obj.initialize(name, val, mut); + obj.initialize(name, rval, mut); } - return val; + return rval; } default: throw std::logic_error("invalid internal condition."); @@ -945,7 +954,7 @@ inline std::shared_ptr parse( std::shared_ptr ast; if (parser.parse_n(expr, len, ast, path.c_str())) { - return peg::AstOptimizer(true, { "PARAMETERS", "ARGUMENTS", "OBJECT", "ARRAY", "RETURN" }).optimize(ast); + return peg::AstOptimizer(true, { "PARAMETERS", "ARGUMENTS", "OBJECT", "ARRAY", "RETURN", "LEXICAL_SCOPE" }).optimize(ast); } return nullptr; diff --git a/language/culebra/samples/test.cul b/language/culebra/samples/test.cul index 556872f..2747eb7 100644 --- a/language/culebra/samples/test.cul +++ b/language/culebra/samples/test.cul @@ -190,6 +190,36 @@ test_interpolated_string = fn () { assert(ret == 'Hello World!') } +test_lexical_scope = fn () { + a = 0 + { + let a = 1; + assert(a == 1) + } + assert(a == 0) + + mut b = 0 + { + b = 1; + assert(b == 1) + } + assert(b == 1) + + c = 0 + { + let mut c = 0; + c = 1 + assert(c == 1) + } + assert(c == 0) + + obj = { + name: 'object' + } + + assert(obj.name == 'object') +} + debugger test_call() test_return() @@ -204,5 +234,6 @@ debugger test_sum() test_fib() test_interpolated_string() +test_lexical_scope() return // end