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 UndefinedValue : Value
56 {
57 mixin SimpleConstructor;
58 override string toString() const { return "<undefined>"; }
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 void set(string i, Layer lay, Value v)
87 {
88 if( setIfExist(i, lay, v) )
89 return;
90 data[i][lay] = v;
91 }
92
93 bool has(string i, Layer lay) const
94 {
95 if( i in data )
96 return !!(lay in data[i]);
97 if( prototype is null )
98 return false;
99 return prototype.has(i, lay);
100 }
101
102 Value get(string i, Layer lay, LexPosition pos=null)
103 {
104 if( i in data ) {
105 // [TODO] consider forwarding to proto also in this case
106 if( lay !in data[i] )
107 throw genex!RuntimeException(pos, sprintf!"'%s' is not set in %s layer"(i,lay));
108 return data[i][lay];
109 }
110 if( prototype is null )
111 throw genex!RuntimeException(pos, sprintf!"'%s' not found in %s layer"(i,lay));
112 return prototype.get(i, lay, pos);
113 }
114
115 T access(T,S...)( Layer lay, string path, S rest )
116 {
117 static if( rest.length == 0 )
118 {
119 if( this.has(path, lay) )
120 return cast(T) this.get(path, lay);
121 }
122 else
123 {
124 if(auto next = this.access!Table(lay,path))
125 return next.access!T(lay,rest);
126 }
127 return null;
128 }
129
130 string toStringWithoutParen() const
131 {
132 string result;
133 bool first = true;
134 foreach(k, l2d; data)
135 foreach(l,d; l2d)
136 {
137 if(first) first=false; else result~=", ";
138 result ~= k;
139 if( l.empty )
140 result ~= "(emptylayer)";
141 else if( l != ValueLayer )
142 result ~= l;
143 result ~= ":";
144 result ~= text(cast(Value)d);
145 }
146 if( prototype !is null )
147 {
148 result ~= " / ";
149 result ~= prototype.toStringWithoutParen();
150 }
151 return result;
152 }
153
154 string toString()
155 {
156 if( isList() )
157 return text(toList());
158 return "{" ~ toStringWithoutParen() ~ "}";
159 }
160
161 public:
162 /// Is this an empty table?
163 bool empty()
164 {
165 return data.length==0 && (prototype is null || prototype.empty);
166 }
167
168 /// Can be seen as a cons-list?
169 bool isList()
170 {
171 Table t = this;
172 while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
173 if(auto tt = cast(Table)t.get("cdr", ValueLayer))
174 t = tt;
175 else
176 return false;
177 return t.empty;
178 }
179
180 /// Regard table as a cons-list and convert to an array
181 Value[] toList()
182 {
183 Value[] result;
184 Table t = this;
185 while(t.has("car", ValueLayer) && t.has("cdr", ValueLayer))
186 {
187 result ~= t.get("car", ValueLayer);
188 if(auto tt = cast(Table)t.get("cdr", ValueLayer))
189 t = tt;
190 else
191 throw genex!RuntimeException("this table is not a cons-list");
192 }
193 if( t.empty )
194 return result;
195 throw genex!RuntimeException("this table is not a cons-list");
196 }
197
198 private:
199 Table prototype;
200 Kind kind;
201 Value[Layer][string] data;
202
203 bool setIfExist(string i, Layer lay, Value v)
204 {
205 if( i in data )
206 {
207 data[i][lay] = v;
208 return true;
209 }
210 if( kind==Kind.PropagateSet && prototype !is null )
211 return prototype.setIfExist(i, lay, v);
212 return false;
213 }
214 }
215
216 unittest
217 {
218 Table c0 = new Table;
219 Table c01 = new Table(c0, Table.Kind.NotPropagateSet);
220 Table c012 = new Table(c01, Table.Kind.PropagateSet);
221 Table c013 = new Table(c01, Table.Kind.PropagateSet);
222
223 assert_nothrow( c012.set("x", ValueLayer, new IntValue(12)) );
224 assert_throw!RuntimeException( c013.get("x", ValueLayer) );
225 assert_nothrow( c013.set("x", ValueLayer, new IntValue(13)) );
226 assert_eq( c013.get("x", ValueLayer), new IntValue(13) );
227 assert_eq( c012.get("x", ValueLayer), new IntValue(12) );
228 assert_throw!RuntimeException( c01.get("x", ValueLayer) );
229
230 assert_nothrow( c01.set("y", ValueLayer, new IntValue(1)) );
231 assert_eq( c013.get("y", ValueLayer), new IntValue(1) );
232 assert_eq( c012.get("y", ValueLayer), new IntValue(1) );
233 assert_eq( c01.get("y", ValueLayer), new IntValue(1) );
234
235 assert_nothrow( c0.set("z", ValueLayer, new IntValue(0)) );
236 assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
237 assert_eq( c012.get("z", ValueLayer), new IntValue(0) );
238 assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
239 assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
240
241 assert_nothrow( c012.set("y", ValueLayer, new IntValue(444)) );
242 assert_eq( c013.get("y", ValueLayer), new IntValue(444) );
243 assert_eq( c012.get("y", ValueLayer), new IntValue(444) );
244 assert_eq( c01.get("y", ValueLayer), new IntValue(444) );
245
246 assert_nothrow( c012.set("z", ValueLayer, new IntValue(555)) );
247 assert_eq( c013.get("z", ValueLayer), new IntValue(0) );
248 assert_eq( c012.get("z", ValueLayer), new IntValue(555) );
249 assert_eq( c01.get("z", ValueLayer), new IntValue(0) );
250 assert_eq( c0.get("z", ValueLayer), new IntValue(0) );
251
252 // [TODO] define the semantics and test @layers
253 }