Index: .poseidon ================================================================== --- .poseidon +++ .poseidon @@ -9,11 +9,11 @@ 0 main.d - -g -profile -unittest + -g -unittest Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -23,12 +23,11 @@ this() { theContext = new Table; } /// Evaluate the AST Value evalAST(AST e) { - AST[void*] mandeCache; - return macroAndEval(e, ValueLayer, theContext, OverwriteCtx, mandeCache)[0]; + return macroAndEval(e, ValueLayer, theContext, OverwriteCtx); } /// Evaluate the string Value evalString(S,T...)(S str, T fn_ln_cn) { @@ -50,169 +49,310 @@ private: Table theContext; enum : bool { CascadeCtx=false, OverwriteCtx=true }; - Value eval( AST e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) + LayerEval getLayerEvaluator(Layer lay) { - // dynamic-overload-resolution-pattern: modify here - enum funName = "eval"; - alias TypeTuple!(e,lay,ctx,overwriteCtx) params; - - // dynamic-overload-resolution-pattern: dispatch - alias typeof(__traits(getOverloads, this, funName)) ovTypes; - alias staticMap!(firstParam, ovTypes) fstTypes; - alias DerivedToFront!(fstTypes) fstTypes_sorted; - foreach(i, T; fstTypes_sorted) - static if( is(T == typeof(params[0])) ) {} else if( auto _x = cast(T)params[0] ) - return __traits(getOverloads, this, funName)[i](_x, params[1..$]); - - // dynamic-overload-resolution-pattern: default behavior - assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined")); + if( lay == ValueLayer ) + return new ValueLayerEval; + if( lay == RawMacroLayer ) + return new RawMacroLayerEval; + if( lay == MacroLayer ) + return new MacroLayerEval; + return new UserDefinedLayerEval(lay); } - Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) - { - if( isASTLayer(lay) ) - return ast2table(e, (AST e){return eval(e,lay,ctx);}); - if( isUserDefinedLayer(lay) ) - return lift(new StrValue(e.data), lay, ctx, e.pos); - return new StrValue(e.data); - } - - Value eval( Int e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) + abstract class LayerEval { - if( isASTLayer(lay) ) - return ast2table(e, (AST e){return eval(e,lay,ctx);}); - if( isUserDefinedLayer(lay) ) - return lift(new IntValue(e.data), lay, ctx, e.pos); - return new IntValue(e.data); - } - - Value eval( Var e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) - { - 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( isUserDefinedLayer(lay) && !ctx.has(e.name, lay) ) - return lift(ctx.get(e.name, ValueLayer, e.pos), lay, ctx, e.pos); - return ctx.get(e.name, lay, e.pos); + /// Concrete layers should implement these + Layer currentLayer(); + Value eval_( Die e, Table ctx, bool ctxMod );/// + Value eval_( Str e, Table ctx, bool ctxMod );/// + Value eval_( Int e, Table ctx, bool ctxMod );/// + Value eval_( Var e, Table ctx, bool ctxMod );/// + Value eval_( Lay e, Table ctx, bool ctxMod );/// + Value eval_( Let e, Table ctx, bool ctxMod );/// + Value eval_( App e, Table ctx, bool ctxMod );/// + Value eval_( Fun e, Table ctx, bool ctxMod );/// + + /// dynamic-overload-resolution + Value eval( AST e, Table ctx, bool ctxMod ) + { + enum funName = "eval_"; // modify here to customize + alias TypeTuple!(e,ctx,ctxMod) params; // modify here to customize + + alias typeof(__traits(getOverloads, this, funName)) ovTypes; + alias staticMap!(firstParam, ovTypes) fstTypes; + alias DerivedToFront!(fstTypes) fstTypes_sorted; + foreach(i, T; fstTypes_sorted) + static if( is(T == typeof(params[0])) ) {} else if( auto _x = cast(T)params[0] ) + return __traits(getOverloads, this, funName)[i](_x, params[1..$]); + + // modify here to customize the default behavior + assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined")); + } + + /// + Value invokeFunction(Value _f, AST[] args, Table ctx, LexPosition pos, string callstackmsg) + { + if(auto f = cast(FunValue)_f) + { + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); + foreach(i,p; f.params()) + if( p.layers.empty ) { + Value v = this.eval(args[i], ctx, CascadeCtx); + if(v is null) v = ast2table(args[i]); + newCtx.set(p.name, currentLayer(), v); + } + else + foreach(argLay; p.layers) { + Layer ll = argLay; + if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) ) + ll = RawMacroLayer; // explicit @macro invokes (rawmacro) + Value v = getLayerEvaluator(ll).eval(args[i], ctx, CascadeCtx); + if(v is null) v = ast2table(args[i]); + newCtx.set(p.name, argLay, v); + } + scope _ = new PushCallStack(pos, callstackmsg); + return f.invoke(currentLayer(), newCtx, pos); + } + throw genex!RuntimeException(pos, text("tried to call non-function: ",_f)); + } + + /// + Value lift(Value v, Table ctx, LexPosition pos) + { + Layer lay = currentLayer(); + + // functions are automatically lifterd + if( cast(FunValue) v ) + return v; + + if( !ctx.has(lay, LiftLayer) ) + throw genex!RuntimeException(pos, "lift function for "~lay~" is not registered" ); + + // similar to invokeFunction, but with only one argument bound to ValueLayer + auto _f = ctx.get(lay, LiftLayer, pos); + if(auto f = cast(FunValue)_f) + { + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); + auto ps = f.params(); + if( ps.length != 1 ) + throw genex!RuntimeException(pos, + text("lift function for", lay, " must take exactly one argument of ", ValueLayer)); + if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer ) + { + newCtx.set(ps[0].name, ValueLayer, v); + scope _ = new PushCallStack(pos, lay); + return f.invoke(ValueLayer, newCtx, pos); + } + else + throw genex!RuntimeException(pos, + text("lift function for", lay, " must take exactly one argument of ", ValueLayer)); + } + throw genex!RuntimeException(pos, + text("non-function ", _f, " is registered as the lift function for ", lay)); + } } - Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) - { - Value f = eval( e.fun, lay, ctx ); - 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 ) + /// Evaluator for standard @value semantics + class ValueLayerEval : LayerEval { - 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, NoopLayer, null); - 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 ) - { - if( isNoLayerChangeLayer(lay) ) - return ast2table(e, (AST e){return eval(e,lay,ctx);}); - else - return eval(e.expr, e.layer, ctx); - } - - Value eval( Let e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) - { - Table newCtx = overwriteCtx ? ctx : new Table(ctx, Table.Kind.NotPropagateSet); - if( isASTLayer(lay) ) - return ast2table(e, (AST ee){ - // need this for correct scoping (outer scope macro variables must be hidden!) - if(e.name!="_" && ee is e.expr) - newCtx.set(e.name, NoopLayer, null); - return eval(ee,lay,newCtx); - }); - else - { - Value ri = eval(e.init, lay, newCtx); + override Layer currentLayer() + { + return ValueLayer; + } + override Value eval_( Die e, Table ctx, bool ctxMod ) + { + throw genex!RuntimeException(e.pos, "undefined case"); + } + override Value eval_( Str e, Table ctx, bool ctxMod ) + { + return new StrValue(e.data); + } + override Value eval_( Int e, Table ctx, bool ctxMod ) + { + return new IntValue(e.data); + } + override Value eval_( Var e, Table ctx, bool ctxMod ) + { + return ctx.get(e.name, currentLayer(), e.pos); + } + override Value eval_( Lay e, Table ctx, bool ctxMod ) + { + auto le = getLayerEvaluator(e.layer); + auto v = le.eval(e.expr,ctx,CascadeCtx); + if( (v is null) && (cast(MacroLayerEval)le !is null) ) + return ast2table(e.expr); + else + return v; + } + override Value eval_( Let e, Table ctx, bool ctxMod ) + { + Table newCtx = ctxMod ? ctx : new Table(ctx, Table.Kind.NotPropagateSet); + Value ri = this.eval(e.init, newCtx, CascadeCtx); if(e.name!="_") - newCtx.set(e.name, e.layer.empty ? lay : e.layer, ri); - return eval(e.expr, lay, newCtx, OverwriteCtx); + newCtx.set(e.name, e.layer.empty ? currentLayer() : e.layer, ri); + return this.eval(e.expr, newCtx, OverwriteCtx); + } + override Value eval_( App e, Table ctx, bool ctxMod ) + { + Value f = this.eval( e.fun, ctx, CascadeCtx ); + return this.invokeFunction(f, e.args, ctx, e.pos, getNameIfPossible(e.fun)); + } + override Value eval_( Fun e, Table ctx, bool ctxMod ) + { + return createNewFunction(e, ctx); } } - Value eval( Die e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) + /// Evaluator for user-defined layer + class UserDefinedLayerEval : ValueLayerEval { - if( isMacroLayer(lay) ) - return ast2table(e, (AST e){return eval(e,lay,ctx);}); - if( isUserDefinedLayer(lay) ) + Layer layerID; + mixin SimpleConstructor; + + override Layer currentLayer() + { + return layerID; + } + override Value eval_( Die e, Table ctx, bool ctxMod ) + { return new UndefinedValue; - throw genex!RuntimeException(e.pos, "undefined case"); + } + override Value eval_( Str e, Table ctx, bool ctxMod ) + { + return this.lift(new StrValue(e.data), ctx, e.pos); + } + override Value eval_( Int e, Table ctx, bool ctxMod ) + { + return this.lift(new IntValue(e.data), ctx, e.pos); + } + override Value eval_( Var e, Table ctx, bool ctxMod ) + { + if( ctx.has(e.name, currentLayer()) ) + return ctx.get(e.name, currentLayer()); + return this.lift(ctx.get(e.name, ValueLayer, e.pos), ctx, e.pos); + } } -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 - , ref AST[void*] mandeCache) // [TODO] avoid assuming non-moving GC + // Convention!! + // returns null if never used macro-like feature + class MacroLayerEval : LayerEval { - assert( !isASTLayer(lay) ); - - AST decodeAST(Value v, LexPosition pos) + override Layer currentLayer() + { + return MacroLayer; + } + override Value eval_( Die e, Table ctx, bool ctxMod ) + { + return null; + } + override Value eval_( Str e, Table ctx, bool ctxMod ) + { + return null; + } + override Value eval_( Int e, Table ctx, bool ctxMod ) + { + return null; + } + override Value eval_( Var e, Table ctx, bool ctxMod ) + { + if( ctx.has(e.name, currentLayer()) ) + return ctx.get(e.name, currentLayer(), e.pos); + else + return null; + } + override Value eval_( Lay e, Table ctx, bool ctxMod ) { - // [TODO] more informative error message - return polemy2d!(AST)(v, pos); + auto le = getLayerEvaluator(e.layer); + return le.eval(e.expr,ctx,CascadeCtx); } - - if(auto e = cast(Let)e_) + override Value eval_( Let e, Table ctx, bool ctxMod ) { - void* key = cast(void*)e.init; - AST ai; - if(auto p = key in mandeCache) - ai = *p; - else { - ai = decodeAST(eval(e.init, RawMacroLayer, ctx), e.init.pos); - mandeCache[key] = ai; - } - Value vi = eval(ai, lay, ctx); - - if( !overwriteCtx ) - ctx = new Table(ctx, Table.Kind.NotPropagateSet); - string theLayer = e.layer.empty ? lay : e.layer; - ctx.set(e.name, theLayer, vi); - - auto ave = macroAndEval( e.expr, lay, ctx, OverwriteCtx, mandeCache ); - AST a = new Let(e.pos, e.name, e.layer, ai, ave[1]); - return tuple(ave[0], a); + Table newCtx = ctxMod ? ctx : new Table(ctx, Table.Kind.NotPropagateSet); + Value ai = this.eval(e.init, newCtx, CascadeCtx); + newCtx.set(e.name, NoopLayer, null); + Value ae = this.eval(e.expr, newCtx, OverwriteCtx); + if( ai is null && ae is null ) + return null; + if( ai is null ) ai = ast2table(e.init); + if( ae is null ) ae = ast2table(e.expr); + return ast2table(e, delegate Value (AST _){ + if(_ is e.init) { return ai; } + if(_ is e.expr) { return ae; } + assert(false); + }); } - else + override Value eval_( App e, Table ctx, bool ctxMod ) { - void* key = cast(void*)e_; - AST a; - if(auto p = key in mandeCache) - a = *p; + Value f = this.eval( e.fun, ctx, CascadeCtx ); + if(auto ff = cast(FunValue)f) + return this.invokeFunction(ff, e.args, ctx, e.pos, getNameIfPossible(e.fun)); else { - a = decodeAST(eval(e_, RawMacroLayer, ctx), e_.pos); - mandeCache[key] = a; + bool allNull = (f is null); + Value[] vas; + foreach(a; e.args) { + Value va = this.eval(a, ctx, CascadeCtx); + if(va !is null) allNull = false; + vas ~= va; + } + if( allNull ) + return null; + return ast2table(e, delegate Value (AST _){ + if(_ is e.fun) return (f is null ? ast2table(e.fun) : f); + foreach(i,a; e.args) if(_ is a) return (vas[i] is null ? ast2table(a) : vas[i]); + assert(false); + }); } - Value v = eval(a, lay, ctx, overwriteCtx); - return tuple(v, a); + } + override Value eval_( Fun e, Table ctx, bool ctxMod ) + { + Table newCtx = new Table(ctx, Table.Kind.NotPropagateSet); + foreach(p; e.params) + newCtx.set(p.name, NoopLayer, null); + Value af = this.eval(e.funbody, newCtx, CascadeCtx); + if( af is null ) + return null; + return ast2table(e, (AST _){if(_ is e.funbody)return af; assert(false);}); + } + } + + class RawMacroLayerEval : MacroLayerEval + { + override Value eval_( Lay e, Table ctx, bool ctxMod ) + { + Value ae = this.eval(e.expr, ctx, CascadeCtx); + return ae is null ? null + : ast2table(e, delegate Value (AST _){if(_ is e.expr)return ae; assert(false);}); + } + } + +private: + Value macroAndEval( AST e_, Layer lay, Table ctx, bool ctxMod ) + { + assert( !isASTLayer(lay) ); + if(auto e = cast(Let)e_) + { + Value vai = getLayerEvaluator(RawMacroLayer).eval(e.init, ctx, CascadeCtx); + AST ai = (vai is null ? e.init : polemy2d!(AST)(vai, e.pos)); + + if( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + + Value vi = getLayerEvaluator(lay).eval(ai, ctx, CascadeCtx); + string theLayer = e.layer.empty ? lay : e.layer; + ctx.set(e.name, theLayer, vi); + + return macroAndEval( e.expr, lay, ctx, OverwriteCtx ); + } + else + { + Value va = getLayerEvaluator(RawMacroLayer).eval(e_, ctx, ctxMod); + AST a = (va is null ? e_ : polemy2d!(AST)(va, e_.pos)); + return getLayerEvaluator(lay).eval(a, ctx, ctxMod); } } private: string getNameIfPossible(AST e) @@ -219,64 +359,10 @@ { if(auto v = cast(Var)e) return v.name; return ""; } - - Value invokeFunction(Value _f, AST[] args, Layer lay, Table ctx, LexPosition pos, string callstackmsg) - { - 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, isMacroLayer(lay)?MacroLayer:lay, eval(args[i], lay, ctx)); - else - foreach(argLay; p.layers) - if( lay!=MacroLayer && isMacroLayer(argLay) ) // explicit @macro invokes (rawmacro) - newCtx.set(p.name, argLay, eval(args[i], RawMacroLayer, ctx)); - else - newCtx.set(p.name, argLay, eval(args[i], argLay, ctx)); - scope _ = new PushCallStack(pos, callstackmsg); - 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( !isASTLayer(lay), "lift to the @macro layer should never happen" ); - - // functions are automatically lifterd - if( cast(FunValue) v ) - return v; - - if( !ctx.has(lay, LiftLayer) ) - throw genex!RuntimeException(pos, "lift function for "~lay~" is not registered" ); - - // similar to invokeFunction, but with only one argument bound to ValueLayer - auto _f = ctx.get(lay, LiftLayer, pos); - if(auto f = cast(FunValue)_f) - { - Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); - auto ps = f.params(); - if( ps.length != 1 ) - throw genex!RuntimeException(pos, - text("lift function for", lay, " must take exactly one argument of ", ValueLayer)); - if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer ) - { - newCtx.set(ps[0].name, ValueLayer, v); - scope _ = new PushCallStack(pos, lay); - return f.invoke(ValueLayer, newCtx, pos); - } - else - throw genex!RuntimeException(pos, - text("lift function for", lay, " must take exactly one argument of ", ValueLayer)); - } - throw genex!RuntimeException(pos, - text("non-function ", _f, " is registered as the lift function for ", lay)); - } Value createNewFunction(Fun e, Table ctx) { class UserDefinedFunValue : FunValue { @@ -320,13 +406,17 @@ } static Tuple!(Value,int)[MemokeyType] memo; override Value invoke(Layer lay, Table ctx, LexPosition pos) { - if( isASTLayer(lay) ) - return eval(ast.funbody, lay, ctx); - + if( isASTLayer(lay) ) { + Value v = getLayerEvaluator(lay).eval(ast.funbody, ctx, CascadeCtx); + if( v is null ) v = ast2table(ast.funbody); + return v; + } + return macroAndEval(ast.funbody, lay, ctx, CascadeCtx); +/* auto nonMemoizedRun = (){ if( macroCache is null ) { auto va = macroAndEval(e.funbody, lay, ctx, CascadeCtx, mandeCache); macroCache = va[1]; @@ -353,10 +443,11 @@ int touched = memo[memokey][1]; memo[memokey] = tuple(r, 12345678); //if(touched) {DBG("rerun :: ",r);r = nonMemoizedRun();} // twice!! return r; +*/ } } return new UserDefinedFunValue(e,ctx); } Index: polemy/valueconv.d ================================================================== --- polemy/valueconv.d +++ polemy/valueconv.d @@ -175,5 +175,23 @@ return t; } else static assert(false, "unknown type <"~T.stringof~"> during AST encoding"); } + +/// No hook version +Value ast2table(T)(T e) +{ + //[TODO] I really need to automate this!!!!!!!!!!!!1 + Value rec(AST _) { + if(auto e = cast(Str)_) return ast2table(e, &rec); + if(auto e = cast(Int)_) return ast2table(e, &rec); + if(auto e = cast(Var)_) return ast2table(e, &rec); + if(auto e = cast(Die)_) return ast2table(e, &rec); + if(auto e = cast(Let)_) return ast2table(e, &rec); + if(auto e = cast(Lay)_) return ast2table(e, &rec); + if(auto e = cast(App)_) return ast2table(e, &rec); + if(auto e = cast(Fun)_) return ast2table(e, &rec); + assert(false); + } + return rec(e); +} Index: sample/macro.pmy ================================================================== --- sample/macro.pmy +++ sample/macro.pmy @@ -96,29 +96,10 @@ @macro test1(y) { fun(y){y}("original") }; @macro test2(y) { let y = "original" in y }; print( test1("replaced?") ); print( test2("replaced?") ); -######################################## -print("----------"); - -# Macro expansion is done only at the first call. -# So by using @macro parameter, it can remember the argument -# of the first call. -def remember1( x @macro, y ) { if x == y then "yes" else "no" }; -print( remember1(1, 1) ); # yes 1 == 1 -print( remember1(2,1) ); # yes "1" == 1 -print( remember1(2,2) ); # no "1" != 2 - -# exactly the same function, but called in different order -def remember2( x @macro, y ) { if x == y then "yes" else "no" }; -print( remember2(2, 2) ); # yes "2" == 2 -print( remember2(2, 1) ); # no "2" != 1 -print( remember2(1, 1) ); # no "2" != 1 - -# Is this a good thing or a bad thing?? - ######################################## print("----------"); # Trick to extract the AST of a function def foo(x) { x + x };