Diff
Not logged in

Differences From Artifact [c5ee9ab6830c78bd]:

To Artifact [cea762cacb86424f]:


2 2 * Authors: k.inaba 3 3 * License: NYSL 0.9982 http://www.kmonos.net/nysl/ 4 4 * 5 5 * Evaluator for Polemy programming language. 6 6 */ 7 7 module polemy.eval; 8 8 import polemy._common; 9 +import polemy.lex : LexPosition; 9 10 import polemy.ast; 10 11 import polemy.parse; 11 12 import polemy.runtime; 13 +import std.typecons; 12 14 13 15 Context createGlobalContext() 14 16 { 15 17 auto ctx = new Context; 16 - ctx.add("+", new PrimitiveFunction(delegate Value(Value[] args){ 18 + ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 17 19 if( args.length != 2 ) 18 - throw new PolemyRuntimeException("+ takes two arguments!!"); // TODO improve this message 20 + throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]"); 19 21 if( auto x = cast(IntValue)args[0] ) 20 22 if( auto y = cast(IntValue)args[1] ) 21 23 return new IntValue(x.data+y.data); 22 - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message 24 + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 23 25 })); 24 - ctx.add("-", new PrimitiveFunction(delegate Value(Value[] args){ 26 + ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 25 27 if( args.length != 2 ) 26 - throw new PolemyRuntimeException("- takes two arguments!!"); // TODO improve this message 28 + throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]"); 27 29 if( auto x = cast(IntValue)args[0] ) 28 30 if( auto y = cast(IntValue)args[1] ) 29 31 return new IntValue(x.data-y.data); 30 - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message 32 + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 31 33 })); 32 - ctx.add("*", new PrimitiveFunction(delegate Value(Value[] args){ 34 + ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 33 35 if( args.length != 2 ) 34 - throw new PolemyRuntimeException("* takes two arguments!!"); // TODO improve this message 36 + throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]"); 35 37 if( auto x = cast(IntValue)args[0] ) 36 38 if( auto y = cast(IntValue)args[1] ) 37 39 return new IntValue(x.data*y.data); 38 - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message 40 + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 39 41 })); 40 - ctx.add("/", new PrimitiveFunction(delegate Value(Value[] args){ 42 + ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 41 43 if( args.length != 2 ) 42 - throw new PolemyRuntimeException("/ takes two arguments!!"); // TODO improve this message 44 + throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]"); 43 45 if( auto x = cast(IntValue)args[0] ) 44 46 if( auto y = cast(IntValue)args[1] ) 45 47 return new IntValue(x.data/y.data); 46 - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message 48 + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 47 49 })); 48 50 return ctx; 49 51 } 50 52 51 -Context evalString(T...)(T params) 53 +Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params) 52 54 { 53 55 return eval( parserFromString(params).parseProgram() ); 54 56 } 55 57 56 -Context evalFile(T...)(T params) 58 +Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params) 57 59 { 58 60 return eval( parserFromFile(params).parseProgram() ); 59 61 } 60 62 61 -Context eval(Program prog) 63 +Tuple!(Value,"val",Context,"ctx") eval(Program prog) 62 64 { 63 - return eval(prog, createGlobalContext()); 65 + Context ctx = createGlobalContext(); 66 + return typeof(return)(eval(prog, ctx), ctx); 64 67 } 65 68 66 -Context eval(Program prog, Context ctx) 69 +Value eval(Program prog, Context ctx) 67 70 { 71 + Value v = new UndefinedValue; 68 72 foreach(s; prog) 69 - ctx = eval(s, ctx); 70 - return ctx; 73 + v = eval(s, ctx); 74 + return v; 71 75 } 72 76 73 -Context eval(Statement _s, Context ctx) 77 +Value eval(Statement _s, Context ctx) 74 78 { 75 79 if( auto s = cast(DeclStatement)_s ) 76 80 { 77 81 auto v = eval(s.expr, ctx); 78 82 ctx.add(s.var, v); 79 - return ctx; 83 + return v; 80 84 } 81 85 else 82 86 if( auto s = cast(ExprStatement)_s ) 83 87 { 84 - eval(s.expr, ctx); 85 - return ctx; 88 + return eval(s.expr, ctx); 86 89 } 87 90 throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos)); 88 91 } 89 92 90 93 Value eval(Expression _e, Context ctx) 91 94 { 92 95 if( auto e = cast(StrLiteralExpression)_e ) ................................................................................ 118 121 if( auto e = cast(FuncallExpression)_e ) 119 122 { 120 123 Value _f = eval(e.fun, ctx); 121 124 if( auto f = cast(FunValue)_f ) { 122 125 Value[] args; 123 126 foreach(a; e.args) 124 127 args ~= eval(a, ctx); 125 - return f.call(args); 128 + return f.call(e.pos, args); 126 129 } else 127 130 throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos)); 131 + } 132 + else 133 + if( auto e = cast(FunLiteralExpression)_e ) 134 + { 135 + return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 136 + if( e.params.length != args.length ) 137 + throw new PolemyRuntimeException(sprintf!"Argument Number Mismatch (%d required but %d given) at [%s]" 138 + (e.params.length, args.length, e.pos)); 139 + Context ctxNeo = new Context(ctx); 140 + foreach(i,p; e.params) 141 + ctxNeo.add(p, args[i]); 142 + return eval(e.funbody, ctxNeo); 143 + }); 128 144 } 129 145 throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos)); 130 146 } 131 147 132 - 133 -version(unittest) import polemy.parse; 134 -version(unittest) import std.stdio; 135 -version(unittest) import std.exception; 148 +import std.stdio; 136 149 unittest 137 150 { 138 - auto ctx = evalString(`var x = 21; x = x + x*x;`); 139 - assert( ctx["x"] == new IntValue(BigInt(21+21*21)) ); 140 - assert( !collectException(ctx["x"]) ); 141 - assert( collectException(ctx["y"]) ); 151 + auto r = evalString(`var x = 21; x = x + x*x;`); 152 + assert( r.val == new IntValue(BigInt(21+21*21)) ); 153 + assert( r.ctx["x"] == new IntValue(BigInt(21+21*21)) ); 154 + assert( !collectException(r.ctx["x"]) ); 155 + assert( collectException(r.ctx["y"]) ); 142 156 } 143 157 unittest 144 158 { 145 159 assert( collectException(evalString(`var x = 21; x = x + x*y;`)) ); 160 + assert( collectException(evalString(`x=1;`)) ); 161 +} 162 +unittest 163 +{ 164 + auto r = evalString(`var x = fun(a){1+a;}(2);`); 165 + assert( r.ctx["x"] == new IntValue(BigInt(3)) ); 166 + assert( r.val == new IntValue(BigInt(3)) ); 146 167 } 147 168 unittest 148 169 { 149 - assert( collectException(evalString(`var x = 21; y = x + x*x;`)) ); 170 + auto r = evalString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); f();`); 171 + assert( r.ctx["x"] == new IntValue(BigInt(4)) ); 172 + assert( r.val == new IntValue(BigInt(4)) ); 150 173 }