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 // functions are automatically lifterd
113 if( cast(FunValue) v )
114 return v;
115
116 // similar to invoke Function, but with only one argument bound to ValueLayer
117 Value _f = callerCtx.get(lay, SystemLayer, pos);
118 if(auto f = cast(FunValue)_f)
119 {
120 Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
121 auto ps = f.params();
122 if( ps.length != 1 )
123 throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
124 if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
125 {
126 ctx.set(ps[0].name, ValueLayer, v);
127 return f.invoke(pos, ValueLayer, ctx);
128 }
129 else
130 throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
131 }
132 throw genex!RuntimeException(pos, "tried to call non-function");
133 }
134
135 /// Entry point of this module
136 /// If splitCtx = true, then inner variable declaration do not overwrite ctx.
137 /// lay is the layer ID for evaluation (standard value semantics uses ValueLayer).
138
139 Value eval(AST e, Table ctx, bool splitCtx, Layer lay)
140 {
141 return e.match(
142 (StrLiteral e)
143 {
144 Value v = new StrValue(e.data);
145 if( lay == ValueLayer )
146 return v;
147 else
148 return lift(e.pos,v,lay,ctx);
149 },
150 (IntLiteral e)
151 {
152 Value v = new IntValue(e.data);
153 if( lay == ValueLayer )
154 return v;
155 else // rise
156 return lift(e.pos,v,lay,ctx);
157 },
158 (VarExpression e)
159 {
160 if( lay == ValueLayer )
161 return ctx.get(e.name, lay, e.pos);
162 if( ctx.has(e.name, lay, e.pos) )
163 return ctx.get(e.name, lay, e.pos);
164 else
165 return lift(e.pos, ctx.get(e.name, ValueLayer, e.pos), lay, ctx);
166 },
167 (LayExpression e)
168 {
169 if( e.layer == MacroLayer )
170 return macroEval(e.expr, ctx, false);
171 else
172 return eval(e.expr, ctx, true, e.layer);
173 },
174 (LetExpression e)
175 {
176 // for letrec, we need this, but should avoid overwriting????
177 // ctx.set(e.var, ValueLayer, new UndefinedValue, e.pos);
178 if(splitCtx)
179 ctx = new Table(ctx, Table.Kind.NotPropagateSet);
180 Value v = eval(e.init, ctx, true, lay);
181 ctx.set(e.name, (e.layer.length ? e.layer : lay), v, e.pos);
182 return eval(e.expr, ctx, false, lay);
183 },
184 (FuncallExpression e)
185 {
186 return invokeFunction(e.pos, eval(e.fun, ctx, true, lay), e.args, ctx, lay);
187 },
188 (FunLiteral e)
189 {
190 return new UserDefinedFunValue(e, ctx);
191 },
192 delegate Value (AST e)
193 {
194 throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
195 }
196 );
197 }
198
199 // [TODO] Optimization
200 Value macroEval(AST e, Table ctx, bool AlwaysMacro)
201 {
202 Layer theLayer = ValueLayer;
203
204 Table makeCons(Value a, Value d)
205 {
206 Table t = new Table;
207 t.set("car", theLayer, a);
208 t.set("cdr", theLayer, d);
209 return t;
210 }
211
212 Table pos = new Table;
213 if( e.pos !is null ) {
214 pos.set("filename", theLayer, new StrValue(e.pos.filename));
215 pos.set("lineno", theLayer, new IntValue(BigInt(e.pos.lineno)));
216 pos.set("column", theLayer, new IntValue(BigInt(e.pos.column)));
217 } else {
218 pos.set("filename", theLayer, new StrValue("nullpos"));
219 pos.set("lineno", theLayer, new IntValue(BigInt(0)));
220 pos.set("column", theLayer, new IntValue(BigInt(0)));
221 }
222
223 return e.match(
224 (StrLiteral e)
225 {
226 Table t = new Table;
227 t.set("pos", theLayer, pos);
228 t.set("is", theLayer, new StrValue("str"));
229 t.set("data", theLayer, new StrValue(e.data));
230 return t;
231 },
232 (IntLiteral e)
233 {
234 Table t = new Table;
235 t.set("pos", theLayer, pos);
236 t.set("is", theLayer, new StrValue("int"));
237 t.set("data", theLayer, new IntValue(e.data));
238 return t;
239 },
240 (VarExpression e)
241 {
242 if( ctx.has(e.name, MacroLayer, e.pos) )
243 return ctx.get(e.name, MacroLayer, e.pos);
244 else {
245 Table t = new Table;
246 t.set("pos", theLayer, pos);
247 t.set("is", theLayer, new StrValue("var"));
248 t.set("name", theLayer, new StrValue(e.name));
249 return cast(Value)t;
250 }
251 },
252 (LayExpression e)
253 {
254 if( AlwaysMacro )
255 {
256 Table t = new Table;
257 t.set("pos", theLayer, pos);
258 t.set("is", theLayer, new StrValue("lay"));
259 t.set("layer", theLayer, new StrValue(e.layer));
260 t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
261 return cast(Value)t;
262 }
263 else
264 {
265 if( e.layer == MacroLayer )
266 return macroEval(e.expr, ctx, false);
267 else
268 return eval(e.expr, ctx, true, e.layer);
269 }
270 },
271 (LetExpression e)
272 {
273 Table t = new Table;
274 t.set("pos", theLayer, pos);
275 t.set("is", theLayer, new StrValue("let"));
276 t.set("name", theLayer, new StrValue(e.name));
277 t.set("init", theLayer, macroEval(e.init,ctx,AlwaysMacro));
278 t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
279 return t;
280 },
281 (FuncallExpression e)
282 {
283 Value _f = macroEval(e.fun,ctx,AlwaysMacro);
284
285 if( auto f = cast(FunValue)_f )
286 return invokeFunction(e.pos, f, e.args, ctx, MacroLayer, AlwaysMacro);
287
288 Table t = new Table;
289 t.set("pos", theLayer, pos);
290 t.set("is", theLayer, new StrValue("app"));
291 t.set("fun", theLayer, _f);
292 Table args = new Table;
293 foreach_reverse(a; e.args) {
294 Table cons = new Table;
295 cons.set("car",theLayer,macroEval(a,ctx,AlwaysMacro));
296 cons.set("cdr",theLayer,args);
297 args = cons;
298 }
299 t.set("args", theLayer, args);
300 return cast(Value)t;
301 },
302 (FunLiteral e)
303 {
304 Table t = new Table;
305 t.set("pos", theLayer, pos);
306 t.set("is", theLayer, new StrValue("fun"));
307 t.set("funbody", theLayer, macroEval(e.funbody,ctx,AlwaysMacro));
308 Table params = new Table;
309 foreach_reverse(p; e.params)
310 {
311 Table lays = new Table;
312 foreach_reverse(lay; p.layers)
313 lays = makeCons(new StrValue(lay), lays);
314 Table kv = new Table;
315 kv.set("name", theLayer, new StrValue(p.name));
316 kv.set("layers", theLayer, lays);
317 Table cons = new Table;
318 params = makeCons(kv, params);
319 }
320 t.set("params", theLayer, params);
321 return t;
322 },
323 delegate Value (AST e)
324 {
325 throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
326 }
327 );
328 }
329
330 unittest
331 {
332 auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) );
333 assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
334 assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21)) );
335 assert_nothrow( r.ctx.get("x",ValueLayer) );
336 assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
337 }
338 unittest
339 {
340 auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) );
341 assert_eq( r.val, new IntValue(BigInt(21+21*21)) );
342 assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) );
343 assert_nothrow( r.ctx.get("x",ValueLayer) );
344 assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) );
345 }
346 unittest
347 {
348 assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) );
349 assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) );
350 }
351 unittest
352 {
353 assert_eq( evalString(`@a x=1; @b x=2; @a(x)`).val, new IntValue(BigInt(1)) );
354 assert_eq( evalString(`@a x=1; @b x=2; @b(x)`).val, new IntValue(BigInt(2)) );
355 assert_eq( evalString(`let x=1; let _ = (@a x=2;2); x`).val, new IntValue(BigInt(1)) );
356 assert_throw!Throwable( evalString(`let x=1; let _ = (@a x=2;2); @a(x)`) );
357 }
358 /*
359 unittest
360 {
361 assert_eq( evalString(`var fac = fun(x){
362 if(x)
363 { x*fac(x-1); }
364 else
365 { 1; };
366 };
367 fac(10);`).val, new IntValue(BigInt(10*9*8*5040)));
368 assert_eq( evalString(`var fib = fun(x){
369 if(x<2)
370 { 1; }
371 else
372 { fib(x-1) + fib(x-2); };
373 };
374 fib(5);`).val, new IntValue(BigInt(8)));
375 }
376
377 unittest
378 {
379 assert_throw!Throwable( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};@s(1+2)`) );
380 assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) );
381 assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) );
382 assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) );
383 }
384
385 unittest
386 {
387 assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) );
388 // there was a bug that declaration in the first line of function definition
389 // cannot be recursive
390 assert_nothrow( evalString(`def foo() {
391 def bar(y) { if(y<1) {0} else {bar(0)} };
392 bar(1)
393 }; foo()`) );
394 }
395 */