Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -27,16 +27,23 @@ this(immutable LexPosition pos, long n) {super(pos); data = n;} this(immutable LexPosition pos, BigInt n) {super(pos); data = n;} this(immutable LexPosition pos, string n) {super(pos); data = BigInt(n);} } -class VarExpression : AST -{ - string var; +class VarExpression : AST +{ + string var; + mixin SimpleClass; +} + +class LayeredExpression : AST +{ + string lay; + AST expr; mixin SimpleClass; -} - +} + class LetExpression : AST { string var; string layer; AST init; @@ -70,8 +77,9 @@ alias genEast!StrLiteral strl; alias genEast!IntLiteral intl; auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } // to help type inference of D alias genEast!VarExpression var; + alias genEast!LayeredExpression lay; alias genEast!LetExpression let; alias genEast!FuncallExpression call; } Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -99,11 +99,11 @@ { Table ctx = createGlobalContext(); return typeof(return)(eval(e, ctx), ctx); } -Value eval(AST _e, Table ctx, bool splitCtx = false) +Value eval(AST _e, Table ctx, bool splitCtx = false, Layer lay="@val") { if( auto e = cast(StrLiteral)_e ) { return new StrValue(e.data); } @@ -113,21 +113,26 @@ return new IntValue(e.data); } else if( auto e = cast(VarExpression)_e ) { - return ctx.get(e.var, "@val", e.pos); + return ctx.get(e.var, lay, e.pos); + } + else + if( auto e = cast(LayeredExpression)_e ) + { + return eval(e.expr, ctx, false, e.lay); } else if( auto e = cast(LetExpression)_e ) { // for letrec, we need this, but should avoid overwriting???? // ctx.set(e.var, "@val", new UndefinedValue, e.pos); Value v = eval(e.init, ctx, true); if(splitCtx) ctx = new Table(ctx, Table.Kind.NotPropagateSet); - ctx.set(e.var, "@val", v, e.pos); + ctx.set(e.var, (e.layer.length ? e.layer : lay), v, e.pos); return eval(e.expr, ctx); } else if( auto e = cast(FuncallExpression)_e ) { @@ -179,11 +184,19 @@ } unittest { assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) ); assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) ); -} +} +unittest +{ + assert_eq( evalString(`@a x=1; @b x=2; @a(x)`).val, new IntValue(BigInt(1)) ); + assert_eq( evalString(`@a x=1; @b x=2; @b(x)`).val, new IntValue(BigInt(2)) ); + assert_eq( evalString(`let x=1; let _ = (@a x=2;2); x`).val, new IntValue(BigInt(1)) ); + assert_throw!Error( evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) ); +} + unittest { assert_nothrow( evalString(`var fac = fun(x){ 1; }; Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -57,16 +57,22 @@ AST Body() { if( lex.empty || !lex.front.quoted && lex.front.str=="}" ) return doNothingExpression(); + auto saved = lex.save; auto pos = lex.front.pos; string kwd = lex.front.str; if( tryEat("let") || tryEat("var") || tryEat("def") || tryEat("@") ) { - if( kwd == "@" ) + if( kwd == "@" ) { kwd ~= eatId("after @"); + if( tryEat("(") ) { + lex = saved; + goto asExpression; + } + } immutable LexPosition varpos = (lex.empty ? null : lex.front.pos); string var = eatId("after "~kwd); eat("=", "after "~kwd); kwd = (kwd[0]=='@' ? kwd : ""); // "let, var, def ==> neutral layer" auto e = E(0); @@ -75,10 +81,11 @@ else return new LetExpression(pos, var, kwd, e, new VarExpression(varpos, var)); } else { + asExpression: auto e = E(0); if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) return new LetExpression(pos, "_", "@val", e, Body()); else return e; @@ -160,10 +167,18 @@ if( isNumber(lex.front.str) ) { scope(exit) lex.popFront; return new IntLiteral(pos, BigInt(cast(string)lex.front.str)); } + if( tryEat("@") ) + { + auto lay = "@"~eatId("for layer ID"); + eat("(", "for layered execution"); + auto e = E(0); + eat(")", "after "~lay~"(..."); + return new LayeredExpression(pos, lay, e); + } if( tryEat("(") ) { auto e = Body(); eat(")", "after parenthesized expression"); return e; @@ -275,10 +290,11 @@ call(call(call(var("if"),intl(1),fun([],intl(178)),fun([],intl(3)))))); assert_eq(parseString(`1+2*3`), call(var("+"),intl(1),call(var("*"),intl(2),intl(3)))); assert_eq(parseString(`(1+2)*3`), call(var("*"),call(var("+"),intl(1),intl(2)),intl(3))); assert_eq(parseString(`1*(2+3)`), call(var("*"),intl(1),call(var("+"),intl(2),intl(3)))); assert_eq(parseString(`1*2+3`), call(var("+"),call(var("*"),intl(1),intl(2)),intl(3))); + assert_eq(parseString(`@x(1)`), lay("@x", intl(1))); assert_eq(parseString(` let x = 100; #comment let y = 200; #comment!!!!! x+y Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -12,12 +12,12 @@ class RuntimeException : Exception { const LexPosition pos; - this( const LexPosition pos, string msg ) - { super(sprintf!"%s at [%s]"(msg, pos)); this.pos = pos; } + this( const LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) + { super(sprintf!"[%s] %s"(pos, msg), file, line, next); this.pos = pos; } } /// Runtime values of Polemy abstract class Value