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 override bool opEquals(Object rhs) { return 0==opCmp(rhs); }
18 }
19
20 ///
21 class IntValue : Value
22 {
23 BigInt data;
24
25 this(bool n) { this.data = n?1:0; }
26 this(int n) { this.data = n; }
27 this(long n) { this.data = n; }
28 this(BigInt n) { this.data = n; }
29 this(string n) { this.data = BigInt(n); }
30 override string toString() const { return toDecimalString(cast(BigInt)data); }
31 override int opCmp(Object rhs) {
32 if(auto r = cast(IntValue)rhs) return data.opCmp(r.data);
33 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
34 throw genex!RuntimeException("comparison with value and somithing other");
35 }
36 mixin SimpleToHash;
37 }
38
39 ///
40 class StrValue : Value
41 {
42 string data;
43
44 mixin SimpleConstructor;
45 override string toString() const { return data; }
46 override int opCmp(Object rhs) {
47 if(auto r = cast(StrValue)rhs) return typeid(string).compare(&data, &r.data);
48 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
49 throw genex!RuntimeException("comparison with value and somithing other");
50 }
51 mixin SimpleToHash;
52 }
53
54 ///
55 class BottomValue : Value
56 {
57 mixin SimpleConstructor;
58 override string toString() const { return "_|_"; }
59 override int opCmp(Object rhs) {
60 if(auto r = cast(StrValue)rhs) return 0;
61 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
62 throw genex!RuntimeException("comparison with value and somithing other");
63 }
64 mixin SimpleToHash;
65 }
66
67 ///
68 abstract class FunValue : Value
69 {
70 const(Parameter[]) params();
71 Table definitionContext();
72 Value invoke(Layer lay, Table ctx, LexPosition pos);
73 }
74
75 /// Context (variable environment)
76 /// Simlar to prototype chain of ECMAScript etc.
77 /// But extended with the notion of "Layer"
78
79 class Table : Value
80 {
81 enum Kind {PropagateSet, NotPropagateSet};
82
83 this( Table proto=null, Kind k = Kind.PropagateSet )
84 { this.prototype = proto; this.kind = k; }
85
86 /// Set the value v to the index i of layer lay
87 void set(string i, Layer lay, Value v)
88 {
89 if( setIfExist(i, lay, v) )
90 return;
91 data[i][lay] = v;
92 }
93
94 /// True if index i has value in layer lay
95 bool has(string i, Layer lay) const
96 {
97 if( i in data )
98 return !!(lay in data[i]);
99 if( prototype is null )
100 return false;
101 return prototype.has(i, lay);
102 }
103
104 /// Return the value of index i at layer lay. Throws if it is not set
105 Value get(string i, Layer lay, LexPosition pos=null)
106 {
107 if( i in data ) {
108 if( lay !in data[i] )
109 throw genex!RuntimeException(pos, sprintf!"'%s' is not set in %s layer"(i,lay));
110 return data[i][lay];
111 }
112 if( prototype is null )
113 throw genex!RuntimeException(pos, sprintf!"'%s' not found in %s layer"(i,lay));
114 return prototype.get(i, lay, pos);
115 }
116
117 /// t.access!T(lay,a,b,...) returns t.get(a,lay).get(b,lay).... if exists
118 /// and has type T. Returns null otherwise
119 T access(T,S...)( Layer lay, string path, S rest )
120 {
121 static if( rest.length == 0 )
122 {
123 if( this.has(path, lay) )
124 return cast(T) this.get(path, lay);
125 }
126 else
127 {
128 if(auto next = this.access!Table(lay,path))
129 return next.access!T(lay,rest);
130 }
131 return null;
132 }
133
134 /// Is this an empty table?
135 bool empty()
136 {
137 return data.length==0 && (prototype is null || prototype.empty);
138 }
139
140 /// Can be seen as a cons-list?
141 bool isList()
142 {
143 Table t = this;
144 while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
145 if(auto tt = cast(Table)t.get("cdr", ValueLayer))
146 t = tt;
147 else
148 return false;
149 return t.empty;
150 }
151
152 /// Regard table as a cons-list and convert to an array
153 Value[] toList()
154 {
155 Value[] result;
156 Table t = this;
157 while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
158 {
159 result ~= t.get("car", ValueLayer);
160 if(auto tt = cast(Table)t.get("cdr", ValueLayer))
161 t = tt;
162 else
163 throw genex!RuntimeException("this table is not a cons-list");
164 }
165 if( t.empty )
166 return result;
167 throw genex!RuntimeException("this table is not a cons-list");
168 }
169
170 /// Get the list of direct entries ignoring prototypes in sorted order
171 Tuple!(string,Layer,Value)[] direct_entries()
172 {
173 Tuple!(string,Layer,Value)[] arr;
174 foreach(k, l2d; data)
175 foreach(l,d; l2d)
176 arr ~= tuple(k,l,d);
177 arr.sort();
178 return arr;
179 }
180
181 /// Get the whole list of observable entries in unspecified order
182 Tuple!(string,Layer,Value)[] entries()
183 {
184 bool[string] hidden;
185 Tuple!(string,Layer,Value)[] arr;
186 enumerateEntries(hidden, arr);
187 return arr;
188 }
189
190 private void enumerateEntries( ref bool[string] hidden, ref Tuple!(string,Layer,Value)[] arr )
191 {
192 foreach(k, l2d; data)
193 if( k !in hidden )
194 {
195 foreach(l,d; l2d)
196 arr ~= tuple(k,l,d);
197 hidden[k] = true;
198 }
199 if(prototype !is null)
200 prototype.enumerateEntries(hidden, arr);
201 }
202
203 override string toString()
204 {
205 if( isList() )
206 return text(toList());
207 return "{" ~ toStringWithoutParen() ~ "}";
208 }
209
210 override int opCmp(Object rhs)
211 {
212 if(auto r = cast(Table)rhs) {
213 Tuple!(string,Layer,Value)[] ls = this.entries();
214 Tuple!(string,Layer,Value)[] rs = r.entries();
215 if( ls.length != rs.length )
216 return (ls.length < rs.length ? -1 : +1);
217 ls.sort();
218 rs.sort();
219 foreach(i,_; ls)
220 if(auto c = ls[i].opCmp(rs[i]))
221 return c;
222 return 0;
223 }
224 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r));
225 throw genex!RuntimeException("comparison with value and somithing other");
226 }
227
228 override hash_t toHash()
229 {
230 Tuple!(string,Layer,Value)[] ls = this.entries();
231 ls.sort();
232 hash_t h;
233 foreach(e; ls)
234 h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]);
235 return h;
236 }
237
238 private:
239 Table prototype;
240 Kind kind;
241 Value[Layer][string] data;
242
243 string toStringWithoutParen() const
244 {
245 string result;
246 bool first = true;
247 foreach(k, l2d; data)
248 foreach(l,d; l2d)
249 {
250 if(first) first=false; else result~=", ";
251 result ~= k;
252 if( l.empty )
253 result ~= "(emptylayer)";
254 else if( l != ValueLayer )
255 result ~= l;
256 result ~= ":";
257 result ~= text(cast(Value)d);
258 }
259 if( prototype !is null )
260 {
261 result ~= " / ";
262 result ~= prototype.toStringWithoutParen();
263 }
264 return result;
265 }
266
267 bool setIfExist(string i, Layer lay, Value v)
268 {
269 if( i in data )
270 {
271 data[i][lay] = v;
272 return true;
273 }
274 if( kind==Kind.PropagateSet && prototype !is null )
275 return prototype.setIfExist(i, lay, v);
276 return false;
277 }
278 }
279
280 unittest
281 {
282 Table c0 = new Table;
283 Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
284 Table c012 = new Table(c01, Table.Kind.PropagateSet);
285 Table c013 = new Table(c01, Table.Kind.PropagateSet);
286
287 assert_nothrow( c012.set("x", ValueLayer, new IntValue(12)) );
288 assert_throw!RuntimeException( c013.get("x", ValueLayer) );
289 assert_nothrow( c013.set("x", ValueLayer, new IntValue(13)) );
290 assert_eq( c013.get("x", ValueLayer), new IntValue(13) );
291 assert_eq( c012.get("x", ValueLayer), new IntValue(12) );
292 assert_throw!RuntimeException( c01.get("x", ValueLayer) );
293
294 assert_nothrow( c01.set("y", ValueLayer, new IntValue(1)) );
295 assert_eq( c013.get("y", ValueLayer), new IntValue(1) );
296 assert_eq( c012.get("y", ValueLayer), new IntValue(1) );
297 assert_eq( c01.get("y", ValueLayer), new IntValue(1) );
298
299 assert_nothrow( c0.set("z", ValueLayer, new IntValue(0)) );
300 assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
301 assert_eq( c012.get("z", ValueLayer), new IntValue(0) );
302 assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
303 assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
304
305 assert_nothrow( c012.set("y", ValueLayer, new IntValue(444)) );
306 assert_eq( c013.get("y", ValueLayer), new IntValue(444) );
307 assert_eq( c012.get("y", ValueLayer), new IntValue(444) );
308 assert_eq( c01.get("y", ValueLayer), new IntValue(444) );
309
310 assert_nothrow( c012.set("z", ValueLayer, new IntValue(555)) );
311 assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
312 assert_eq( c012.get("z", ValueLayer), new IntValue(555) );
313 assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
314 assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
315
316 // [TODO] define the semantics and test @layers
317 }