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 std.typecons;
15 import std.stdio;
16
17 ///
18 Table createGlobalContext()
19 {
20 auto ctx = new Table;
21 ctx.set("+", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} ));
22 ctx.set("-", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} ));
23 ctx.set("*", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} ));
24 ctx.set("/", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} ));
25 ctx.set("%", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} ));
26 ctx.set("||", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) || (rhs.data!=0) ? 1:0));} ));
27 ctx.set("&&", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) && (rhs.data!=0) ? 1:0));} ));
28 ctx.set("<", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs < rhs ? 1: 0));} ));
29 ctx.set(">", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs > rhs ? 1: 0));} ));
30 ctx.set("<=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs <= rhs ? 1: 0));} ));
31 ctx.set(">=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs >= rhs ? 1: 0));} ));
32 ctx.set("==", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs == rhs ? 1: 0));} ));
33 ctx.set("!=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs != rhs ? 1: 0));} ));
34 ctx.set("print", ValueLayer, native( (Value a){
35 writeln(a);
36 return new IntValue(BigInt(0));
37 }));
38 ctx.set("if", ValueLayer, native( (IntValue x, FunValue ft, FunValue fe){
39 auto toRun = (x.data==0 ? fe : ft);
40 // [TODO] fill positional information
41 return toRun.invoke(null, ValueLayer, toRun.definitionContext());
42 // return toRun.invoke(pos, lay, toRun.definitionContext());
43 }));
44 ctx.set("_isint", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(IntValue)v is null ? 0 : 1));} ));
45 ctx.set("_isstr", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(StrValue)v is null ? 0 : 1));} ));
46 ctx.set("_isfun", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(FunValue)v is null ? 0 : 1));} ));
47 ctx.set("_isundefined", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(UndValue)v is null ? 0 : 1));} ));
48 ctx.set("_istable", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(Table)v is null ? 0 : 1));} ));
49 ctx.set(".", ValueLayer, native( (Table t, StrValue s){
50 return (t.has(s.data, ValueLayer) ? t.get(s.data, ValueLayer) : new UndValue);
51 }) );
52 ctx.set(".?", ValueLayer, native( (Table t, StrValue s){
53 return new IntValue(BigInt(t.has(s.data, ValueLayer) ? 1 : 0));
54 }) );
55 ctx.set(".=", ValueLayer, native( (Table t, StrValue s, Value v){
56 auto t2 = new Table(t, Table.Kind.NotPropagateSet);
57 t2.set(s.data, ValueLayer, v);
58 return t2;
59 }) );
60 ctx.set("{}", ValueLayer, native( (){
61 return new Table;
62 }) );
63 return ctx;
64 }
65
66 /// Entry point of this module
67
68 Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn)
69 {
70 return eval( polemy.parse.parseString(str, fn_ln_cn) );
71 }
72
73 /// Entry point of this module
74
75 Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filename, T ln_cn)
76 {
77 return eval( polemy.parse.parseFile(filename, ln_cn) );
78 }
79
80 /// Entry point of this module
81
82 Tuple!(Value,"val",Table,"ctx") eval(AST e)
83 {
84 Table ctx = createGlobalContext();
85 return typeof(return)(eval(e, ctx, false, ValueLayer), ctx);
86 }
87
88 Value invokeFunction(in LexPosition pos, Value _f, AST[] args, Table callerCtx, Layer lay, bool AlwaysMacro=false)
89 {
90 if(auto f = cast(FunValue)_f)
91 {
92 Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
93 foreach(i,p; f.params())
94 if( p.layers.empty )
95 if(lay==MacroLayer)
96 ctx.set(p.name, lay, macroEval(args[i], callerCtx, AlwaysMacro));
97 else
98 ctx.set(p.name, lay, eval(args[i], callerCtx, true, lay));
99 else
100 foreach(argLay; p.layers)
101 if(argLay==MacroLayer)
102 ctx.set(p.name, argLay, macroEval(args[i], callerCtx, AlwaysMacro));
103 else
104 ctx.set(p.name, argLay, eval(args[i], callerCtx, true, argLay));
105 return f.invoke(pos, lay, ctx);
106 }
107 throw genex!RuntimeException(pos, "tried to call non-function");
108 }
109
110 Value lift(in LexPosition pos, Value v, Layer lay, Table callerCtx)
111 {
112 // similar to invoke Function, but with only one argument bound to ValueLayer
113 Value _f = callerCtx.get(lay, SystemLayer, pos);
114 if(auto f = cast(FunValue)_f)
115 {
116 Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
117 auto ps = f.params();
118 if( ps.length != 1 )
119 throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
120 if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
121 {
122 ctx.set(ps[0].name, ValueLayer, v);
123 return f.invoke(pos, ValueLayer, ctx);
124 }
125 else
126 throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
127 }
128 throw genex!RuntimeException(pos, "tried to call non-function");
129 }
130
131 /// Entry point of this module
132 /// If splitCtx = true, then inner variable declaration do not overwrite ctx.
133 /// lay is the layer ID for evaluation (standard value semantics uses ValueLayer).
134
135 Value eval(AST e, Table ctx, bool splitCtx, Layer lay)
136 {
137 return e.match(
138 (StrLiteral e)
139 {
140 Value v = new StrValue(e.data);
141 if( lay == ValueLayer )
142 return v;
143 else
144 return lift(e.pos,v,lay,ctx);
145 },
146 (IntLiteral e)
147 {
148 Value v = new IntValue(e.data);
149 if( lay == ValueLayer )
150 return v;
151 else // rise
152 return lift(e.pos,v,lay,ctx);
153 },
154 (VarExpression e)
155 {
156 if( lay == ValueLayer )
157 return ctx.get(e.name, lay, e.pos);
158 try {
159 return ctx.get(e.name, lay, e.pos);
160 } catch( Throwable ) { // [TODO] more precise...
161 return lift(e.pos, ctx.get(e.name, ValueLayer, e.pos), lay, ctx);
162 }
163 },
164 (LayExpression e)
165 {
166 if( e.layer == MacroLayer )
167 return macroEval(e.expr, ctx, false);
168 else
169 return eval(e.expr, ctx, true, e.layer);
170 },
171 (LetExpression e)
172 {
173 // for letrec, we need this, but should avoid overwriting????
174 // ctx.set(e.var, ValueLayer, new UndefinedValue, e.pos);
175 if(splitCtx)
176 ctx = new Table(ctx, Table.Kind.NotPropagateSet);
177 Value v = eval(e.init, ctx, true, lay);
178 ctx.set(e.name, (e.layer.length ? e.layer : lay), v, e.pos);
179 return eval(e.expr, ctx, false, lay);
180 },
181 (FuncallExpression e)
182 {
183 return invokeFunction(e.pos, eval(e.fun, ctx, true, lay), e.args, ctx, lay);
184 },
185 (FunLiteral e)
186 {
187 Value[Value[]][Layer] memo;
188 AST macroMemo = null; // cache
189
190 // funvalue need not be rised
191 // no, need to be rised !! suppose @t(fib)("int")
192 return new UserDefinedFunValue(e, ctx);
193 },
194 delegate Value (AST e)
195 {
196 throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
197 }
198 );
199 }
200
201 // [TODO] Optimization
202 Value macroEval(AST e, Table ctx, bool AlwaysMacro)
203 {
204 Layer theLayer = ValueLayer;
205
206 Table makeCons(Value a, Value d)
207 {
208 Table t = new Table;
209 t.set("car", theLayer, a);
210 t.set("cdr", theLayer, d);
211 return t;
212 }
213
214 Table pos = new Table;
215 if( e.pos !is null ) {
216 pos.set("filename", theLayer, new StrValue(e.pos.filename));
217 pos.set("lineno", theLayer, new IntValue(BigInt(e.pos.lineno)));
218 pos.set("column", theLayer, new IntValue(BigInt(e.pos.column)));
219 } else {
220 pos.set("filename", theLayer, new StrValue("nullpos"));
221 pos.set("lineno", theLayer, new IntValue(BigInt(0)));
222 pos.set("column", theLayer, new IntValue(BigInt(0)));
223 }
224
225 return e.match(
226 (StrLiteral e)
227 {
228 Table t = new Table;
229 t.set("pos", theLayer, pos);
230 t.set("is", theLayer, new StrValue("str"));
231 t.set("data", theLayer, new StrValue(e.data));
232 return t;
233 },
234 (IntLiteral e)
235 {
236 Table t = new Table;
237 t.set("pos", theLayer, pos);
238 t.set("is", theLayer, new StrValue("int"));
239 t.set("data", theLayer, new IntValue(e.data));
240 return t;
241 },
242 (VarExpression e)
243 {
244 try {
245 return ctx.get(e.name, MacroLayer, e.pos);
246 } catch( Throwable ) {// [TODO] more precies...
247 Table t = new Table;
248 t.set("pos", theLayer, pos);
249 t.set("is", theLayer, new StrValue("var"));
250 t.set("name", theLayer, new StrValue(e.name));
251 return cast(Value)t;
252 }
253 },
254 (LayExpression e)
255 {
256 if( AlwaysMacro )
257 {
258 Table t = new Table;
259 t.set("pos", theLayer, pos);
260 t.set("is", theLayer, new StrValue("lay"));
261 t.set("layer", theLayer, new StrValue(e.layer));
262 t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
263 return cast(Value)t;
264 }
265 else
266 {
267 if( e.layer == MacroLayer )
268 return macroEval(e.expr, ctx, false);
269 else
270 return eval(e.expr, ctx, true, e.layer);
271 }
272 },
273 (LetExpression e)
274 {
275 Table t = new Table;
276 t.set("pos", theLayer, pos);
277 t.set("is", theLayer, new StrValue("let"));
278 t.set("name", theLayer, new StrValue(e.name));
279 t.set("init", theLayer, macroEval(e.init,ctx,AlwaysMacro));
280 t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
281 return t;
282 },
283 (FuncallExpression e)
284 {
285 Value _f = macroEval(e.fun,ctx,AlwaysMacro);
286
287 if( auto f = cast(FunValue)_f )
288 return invokeFunction(e.pos, f, e.args, ctx, MacroLayer, AlwaysMacro);
289
290 Table t = new Table;
291 t.set("pos", theLayer, pos);
292 t.set("is", theLayer, new StrValue("app"));
293 t.set("fun", theLayer, _f);
294 Table args = new Table;
295 foreach_reverse(a; e.args) {
296 Table cons = new Table;
297 cons.set("car",theLayer,macroEval(a,ctx,AlwaysMacro));
298 cons.set("cdr",theLayer,args);
299 args = cons;
300 }
301 t.set("args", theLayer, args);
302 return cast(Value)t;
303 },
304 (FunLiteral e)
305 {
306 Table t = new Table;
307 t.set("pos", theLayer, pos);
308 t.set("is", theLayer, new StrValue("fun"));
309 t.set("funbody", theLayer, macroEval(e.funbody,ctx,AlwaysMacro));
310 Table params = new Table;
311 foreach_reverse(p; e.params)
312 {
313 Table lays = new Table;
314 foreach_reverse(lay; p.layers)
315 lays = makeCons(new StrValue(lay), lays);
316 Table kv = new Table;
317 kv.set("name", theLayer, new StrValue(p.name));
318 kv.set("layers", theLayer, lays);
319 Table cons = new Table;
320 params = makeCons(kv, params);
321 }
322 t.set("params", theLayer, params);
323 return t;
324 },
325 delegate Value (AST e)
326 {
327 throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
328 }
329 );
330 }
331
332 unittest
333 {
334 auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) );
335 assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
336 assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21)) );
337 assert_nothrow( r.ctx.get("x",ValueLayer) );
338 assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
339 }
340 unittest
341 {
342 auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) );
343 assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
344 assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) );
345 assert_nothrow( r.ctx.get("x",ValueLayer) );
346 assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
347 }
348 unittest
349 {
350 assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) );
351 assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) );
352 }
353 unittest
354 {
355 assert_eq( evalString(`@a x=1; @b x=2; @a(x)`).val, new IntValue(BigInt(1)) );
356 assert_eq( evalString(`@a x=1; @b x=2; @b(x)`).val, new IntValue(BigInt(2)) );
357 assert_eq( evalString(`let x=1; let _ = (@a x=2;2); x`).val, new IntValue(BigInt(1)) );
358 assert_throw!Throwable( evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
359 }
360 /*
361 unittest
362 {
363 assert_eq( evalString(`var fac = fun(x){
364 if(x)
365 { x*fac(x-1); }
366 else
367 { 1; };
368 };
369 fac(10);`).val, new IntValue(BigInt(10*9*8*5040)));
370 assert_eq( evalString(`var fib = fun(x){
371 if(x<2)
372 { 1; }
373 else
374 { fib(x-1) + fib(x-2); };
375 };
376 fib(5);`).val, new IntValue(BigInt(8)));
377 }
378
379 unittest
380 {
381 assert_throw!Throwable( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};@s(1+2)`) );
382 assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) );
383 assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) );
384 assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) );
385 }
386
387 unittest
388 {
389 assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
390 // there was a bug that declaration in the first line of function definition
391 // cannot be recursive
392 assert_nothrow( evalString(`def foo() {
393 def bar(y) { if(y<1) {0} else {bar(0)} };
394 bar(1)
395 }; foo()`) );
396 }
397 */