mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-11-13 06:45:29 +00:00
Added lexical scope support.
This commit is contained in:
parent
a0bee1d20b
commit
778b175ab0
@ -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<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();
|
||||
}
|
||||
|
||||
@ -806,28 +813,30 @@ private:
|
||||
}
|
||||
|
||||
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 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<long>(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<peg::Ast> parse(
|
||||
|
||||
std::shared_ptr<peg::Ast> 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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user