@@ -11,28 +11,34 @@ import polemy.parse; import polemy.value; import polemy.layer; +/// 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 eval(e, ValueLayer, theContext, OverwriteCtx); } + /// Evaluate the string Value evalString(S,T...)(S str, T 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)); } + /// Get the global context Table globalContext() { return theContext; } @@ -39,11 +45,9 @@ private: Table theContext; -private: 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"; @@ -60,53 +64,33 @@ // dynamic-overload-resolution-pattern: default behavior assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined")); } -private: Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - Value v = new StrValue(e.data); - if( lay==RawMacroLayer || lay==MacroLayer ) - { - auto ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("str")); - ast.set("data", ValueLayer, v); - return ast; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer ) - return v; - return lift(v, lay, ctx, e.pos); + 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 ) { - Value v = new IntValue(e.data); - if( lay==RawMacroLayer || lay==MacroLayer ) - { - auto ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("int")); - ast.set("data", ValueLayer, v); - return ast; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer ) - return v; - return lift(v, lay, ctx, e.pos); + 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( lay==RawMacroLayer || lay==MacroLayer ) - { + if( isMacroishLayer(lay) ) if( ctx.has(e.name,MacroLayer) ) return ctx.get(e.name, MacroLayer, e.pos); - auto ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("var")); - ast.set("name", ValueLayer, new StrValue(e.name)); - return ast; - } + 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); return lift(ctx.get(e.name, ValueLayer, e.pos), lay, ctx, e.pos); } @@ -113,93 +97,43 @@ Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { Value f = eval( e.fun, lay, ctx ); - if( lay==RawMacroLayer || lay==MacroLayer ) - { + if( isMacroishLayer(lay) ) if( auto ff = cast(FunValue)f ) return invokeFunction(ff, e.args, MacroLayer, ctx, e.pos); - Table ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("app")); - ast.set("fun", ValueLayer, f); - Table args = new Table; - foreach_reverse(a; e.args) - args = makeCons(eval(a, lay, ctx), args); - ast.set("args", ValueLayer, args); - return ast; - } - else - { - return invokeFunction(f, e.args, lay, ctx, e.pos); - } + else + return ast2table(e, (AST e){return eval(e,lay,ctx);}); + return invokeFunction(f, e.args, lay, ctx, e.pos); } Value eval( Fun e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( lay==RawMacroLayer || lay==MacroLayer ) - { - Table t = new Table; - t.set("pos", ValueLayer, fromPos(e.pos)); - t.set("is", ValueLayer, new StrValue("fun")); - t.set("funbody", ValueLayer, eval(e.funbody,lay,ctx)); - Table params = new Table; - foreach_reverse(p; e.params) - { - Table lays = new Table; - foreach_reverse(l; p.layers) - lays = makeCons(new StrValue(l), lays); - Table kv = new Table; - kv.set("name", ValueLayer, new StrValue(p.name)); - kv.set("layers", ValueLayer, lays); - Table cons = new Table; - params = makeCons(kv, params); - } - t.set("params", ValueLayer, params); - return t; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); else - { return createNewFunction(e, ctx); - } } Value eval( Lay e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( lay == RawMacroLayer ) - { - Value r = eval(e.expr, lay, ctx); - auto ast = new Table; // todo: pos - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("lay")); - ast.set("layer", ValueLayer, new StrValue(e.layer)); - ast.set("expr", ValueLayer, r); - return ast; - } + 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 ) { // todo @macro let - if( lay==RawMacroLayer || lay==MacroLayer ) - { - auto ast = new Table; // todo: pos - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("let")); - ast.set("name", ValueLayer, new StrValue(e.name)); - ast.set("layer", ValueLayer, new StrValue(e.layer)); - ast.set("init", ValueLayer, eval(e.init, lay, ctx)); - ast.set("expr", ValueLayer, eval(e.expr, lay, ctx)); - return ast; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); else { if( !overwriteCtx ) ctx = new Table(ctx, Table.Kind.NotPropagateSet); Value ri = eval(e.init, lay, ctx); - string theLayer = e.layer.empty ? (lay==RawMacroLayer ? MacroLayer : lay) : e.layer; + string theLayer = e.layer.empty ? lay : e.layer; // neutral layer ctx.set(e.name, theLayer, ri); return eval(e.expr, lay, ctx, OverwriteCtx); } } @@ -215,15 +149,17 @@ newCtx.set(p.name, (lay==RawMacroLayer ? MacroLayer : lay), eval(args[i], lay, ctx)); else foreach(argLay; p.layers) newCtx.set(p.name, argLay, eval(args[i], argLay, ctx)); - return f.invoke(pos, lay, newCtx); + return f.invoke(lay==RawMacroLayer ? 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=null) { + assert( !isMacroishLayer(lay), "lift to the @macro layer should not happen" ); + // functions are automatically lifterd if( cast(FunValue) v ) return v; @@ -236,9 +172,9 @@ throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer"); if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer ) { newCtx.set(ps[0].name, ValueLayer, v); - return f.invoke(pos, ValueLayer, newCtx); + return f.invoke(ValueLayer, newCtx, pos); } else throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer"); } @@ -276,43 +212,58 @@ } assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); } - override Value invoke(LexPosition pos, Layer lay, Table ctx) + override Value invoke(Layer lay, Table ctx, LexPosition pos) { if( lay == MacroLayer ) return eval(ast.funbody, lay, ctx); - auto macroed = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx)); - return eval(macroed, lay, ctx); + if( afterMacroAST is null ) + afterMacroAST = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx)); + return eval(afterMacroAST, lay, ctx); } + + AST afterMacroAST; } return new UserDefinedFunValue(e,ctx); } public: - /// TODO: move up - /// TDOO: to other layers? - void addPrimitive(R,T...)(string name, Layer lay, R delegate (T) dg) + /// Add primitive function to the global context + void addPrimitive(R,T...)(string name, Layer defLay, R delegate (T) dg) { class NativeFunValue : FunValue { - Parameter[] params_data; - override string toString() { return sprintf!"(native:%x)"(dg.funcptr); } override const(Parameter[]) params() { return params_data; } - override Table definitionContext() { return new Table; } // todo: cache - this(){ + override Table definitionContext() { return theContext; } + + override string toString() { return sprintf!"(native:%x)"(dg.funcptr); } + override int opCmp(Object rhs) { + if(auto r = cast(NativeFunValue)rhs) return typeid(typeof(dg)).compare(&dg,&r.dg); + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + } + mixin SimpleToHash; + + R delegate(T) dg; + Parameter[] params_data; + + this(R delegate(T) dg) + { + this.dg = dg; foreach(i, Ti; T) params_data ~= new Parameter(text(i), []); } - override Value invoke(LexPosition pos, Layer lay, Table ctx) + + override Value invoke(Layer lay, Table ctx, LexPosition pos) { - if( lay != ValueLayer ) - throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function"); + if( lay != defLay ) + throw genex!RuntimeException(pos, text("only ", defLay, " layer can call native function: ", name)); T typed_args; foreach(i, Ti; T) { - typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer); + typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer, pos); if( typed_args[i] is null ) - throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1)); + throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d of native function: %s"(i+1,name)); } try { return dg(typed_args); } catch( RuntimeException e ) { @@ -319,9 +270,9 @@ throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e; } } } - theContext.set(name, lay, new NativeFunValue); + theContext.set(name, defLay, new NativeFunValue(dg)); } } version(unittest) import polemy.runtime;