Diff
Not logged in

Differences From Artifact [003971cb94b55966]:

To Artifact [1a40d715cf0caee4]:


1 -/** 1 +/** 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 9 import polemy.lex : LexPosition; 10 10 import polemy.ast; 11 11 import polemy.parse; 12 12 import polemy.value; 13 13 import std.typecons; 14 14 import std.stdio; 15 15 16 -Context createGlobalContext() 16 +Table createGlobalContext() 17 17 { 18 - auto ctx = new Context; 19 - ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 18 + auto ctx = new Table; 19 + // [TODO] autogenerate these typechecks 20 + ctx.set("+", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 20 21 if( args.length != 2 ) 21 - throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]"); 22 + throw new RuntimeException(pos, "+ takes two arguments!!"); 22 23 if( auto x = cast(IntValue)args[0] ) 23 24 if( auto y = cast(IntValue)args[1] ) 24 25 return new IntValue(x.data+y.data); 25 - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 26 + throw new RuntimeException(pos, "cannot add non-integers"); 26 27 })); 27 - ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 28 + ctx.set("-", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 28 29 if( args.length != 2 ) 29 - throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]"); 30 + throw new RuntimeException(pos, "- takes two arguments!!"); 30 31 if( auto x = cast(IntValue)args[0] ) 31 32 if( auto y = cast(IntValue)args[1] ) 32 33 return new IntValue(x.data-y.data); 33 - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 34 + throw new RuntimeException(pos, "cannot subtract non-integers"); 34 35 })); 35 - ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 36 + ctx.set("*", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 36 37 if( args.length != 2 ) 37 - throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]"); 38 + throw new RuntimeException(pos, "* takes two arguments!!"); 38 39 if( auto x = cast(IntValue)args[0] ) 39 40 if( auto y = cast(IntValue)args[1] ) 40 41 return new IntValue(x.data*y.data); 41 - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 42 + throw new RuntimeException(pos, "cannot multiply non-integers"); 42 43 })); 43 - ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 44 + ctx.set("/", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 44 45 if( args.length != 2 ) 45 - throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]"); 46 + throw new RuntimeException(pos, "/ takes two arguments!!"); 46 47 if( auto x = cast(IntValue)args[0] ) 47 48 if( auto y = cast(IntValue)args[1] ) 48 49 return new IntValue(x.data/y.data); 49 - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 50 + throw new RuntimeException(pos, "cannot divide non-integers"); 50 51 })); 51 - ctx.add("<", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 52 + ctx.set("<", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 52 53 if( args.length != 2 ) 53 - throw new PolemyRuntimeException("< takes two arguments!! ["~to!string(pos)~"]"); 54 + throw new RuntimeException(pos, "< takes two arguments!!"); 54 55 if( auto x = cast(IntValue)args[0] ) 55 56 if( auto y = cast(IntValue)args[1] ) 56 57 return new IntValue(BigInt(to!int(x.data < y.data))); 57 - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 58 + throw new RuntimeException(pos, "cannot compare non-integers"); 58 59 })); 59 - ctx.add(">", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 60 + ctx.set(">", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 60 61 if( args.length != 2 ) 61 - throw new PolemyRuntimeException("> takes two arguments!! ["~to!string(pos)~"]"); 62 + throw new RuntimeException(pos, "> takes two arguments!!"); 62 63 if( auto x = cast(IntValue)args[0] ) 63 64 if( auto y = cast(IntValue)args[1] ) 64 65 return new IntValue(BigInt(to!int(x.data>y.data))); 65 - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); 66 + throw new RuntimeException(pos, "cannot compare non-integers"); 66 67 })); 67 - ctx.add("print", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 68 + ctx.set("print", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 68 69 foreach(a; args) 69 70 write(a); 70 71 writeln(""); 71 - return new UndefinedValue; 72 + return new IntValue(BigInt(178)); 72 73 })); 73 - ctx.add("if", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 74 + ctx.set("if", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 74 75 if( args.length != 3 ) 75 - throw new PolemyRuntimeException("if takes three arguments!! ["~to!string(pos)~"]"); 76 + throw new RuntimeException(pos, "if takes three arguments!!"); 76 77 if( auto x = cast(IntValue)args[0] ) 77 78 if( auto ft = cast(FunValue)args[1] ) 78 79 if( auto fe = cast(FunValue)args[2] ) 79 80 return (x.data == 0 ? fe : ft).call(pos,[]); 80 - throw new PolemyRuntimeException("type mismatch in if ["~to!string(pos)~"]"); 81 + throw new RuntimeException(pos, "type mismatch in if"); 81 82 })); 82 83 return ctx; 83 84 } 84 85 85 -Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params) 86 +/// Entry point of this module 87 + 88 +Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn) 86 89 { 87 - return eval( parserFromString(params).parseProgram() ); 90 + return eval( polemy.parse.parseString(str, fn_ln_cn) ); 88 91 } 89 92 90 -Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params) 93 +Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filenae, T ln_cn) 91 94 { 92 - return eval( parserFromFile(params).parseProgram() ); 95 + return eval( polemy.parse.parseFile(filename, ln_cn) ); 93 96 } 94 97 95 -Tuple!(Value,"val",Context,"ctx") eval(Program prog) 98 +Tuple!(Value,"val",Table,"ctx") eval(AST e) 96 99 { 97 - Context ctx = createGlobalContext(); 98 - return typeof(return)(eval(prog, ctx), ctx); 100 + Table ctx = createGlobalContext(); 101 + return typeof(return)(eval(e, ctx), ctx); 99 102 } 100 103 101 -Value eval(Program prog, Context ctx) 102 -{ 103 - Value v = new UndefinedValue; 104 - foreach(s; prog) 105 - v = eval(s, ctx); 106 - return v; 107 -} 108 - 109 -Value eval(Statement _s, Context ctx) 104 +Value eval(AST _e, Table ctx, bool splitCtx = true) 110 105 { 111 - if( auto s = cast(DeclStatement)_s ) 112 - { 113 - auto v = eval(s.expr, ctx); 114 - ctx.add(s.var, v); 115 - return v; 116 - } 117 - else 118 - if( auto s = cast(ExprStatement)_s ) 119 - { 120 - return eval(s.expr, ctx); 121 - } 122 - throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos)); 123 -} 124 - 125 -Value eval(Expression _e, Context ctx) 126 -{ 127 - if( auto e = cast(StrLiteralExpression)_e ) 106 + if( auto e = cast(StrLiteral)_e ) 128 107 { 129 108 return new StrValue(e.data); 130 109 } 131 110 else 132 - if( auto e = cast(IntLiteralExpression)_e ) 111 + if( auto e = cast(IntLiteral)_e ) 133 112 { 134 113 return new IntValue(e.data); 135 114 } 136 115 else 137 116 if( auto e = cast(VarExpression)_e ) 138 117 { 139 - return ctx[e.var]; 118 + return ctx.get(e.var, "@val", e.pos); 140 119 } 141 120 else 142 - if( auto e = cast(AssignExpression)_e ) 121 + if( auto e = cast(LetExpression)_e ) 143 122 { 144 - if( auto ev = cast(VarExpression)e.lhs ) 145 - { 146 - Value r = eval(e.rhs, ctx); 147 - ctx[ev.var] = r; 148 - return r; 149 - } 150 - throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos)); 123 + // for letrec, we need this, but should avoid overwriting???? 124 + // ctx.set(e.var, "@val", new UndefinedValue, e.pos); 125 + Value v = eval(e.init, ctx, true); 126 + ctx.set(e.var, "@val", v, e.pos); 127 + return eval(e.expr, ctx); 151 128 } 152 129 else 153 130 if( auto e = cast(FuncallExpression)_e ) 154 131 { 155 132 Value _f = eval(e.fun, ctx); 156 133 if( auto f = cast(FunValue)_f ) { 157 134 Value[] args; 158 135 foreach(a; e.args) 159 136 args ~= eval(a, ctx); 160 137 return f.call(e.pos, args); 161 138 } else 162 - throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos)); 139 + throw new RuntimeException(e.pos, "Non-funcion is applied"); 163 140 } 164 141 else 165 - if( auto e = cast(FunLiteralExpression)_e ) 142 + if( auto e = cast(FunLiteral)_e ) 166 143 { 167 144 return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ 168 145 if( e.params.length != args.length ) 169 - throw new PolemyRuntimeException(sprintf!"Argument Number Mismatch (%d required but %d given) at [%s]" 170 - (e.params.length, args.length, e.pos)); 171 - Context ctxNeo = new Context(ctx); 146 + throw new RuntimeException(e.pos, sprintf!"Argument Number Mismatch (%d required but %d given)" 147 + (e.params.length, args.length)); 148 + Table ctxNeo = new Table(ctx, Table.Kind.NotPropagateSet); 172 149 foreach(i,p; e.params) 173 - ctxNeo.add(p, args[i]); 150 + ctxNeo.set(p, "@val", args[i]); 174 151 return eval(e.funbody, ctxNeo); 175 152 }); 176 153 } 177 - throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos)); 154 + throw new RuntimeException(_e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(_e))); 178 155 } 179 156 180 157 unittest 181 158 { 182 - auto r = evalString(`var x = 21; x = x + x*x;`); 183 - assert( r.val == new IntValue(BigInt(21+21*21)) ); 184 - assert( r.ctx["x"] == new IntValue(BigInt(21+21*21)) ); 185 - assert( !collectException(r.ctx["x"]) ); 186 - assert( collectException(r.ctx["y"]) ); 159 + auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) ); 160 + assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); 161 + assert_eq( r.ctx.get("x","@val"), new IntValue(BigInt(21)) ); 162 + assert_nothrow( r.ctx.get("x","@val") ); 163 + assert_throw!RuntimeException( r.ctx.get("y","@val") ); 187 164 } 188 165 unittest 189 166 { 190 - assert( collectException(evalString(`var x = 21; x = x + x*y;`)) ); 191 - assert( collectException(evalString(`x=1;`)) ); 167 + auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) ); 168 + assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); 169 + assert_eq( r.ctx.get("x","@val"), new IntValue(BigInt(21+21*21)) ); 170 + assert_nothrow( r.ctx.get("x","@val") ); 171 + assert_throw!RuntimeException( r.ctx.get("y","@val") ); 192 172 } 193 173 unittest 194 174 { 195 - auto r = evalString(`var x = fun(a){1+a;}(2);`); 196 - assert( r.ctx["x"] == new IntValue(BigInt(3)) ); 197 - assert( r.val == new IntValue(BigInt(3)) ); 175 + assert_nothrow( evalString(`print("Hello, world!");`) ); 176 + assert_nothrow( evalString(`print(fun(){});`) ); 198 177 } 199 178 unittest 200 179 { 201 - auto r = evalString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); f();`); 202 - assert( r.ctx["x"] == new IntValue(BigInt(4)) ); 203 - assert( r.val == new IntValue(BigInt(4)) ); 204 -} 205 -unittest 206 -{ 207 - evalString(`print("Hello, world!");`); 208 - evalString(`print(fun(){});`); 209 -} 210 -unittest 211 -{ 212 - evalString(`var fac = fun(x){ 180 + assert_nothrow( evalString(`var fac = fun(x){ 213 181 1; 214 182 }; 215 - print(fac(3));`); 216 - evalString(`var fac = fun(x){ 183 + print(fac(3));`)); 184 + assert_nothrow( evalString(`var fac = fun(x){ 217 185 if(x) 218 186 { x*fac(x-1); } 219 187 else 220 188 { 1; }; 221 189 }; 222 - print(fac(10));`); 223 - evalString(`var fib = fun(x){ 190 + print(fac(10));`)); 191 + assert_nothrow( evalString(`var fib = fun(x){ 224 192 if(x<2) 225 193 { 1; } 226 194 else 227 195 { fib(x-1) + fib(x-2); }; 228 196 }; 229 - print(fib(10));`); 197 + print(fib(10));`)); 230 198 }