Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -22,11 +22,11 @@ this() { theContext = new Table; } /// 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) { @@ -47,10 +47,11 @@ 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"; alias TypeTuple!(e,lay,ctx,overwriteCtx) params; @@ -67,30 +68,30 @@ assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined")); } 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); } 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); } 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) ) return ctx.get(e.name, lay, e.pos); @@ -98,22 +99,30 @@ } 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); } Value eval( Lay e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) @@ -124,21 +133,58 @@ return eval(e.expr, e.layer, ctx); } 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: string getNameIfPossible(AST e) @@ -153,23 +199,23 @@ if(auto f = cast(FunValue)_f) { 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; @@ -231,19 +277,20 @@ assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); } 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; } return new UserDefinedFunValue(e,ctx); @@ -312,20 +359,20 @@ { 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 { auto e = new Evaluator; Index: polemy/layer.d ================================================================== --- polemy/layer.d +++ polemy/layer.d @@ -15,36 +15,55 @@ { SystemLayer = "(system)", /// Predefined layer for internal data ValueLayer = "@value", /// Predefined layer for normal run MacroLayer = "@macro", /// Predefined layer for macro run (@lay() changes layer) RawMacroLayer = "(rawmacro)", /// Predefined layer for macro run (@lay() becomes AST) + AstLayer = "(ast)", /// Predefined layer for macro run (never invoke macro) } /// True if it is macro-like layer that basically generates syntax tree -bool isMacroishLayer( Layer lay ) +bool isASTLayer( Layer lay ) { - return lay==MacroLayer || lay==RawMacroLayer; + return lay==MacroLayer || lay==RawMacroLayer || lay==AstLayer; } unittest { - assert( !isMacroishLayer(SystemLayer) ); - assert( !isMacroishLayer(ValueLayer) ); - assert( isMacroishLayer(MacroLayer) ); - assert( isMacroishLayer(RawMacroLayer) ); + assert( !isASTLayer(SystemLayer) ); + assert( !isASTLayer(ValueLayer) ); + assert( isASTLayer(MacroLayer) ); + assert( isASTLayer(RawMacroLayer) ); + assert( isASTLayer(AstLayer) ); } /// True if in the specified layer @lay(...) has no effect and merely produces a syntax tree bool isNoLayerChangeLayer( Layer lay ) { - return lay==RawMacroLayer; + return lay==RawMacroLayer || lay==AstLayer; } unittest { assert( !isNoLayerChangeLayer(SystemLayer) ); assert( !isNoLayerChangeLayer(ValueLayer) ); assert( !isNoLayerChangeLayer(MacroLayer) ); assert( isNoLayerChangeLayer(RawMacroLayer) ); + assert( isNoLayerChangeLayer(AstLayer) ); +} + +/// True if do macro expanstion + +bool isMacroLayer( Layer lay ) +{ + return lay==MacroLayer || lay==RawMacroLayer; +} + +unittest +{ + assert( !isMacroLayer(SystemLayer) ); + assert( !isMacroLayer(ValueLayer) ); + assert( isMacroLayer(MacroLayer) ); + assert( isMacroLayer(RawMacroLayer) ); + assert( !isMacroLayer(AstLayer) ); } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -116,12 +116,12 @@ auto varpos = currentPosition(); string var = eatId("after "~kwd, AllowQuoted); // name of the declared variable auto e = tryEat("(") - ? parseLambdaAfterOpenParen(varpos) // let var ( ... - : (eat("=", "after "~kwd), E(0)); // let var = ... + ? parseLambdaAfterOpenParen(pos) // let var ( ... + : (eat("=", "after "~kwd), E(0)); // let var = ... if( moreDeclarationExists() ) return new Let(pos, var, layer, e, Body()); else return new Let(pos, var, layer, e, new Var(varpos, var)); } @@ -482,11 +482,11 @@ { scope(exit) lex.popFront; return new Str(currentPosition(), lex.front.str); } - AST parseLambdaAfterOpenParen(immutable LexPosition pos) + AST parseLambdaAfterOpenParen(LexPosition pos) { Parameter[] params; while( !tryEat(")") ) { params ~= parseParam(); @@ -561,11 +561,11 @@ AST doNothingExpression() { return new Str(currentPosition(), "(empty function body)"); } - immutable(LexPosition) currentPosition() + LexPosition currentPosition() { return lex.empty ? null : lex.front.pos; } } Index: polemy/valueconv.d ================================================================== --- polemy/valueconv.d +++ polemy/valueconv.d @@ -64,11 +64,11 @@ { LexPosition pos = extractPos(t); StrValue typ = cast(StrValue) t.access!StrValue(ValueLayer, "is"); if( typ is null ) - throw genex!RuntimeException(text(`Invalid AST (no "is" field): `, _v)); + throw genex!RuntimeException(callpos, text(`Invalid AST (no "is" field): `, _v)); foreach(AT; ListOfASTTypes) if(typ.data == typeid(AT).name.split(".")[$-1].tolower()) { typeof(AT.tupleof) mems; Index: sample/macro.pmy ================================================================== --- sample/macro.pmy +++ sample/macro.pmy @@ -1,6 +1,6 @@ -@macro twice(x) { x; x }; +@macro twice = fun(x) { x; x }; @macro max(x,y) { var _x = x; # no hygenic macro btw.... var _y = y; # no hygenic macro btw.... if(_x<_y){_y}else{_x} }; @@ -13,30 +13,39 @@ @macro LetItBe(x, y) { let it = x in y }; @macro pow10(x) { @value( + def pow(y, n) { + if( n == 1 ) { y } + else { + @macro( @value(y) * @value(pow(y,n-1)) ) + } + } + in + pow(@macro(x+1),10) + ) +}; + +@macro pow10Hard(x) { + @value( def pow(x, n) { if( n == 1 ) { x } else { @macro( @value(x) * @value(pow(x,n-1)) ) } } in - pow(@macro(x),10) + pow(@macro(x+1),10) ) }; def printAndReturn(x) { print(x); x }; - - - - def main() { twice( print("foo") ); print("--------------"); @@ -46,9 +55,22 @@ print("--------------"); print(maxBad(printAndReturn(100),printAndReturn(200))); print("--------------"); print( LetItBe( 1+2+3, it*it ) ); print("--------------"); + print(pow10(1)); print(pow10(2)); + print("--------------"); + print(pow10Hard(1)); + print(pow10Hard(2)); +}; + +main(); + +@macro foo(y) +{ + fun(y){y}(300) +# let y = 300 in y }; -main() +print("--------------"); +print(foo(200));