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(LexPosition pos, Layer lay, Table ctx);
51 }
52
53 /// Context (variable environment)
54 /// Simlar to prototype chain of ECMAScript etc.
55 /// But extended with the notion of "Layer"
56
57 class Table : Value
58 {
59 enum Kind {PropagateSet, NotPropagateSet};
60 bool kill = false; // to refactor
61
62 this( Table proto=null, Kind k = Kind.PropagateSet )
63 { this.prototype = proto; this.kind = k; }
64
65 void set(string i, Layer lay, Value v, LexPosition pos=null)
66 {
67 if( setIfExist(i, lay, v) )
68 return;
69 data[i][lay] = v;
70 }
71
72 bool has(string i, Layer lay) const
73 {
74 if( i in data ) {
75 if( lay !in data[i] )
76 return false;
77 if(kill)
78 return false;
79 return true;
80 }
81 if( prototype is null )
82 return false;
83 return prototype.has(i, lay);
84 }
85
86 Value get(string i, Layer lay, LexPosition pos=null)
87 {
88 if( i in data ) {
89 // [TODO] consider forwarding to proto also in this case
90 if( lay !in data[i] )
91 throw genex!RuntimeException(pos, sprintf!"'%s' is not set in layer %s"(i,lay));
92 if(kill)
93 throw genex!RuntimeException(pos, sprintf!"'%s' is killed in macro"(i));
94 return data[i][lay];
95 }
96 if( prototype is null )
97 throw new RuntimeException(pos, sprintf!"'%s' not found"(i));
98 return prototype.get(i, lay, pos);
99 }
100
101 T access(T,S...)( Layer lay, string path, S rest )
102 {
103 static if( rest.length == 0 )
104 {
105 if( this.has(path, lay) )
106 return cast(T) this.get(path, lay);
107 }
108 else
109 {
110 if(auto next = this.access!Table(lay,path))
111 return next.access!T(lay,rest);
112 }
113 return null;
114 }
115
116 string toStringWithoutParen() const
117 {
118 string result;
119 bool first = true;
120 foreach(k, l2d; data)
121 foreach(l,d; l2d)
122 {
123 if(first) first=false; else result~=", ";
124 result ~= k;
125 if( l.empty )
126 result ~= "(emptylayer)";
127 else if( l != ValueLayer )
128 result ~= l;
129 result ~= ":";
130 result ~= text(cast(Value)d);
131 }
132 if( prototype !is null )
133 {
134 result ~= " / ";
135 result ~= prototype.toStringWithoutParen();
136 }
137 return result;
138 }
139
140 string toString() const
141 {
142 return "{" ~ toStringWithoutParen() ~ "}";
143 }
144
145 private:
146 Table prototype;
147 Kind kind;
148 Value[Layer][string] data;
149
150 bool setIfExist(string i, Layer lay, Value v)
151 {
152 if( i in data )
153 {
154 data[i][lay] = v;
155 return true;
156 }
157 if( kind==Kind.PropagateSet && prototype !is null )
158 return prototype.setIfExist(i, lay, v);
159 return false;
160 }
161 }
162
163 unittest
164 {
165 Table c0 = new Table;
166 Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
167 Table c012 = new Table(c01, Table.Kind.PropagateSet);
168 Table c013 = new Table(c01, Table.Kind.PropagateSet);
169
170 assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) );
171 assert_throw!RuntimeException( c013.get("x", ValueLayer) );
172 assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) );
173 assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) );
174 assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) );
175 assert_throw!RuntimeException( c01.get("x", ValueLayer) );
176
177 assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) );
178 assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) );
179 assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) );
180 assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) );
181
182 assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) );
183 assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
184 assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) );
185 assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
186 assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
187
188 assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) );
189 assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) );
190 assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) );
191 assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) );
192
193 assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) );
194 assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) );
195 assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) );
196 assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) );
197 assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) );
198
199 // [TODO] define the semantics and test @layers
200 }
201
202 immutable(LexPosition) extractPos( Table t )
203 {
204 Layer theLayer = ValueLayer;
205 if(auto tt = t.access!Table(theLayer, "pos"))
206 {
207 auto fn = tt.access!StrValue(theLayer, "filename");
208 auto ln = tt.access!IntValue(theLayer, "lineno");
209 auto cl = tt.access!IntValue(theLayer, "column");
210 if(fn !is null && ln !is null && cl !is null)
211 return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
212 }
213 return null;
214 }
215
216 Value[] tableAsConsList( Layer theLayer, Table t )
217 {
218 Value[] result;
219 while(t)
220 if(auto v = t.access!Value(theLayer, "car"))
221 {
222 result ~= v;
223 t = t.access!Table(theLayer, "cdr");
224 }
225 else
226 break;
227 return result;
228 }
229
230 AST[] tableToASTList( Layer theLayer, Table t )
231 {
232 AST[] result;
233 foreach(v; tableAsConsList(theLayer, t))
234 if(auto t = cast(Table)v)
235 result ~= tableToAST(theLayer,t);
236 else
237 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
238 return result;
239 }
240
241 AST tableToAST( Layer theLayer, Value vvvv )
242 {
243 Table t = cast(Table)vvvv;
244 if( t is null )
245 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)");
246
247 auto nodeType = t.access!StrValue(theLayer, "is");
248 if( nodeType is null )
249 throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
250 auto pos = extractPos(t);
251 switch(nodeType.data)
252 {
253 case "int":
254 if(auto v = t.access!IntValue(theLayer, "data"))
255 return new Int(pos, v.data);
256 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
257 case "str":
258 if(auto v = t.access!StrValue(theLayer, "data"))
259 return new Str(pos, v.data);
260 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
261 case "var":
262 if(auto v = t.access!StrValue(theLayer, "name"))
263 return new Var(pos, v.data);
264 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
265 case "lay":
266 if(auto v = t.access!StrValue(theLayer, "layer"))
267 if(auto e = t.access!Table(theLayer, "expr"))
268 return new Lay(pos, v.data, tableToAST(theLayer,e));
269 else
270 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
271 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
272 case "let":
273 if(auto n = t.access!StrValue(theLayer, "name"))
274 if(auto e = t.access!Table(theLayer, "init"))
275 if(auto b = t.access!Table(theLayer, "expr"))
276 {
277 string nn = n.data;
278 auto ee = tableToAST(theLayer, e);
279 auto bb = tableToAST(theLayer, b);
280 Layer lay="";
281 if(auto l = t.access!StrValue(theLayer, "layer"))
282 lay = l.data;
283 return new Let(pos, nn, lay, ee, bb);
284 }
285 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
286 case "app":
287 if(auto f = t.access!Table(theLayer, "fun"))
288 if(auto a = t.access!Table(theLayer, "args"))
289 return new App(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
290 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`);
291 case "fun":
292 if(auto p = t.access!Table(theLayer, "params"))
293 if(auto b = t.access!Table(theLayer, "funbody"))
294 {
295 Parameter[] ps;
296 foreach(v; tableAsConsList(theLayer, p))
297 {
298 if(auto tt = cast(Table)v)
299 if(auto ss = tt.access!StrValue(theLayer, "name"))
300 if(auto ll = tt.access!Table(theLayer, "layers"))
301 {
302 Layer[] ls;
303 foreach(lll; tableAsConsList(theLayer, ll))
304 if(auto l = cast(StrValue)lll)
305 ls ~= l.data;
306 else
307 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll));
308 ps ~= new Parameter(ss.data, ls);
309 continue;
310 }
311 else
312 {
313 Layer[] emp;
314 ps ~= new Parameter(ss.data, emp);
315 continue;
316 }
317 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v));
318 }
319 auto bb = tableToAST(theLayer, b);
320 return new Fun(pos,ps,bb);
321 }
322 throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
323 default:
324 throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
325 }
326 }
327
328 Table makeCons(Value a, Value d)
329 {
330 Table t = new Table;
331 t.set("car", ValueLayer, a);
332 t.set("cdr", ValueLayer, d);
333 return t;
334 }
335
336 Table fromPos(LexPosition pos)
337 {
338 Table t = new Table;
339 if( pos !is null ) {
340 t.set("filename", ValueLayer, new StrValue(pos.filename));
341 t.set("lineno", ValueLayer, new IntValue(BigInt(pos.lineno)));
342 t.set("column", ValueLayer, new IntValue(BigInt(pos.column)));
343 } else {
344 t.set("filename", ValueLayer, new StrValue("nullpos"));
345 t.set("lineno", ValueLayer, new IntValue(BigInt(0)));
346 t.set("column", ValueLayer, new IntValue(BigInt(0)));
347 }
348 return t;
349 }