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