1 /**
2 * Authors: k.inaba
3 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
4 *
5 * Runtime data structures for Polemy programming language.
6 */
7 module polemy.value;
8 import polemy._common;
9 import polemy.failure;
10 import polemy.ast;
11 import polemy.layer;
12
13 /// Runtime values of Polemy
14
15 abstract class Value
16 {
17 }
18
19 ///
20 class IntValue : Value
21 {
22 BigInt data;
23
24 mixin SimpleClass;
25 override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); }
26 }
27
28 ///
29 class StrValue : Value
30 {
31 string data;
32
33 mixin SimpleClass;
34 override string toString() const { return data; }
35 }
36
37 ///
38 class UndValue : Value
39 {
40 mixin SimpleClass;
41 override string toString() const { return "<undefined>"; }
42 }
43
44
45 ///
46 abstract class FunValue : Value
47 {
48 const(Parameter[]) params();
49 Table definitionContext();
50 Value invoke(in LexPosition pos, Layer lay, Table ctx);
51 }
52
53 import polemy.eval; // circular...
54 version = MacroCache;
55 //version = AutoMemoization;
56 //version = AutoRerun;
57
58 ///
59 class UserDefinedFunValue : FunValue
60 {
61 FunLiteral ast;
62 Table defCtx;
63
64 this(FunLiteral ast, Table defCtx) { this.ast=ast; this.defCtx=defCtx; }
65 override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
66 override bool opEquals(Object rhs_) const /// member-by-member equality
67 {
68 if( auto rhs = cast(typeof(this))rhs_ )
69 return this.ast==rhs.ast && this.defCtx==rhs.defCtx;
70 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
71 }
72 override hash_t toHash() const /// member-by-member hash
73 {
74 return typeid(this.ast).getHash(&this.ast) + typeid(this.defCtx).getHash(&this.defCtx);
75 }
76 override int opCmp(Object rhs_) /// member-by-member compare
77 {
78 if( auto rhs = cast(typeof(this))rhs_ )
79 {
80 if(auto i = this.ast.opCmp(rhs.ast))
81 return i;
82 return this.defCtx.opCmp(rhs.defCtx);
83 }
84 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
85 }
86
87 private AST preprocessed_funbody;
88 private Value[Value[]][Layer] memo;
89
90 override const(Parameter[]) params() { return ast.params; }
91 override Table definitionContext() { return defCtx; }
92 override Value invoke(in LexPosition pos, Layer lay, Table ctx)
93 {
94 // TODO: only auto raised ones need memo? no?
95 // how can we integrate re-run ??
96 version(AutoMemoization)
97 {
98 Value[] memokey;
99 if( lay != ValueLayer && lay != MacroLayer )
100 {
101 foreach(i,p; ast.params)
102 memokey ~= ctx.get(p.name, lay); // lay?
103 if( auto memolay = lay in memo )
104 if( auto pv = memokey in *memolay )
105 return *pv;
106 memo[lay][memokey] = lift(ast.pos,new UndValue,lay,ctx);
107 }
108 }
109
110 // @macro run!!!
111 if( lay == MacroLayer )
112 return macroEval(ast.funbody, ctx, false);
113
114 version(MacroCache) {
115 if( preprocessed_funbody is null ) {
116 // .prototype!, forced macro cannot access parameters
117 ctx.kill = true; scope(exit)ctx.kill=false;
118 preprocessed_funbody = tableToAST(ValueLayer,macroEval(ast.funbody, ctx, true));
119 }
120 } else {
121 if( preprocessed_funbody is null ) {
122 // .prototype!, forced macro cannot access parameters
123 ctx.kill = true; scope(exit)ctx.kill=false;
124 preprocessed_funbody = tableToAST(ValueLayer,macroEval(ast.funbody, ctx, true));
125 }
126 }
127
128 auto v = eval(preprocessed_funbody, ctx, true, lay);
129 version(AutoMemoization)
130 {
131 if( lay != ValueLayer && lay != MacroLayer )
132 {
133 memo[lay][memokey] = v;
134 version(AutoReRun)
135 memo[lay][memokey] = eval(preprocessed_funbody, ctx, true, lay); // re-Run!!
136 }
137 }
138 return v;
139 }
140 }
141
142 ///
143 abstract class NativeFunValue : FunValue
144 {
145 Parameter[] params_data;
146 override const(Parameter[]) params() { return params_data; }
147 override Table definitionContext() { return new Table; } // todo: cache overrie
148 }
149
150 /// Named Constructor for FunValue
151
152 FunValue native(R,T...)(R delegate (T) dg)
153 {
154 return new class NativeFunValue {
155 this()
156 {
157 foreach(i, Ti; T)
158 params_data ~= new Parameter(text(i), []);
159 }
160 override Value invoke(in LexPosition pos, Layer lay, Table ctx)
161 {
162 if( lay != ValueLayer )
163 throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
164 T typed_args;
165 foreach(i, Ti; T) {
166 typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
167 if( typed_args[i] is null )
168 throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
169 }
170 try {
171 return dg(typed_args);
172 } catch( RuntimeException e ) {
173 throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
174 }
175 }
176 };
177 }
178
179 /// Context (variable environment)
180 /// Simlar to prototype chain of ECMAScript etc.
181 /// But extended with the notion of "Layer"
182
183 class Table : Value
184 {
185 enum Kind {PropagateSet, NotPropagateSet};
186 bool kill = false; // to refactor
187
188 this( Table proto=null, Kind k = Kind.PropagateSet )
189 { this.prototype = proto; this.kind = k; }
190
191 void set(string i, Layer lay, Value v, in LexPosition pos=null)
192 {
193 if( setIfExist(i, lay, v) )
194 return;
195 data[i][lay] = v;
196 }
197
198 bool has(string i, Layer lay, in LexPosition pos=null)
199 {
200 if( i in data ) {
201 if( lay !in data[i] )
202 return false;
203 if(kill)
204 return false;
205 return true;
206 }
207 if( prototype is null )
208 return false;
209 return prototype.has(i, lay, pos);
210 }
211
212 Value get(string i, Layer lay, in LexPosition pos=null)
213 {
214 if( i in data ) {
215 // [TODO] consider forwarding to proto also in this case
216 if( lay !in data[i] )
217 throw genex!RuntimeException(pos, sprintf!"'%s' is not set in layer %s"(i,lay));
218 if(kill)
219 throw genex!RuntimeException(pos, sprintf!"'%s' is killed in macro"(i));
220 return data[i][lay];
221 }
222 if( prototype is null )
223 throw new RuntimeException(pos, sprintf!"'%s' not found"(i));
224 return prototype.get(i, lay, pos);
225 }
226
227 T access(T,S...)( Layer lay, string path, S rest )
228 {
229 static if( rest.length == 0 )
230 {
231 if( this.has(path, lay) )
232 return cast(T) this.get(path, lay);
233 }
234 else
235 {
236 if(auto next = this.access!Table(lay,path))
237 return next.access!T(lay,rest);
238 }
239 return null;
240 }
241
242 string toStringWithoutParen() const
243 {
244 string result;
245 bool first = true;
246 foreach(k, l2d; data)
247 foreach(l,d; l2d)
248 {
249 if(first) first=false; else result~=", ";
250 result ~= k;
251 result ~= l;
252 result ~= ":";
253 result ~= text(cast(Value)d);
254 }
255 if( prototype !is null )
256 {
257 result ~= " / ";
258 result ~= prototype.toStringWithoutParen();
259 }
260 return result;
261 }
262
263 string toString() const
264 {
265 return "{" ~ toStringWithoutParen() ~ "}";
266 }
267
268 private:
269 Table prototype;
270 Kind kind;
271 Value[Layer][string] data;
272
273 bool setIfExist(string i, Layer lay, Value v)
274 {
275 if( i in data )
276 {
277 data[i][lay] = v;
278 return true;
279 }
280 if( kind==Kind.PropagateSet && prototype !is null )
281 return prototype.setIfExist(i, lay, v);
282 return false;
283 }
284 }
285
286 unittest
287 {
288 Table c0 = new Table;
289 Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
290 Table c012 = new Table(c01, Table.Kind.PropagateSet);
291 Table c013 = new Table(c01, Table.Kind.PropagateSet);
292
293 assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
294 assert_throw!RuntimeException( c013.get("x", ValueLayer) );
295 assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
296 assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
297 assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
298 assert_throw!RuntimeException( c01.get("x", ValueLayer) );
299
300 assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
301 assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
302 assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
303 assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
304
305 assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
306 assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
307 assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
308 assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
309 assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
310
311 assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
312 assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
313 assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
314 assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
315
316 assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
317 assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
318 assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
319 assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
320 assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
321
322 // [TODO] define the semantics and test @layers
323 }
324
325 immutable(LexPosition) extractPos( Table t )
326 {
327 Layer theLayer = ValueLayer;
328 if(auto tt = t.access!Table(theLayer, "pos"))
329 {
330 auto fn = tt.access!StrValue(theLayer, "filename");
331 auto ln = tt.access!IntValue(theLayer, "lineno");
332 auto cl = tt.access!IntValue(theLayer, "column");
333 if(fn !is null && ln !is null && cl !is null)
334 return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
335 }
336 return null;
337 }
338
339 Value[] tableAsConsList( Layer theLayer, Table t )
340 {
341 Value[] result;
342 while(t)
343 if(auto v = t.access!Value(theLayer, "car"))
344 {
345 result ~= v;
346 t = t.access!Table(theLayer, "cdr");
347 }
348 else
349 break;
350 return result;
351 }
352
353 AST[] tableToASTList( Layer theLayer, Table t )
354 {
355 AST[] result;
356 foreach(v; tableAsConsList(theLayer, t))
357 if(auto t = cast(Table)v)
358 result ~= tableToAST(theLayer,t);
359 else
360 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
361 return result;
362 }
363
364 AST tableToAST( Layer theLayer, Value vvvv )
365 {
366 Table t = cast(Table)vvvv;
367 if( t is null )
368 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)");
369
370 auto nodeType = t.access!StrValue(theLayer, "is");
371 if( nodeType is null )
372 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
373 auto pos = extractPos(t);
374 switch(nodeType.data)
375 {
376 case "int":
377 if(auto v = t.access!IntValue(theLayer, "data"))
378 return new IntLiteral(pos, v.data);
379 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
380 case "str":
381 if(auto v = t.access!StrValue(theLayer, "data"))
382 return new StrLiteral(pos, v.data);
383 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
384 case "var":
385 if(auto v = t.access!StrValue(theLayer, "name"))
386 return new VarExpression(pos, v.data);
387 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
388 case "lay":
389 if(auto v = t.access!StrValue(theLayer, "layer"))
390 if(auto e = t.access!Table(theLayer, "expr"))
391 return new LayExpression(pos, v.data, tableToAST(theLayer,e));
392 else
393 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
394 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
395 case "let":
396 if(auto n = t.access!StrValue(theLayer, "name"))
397 if(auto e = t.access!Table(theLayer, "init"))
398 if(auto b = t.access!Table(theLayer, "expr"))
399 {
400 string nn = n.data;
401 auto ee = tableToAST(theLayer, e);
402 auto bb = tableToAST(theLayer, b);
403 Layer lay="";
404 if(auto l = t.access!StrValue(theLayer, "layer"))
405 lay = l.data;
406 return new LetExpression(pos, nn, lay, ee, bb);
407 }
408 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
409 case "app":
410 if(auto f = t.access!Table(theLayer, "fun"))
411 if(auto a = t.access!Table(theLayer, "args"))
412 return new FuncallExpression(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
413 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`);
414 case "fun":
415 if(auto p = t.access!Table(theLayer, "params"))
416 if(auto b = t.access!Table(theLayer, "funbody"))
417 {
418 Parameter[] ps;
419 foreach(v; tableAsConsList(theLayer, p))
420 {
421 if(auto tt = cast(Table)v)
422 if(auto ss = tt.access!StrValue(theLayer, "name"))
423 if(auto ll = tt.access!Table(theLayer, "layers"))
424 {
425 Layer[] ls;
426 foreach(lll; tableAsConsList(theLayer, ll))
427 if(auto l = cast(StrValue)lll)
428 ls ~= l.data;
429 else
430 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll));
431 ps ~= new Parameter(ss.data, ls);
432 continue;
433 }
434 else
435 {
436 Layer[] emp;
437 ps ~= new Parameter(ss.data, emp);
438 continue;
439 }
440 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v));
441 }
442 auto bb = tableToAST(theLayer, b);
443 return new FunLiteral(pos,ps,bb);
444 }
445 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
446 default:
447 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
448 }
449 }