@@ -18,69 +18,74 @@ /// Objects for maitaining global environment and evaluation of expression on it class Evaluator { public: - /// Initialize evaluator with empty context - this() { theContext = new Table; } - /// Evaluate the AST Value evalAST(AST e) - { - return macroAndEval(e, ValueLayer, theContext, OverwriteCtx); - } + { return getLayerEval(ValueLayer).macroAndEval(e, theContext, LayerEval.OverwriteCtx); } /// Evaluate the string Value evalString(S,T...)(S str, T fn_ln_cn) - { - return evalAST(parseString(str,fn_ln_cn)); - } + { return evalAST(parseString(str,fn_ln_cn)); } /// Evaluate the file Value evalFile(S,T...)(S filename, T ln_cn) - { - return evalAST(parseFile(filename,ln_cn)); - } + { return evalAST(parseFile(filename,ln_cn)); } /// Get the global context Table globalContext() + { return theContext; } + + /// Initialize evaluator with empty context + this() { - return theContext; + theContext = new Table; + theLayers[ValueLayer] = new ValueLayerEval; + theLayers[MacroLayer] = new MacroLayerEval; + theLayers[RawMacroLayer] = new RawMacroLayerEval; } private: - Table theContext; + Table theContext; + LayerEval[Layer] theLayers; - enum : bool { CascadeCtx=false, OverwriteCtx=true }; - - LayerEval getLayerEvaluator(Layer lay) + /// Get the layer evaluator from layer ID + LayerEval getLayerEval(Layer lay) { - if( lay == ValueLayer ) - return new ValueLayerEval; - if( lay == RawMacroLayer ) - return new RawMacroLayerEval; - if( lay == MacroLayer ) - return new MacroLayerEval; - return new UserDefinedLayerEval(lay); + if(auto p = lay in theLayers) + return *p; + return theLayers[lay] = new UserDefinedLayerEval(lay); } + /// Interface of layers abstract class LayerEval { + enum : bool { CascadeCtx=false, OverwriteCtx=true }; + /// 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 );/// + protected Layer layerID(); + protected Value eval_( Die e, Table ctx, bool ctxMod );/// + protected Value eval_( Str e, Table ctx, bool ctxMod );/// + protected Value eval_( Int e, Table ctx, bool ctxMod );/// + protected Value eval_( Var e, Table ctx, bool ctxMod );/// + protected Value eval_( Lay e, Table ctx, bool ctxMod );/// + protected Value eval_( Let e, Table ctx, bool ctxMod );/// + protected Value eval_( App e, Table ctx, bool ctxMod );/// + protected Value eval_( Fun e, Table ctx, bool ctxMod );/// + + /// Should override this also, if the layer may return null for optimization + Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx ) + { return eval(e,ctx,ctxMod); } + + /// Do macro expansion first, and then eval. Should override this also if it is NOT macro layer. + Value macroAndEval( AST e, Table ctx, bool ctxMod = CascadeCtx ) + { return evalToNonNull(e,ctx,ctxMod); } /// dynamic-overload-resolution - Value eval( AST e, Table ctx, bool ctxMod ) + Value eval( AST e, Table ctx, bool ctxMod = CascadeCtx ) { - enum funName = "eval_"; // modify here to customize - alias TypeTuple!(e,ctx,ctxMod) params; // modify here to customize + enum funName = "eval_"; // modify here for customization + alias TypeTuple!(e,ctx,ctxMod) params; // modify here for customization alias typeof(__traits(getOverloads, this, funName)) ovTypes; alias staticMap!(firstParam, ovTypes) fstTypes; alias DerivedToFront!(fstTypes) fstTypes_sorted; @@ -91,75 +96,69 @@ // 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) + /// Function calling convention. + /// Run all arugment AST in the appropriate layer and invoke the function. + 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); + FunValue f = cast(FunValue)f_; + if( f is null ) + throw genex!RuntimeException(pos, text("tried to call non-function: ",f)); + if( f.params().length != args.length ) + throw genex!RuntimeException(pos, + sprintf!("%d arguments required but %d passed")(f.params().length, args.length)); + + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); + foreach(i,p; f.params()) + if( p.layers.empty ) + newCtx.set(p.name, layerID(), this.evalToNonNull(args[i], ctx)); + else + foreach(argLay; p.layers) + { + Layer ll = argLay; + if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) ) + ll = RawMacroLayer; // explicit @macro invokes (rawmacro) + newCtx.set(p.name, argLay, getLayerEval(ll).evalToNonNull(args[i], ctx)); } - 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)); + scope _ = new PushCallStack(pos, callstackmsg); + return f.invoke(layerID(), newCtx, pos); } - /// + /// Lift the value v to the current layer Value lift(Value v, Table ctx, LexPosition pos) { - Layer lay = currentLayer(); - - // functions are automatically lifterd + // functions are automatically lifted by itself if( cast(FunValue) v ) return v; + Layer lay = layerID(); + + // load the lift function 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 f_ = ctx.get(lay, LiftLayer, pos); + FunValue f = cast(FunValue) f_; + if( f is null ) + throw genex!RuntimeException(pos, + text("non-function ", f_, " is registered as the lift function for ", lay)); + if( f.params().length != 1 || f.params()[0].layers.length>=1 ) + throw genex!RuntimeException(pos, + text("lift function must take exactly 1 neutral parameter:",lay)); + + // invokeFunction + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); + newCtx.set(f.params()[0].name, ValueLayer, v); + scope _ = new PushCallStack(pos, "lift to "~lay); + return f.invoke(ValueLayer, newCtx, pos); } } /// Evaluator for standard @value semantics class ValueLayerEval : LayerEval { - override Layer currentLayer() + override Layer layerID() { return ValueLayer; } override Value eval_( Die e, Table ctx, bool ctxMod ) @@ -175,26 +174,21 @@ return new IntValue(e.data); } override Value eval_( Var e, Table ctx, bool ctxMod ) { - return ctx.get(e.name, currentLayer(), e.pos); + return ctx.get(e.name, layerID(), 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; + return getLayerEval(e.layer).evalToNonNull(e.expr, ctx); } 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 ? currentLayer() : e.layer, ri); - return this.eval(e.expr, newCtx, OverwriteCtx); + if( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + Value ri = this.eval(e.init, ctx); + ctx.set(e.name, e.layer.empty ? layerID(): e.layer, ri); + return this.eval(e.expr, ctx, OverwriteCtx); } override Value eval_( App e, Table ctx, bool ctxMod ) { Value f = this.eval( e.fun, ctx, CascadeCtx ); @@ -203,47 +197,68 @@ override Value eval_( Fun e, Table ctx, bool ctxMod ) { return createNewFunction(e, ctx); } + override Value macroAndEval( AST e, Table ctx, bool ctxMod ) + { + // incremental execution of let-expressions + if(auto le = cast(Let)e) + { + AST ai = runMacro(le.init, ctx); + + if( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + + Value vi = this.eval(ai, ctx); + ctx.set(le.name, le.layer.empty ? layerID() : le.layer, vi); + return this.macroAndEval(le.expr, ctx, OverwriteCtx); + } + else + return this.eval(runMacro(e,ctx,ctxMod), ctx, ctxMod); + } } /// Evaluator for user-defined layer class UserDefinedLayerEval : ValueLayerEval { - Layer layerID; + Layer theID; mixin SimpleConstructor; - override Layer currentLayer() + override Layer layerID() { - return layerID; + return theID; } override Value eval_( Die e, Table ctx, bool ctxMod ) { - return new UndefinedValue; + return new BottomValue; } override Value eval_( Str e, Table ctx, bool ctxMod ) { - return this.lift(new StrValue(e.data), ctx, e.pos); + return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos); } override Value eval_( Int e, Table ctx, bool ctxMod ) { - return this.lift(new IntValue(e.data), ctx, e.pos); + return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos); } override Value eval_( Var e, Table ctx, bool ctxMod ) { - if( ctx.has(e.name, currentLayer()) ) - return ctx.get(e.name, currentLayer()); + if( ctx.has(e.name, layerID()) ) + return ctx.get(e.name, layerID()); return this.lift(ctx.get(e.name, ValueLayer, e.pos), ctx, e.pos); } } - // Convention!! - // returns null if never used macro-like feature + // Macro layer. For optimization, if AST didn't change it returns null class MacroLayerEval : LayerEval { - override Layer currentLayer() + override Layer layerID() { return MacroLayer; + } + override Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx ) + { + Value v = this.eval(e, ctx, ctxMod); + return v is null ? ast2table(e) : v; } override Value eval_( Die e, Table ctx, bool ctxMod ) { return null; @@ -257,28 +272,29 @@ 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; + if( ctx.has(e.name, layerID()) ) + return ctx.get(e.name, layerID(), e.pos); + return null; } override Value eval_( Lay e, Table ctx, bool ctxMod ) { - auto le = getLayerEvaluator(e.layer); - return le.eval(e.expr,ctx,CascadeCtx); + return getLayerEval(e.layer).eval(e.expr,ctx); } override Value eval_( Let e, Table ctx, bool ctxMod ) { - 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( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + + Value ai = this.eval(e.init, ctx); + ctx.set(e.name, NoopLayer, null); + Value ae = this.eval(e.expr, ctx, 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); @@ -285,15 +301,17 @@ }); } override Value eval_( App e, Table ctx, bool ctxMod ) { - Value f = this.eval( e.fun, ctx, CascadeCtx ); + Value f = this.eval( e.fun, ctx ); if(auto ff = cast(FunValue)f) return this.invokeFunction(ff, e.args, ctx, e.pos, getNameIfPossible(e.fun)); - else { + else + { bool allNull = (f is null); Value[] vas; - foreach(a; e.args) { + foreach(a; e.args) + { Value va = this.eval(a, ctx, CascadeCtx); if(va !is null) allNull = false; vas ~= va; } @@ -307,62 +325,45 @@ } } override Value eval_( Fun e, Table ctx, bool ctxMod ) { - Table newCtx = new Table(ctx, Table.Kind.NotPropagateSet); + ctx = new Table(ctx, Table.Kind.NotPropagateSet); foreach(p; e.params) - newCtx.set(p.name, NoopLayer, null); - Value af = this.eval(e.funbody, newCtx, CascadeCtx); + ctx.set(p.name, NoopLayer, null); + Value af = this.eval(e.funbody, ctx); if( af is null ) return null; return ast2table(e, (AST _){if(_ is e.funbody)return af; assert(false);}); } } + /// (rawmacro) layer. almost same as @macro, but the Layer expression becomes AST, too. class RawMacroLayerEval : MacroLayerEval { override Value eval_( Lay e, Table ctx, bool ctxMod ) { - Value ae = this.eval(e.expr, ctx, CascadeCtx); + Value ae = this.eval(e.expr, ctx); 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)); +private: // short utils - 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) { if(auto v = cast(Var)e) return v.name; return ""; } + AST runMacro(AST e, Table ctx, bool ctxMod=LayerEval.CascadeCtx) + { + Value v = getLayerEval(RawMacroLayer).eval(e, ctx, ctxMod); + return (v is null ? e : polemy2d!(AST)(v, e.pos)); + } + +private: Value createNewFunction(Fun e, Table ctx) { class UserDefinedFunValue : FunValue { @@ -388,65 +389,39 @@ override hash_t toHash() { return (cast(hash_t)cast(void*)ast) + (cast(hash_t)cast(void*)defCtx); } - AST macroCache; AST[void*] mandeCache; static class MemokeyType { - void* a; Layer b; Tuple!(string,Layer,Value)[] c; - hash_t toHash() { - hash_t h = structuralHash(a) + structuralHash(b); - foreach(e; c) - h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]); - return h; - } - mixin SimpleToString; - mixin SimpleConstructor; - mixin SimpleCompareWithoutToHash; + void* a; Layer b; Table c; + mixin SimpleClass; } static Tuple!(Value,int)[MemokeyType] memo; override Value invoke(Layer lay, Table ctx, LexPosition pos) { - 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]; - return va[0]; - } - else - return eval(macroCache, lay, ctx); - }; + auto evlay = getLayerEval(lay); + auto nonMemoizedRun = (){ return evlay.macroAndEval(ast.funbody, ctx); }; if( !isUserDefinedLayer(lay) ) return nonMemoizedRun(); - MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx.direct_entries()); - + // automatic memoized co-recursive execution + MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx); if(auto p = memokey in memo) { (*p)[1] ++; return (*p)[0]; } else - memo[memokey] = tuple(lift(new UndefinedValue, lay, ctx, pos), 0); + memo[memokey] = tuple(evlay.lift(new BottomValue, ctx, pos), 0); Value r = nonMemoizedRun(); 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); } @@ -502,9 +477,11 @@ theContext.set(name, defLay, new NativeFunValue(dg)); } } -version(unittest) import polemy.runtime; +version(unittest) + import polemy.runtime; + unittest { auto e = new Evaluator; enrollRuntimeLibrary(e); @@ -513,8 +490,9 @@ assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21)) ); assert_nothrow( e.globalContext.get("x",ValueLayer) ); assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) ); } + unittest { auto e = new Evaluator; enrollRuntimeLibrary(e); @@ -596,6 +574,6 @@ { auto e = new Evaluator; enrollRuntimeLibrary(e); assert_throw!RuntimeException( e.evalString(`...`) ); - assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new UndefinedValue ); + assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new BottomValue ); }