Differences From Artifact [c5ee9ab6830c78bd]:
- File
polemy/eval.d
- 2010-11-07 12:46:23 - part of checkin [3f5dc76a75] on branch trunk - Added funcall expression parser and function literal parser. (user: kinaba) [annotate]
To Artifact [cea762cacb86424f]:
- File
polemy/eval.d
- 2010-11-07 14:34:29 - part of checkin [0569f7b8c2] on branch trunk - - Added function literal evaluator (i.e., closure). - Workaround for d2stacktrace's infinite-loop bug. (when std.demangle.demangle use exception inside it, it will go into an infinite loop. to avoid this, I choose to unset TraceHandler during stacktrace generation. This is far from the complete solution, but at least it should work as expected under single-thread environment...) (user: kinaba) [annotate]
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 }