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