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