Artifact cea762cacb86424f3fe949fbebf4b4d43121324d
- 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]
1 /**
2 * Authors: k.inaba
3 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
4 *
5 * Evaluator for Polemy programming language.
6 */
7 module polemy.eval;
8 import polemy._common;
9 import polemy.lex : LexPosition;
10 import polemy.ast;
11 import polemy.parse;
12 import polemy.runtime;
13 import std.typecons;
14
15 Context createGlobalContext()
16 {
17 auto ctx = new Context;
18 ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
19 if( args.length != 2 )
20 throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]");
21 if( auto x = cast(IntValue)args[0] )
22 if( auto y = cast(IntValue)args[1] )
23 return new IntValue(x.data+y.data);
24 throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
25 }));
26 ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
27 if( args.length != 2 )
28 throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]");
29 if( auto x = cast(IntValue)args[0] )
30 if( auto y = cast(IntValue)args[1] )
31 return new IntValue(x.data-y.data);
32 throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
33 }));
34 ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
35 if( args.length != 2 )
36 throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]");
37 if( auto x = cast(IntValue)args[0] )
38 if( auto y = cast(IntValue)args[1] )
39 return new IntValue(x.data*y.data);
40 throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
41 }));
42 ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
43 if( args.length != 2 )
44 throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]");
45 if( auto x = cast(IntValue)args[0] )
46 if( auto y = cast(IntValue)args[1] )
47 return new IntValue(x.data/y.data);
48 throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
49 }));
50 return ctx;
51 }
52
53 Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params)
54 {
55 return eval( parserFromString(params).parseProgram() );
56 }
57
58 Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params)
59 {
60 return eval( parserFromFile(params).parseProgram() );
61 }
62
63 Tuple!(Value,"val",Context,"ctx") eval(Program prog)
64 {
65 Context ctx = createGlobalContext();
66 return typeof(return)(eval(prog, ctx), ctx);
67 }
68
69 Value eval(Program prog, Context ctx)
70 {
71 Value v = new UndefinedValue;
72 foreach(s; prog)
73 v = eval(s, ctx);
74 return v;
75 }
76
77 Value eval(Statement _s, Context ctx)
78 {
79 if( auto s = cast(DeclStatement)_s )
80 {
81 auto v = eval(s.expr, ctx);
82 ctx.add(s.var, v);
83 return v;
84 }
85 else
86 if( auto s = cast(ExprStatement)_s )
87 {
88 return eval(s.expr, ctx);
89 }
90 throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos));
91 }
92
93 Value eval(Expression _e, Context ctx)
94 {
95 if( auto e = cast(StrLiteralExpression)_e )
96 {
97 return new StrValue(e.data);
98 }
99 else
100 if( auto e = cast(IntLiteralExpression)_e )
101 {
102 return new IntValue(e.data);
103 }
104 else
105 if( auto e = cast(VarExpression)_e )
106 {
107 return ctx[e.var];
108 }
109 else
110 if( auto e = cast(AssignExpression)_e )
111 {
112 if( auto ev = cast(VarExpression)e.lhs )
113 {
114 Value r = eval(e.rhs, ctx);
115 ctx[ev.var] = r;
116 return r;
117 }
118 throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos));
119 }
120 else
121 if( auto e = cast(FuncallExpression)_e )
122 {
123 Value _f = eval(e.fun, ctx);
124 if( auto f = cast(FunValue)_f ) {
125 Value[] args;
126 foreach(a; e.args)
127 args ~= eval(a, ctx);
128 return f.call(e.pos, args);
129 } else
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 });
144 }
145 throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos));
146 }
147
148 import std.stdio;
149 unittest
150 {
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"]) );
156 }
157 unittest
158 {
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)) );
167 }
168 unittest
169 {
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)) );
173 }