Added lexical scope support.

pull/3/head
yhirose 9 years ago
parent a0bee1d20b
commit 778b175ab0
  1. 55
      language/culebra/culebra.h
  2. 31
      language/culebra/samples/test.cul

@ -9,14 +9,15 @@ const auto grammar_ = R"(
PROGRAM <- _ STATEMENTS _ PROGRAM <- _ STATEMENTS _
STATEMENTS <- (STATEMENT (_sp_ (';' / _nl_) (_ STATEMENT)?)*)? STATEMENTS <- (STATEMENT (_sp_ (';' / _nl_) (_ STATEMENT)?)*)?
STATEMENT <- DEBUGGER / RETURN / EXPRESSION STATEMENT <- DEBUGGER / RETURN / LEXICAL_SCOPE / EXPRESSION
DEBUGGER <- debugger DEBUGGER <- debugger
RETURN <- return (_sp_ !_nl_ EXPRESSION)? RETURN <- return (_sp_ !_nl_ EXPRESSION)?
LEXICAL_SCOPE <- BLOCK
EXPRESSION <- ASSIGNMENT / LOGICAL_OR 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_OR <- LOGICAL_AND (_ '||' _ LOGICAL_AND)*
LOGICAL_AND <- CONDITION (_ '&&' _ CONDITION)* LOGICAL_AND <- CONDITION (_ '&&' _ CONDITION)*
@ -50,6 +51,7 @@ const auto grammar_ = R"(
UNARY_NOT_OPERATOR <- '!' UNARY_NOT_OPERATOR <- '!'
MULTIPLICATIVE_OPERATOR <- [*/%] MULTIPLICATIVE_OPERATOR <- [*/%]
LET <- < ('let' _wd_)? >
MUTABLE <- < ('mut' _wd_)? > MUTABLE <- < ('mut' _wd_)? >
IDENTIFIER <- < IdentInitChar IdentChar* > IDENTIFIER <- < IdentInitChar IdentChar* >
@ -558,7 +560,7 @@ struct Interpreter
case "IF"_: return eval_if(ast, env); case "IF"_: return eval_if(ast, env);
case "FUNCTION"_: return eval_function(ast, env); case "FUNCTION"_: return eval_function(ast, env);
case "CALL"_: return eval_call(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 "ASSIGNMENT"_: return eval_assignment(ast, env);
case "LOGICAL_OR"_: return eval_logical_or(ast, env); case "LOGICAL_OR"_: return eval_logical_or(ast, env);
case "LOGICAL_AND"_: return eval_logical_and(ast, env); case "LOGICAL_AND"_: return eval_logical_and(ast, env);
@ -726,7 +728,12 @@ private:
return val; return val;
} }
Value eval_block(const peg::Ast& ast, std::shared_ptr<Environment> env) { Value eval_lexical_scope(const peg::Ast& ast, std::shared_ptr<Environment> env) {
auto scopeEnv = std::make_shared<Environment>();
scopeEnv->append_outer(env);
for (auto node: ast.nodes) {
eval(*node, scopeEnv);
}
return Value(); return Value();
} }
@ -806,28 +813,30 @@ private:
} }
Value eval_assignment(const peg::Ast& ast, std::shared_ptr<Environment> env) { Value eval_assignment(const peg::Ast& ast, std::shared_ptr<Environment> env) {
auto end = ast.nodes.size() - 1; auto lvaloff = 2;
auto lvalcnt = ast.nodes.size() - 3;
auto mut = ast.nodes[0]->token == "mut"; auto let = ast.nodes[0]->token == "let";
auto val = eval(*ast.nodes[end], env); auto mut = ast.nodes[1]->token == "mut";
auto rval = eval(*ast.nodes.back(), env);
if (ast.nodes.size() == 3) { if (lvalcnt == 1) {
const auto& ident = ast.nodes[1]->token; const auto& ident = ast.nodes[lvaloff]->token;
if (env->has(ident)) { if (!let && env->has(ident)) {
env->assign(ident, val); env->assign(ident, rval);
} else if (is_keyword(ident)) { } else if (is_keyword(ident)) {
throw std::runtime_error("left-hand side is invalid variable name."); throw std::runtime_error("left-hand side is invalid variable name.");
} else { } else {
env->initialize(ident, val, mut); env->initialize(ident, rval, mut);
} }
return val; return rval;
} else { } else {
using peg::operator"" _; using peg::operator"" _;
Value lval = eval(*ast.nodes[1], env); Value lval = eval(*ast.nodes[lvaloff], env);
auto end = ast.nodes.size() - 2; auto end = lvaloff + lvalcnt - 1;
for (auto i = 2u; i < end; i++) { for (auto i = lvaloff + 1; i < end; i++) {
const auto& postfix = *ast.nodes[i]; const auto& postfix = *ast.nodes[i];
switch (postfix.original_tag) { switch (postfix.original_tag) {
@ -845,21 +854,21 @@ private:
const auto& arr = lval.to_array(); const auto& arr = lval.to_array();
auto idx = eval(postfix, env).to_long(); auto idx = eval(postfix, env).to_long();
if (0 <= idx && idx < static_cast<long>(arr.values->size())) { if (0 <= idx && idx < static_cast<long>(arr.values->size())) {
arr.values->at(idx) = val; arr.values->at(idx) = rval;
} else { } else {
// TODO: error? or set 'undefined'? throw std::logic_error("index out of range.");
} }
return val; return rval;
} }
case "DOT"_: { case "DOT"_: {
auto& obj = lval.to_object(); auto& obj = lval.to_object();
auto name = postfix.token; auto name = postfix.token;
if (obj.has(name)) { if (obj.has(name)) {
obj.assign(name, val); obj.assign(name, rval);
} else { } else {
obj.initialize(name, val, mut); obj.initialize(name, rval, mut);
} }
return val; return rval;
} }
default: default:
throw std::logic_error("invalid internal condition."); throw std::logic_error("invalid internal condition.");
@ -945,7 +954,7 @@ inline std::shared_ptr<peg::Ast> parse(
std::shared_ptr<peg::Ast> ast; std::shared_ptr<peg::Ast> ast;
if (parser.parse_n(expr, len, ast, path.c_str())) { 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; return nullptr;

@ -190,6 +190,36 @@ test_interpolated_string = fn () {
assert(ret == 'Hello World!') 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 debugger
test_call() test_call()
test_return() test_return()
@ -204,5 +234,6 @@ debugger
test_sum() test_sum()
test_fib() test_fib()
test_interpolated_string() test_interpolated_string()
test_lexical_scope()
return // end return // end

Loading…
Cancel
Save