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.failure;
10 import polemy.ast;
11 import polemy.parse;
12 import polemy.value;
13 import polemy.layer;
14
15 class Evaluator
16 {
17 public:
18 this() { theContext = new Table; }
19
20 Value evalAST(AST e)
21 {
22 return eval(e, ValueLayer, theContext, OverwriteCtx);
23 }
24
25 Value evalString(S,T...)(S str, T fn_ln_cn)
26 {
27 return evalAST(parseString(str,fn_ln_cn));
28 }
29
30 Value evalFile(S,T...)(S filename, T ln_cn)
31 {
32 return evalAST(parseFile(filename,ln_cn));
33 }
34
35 Table globalContext()
36 {
37 return theContext;
38 }
39
40 private:
41 Table theContext;
42
43 private:
44 enum : bool { CascadeCtx=false, OverwriteCtx=true };
45
46 Value eval( AST e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
47 {
48 // dynamic-overload-resolution-pattern: modify here
49 enum funName = "eval";
50 alias TypeTuple!(e,lay,ctx,overwriteCtx) params;
51
52 // dynamic-overload-resolution-pattern: dispatch
53 alias typeof(__traits(getOverloads, this, funName)) ovTypes;
54 alias staticMap!(firstParam, ovTypes) fstTypes;
55 alias DerivedToFront!(fstTypes) fstTypes_sorted;
56 foreach(i, T; fstTypes_sorted)
57 static if( is(T == typeof(params[0])) ) {} else if( auto _x = cast(T)params[0] )
58 return __traits(getOverloads, this, funName)[i](_x, params[1..$]);
59
60 // dynamic-overload-resolution-pattern: default behavior
61 assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined"));
62 }
63
64 private:
65 Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
66 {
67 Value v = new StrValue(e.data);
68 if( lay==RawMacroLayer || lay==MacroLayer )
69 {
70 auto ast = new Table;
71 ast.set("pos", ValueLayer, fromPos(e.pos));
72 ast.set("is", ValueLayer, new StrValue("str"));
73 ast.set("data", ValueLayer, v);
74 return ast;
75 }
76 if( lay==ValueLayer )
77 return v;
78 return lift(v, lay, ctx, e.pos);
79 }
80
81 Value eval( Int e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
82 {
83 Value v = new IntValue(e.data);
84 if( lay==RawMacroLayer || lay==MacroLayer )
85 {
86 auto ast = new Table;
87 ast.set("pos", ValueLayer, fromPos(e.pos));
88 ast.set("is", ValueLayer, new StrValue("int"));
89 ast.set("data", ValueLayer, v);
90 return ast;
91 }
92 if( lay==ValueLayer )
93 return v;
94 return lift(v, lay, ctx, e.pos);
95 }
96
97 Value eval( Var e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
98 {
99 if( lay==RawMacroLayer || lay==MacroLayer )
100 {
101 if( ctx.has(e.name,MacroLayer) )
102 return ctx.get(e.name, MacroLayer, e.pos);
103 auto ast = new Table;
104 ast.set("pos", ValueLayer, fromPos(e.pos));
105 ast.set("is", ValueLayer, new StrValue("var"));
106 ast.set("name", ValueLayer, new StrValue(e.name));
107 return ast;
108 }
109 if( lay==ValueLayer || ctx.has(e.name, lay) )
110 return ctx.get(e.name, lay, e.pos);
111 return lift(ctx.get(e.name, ValueLayer, e.pos), lay, ctx, e.pos);
112 }
113
114 Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
115 {
116 Value f = eval( e.fun, lay, ctx );
117 if( lay==RawMacroLayer || lay==MacroLayer )
118 {
119 if( auto ff = cast(FunValue)f )
120 return invokeFunction(ff, e.args, MacroLayer, ctx, e.pos);
121 Table ast = new Table;
122 ast.set("pos", ValueLayer, fromPos(e.pos));
123 ast.set("is", ValueLayer, new StrValue("app"));
124 ast.set("fun", ValueLayer, f);
125 Table args = new Table;
126 foreach_reverse(a; e.args)
127 args = makeCons(eval(a, lay, ctx), args);
128 ast.set("args", ValueLayer, args);
129 return ast;
130 }
131 else
132 {
133 return invokeFunction(f, e.args, lay, ctx, e.pos);
134 }
135 }
136
137 Value eval( Fun e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
138 {
139 if( lay==RawMacroLayer || lay==MacroLayer )
140 {
141 Table t = new Table;
142 t.set("pos", ValueLayer, fromPos(e.pos));
143 t.set("is", ValueLayer, new StrValue("fun"));
144 t.set("funbody", ValueLayer, eval(e.funbody,lay,ctx));
145 Table params = new Table;
146 foreach_reverse(p; e.params)
147 {
148 Table lays = new Table;
149 foreach_reverse(l; p.layers)
150 lays = makeCons(new StrValue(l), lays);
151 Table kv = new Table;
152 kv.set("name", ValueLayer, new StrValue(p.name));
153 kv.set("layers", ValueLayer, lays);
154 Table cons = new Table;
155 params = makeCons(kv, params);
156 }
157 t.set("params", ValueLayer, params);
158 return t;
159 }
160 else
161 {
162 return createNewFunction(e, ctx);
163 }
164 }
165
166 Value eval( Lay e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
167 {
168 if( lay == RawMacroLayer )
169 {
170 Value r = eval(e.expr, lay, ctx);
171 auto ast = new Table; // todo: pos
172 ast.set("pos", ValueLayer, fromPos(e.pos));
173 ast.set("is", ValueLayer, new StrValue("lay"));
174 ast.set("layer", ValueLayer, new StrValue(e.layer));
175 ast.set("expr", ValueLayer, r);
176 return ast;
177 }
178 else
179 return eval(e.expr, e.layer, ctx);
180 }
181
182 Value eval( Let e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
183 {
184 // todo @macro let
185 if( lay==RawMacroLayer || lay==MacroLayer )
186 {
187 auto ast = new Table; // todo: pos
188 ast.set("pos", ValueLayer, fromPos(e.pos));
189 ast.set("is", ValueLayer, new StrValue("let"));
190 ast.set("name", ValueLayer, new StrValue(e.name));
191 ast.set("layer", ValueLayer, new StrValue(e.layer));
192 ast.set("init", ValueLayer, eval(e.init, lay, ctx));
193 ast.set("expr", ValueLayer, eval(e.expr, lay, ctx));
194 return ast;
195 }
196 else
197 {
198 if( !overwriteCtx )
199 ctx = new Table(ctx, Table.Kind.NotPropagateSet);
200 Value ri = eval(e.init, lay, ctx);
201 string theLayer = e.layer.empty ? (lay==RawMacroLayer ? MacroLayer : lay) : e.layer;
202 ctx.set(e.name, theLayer, ri);
203 return eval(e.expr, lay, ctx, OverwriteCtx);
204 }
205 }
206
207 private:
208 Value invokeFunction(Value _f, AST[] args, Layer lay, Table ctx, LexPosition pos=null)
209 {
210 if(auto f = cast(FunValue)_f)
211 {
212 Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
213 foreach(i,p; f.params())
214 if( p.layers.empty )
215 newCtx.set(p.name, (lay==RawMacroLayer ? MacroLayer : lay), eval(args[i], lay, ctx));
216 else
217 foreach(argLay; p.layers)
218 newCtx.set(p.name, argLay, eval(args[i], argLay, ctx));
219 return f.invoke(pos, lay, newCtx);
220 }
221 throw genex!RuntimeException(pos, text("tried to call non-function: ",_f));
222 }
223
224 Value lift(Value v, Layer lay, Table ctx, LexPosition pos=null)
225 {
226 // functions are automatically lifterd
227 if( cast(FunValue) v )
228 return v;
229
230 // similar to invoke Function, but with only one argument bound to ValueLayer
231 if(auto f = cast(FunValue)ctx.get(lay, SystemLayer, pos))
232 {
233 Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
234 auto ps = f.params();
235 if( ps.length != 1 )
236 throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
237 if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
238 {
239 newCtx.set(ps[0].name, ValueLayer, v);
240 return f.invoke(pos, ValueLayer, newCtx);
241 }
242 else
243 throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
244 }
245 throw genex!RuntimeException(pos, "tried to call non-function");
246 }
247
248 Value createNewFunction(Fun e, Table ctx)
249 {
250 class UserDefinedFunValue : FunValue
251 {
252 Fun ast;
253 Table defCtx;
254 override const(Parameter[]) params() { return ast.params; }
255 override Table definitionContext() { return defCtx; }
256
257 this(Fun ast, Table defCtx) { this.ast=ast; this.defCtx=defCtx; }
258 override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
259 override bool opEquals(Object rhs_) const /// member-by-member equality
260 {
261 if( auto rhs = cast(typeof(this))rhs_ )
262 return this.ast==rhs.ast && this.defCtx==rhs.defCtx;
263 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
264 }
265 override hash_t toHash() const /// member-by-member hash
266 {
267 return typeid(this.ast).getHash(&this.ast) + typeid(this.defCtx).getHash(&this.defCtx);
268 }
269 override int opCmp(Object rhs_) /// member-by-member compare
270 {
271 if( auto rhs = cast(typeof(this))rhs_ )
272 {
273 if(auto i = this.ast.opCmp(rhs.ast))
274 return i;
275 return this.defCtx.opCmp(rhs.defCtx);
276 }
277 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
278 }
279
280 override Value invoke(LexPosition pos, Layer lay, Table ctx)
281 {
282 if( lay == MacroLayer )
283 return eval(ast.funbody, lay, ctx);
284 auto macroed = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx));
285 return eval(macroed, lay, ctx);
286 }
287 }
288 return new UserDefinedFunValue(e,ctx);
289 }
290
291 public:
292 /// TODO: move up
293 /// TDOO: to other layers?
294 void addPrimitive(R,T...)(string name, Layer lay, R delegate (T) dg)
295 {
296 class NativeFunValue : FunValue
297 {
298 Parameter[] params_data;
299 override string toString() { return sprintf!"(native:%x)"(dg.funcptr); }
300 override const(Parameter[]) params() { return params_data; }
301 override Table definitionContext() { return new Table; } // todo: cache
302 this(){
303 foreach(i, Ti; T)
304 params_data ~= new Parameter(text(i), []);
305 }
306 override Value invoke(LexPosition pos, Layer lay, Table ctx)
307 {
308 if( lay != ValueLayer )
309 throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
310 T typed_args;
311 foreach(i, Ti; T) {
312 typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
313 if( typed_args[i] is null )
314 throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
315 }
316 try {
317 return dg(typed_args);
318 } catch( RuntimeException e ) {
319 throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
320 }
321 }
322 }
323 theContext.set(name, lay, new NativeFunValue);
324 }
325 }
326
327 version(unittest) import polemy.runtime;
328 unittest
329 {
330 auto e = new Evaluator;
331 enrollRuntimeLibrary(e);
332 auto r = assert_nothrow( e.evalString(`var x = 21; x + x*x;`) );
333 assert_eq( r, new IntValue(BigInt(21+21*21)) );
334 assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21)) );
335 assert_nothrow( e.globalContext.get("x",ValueLayer) );
336 assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
337 }
338 unittest
339 {
340 auto e = new Evaluator;
341 enrollRuntimeLibrary(e);
342 auto r = assert_nothrow( e.evalString(`var x = 21; var x = x + x*x;`) );
343 assert_eq( r, new IntValue(BigInt(21+21*21)) );
344 assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) );
345 assert_nothrow( e.globalContext.get("x",ValueLayer) );
346 assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
347 }
348 unittest
349 {
350 auto e = new Evaluator;
351 enrollRuntimeLibrary(e);
352 assert_eq( e.evalString(`let x=1; let y=(let x=2); x`), new IntValue(BigInt(1)) );
353 assert_eq( e.evalString(`let x=1; let y=(let x=2;fun(){x}); y()`), new IntValue(BigInt(2)) );
354 }
355
356 unittest
357 {
358 auto e = new Evaluator;
359 enrollRuntimeLibrary(e);
360 assert_eq( e.evalString(`@a x=1; @b x=2; @a(x)`), new IntValue(BigInt(1)) );
361 assert_eq( e.evalString(`@a x=1; @b x=2; @b(x)`), new IntValue(BigInt(2)) );
362 assert_eq( e.evalString(`let x=1; let _ = (@a x=2;2); x`), new IntValue(BigInt(1)) );
363 e = new Evaluator;
364 assert_throw!Throwable( e.evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
365 }
366
367 unittest
368 {
369 auto e = new Evaluator;
370 enrollRuntimeLibrary(e);
371 assert_eq( e.evalString(`
372 @@s(x){x};
373 @s "+" = fun(x, y) {@value(
374 @s(x) - @s(y)
375 )};
376 @s(1 + 2)
377 `), new IntValue(BigInt(-1)) );
378 }
379
380 unittest
381 {
382 auto e = new Evaluator;
383 enrollRuntimeLibrary(e);
384 assert_eq( e.evalString(`
385 @@3(x){x};
386 def incr(x) { x+1 };
387 @ 3 incr(x) {@value( if(@ 3(x)+1< 3){@ 3(x)+1}else{0} )};
388 def fb(n @value @3) { @3(n) };
389 fb(incr(incr(incr(0))))
390 `), new IntValue(BigInt(0)) );
391 }
392
393 unittest
394 {
395 auto e = new Evaluator;
396 enrollRuntimeLibrary(e);
397 assert_nothrow( e.evalString(`
398 @macro twice(x) { x; x };
399 def main() { twice(1) };
400 main()
401 `) );
402 }
403 /*
404 unittest
405 {
406 assert_eq( evalString(`var fac = fun(x){
407 if(x)
408 { x*fac(x-1); }
409 else
410 { 1; };
411 };
412 fac(10);`).val, new IntValue(BigInt(10*9*8*5040)));
413 assert_eq( evalString(`var fib = fun(x){
414 if(x<2)
415 { 1; }
416 else
417 { fib(x-1) + fib(x-2); };
418 };
419 fib(5);`).val, new IntValue(BigInt(8)));
420 }
421 unittest
422 {
423 assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
424 // there was a bug that declaration in the first line of function definition
425 // cannot be recursive
426 assert_nothrow( evalString(`def foo() {
427 def bar(y) { if(y<1) {0} else {bar(0)} };
428 bar(1)
429 }; foo()`) );
430 }
431 */