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
55 ///
56 class UserDefinedFunValue : FunValue
57 {
58 FunLiteral ast;
59 Table defCtx;
60 override const(Parameter[]) params() { return ast.params; }
61 override Table definitionContext() { return defCtx; }
62 override Value invoke(in LexPosition pos, Layer lay, Table ctx)
63 {
64 // TODO: only auto raised ones need memo? no?
65 // auto memoization
66 /*
67 if( lay != ValueLayer && lay != MacroLayer )
68 {
69 if( auto memolay = lay in memo )
70 if( auto pv = args in *memolay )
71 return *pv;
72 memo[lay][args] = lift(e.pos,new UndValue,lay,ctx);
73 }
74
75 */
76 // @macro run!!!
77 if( lay == MacroLayer )
78 return macroEval(ast.funbody, ctx, false);
79 /*TODO memo*/ AST macroMemo;
80 if( macroMemo is null ) {
81 // .prototype!, forced macro cannot access parameters
82 ctx.kill = true; scope(exit)ctx.kill=false;
83 auto tbl = macroEval(ast.funbody, ctx, true);
84 macroMemo = tableToAST(ValueLayer,tbl);
85 }
86 auto v = eval(macroMemo, ctx, true, lay);
87
88 //auto v = eval(e.funbody, ctxNeo, true, lay);
89 // auto memoization
90 // if( lay != ValueLayer && lay != MacroLayer )
91 // memo[lay][args] = v;
92 return v;
93 }
94
95 mixin SimpleClass;
96 override string toString() const { return sprintf!"(function:%x:%x)"(cast(void*)ast, cast(void*)defCtx); }
97 }
98
99 ///
100 abstract class NativeFunValue : FunValue
101 {
102 Parameter[] params_data;
103 override const(Parameter[]) params() { return params_data; }
104 override Table definitionContext() { return new Table; } // todo: cache overrie
105 }
106
107 /// Named Constructor for FunValue
108
109 FunValue native(R,T...)(R delegate (T) dg)
110 {
111 return new class NativeFunValue {
112 this()
113 {
114 foreach(i, Ti; T)
115 params_data ~= new Parameter(text(i), []);
116 }
117 override Value invoke(in LexPosition pos, Layer lay, Table ctx)
118 {
119 if( lay != ValueLayer )
120 throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
121 T typed_args;
122 foreach(i, Ti; T) {
123 typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
124 if( typed_args[i] is null )
125 throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
126 }
127 try {
128 return dg(typed_args);
129 } catch( RuntimeException e ) {
130 throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
131 }
132 }
133 };
134 }
135
136 /// Context (variable environment)
137 /// Simlar to prototype chain of ECMAScript etc.
138 /// But extended with the notion of "Layer"
139
140 class Table : Value
141 {
142 enum Kind {PropagateSet, NotPropagateSet};
143 bool kill = false; // to refactor
144
145 this( Table proto=null, Kind k = Kind.PropagateSet )
146 { this.prototype = proto; this.kind = k; }
147
148 void set(string i, Layer lay, Value v, in LexPosition pos=null)
149 {
150 if( setIfExist(i, lay, v) )
151 return;
152 data[i][lay] = v;
153 }
154
155 bool has(string i, Layer lay, in LexPosition pos=null)
156 {
157 if( i in data ) {
158 if( lay !in data[i] )
159 return false;
160 if(kill)
161 return false;
162 return true;
163 }
164 if( prototype is null )
165 return false;
166 return prototype.has(i, lay, pos);
167 }
168
169 Value get(string i, Layer lay, in LexPosition pos=null)
170 {
171 if( i in data ) {
172 // [TODO] consider forwarding to proto also in this case
173 if( lay !in data[i] )
174 throw genex!RuntimeException(pos, sprintf!"variable %s is not set in layer %s"(i,lay));
175 if(kill)
176 throw genex!RuntimeException(pos, sprintf!"variable %s is killed in macro"(i));
177 return data[i][lay];
178 }
179 if( prototype is null )
180 throw new RuntimeException(pos, sprintf!"variable %s not found"(i));
181 return prototype.get(i, lay, pos);
182 }
183
184 T access(T,S...)( Layer lay, string path, S rest )
185 {
186 static if( rest.length == 0 )
187 {
188 if( this.has(path, lay) )
189 return cast(T) this.get(path, lay);
190 }
191 else
192 {
193 if(auto next = this.access!Table(lay,path))
194 return next.access!T(lay,rest);
195 }
196 return null;
197 }
198
199 string toStringWithoutParen() const
200 {
201 string result;
202 bool first = true;
203 foreach(k, l2d; data)
204 foreach(l,d; l2d)
205 {
206 if(first) first=false; else result~=", ";
207 result ~= k;
208 result ~= l;
209 result ~= ":";
210 result ~= text(cast(Value)d);
211 }
212 if( prototype !is null )
213 {
214 result ~= " / ";
215 result ~= prototype.toStringWithoutParen();
216 }
217 return result;
218 }
219
220 string toString() const
221 {
222 return "{" ~ toStringWithoutParen() ~ "}";
223 }
224
225 private:
226 Table prototype;
227 Kind kind;
228 Value[Layer][string] data;
229
230 bool setIfExist(string i, Layer lay, Value v)
231 {
232 if( i in data )
233 {
234 data[i][lay] = v;
235 return true;
236 }
237 if( kind==Kind.PropagateSet && prototype !is null )
238 return prototype.setIfExist(i, lay, v);
239 return false;
240 }
241 }
242
243 unittest
244 {
245 Table c0 = new Table;
246 Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
247 Table c012 = new Table(c01, Table.Kind.PropagateSet);
248 Table c013 = new Table(c01, Table.Kind.PropagateSet);
249
250 assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
251 assert_throw!RuntimeException( c013.get("x", ValueLayer) );
252 assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
253 assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
254 assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
255 assert_throw!RuntimeException( c01.get("x", ValueLayer) );
256
257 assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
258 assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
259 assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
260 assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
261
262 assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
263 assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
264 assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
265 assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
266 assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
267
268 assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
269 assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
270 assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
271 assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
272
273 assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
274 assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
275 assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
276 assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
277 assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
278
279 // [TODO] define the semantics and test @layers
280 }
281
282 immutable(LexPosition) extractPos( Table t )
283 {
284 Layer theLayer = ValueLayer;
285 if(auto tt = t.access!Table(theLayer, "pos"))
286 {
287 auto fn = tt.access!StrValue(theLayer, "filename");
288 auto ln = tt.access!IntValue(theLayer, "lineno");
289 auto cl = tt.access!IntValue(theLayer, "column");
290 if(fn !is null && ln !is null && cl !is null)
291 return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
292 }
293 return null;
294 }
295
296 Value[] tableAsConsList( Layer theLayer, Table t )
297 {
298 Value[] result;
299 while(t)
300 if(auto v = t.access!Value(theLayer, "car"))
301 {
302 result ~= v;
303 t = t.access!Table(theLayer, "cdr");
304 }
305 else
306 break;
307 return result;
308 }
309
310 AST[] tableToASTList( Layer theLayer, Table t )
311 {
312 AST[] result;
313 foreach(v; tableAsConsList(theLayer, t))
314 if(auto t = cast(Table)v)
315 result ~= tableToAST(theLayer,t);
316 else
317 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
318 return result;
319 }
320
321 AST tableToAST( Layer theLayer, Value vvvv )
322 {
323 Table t = cast(Table)vvvv;
324 if( t is null )
325 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)");
326
327 auto nodeType = t.access!StrValue(theLayer, "is");
328 if( nodeType is null )
329 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
330 auto pos = extractPos(t);
331 switch(nodeType.data)
332 {
333 case "int":
334 if(auto v = t.access!IntValue(theLayer, "data"))
335 return new IntLiteral(pos, v.data);
336 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
337 case "str":
338 if(auto v = t.access!StrValue(theLayer, "data"))
339 return new StrLiteral(pos, v.data);
340 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
341 case "var":
342 if(auto v = t.access!StrValue(theLayer, "name"))
343 return new VarExpression(pos, v.data);
344 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
345 case "lay":
346 if(auto v = t.access!StrValue(theLayer, "layer"))
347 if(auto e = t.access!Table(theLayer, "expr"))
348 return new LayExpression(pos, v.data, tableToAST(theLayer,e));
349 else
350 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
351 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
352 case "let":
353 if(auto n = t.access!StrValue(theLayer, "name"))
354 if(auto e = t.access!Table(theLayer, "init"))
355 if(auto b = t.access!Table(theLayer, "expr"))
356 {
357 string nn = n.data;
358 auto ee = tableToAST(theLayer, e);
359 auto bb = tableToAST(theLayer, b);
360 Layer lay="";
361 if(auto l = t.access!StrValue(theLayer, "layer"))
362 lay = l.data;
363 return new LetExpression(pos, nn, lay, ee, bb);
364 }
365 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
366 case "app":
367 if(auto f = t.access!Table(theLayer, "fun"))
368 if(auto a = t.access!Table(theLayer, "args"))
369 return new FuncallExpression(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
370 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`);
371 case "fun":
372 if(auto p = t.access!Table(theLayer, "params"))
373 if(auto b = t.access!Table(theLayer, "funbody"))
374 {
375 Parameter[] ps;
376 foreach(v; tableAsConsList(theLayer, p))
377 {
378 if(auto tt = cast(Table)v)
379 if(auto ss = tt.access!StrValue(theLayer, "name"))
380 if(auto ll = tt.access!Table(theLayer, "layers"))
381 {
382 Layer[] ls;
383 foreach(lll; tableAsConsList(theLayer, ll))
384 if(auto l = cast(StrValue)lll)
385 ls ~= l.data;
386 else
387 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll));
388 ps ~= new Parameter(ss.data, ls);
389 continue;
390 }
391 else
392 {
393 Layer[] emp;
394 ps ~= new Parameter(ss.data, emp);
395 continue;
396 }
397 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v));
398 }
399 auto bb = tableToAST(theLayer, b);
400 return new FunLiteral(pos,ps,bb);
401 }
402 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
403 default:
404 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
405 }
406 }