@@ -1,5 +1,5 @@ -/** +/** * Authors: k.inaba * License: NYSL 0.9982 http://www.kmonos.net/nysl/ * * Evaluator for Polemy programming language. @@ -12,144 +12,121 @@ import polemy.value; import std.typecons; import std.stdio; -Context createGlobalContext() -{ - auto ctx = new Context; - ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ +Table createGlobalContext() +{ + auto ctx = new Table; + // [TODO] autogenerate these typechecks + ctx.set("+", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "+ takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data+y.data); - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "cannot add non-integers"); })); - ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("-", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "- takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data-y.data); - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "cannot subtract non-integers"); })); - ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("*", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "* takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data*y.data); - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "cannot multiply non-integers"); })); - ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("/", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "/ takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data/y.data); - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "cannot divide non-integers"); })); - ctx.add("<", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("<", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("< takes two arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "< takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(BigInt(to!int(x.data < y.data))); - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "cannot compare non-integers"); })); - ctx.add(">", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set(">", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("> takes two arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "> takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(BigInt(to!int(x.data>y.data))); - throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "cannot compare non-integers"); })); - ctx.add("print", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("print", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ foreach(a; args) write(a); writeln(""); - return new UndefinedValue; + return new IntValue(BigInt(178)); })); - ctx.add("if", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("if", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 3 ) - throw new PolemyRuntimeException("if takes three arguments!! ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "if takes three arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto ft = cast(FunValue)args[1] ) if( auto fe = cast(FunValue)args[2] ) return (x.data == 0 ? fe : ft).call(pos,[]); - throw new PolemyRuntimeException("type mismatch in if ["~to!string(pos)~"]"); + throw new RuntimeException(pos, "type mismatch in if"); })); return ctx; } -Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params) +/// Entry point of this module + +Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn) { - return eval( parserFromString(params).parseProgram() ); + return eval( polemy.parse.parseString(str, fn_ln_cn) ); } -Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params) +Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filenae, T ln_cn) { - return eval( parserFromFile(params).parseProgram() ); + return eval( polemy.parse.parseFile(filename, ln_cn) ); } -Tuple!(Value,"val",Context,"ctx") eval(Program prog) +Tuple!(Value,"val",Table,"ctx") eval(AST e) { - Context ctx = createGlobalContext(); - return typeof(return)(eval(prog, ctx), ctx); + Table ctx = createGlobalContext(); + return typeof(return)(eval(e, ctx), ctx); } -Value eval(Program prog, Context ctx) -{ - Value v = new UndefinedValue; - foreach(s; prog) - v = eval(s, ctx); - return v; -} - -Value eval(Statement _s, Context ctx) +Value eval(AST _e, Table ctx, bool splitCtx = true) { - if( auto s = cast(DeclStatement)_s ) - { - auto v = eval(s.expr, ctx); - ctx.add(s.var, v); - return v; - } - else - if( auto s = cast(ExprStatement)_s ) - { - return eval(s.expr, ctx); - } - throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos)); -} - -Value eval(Expression _e, Context ctx) -{ - if( auto e = cast(StrLiteralExpression)_e ) + if( auto e = cast(StrLiteral)_e ) { return new StrValue(e.data); } else - if( auto e = cast(IntLiteralExpression)_e ) + if( auto e = cast(IntLiteral)_e ) { return new IntValue(e.data); } - else - if( auto e = cast(VarExpression)_e ) - { - return ctx[e.var]; - } - else - if( auto e = cast(AssignExpression)_e ) - { - if( auto ev = cast(VarExpression)e.lhs ) - { - Value r = eval(e.rhs, ctx); - ctx[ev.var] = r; - return r; - } - throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos)); - } + else + if( auto e = cast(VarExpression)_e ) + { + return ctx.get(e.var, "@val", e.pos); + } + else + if( auto e = cast(LetExpression)_e ) + { + // for letrec, we need this, but should avoid overwriting???? + // ctx.set(e.var, "@val", new UndefinedValue, e.pos); + Value v = eval(e.init, ctx, true); + ctx.set(e.var, "@val", v, e.pos); + return eval(e.expr, ctx); + } else if( auto e = cast(FuncallExpression)_e ) { Value _f = eval(e.fun, ctx); @@ -158,73 +135,64 @@ foreach(a; e.args) args ~= eval(a, ctx); return f.call(e.pos, args); } else - throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos)); + throw new RuntimeException(e.pos, "Non-funcion is applied"); } else - if( auto e = cast(FunLiteralExpression)_e ) + if( auto e = cast(FunLiteral)_e ) { return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( e.params.length != args.length ) - throw new PolemyRuntimeException(sprintf!"Argument Number Mismatch (%d required but %d given) at [%s]" - (e.params.length, args.length, e.pos)); - Context ctxNeo = new Context(ctx); + throw new RuntimeException(e.pos, sprintf!"Argument Number Mismatch (%d required but %d given)" + (e.params.length, args.length)); + Table ctxNeo = new Table(ctx, Table.Kind.NotPropagateSet); foreach(i,p; e.params) - ctxNeo.add(p, args[i]); + ctxNeo.set(p, "@val", args[i]); return eval(e.funbody, ctxNeo); }); } - throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos)); + throw new RuntimeException(_e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(_e))); } - + unittest -{ - auto r = evalString(`var x = 21; x = x + x*x;`); - assert( r.val == new IntValue(BigInt(21+21*21)) ); - assert( r.ctx["x"] == new IntValue(BigInt(21+21*21)) ); - assert( !collectException(r.ctx["x"]) ); - assert( collectException(r.ctx["y"]) ); +{ + auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) ); + assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); + assert_eq( r.ctx.get("x","@val"), new IntValue(BigInt(21)) ); + assert_nothrow( r.ctx.get("x","@val") ); + assert_throw!RuntimeException( r.ctx.get("y","@val") ); } +unittest +{ + auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) ); + assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); + assert_eq( r.ctx.get("x","@val"), new IntValue(BigInt(21+21*21)) ); + assert_nothrow( r.ctx.get("x","@val") ); + assert_throw!RuntimeException( r.ctx.get("y","@val") ); +} unittest { - assert( collectException(evalString(`var x = 21; x = x + x*y;`)) ); - assert( collectException(evalString(`x=1;`)) ); + assert_nothrow( evalString(`print("Hello, world!");`) ); + assert_nothrow( evalString(`print(fun(){});`) ); } unittest { - auto r = evalString(`var x = fun(a){1+a;}(2);`); - assert( r.ctx["x"] == new IntValue(BigInt(3)) ); - assert( r.val == new IntValue(BigInt(3)) ); -} -unittest -{ - auto r = evalString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); f();`); - assert( r.ctx["x"] == new IntValue(BigInt(4)) ); - assert( r.val == new IntValue(BigInt(4)) ); -} -unittest -{ - evalString(`print("Hello, world!");`); - evalString(`print(fun(){});`); -} -unittest -{ - evalString(`var fac = fun(x){ + assert_nothrow( evalString(`var fac = fun(x){ 1; }; - print(fac(3));`); - evalString(`var fac = fun(x){ + print(fac(3));`)); + assert_nothrow( evalString(`var fac = fun(x){ if(x) { x*fac(x-1); } else { 1; }; }; - print(fac(10));`); - evalString(`var fib = fun(x){ + print(fac(10));`)); + assert_nothrow( evalString(`var fib = fun(x){ if(x<2) { 1; } else { fib(x-1) + fib(x-2); }; }; - print(fib(10));`); + print(fib(10));`)); }