Differences From Artifact [10da2d7378e56ec4]:
- File
polemy/eval.d
- 2010-11-26 15:13:58 - part of checkin [6760e0dd02] on branch trunk - evaluator refactoring done. x6 speed up. (user: kinaba) [annotate]
To Artifact [9460508c714f0a1b]:
- File
polemy/eval.d
- 2010-11-27 11:46:26 - part of checkin [203e4cb208] on branch trunk - fixed automatic memoization bug (whole part of the contexts are now used as the memo key) (user: kinaba) [annotate]
15 15 import polemy.valueconv;
16 16 import std.signals;
17 17
18 18 /// Objects for maitaining global environment and evaluation of expression on it
19 19 class Evaluator
20 20 {
21 21 public:
22 - /// Initialize evaluator with empty context
23 - this() { theContext = new Table; }
24 -
25 22 /// Evaluate the AST
26 23 Value evalAST(AST e)
27 - {
28 - return macroAndEval(e, ValueLayer, theContext, OverwriteCtx);
29 - }
24 + { return getLayerEval(ValueLayer).macroAndEval(e, theContext, LayerEval.OverwriteCtx); }
30 25
31 26 /// Evaluate the string
32 27 Value evalString(S,T...)(S str, T fn_ln_cn)
33 - {
34 - return evalAST(parseString(str,fn_ln_cn));
35 - }
28 + { return evalAST(parseString(str,fn_ln_cn)); }
36 29
37 30 /// Evaluate the file
38 31 Value evalFile(S,T...)(S filename, T ln_cn)
39 - {
40 - return evalAST(parseFile(filename,ln_cn));
41 - }
32 + { return evalAST(parseFile(filename,ln_cn)); }
42 33
43 34 /// Get the global context
44 35 Table globalContext()
36 + { return theContext; }
37 +
38 + /// Initialize evaluator with empty context
39 + this()
45 40 {
46 - return theContext;
41 + theContext = new Table;
42 + theLayers[ValueLayer] = new ValueLayerEval;
43 + theLayers[MacroLayer] = new MacroLayerEval;
44 + theLayers[RawMacroLayer] = new RawMacroLayerEval;
47 45 }
48 46
49 47 private:
50 - Table theContext;
48 + Table theContext;
49 + LayerEval[Layer] theLayers;
51 50
52 - enum : bool { CascadeCtx=false, OverwriteCtx=true };
53 -
54 - LayerEval getLayerEvaluator(Layer lay)
51 + /// Get the layer evaluator from layer ID
52 + LayerEval getLayerEval(Layer lay)
55 53 {
56 - if( lay == ValueLayer )
57 - return new ValueLayerEval;
58 - if( lay == RawMacroLayer )
59 - return new RawMacroLayerEval;
60 - if( lay == MacroLayer )
61 - return new MacroLayerEval;
62 - return new UserDefinedLayerEval(lay);
54 + if(auto p = lay in theLayers)
55 + return *p;
56 + return theLayers[lay] = new UserDefinedLayerEval(lay);
63 57 }
64 58
59 + /// Interface of layers
65 60 abstract class LayerEval
66 61 {
62 + enum : bool { CascadeCtx=false, OverwriteCtx=true };
63 +
67 64 /// Concrete layers should implement these
68 - Layer currentLayer();
69 - Value eval_( Die e, Table ctx, bool ctxMod );///
70 - Value eval_( Str e, Table ctx, bool ctxMod );///
71 - Value eval_( Int e, Table ctx, bool ctxMod );///
72 - Value eval_( Var e, Table ctx, bool ctxMod );///
73 - Value eval_( Lay e, Table ctx, bool ctxMod );///
74 - Value eval_( Let e, Table ctx, bool ctxMod );///
75 - Value eval_( App e, Table ctx, bool ctxMod );///
76 - Value eval_( Fun e, Table ctx, bool ctxMod );///
65 + protected Layer layerID();
66 + protected Value eval_( Die e, Table ctx, bool ctxMod );///
67 + protected Value eval_( Str e, Table ctx, bool ctxMod );///
68 + protected Value eval_( Int e, Table ctx, bool ctxMod );///
69 + protected Value eval_( Var e, Table ctx, bool ctxMod );///
70 + protected Value eval_( Lay e, Table ctx, bool ctxMod );///
71 + protected Value eval_( Let e, Table ctx, bool ctxMod );///
72 + protected Value eval_( App e, Table ctx, bool ctxMod );///
73 + protected Value eval_( Fun e, Table ctx, bool ctxMod );///
74 +
75 + /// Should override this also, if the layer may return null for optimization
76 + Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx )
77 + { return eval(e,ctx,ctxMod); }
78 +
79 + /// Do macro expansion first, and then eval. Should override this also if it is NOT macro layer.
80 + Value macroAndEval( AST e, Table ctx, bool ctxMod = CascadeCtx )
81 + { return evalToNonNull(e,ctx,ctxMod); }
77 82
78 83 /// dynamic-overload-resolution
79 - Value eval( AST e, Table ctx, bool ctxMod )
84 + Value eval( AST e, Table ctx, bool ctxMod = CascadeCtx )
80 85 {
81 - enum funName = "eval_"; // modify here to customize
82 - alias TypeTuple!(e,ctx,ctxMod) params; // modify here to customize
86 + enum funName = "eval_"; // modify here for customization
87 + alias TypeTuple!(e,ctx,ctxMod) params; // modify here for customization
83 88
84 89 alias typeof(__traits(getOverloads, this, funName)) ovTypes;
85 90 alias staticMap!(firstParam, ovTypes) fstTypes;
86 91 alias DerivedToFront!(fstTypes) fstTypes_sorted;
87 92 foreach(i, T; fstTypes_sorted)
88 93 static if( is(T == typeof(params[0])) ) {} else if( auto _x = cast(T)params[0] )
89 94 return __traits(getOverloads, this, funName)[i](_x, params[1..$]);
90 95
91 96 // modify here to customize the default behavior
92 97 assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined"));
93 98 }
94 99
95 - ///
96 - Value invokeFunction(Value _f, AST[] args, Table ctx, LexPosition pos, string callstackmsg)
100 + /// Function calling convention.
101 + /// Run all arugment AST in the appropriate layer and invoke the function.
102 + Value invokeFunction(Value f_, AST[] args, Table ctx, LexPosition pos, string callstackmsg)
97 103 {
98 - if(auto f = cast(FunValue)_f)
99 - {
100 - Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
101 - foreach(i,p; f.params())
102 - if( p.layers.empty ) {
103 - Value v = this.eval(args[i], ctx, CascadeCtx);
104 - if(v is null) v = ast2table(args[i]);
105 - newCtx.set(p.name, currentLayer(), v);
104 + FunValue f = cast(FunValue)f_;
105 + if( f is null )
106 + throw genex!RuntimeException(pos, text("tried to call non-function: ",f));
107 + if( f.params().length != args.length )
108 + throw genex!RuntimeException(pos,
109 + sprintf!("%d arguments required but %d passed")(f.params().length, args.length));
110 +
111 + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
112 + foreach(i,p; f.params())
113 + if( p.layers.empty )
114 + newCtx.set(p.name, layerID(), this.evalToNonNull(args[i], ctx));
115 + else
116 + foreach(argLay; p.layers)
117 + {
118 + Layer ll = argLay;
119 + if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) )
120 + ll = RawMacroLayer; // explicit @macro invokes (rawmacro)
121 + newCtx.set(p.name, argLay, getLayerEval(ll).evalToNonNull(args[i], ctx));
106 122 }
107 - else
108 - foreach(argLay; p.layers) {
109 - Layer ll = argLay;
110 - if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) )
111 - ll = RawMacroLayer; // explicit @macro invokes (rawmacro)
112 - Value v = getLayerEvaluator(ll).eval(args[i], ctx, CascadeCtx);
113 - if(v is null) v = ast2table(args[i]);
114 - newCtx.set(p.name, argLay, v);
115 - }
116 - scope _ = new PushCallStack(pos, callstackmsg);
117 - return f.invoke(currentLayer(), newCtx, pos);
118 - }
119 - throw genex!RuntimeException(pos, text("tried to call non-function: ",_f));
123 + scope _ = new PushCallStack(pos, callstackmsg);
124 + return f.invoke(layerID(), newCtx, pos);
120 125 }
121 126
122 - ///
127 + /// Lift the value v to the current layer
123 128 Value lift(Value v, Table ctx, LexPosition pos)
124 129 {
125 - Layer lay = currentLayer();
126 -
127 - // functions are automatically lifterd
130 + // functions are automatically lifted by itself
128 131 if( cast(FunValue) v )
129 132 return v;
130 133
134 + Layer lay = layerID();
135 +
136 + // load the lift function
131 137 if( !ctx.has(lay, LiftLayer) )
132 138 throw genex!RuntimeException(pos, "lift function for "~lay~" is not registered" );
133 139
134 - // similar to invokeFunction, but with only one argument bound to ValueLayer
135 - auto _f = ctx.get(lay, LiftLayer, pos);
136 - if(auto f = cast(FunValue)_f)
137 - {
138 - Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
139 - auto ps = f.params();
140 - if( ps.length != 1 )
141 - throw genex!RuntimeException(pos,
142 - text("lift function for", lay, " must take exactly one argument of ", ValueLayer));
143 - if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
144 - {
145 - newCtx.set(ps[0].name, ValueLayer, v);
146 - scope _ = new PushCallStack(pos, lay);
147 - return f.invoke(ValueLayer, newCtx, pos);
148 - }
149 - else
150 - throw genex!RuntimeException(pos,
151 - text("lift function for", lay, " must take exactly one argument of ", ValueLayer));
152 - }
153 - throw genex!RuntimeException(pos,
154 - text("non-function ", _f, " is registered as the lift function for ", lay));
140 + Value f_ = ctx.get(lay, LiftLayer, pos);
141 + FunValue f = cast(FunValue) f_;
142 + if( f is null )
143 + throw genex!RuntimeException(pos,
144 + text("non-function ", f_, " is registered as the lift function for ", lay));
145 + if( f.params().length != 1 || f.params()[0].layers.length>=1 )
146 + throw genex!RuntimeException(pos,
147 + text("lift function must take exactly 1 neutral parameter:",lay));
148 +
149 + // invokeFunction
150 + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet);
151 + newCtx.set(f.params()[0].name, ValueLayer, v);
152 + scope _ = new PushCallStack(pos, "lift to "~lay);
153 + return f.invoke(ValueLayer, newCtx, pos);
155 154 }
156 155 }
157 156
158 157 /// Evaluator for standard @value semantics
159 158 class ValueLayerEval : LayerEval
160 159 {
161 - override Layer currentLayer()
160 + override Layer layerID()
162 161 {
163 162 return ValueLayer;
164 163 }
165 164 override Value eval_( Die e, Table ctx, bool ctxMod )
166 165 {
167 166 throw genex!RuntimeException(e.pos, "undefined case");
168 167 }
................................................................................
172 171 }
173 172 override Value eval_( Int e, Table ctx, bool ctxMod )
174 173 {
175 174 return new IntValue(e.data);
176 175 }
177 176 override Value eval_( Var e, Table ctx, bool ctxMod )
178 177 {
179 - return ctx.get(e.name, currentLayer(), e.pos);
178 + return ctx.get(e.name, layerID(), e.pos);
180 179 }
181 180 override Value eval_( Lay e, Table ctx, bool ctxMod )
182 181 {
183 - auto le = getLayerEvaluator(e.layer);
184 - auto v = le.eval(e.expr,ctx,CascadeCtx);
185 - if( (v is null) && (cast(MacroLayerEval)le !is null) )
186 - return ast2table(e.expr);
187 - else
188 - return v;
182 + return getLayerEval(e.layer).evalToNonNull(e.expr, ctx);
189 183 }
190 184 override Value eval_( Let e, Table ctx, bool ctxMod )
191 185 {
192 - Table newCtx = ctxMod ? ctx : new Table(ctx, Table.Kind.NotPropagateSet);
193 - Value ri = this.eval(e.init, newCtx, CascadeCtx);
194 - if(e.name!="_")
195 - newCtx.set(e.name, e.layer.empty ? currentLayer() : e.layer, ri);
196 - return this.eval(e.expr, newCtx, OverwriteCtx);
186 + if( !ctxMod )
187 + ctx = new Table(ctx, Table.Kind.NotPropagateSet);
188 + Value ri = this.eval(e.init, ctx);
189 + ctx.set(e.name, e.layer.empty ? layerID(): e.layer, ri);
190 + return this.eval(e.expr, ctx, OverwriteCtx);
197 191 }
198 192 override Value eval_( App e, Table ctx, bool ctxMod )
199 193 {
200 194 Value f = this.eval( e.fun, ctx, CascadeCtx );
201 195 return this.invokeFunction(f, e.args, ctx, e.pos, getNameIfPossible(e.fun));
202 196 }
203 197 override Value eval_( Fun e, Table ctx, bool ctxMod )
204 198 {
205 199 return createNewFunction(e, ctx);
206 200 }
201 + override Value macroAndEval( AST e, Table ctx, bool ctxMod )
202 + {
203 + // incremental execution of let-expressions
204 + if(auto le = cast(Let)e)
205 + {
206 + AST ai = runMacro(le.init, ctx);
207 +
208 + if( !ctxMod )
209 + ctx = new Table(ctx, Table.Kind.NotPropagateSet);
210 +
211 + Value vi = this.eval(ai, ctx);
212 + ctx.set(le.name, le.layer.empty ? layerID() : le.layer, vi);
213 + return this.macroAndEval(le.expr, ctx, OverwriteCtx);
214 + }
215 + else
216 + return this.eval(runMacro(e,ctx,ctxMod), ctx, ctxMod);
217 + }
207 218 }
208 219
209 220 /// Evaluator for user-defined layer
210 221 class UserDefinedLayerEval : ValueLayerEval
211 222 {
212 - Layer layerID;
223 + Layer theID;
213 224 mixin SimpleConstructor;
214 225
215 - override Layer currentLayer()
226 + override Layer layerID()
216 227 {
217 - return layerID;
228 + return theID;
218 229 }
219 230 override Value eval_( Die e, Table ctx, bool ctxMod )
220 231 {
221 - return new UndefinedValue;
232 + return new BottomValue;
222 233 }
223 234 override Value eval_( Str e, Table ctx, bool ctxMod )
224 235 {
225 - return this.lift(new StrValue(e.data), ctx, e.pos);
236 + return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos);
226 237 }
227 238 override Value eval_( Int e, Table ctx, bool ctxMod )
228 239 {
229 - return this.lift(new IntValue(e.data), ctx, e.pos);
240 + return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos);
230 241 }
231 242 override Value eval_( Var e, Table ctx, bool ctxMod )
232 243 {
233 - if( ctx.has(e.name, currentLayer()) )
234 - return ctx.get(e.name, currentLayer());
244 + if( ctx.has(e.name, layerID()) )
245 + return ctx.get(e.name, layerID());
235 246 return this.lift(ctx.get(e.name, ValueLayer, e.pos), ctx, e.pos);
236 247 }
237 248 }
238 249
239 - // Convention!!
240 - // returns null if never used macro-like feature
250 + // Macro layer. For optimization, if AST didn't change it returns null
241 251 class MacroLayerEval : LayerEval
242 252 {
243 - override Layer currentLayer()
253 + override Layer layerID()
244 254 {
245 255 return MacroLayer;
256 + }
257 + override Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx )
258 + {
259 + Value v = this.eval(e, ctx, ctxMod);
260 + return v is null ? ast2table(e) : v;
246 261 }
247 262 override Value eval_( Die e, Table ctx, bool ctxMod )
248 263 {
249 264 return null;
250 265 }
251 266 override Value eval_( Str e, Table ctx, bool ctxMod )
252 267 {
................................................................................
254 269 }
255 270 override Value eval_( Int e, Table ctx, bool ctxMod )
256 271 {
257 272 return null;
258 273 }
259 274 override Value eval_( Var e, Table ctx, bool ctxMod )
260 275 {
261 - if( ctx.has(e.name, currentLayer()) )
262 - return ctx.get(e.name, currentLayer(), e.pos);
263 - else
264 - return null;
276 + if( ctx.has(e.name, layerID()) )
277 + return ctx.get(e.name, layerID(), e.pos);
278 + return null;
265 279 }
266 280 override Value eval_( Lay e, Table ctx, bool ctxMod )
267 281 {
268 - auto le = getLayerEvaluator(e.layer);
269 - return le.eval(e.expr,ctx,CascadeCtx);
282 + return getLayerEval(e.layer).eval(e.expr,ctx);
270 283 }
271 284 override Value eval_( Let e, Table ctx, bool ctxMod )
272 285 {
273 - Table newCtx = ctxMod ? ctx : new Table(ctx, Table.Kind.NotPropagateSet);
274 - Value ai = this.eval(e.init, newCtx, CascadeCtx);
275 - newCtx.set(e.name, NoopLayer, null);
276 - Value ae = this.eval(e.expr, newCtx, OverwriteCtx);
277 - if( ai is null && ae is null )
278 - return null;
286 + if( !ctxMod )
287 + ctx = new Table(ctx, Table.Kind.NotPropagateSet);
288 +
289 + Value ai = this.eval(e.init, ctx);
290 + ctx.set(e.name, NoopLayer, null);
291 + Value ae = this.eval(e.expr, ctx, OverwriteCtx);
292 +
293 + if( ai is null && ae is null ) return null;
279 294 if( ai is null ) ai = ast2table(e.init);
280 295 if( ae is null ) ae = ast2table(e.expr);
296 +
281 297 return ast2table(e, delegate Value (AST _){
282 298 if(_ is e.init) { return ai; }
283 299 if(_ is e.expr) { return ae; }
284 300 assert(false);
285 301 });
286 302 }
287 303 override Value eval_( App e, Table ctx, bool ctxMod )
288 304 {
289 - Value f = this.eval( e.fun, ctx, CascadeCtx );
305 + Value f = this.eval( e.fun, ctx );
290 306 if(auto ff = cast(FunValue)f)
291 307 return this.invokeFunction(ff, e.args, ctx, e.pos, getNameIfPossible(e.fun));
292 - else {
308 + else
309 + {
293 310 bool allNull = (f is null);
294 311 Value[] vas;
295 - foreach(a; e.args) {
312 + foreach(a; e.args)
313 + {
296 314 Value va = this.eval(a, ctx, CascadeCtx);
297 315 if(va !is null) allNull = false;
298 316 vas ~= va;
299 317 }
300 318 if( allNull )
301 319 return null;
302 320 return ast2table(e, delegate Value (AST _){
................................................................................
304 322 foreach(i,a; e.args) if(_ is a) return (vas[i] is null ? ast2table(a) : vas[i]);
305 323 assert(false);
306 324 });
307 325 }
308 326 }
309 327 override Value eval_( Fun e, Table ctx, bool ctxMod )
310 328 {
311 - Table newCtx = new Table(ctx, Table.Kind.NotPropagateSet);
329 + ctx = new Table(ctx, Table.Kind.NotPropagateSet);
312 330 foreach(p; e.params)
313 - newCtx.set(p.name, NoopLayer, null);
314 - Value af = this.eval(e.funbody, newCtx, CascadeCtx);
331 + ctx.set(p.name, NoopLayer, null);
332 + Value af = this.eval(e.funbody, ctx);
315 333 if( af is null )
316 334 return null;
317 335 return ast2table(e, (AST _){if(_ is e.funbody)return af; assert(false);});
318 336 }
319 337 }
320 338
339 + /// (rawmacro) layer. almost same as @macro, but the Layer expression becomes AST, too.
321 340 class RawMacroLayerEval : MacroLayerEval
322 341 {
323 342 override Value eval_( Lay e, Table ctx, bool ctxMod )
324 343 {
325 - Value ae = this.eval(e.expr, ctx, CascadeCtx);
344 + Value ae = this.eval(e.expr, ctx);
326 345 return ae is null ? null
327 346 : ast2table(e, delegate Value (AST _){if(_ is e.expr)return ae; assert(false);});
328 347 }
329 348 }
330 349
331 -private:
332 - Value macroAndEval( AST e_, Layer lay, Table ctx, bool ctxMod )
333 - {
334 - assert( !isASTLayer(lay) );
335 - if(auto e = cast(Let)e_)
336 - {
337 - Value vai = getLayerEvaluator(RawMacroLayer).eval(e.init, ctx, CascadeCtx);
338 - AST ai = (vai is null ? e.init : polemy2d!(AST)(vai, e.pos));
350 +private: // short utils
339 351
340 - if( !ctxMod )
341 - ctx = new Table(ctx, Table.Kind.NotPropagateSet);
342 -
343 - Value vi = getLayerEvaluator(lay).eval(ai, ctx, CascadeCtx);
344 - string theLayer = e.layer.empty ? lay : e.layer;
345 - ctx.set(e.name, theLayer, vi);
346 -
347 - return macroAndEval( e.expr, lay, ctx, OverwriteCtx );
348 - }
349 - else
350 - {
351 - Value va = getLayerEvaluator(RawMacroLayer).eval(e_, ctx, ctxMod);
352 - AST a = (va is null ? e_ : polemy2d!(AST)(va, e_.pos));
353 - return getLayerEvaluator(lay).eval(a, ctx, ctxMod);
354 - }
355 - }
356 -
357 -private:
358 352 string getNameIfPossible(AST e)
359 353 {
360 354 if(auto v = cast(Var)e)
361 355 return v.name;
362 356 return "";
363 357 }
364 358
359 + AST runMacro(AST e, Table ctx, bool ctxMod=LayerEval.CascadeCtx)
360 + {
361 + Value v = getLayerEval(RawMacroLayer).eval(e, ctx, ctxMod);
362 + return (v is null ? e : polemy2d!(AST)(v, e.pos));
363 + }
364 +
365 +private:
365 366 Value createNewFunction(Fun e, Table ctx)
366 367 {
367 368 class UserDefinedFunValue : FunValue
368 369 {
369 370 Fun ast;
370 371 Table defCtx;
371 372 override const(Parameter[]) params() { return ast.params; }
................................................................................
385 386 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
386 387 throw genex!RuntimeException("comparison with value and something other");
387 388 }
388 389 override hash_t toHash() {
389 390 return (cast(hash_t)cast(void*)ast) + (cast(hash_t)cast(void*)defCtx);
390 391 }
391 392
392 - AST macroCache;
393 393 AST[void*] mandeCache;
394 394 static class MemokeyType
395 395 {
396 - void* a; Layer b; Tuple!(string,Layer,Value)[] c;
397 - hash_t toHash() {
398 - hash_t h = structuralHash(a) + structuralHash(b);
399 - foreach(e; c)
400 - h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]);
401 - return h;
402 - }
403 - mixin SimpleToString;
404 - mixin SimpleConstructor;
405 - mixin SimpleCompareWithoutToHash;
396 + void* a; Layer b; Table c;
397 + mixin SimpleClass;
406 398 }
407 399 static Tuple!(Value,int)[MemokeyType] memo;
408 400
409 401 override Value invoke(Layer lay, Table ctx, LexPosition pos)
410 402 {
411 - if( isASTLayer(lay) ) {
412 - Value v = getLayerEvaluator(lay).eval(ast.funbody, ctx, CascadeCtx);
413 - if( v is null ) v = ast2table(ast.funbody);
414 - return v;
415 - }
416 - return macroAndEval(ast.funbody, lay, ctx, CascadeCtx);
417 -/*
418 - auto nonMemoizedRun = (){
419 - if( macroCache is null )
420 - {
421 - auto va = macroAndEval(e.funbody, lay, ctx, CascadeCtx, mandeCache);
422 - macroCache = va[1];
423 - return va[0];
424 - }
425 - else
426 - return eval(macroCache, lay, ctx);
427 - };
403 + auto evlay = getLayerEval(lay);
404 + auto nonMemoizedRun = (){ return evlay.macroAndEval(ast.funbody, ctx); };
428 405
429 406 if( !isUserDefinedLayer(lay) )
430 407 return nonMemoizedRun();
431 408
432 - MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx.direct_entries());
433 -
409 + // automatic memoized co-recursive execution
410 + MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx);
434 411 if(auto p = memokey in memo)
435 412 {
436 413 (*p)[1] ++;
437 414 return (*p)[0];
438 415 }
439 416 else
440 - memo[memokey] = tuple(lift(new UndefinedValue, lay, ctx, pos), 0);
417 + memo[memokey] = tuple(evlay.lift(new BottomValue, ctx, pos), 0);
441 418
442 419 Value r = nonMemoizedRun();
443 420
444 421 int touched = memo[memokey][1];
445 422 memo[memokey] = tuple(r, 12345678);
446 - //if(touched) {DBG("rerun :: ",r);r = nonMemoizedRun();} // twice!!
447 423 return r;
448 -*/
449 424 }
450 425 }
451 426 return new UserDefinedFunValue(e,ctx);
452 427 }
453 428
454 429 public:
455 430 /// Add primitive function to the global context
................................................................................
499 474 }
500 475 }
501 476 }
502 477 theContext.set(name, defLay, new NativeFunValue(dg));
503 478 }
504 479 }
505 480
506 -version(unittest) import polemy.runtime;
481 +version(unittest)
482 + import polemy.runtime;
483 +
507 484 unittest
508 485 {
509 486 auto e = new Evaluator;
510 487 enrollRuntimeLibrary(e);
511 488 auto r = assert_nothrow( e.evalString(`var x = 21; x + x*x;`) );
512 489 assert_eq( r, new IntValue(BigInt(21+21*21)) );
513 490 assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21)) );
514 491 assert_nothrow( e.globalContext.get("x",ValueLayer) );
515 492 assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) );
516 493 }
494 +
517 495 unittest
518 496 {
519 497 auto e = new Evaluator;
520 498 enrollRuntimeLibrary(e);
521 499 auto r = assert_nothrow( e.evalString(`var x = 21; var x = x + x*x;`) );
522 500 assert_eq( r, new IntValue(BigInt(21+21*21)) );
523 501 assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(21+21*21) );
................................................................................
593 571 }
594 572
595 573 unittest
596 574 {
597 575 auto e = new Evaluator;
598 576 enrollRuntimeLibrary(e);
599 577 assert_throw!RuntimeException( e.evalString(`...`) );
600 - assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new UndefinedValue );
578 + assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new BottomValue );
601 579 }