@@ -23,9 +23,9 @@ /// Evaluate the AST Value evalAST(AST e) { - return eval(e, ValueLayer, theContext, OverwriteCtx); + return macroAndEval(e, ValueLayer, theContext, OverwriteCtx)[0]; } /// Evaluate the string Value evalString(S,T...)(S str, T fn_ln_cn) @@ -48,8 +48,9 @@ private: Table theContext; enum : bool { CascadeCtx=false, OverwriteCtx=true }; + Value eval( AST e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { // dynamic-overload-resolution-pattern: modify here enum funName = "eval"; @@ -68,9 +69,9 @@ } Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( isMacroishLayer(lay) ) + if( isASTLayer(lay) ) return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer ) return new StrValue(e.data); return lift(new StrValue(e.data), lay, ctx, e.pos); @@ -77,9 +78,9 @@ } Value eval( Int e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( isMacroishLayer(lay) ) + if( isASTLayer(lay) ) return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer ) return new IntValue(e.data); return lift(new IntValue(e.data), lay, ctx, e.pos); @@ -86,10 +87,10 @@ } Value eval( Var e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( isMacroishLayer(lay) ) - if( ctx.has(e.name,MacroLayer) ) + if( isASTLayer(lay) ) + if( isMacroLayer(lay) && ctx.has(e.name,MacroLayer) ) return ctx.get(e.name, MacroLayer, e.pos); else return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer || ctx.has(e.name, lay) ) @@ -99,20 +100,28 @@ Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { Value f = eval( e.fun, lay, ctx ); - if( isMacroishLayer(lay) ) - if( auto ff = cast(FunValue)f ) - return invokeFunction(ff, e.args, MacroLayer, ctx, e.pos, getNameIfPossible(e.fun)); + if( isASTLayer(lay) ) { + auto ff = cast(FunValue)f; + if( ff !is null && isMacroLayer(lay) ) + return invokeFunction(ff, e.args, lay, ctx, e.pos, getNameIfPossible(e.fun)); else return ast2table(e, (AST e){return eval(e,lay,ctx);}); + } return invokeFunction(f, e.args, lay, ctx, e.pos, getNameIfPossible(e.fun)); } Value eval( Fun e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( isMacroishLayer(lay) ) - return ast2table(e, (AST e){return eval(e,lay,ctx);}); + if( isASTLayer(lay) ) + { + // need this for correct scoping (outer scope macro variables must be hidden!) + Table newCtx = new Table(ctx, Table.Kind.NotPropagateSet); + foreach(p; e.params) + newCtx.set(p.name, ValueLayer, new UndefinedValue); + return ast2table(e, (AST e){return eval(e,lay,newCtx);}); + } else return createNewFunction(e, ctx); } @@ -125,19 +134,56 @@ } Value eval( Let e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - // todo @macro let - if( isMacroishLayer(lay) ) - return ast2table(e, (AST e){return eval(e,lay,ctx);}); + Table newCtx = overwriteCtx ? ctx : new Table(ctx, Table.Kind.NotPropagateSet); + if( isASTLayer(lay) ) + return ast2table(e, (AST ee){ + if(ee is e.expr) // need this for correct scoping (outer scope macro variables must be hidden!) + newCtx.set(e.name, ValueLayer, new UndefinedValue); + return eval(ee,lay,newCtx); + }); else { + Value ri = eval(e.init, lay, newCtx); + newCtx.set(e.name, e.layer.empty ? lay : e.layer, ri); + return eval(e.expr, lay, newCtx, OverwriteCtx); + } + } + +private: + // little little bit incremental macro defining version. + // enables @macro foo(x)=... in ... foo ..., only at the top level of the + // interpreter and functions. better than nothing :P + Tuple!(Value,AST) macroAndEval( AST e_, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) + { + assert( !isASTLayer(lay) ); + + AST decodeAST(Value v, LexPosition pos) + { + // [TODO] more informative error message + return polemy2d!(AST)(v, pos); + } + + if(auto e = cast(Let)e_) + { + AST ai = decodeAST(eval(e.init, RawMacroLayer, ctx), e.init.pos); + Value vi = eval(ai, lay, ctx); + if( !overwriteCtx ) ctx = new Table(ctx, Table.Kind.NotPropagateSet); - Value ri = eval(e.init, lay, ctx); - string theLayer = e.layer.empty ? lay : e.layer; // neutral layer - ctx.set(e.name, theLayer, ri); - return eval(e.expr, lay, ctx, OverwriteCtx); + string theLayer = e.layer.empty ? lay : e.layer; + ctx.set(e.name, theLayer, vi); + + auto ave = macroAndEval( e.expr, lay, ctx, OverwriteCtx ); + AST a = new Let(e.pos, e.name, e.layer, ai, ave[1]); + return tuple(ave[0], a); + } + else + { + AST a = decodeAST(eval(e_, RawMacroLayer, ctx), e_.pos); + Value v = eval(a, lay, ctx); + return tuple(v, a); } } private: @@ -154,21 +200,21 @@ { Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); foreach(i,p; f.params()) if( p.layers.empty ) - newCtx.set(p.name, (lay==RawMacroLayer ? MacroLayer : lay), eval(args[i], lay, ctx)); + newCtx.set(p.name, isMacroLayer(lay)?MacroLayer:lay, eval(args[i], lay, ctx)); else foreach(argLay; p.layers) newCtx.set(p.name, argLay, eval(args[i], argLay, ctx)); scope _ = new PushCallStack(pos, callstackmsg); - return f.invoke(lay==RawMacroLayer ? MacroLayer : lay, newCtx, pos); + return f.invoke(isMacroLayer(lay)?MacroLayer:lay, newCtx, pos); } throw genex!RuntimeException(pos, text("tried to call non-function: ",_f)); } Value lift(Value v, Layer lay, Table ctx, LexPosition pos) { - assert( !isMacroishLayer(lay), "lift to the @macro layer should never happen" ); + assert( !isASTLayer(lay), "lift to the @macro layer should never happen" ); // functions are automatically lifterd if( cast(FunValue) v ) return v; @@ -232,17 +278,18 @@ } override Value invoke(Layer lay, Table ctx, LexPosition pos) { - if( lay == MacroLayer ) + if( isASTLayer(lay) ) return eval(ast.funbody, lay, ctx); - try { - if( afterMacroAST is null ) - afterMacroAST = polemy2d!(AST)(eval(e.funbody, RawMacroLayer, ctx), pos); - return eval(afterMacroAST, lay, ctx); - } catch( RuntimeException e ) { - throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line, e.next) : e; + if( afterMacroAST is null ) + { + auto va = macroAndEval(e.funbody, lay, ctx); + afterMacroAST = va[1]; + return va[0]; } + else + return eval(afterMacroAST, lay, ctx); } AST afterMacroAST; } @@ -313,18 +360,18 @@ auto e = new Evaluator; enrollRuntimeLibrary(e); auto r = assert_nothrow( e.evalString(`var x = 21; var x = x + x*x;`) ); assert_eq( r, new IntValue(BigInt(21+21*21)) ); - assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) ); + assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(21+21*21) ); assert_nothrow( e.globalContext.get("x",ValueLayer) ); assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) ); } unittest { auto e = new Evaluator; enrollRuntimeLibrary(e); - assert_eq( e.evalString(`let x=1; let y=(let x=2); x`), new IntValue(BigInt(1)) ); - assert_eq( e.evalString(`let x=1; let y=(let x=2;fun(){x}); y()`), new IntValue(BigInt(2)) ); + assert_eq( e.evalString(`let x=1; let y=(let x=2); x`), new IntValue(1) ); + assert_eq( e.evalString(`let x=1; let y=(let x=2;fun(){x}); y()`), new IntValue(2) ); } unittest {