1 module orelang.Value;
2 import orelang.expression.ImmediateValue,
3        orelang.operator.DynamicOperator,
4        orelang.expression.IExpression,
5        orelang.expression.SymbolValue,
6        orelang.expression.ClassType,
7        orelang.operator.IOperator,
8        orelang.Closure;
9 import std.algorithm,
10        std.exception,
11        std.array,
12        std.traits,
13        std.regex,
14        std.conv;
15 
16 enum ValueType {
17   ImmediateValue,
18   SymbolValue,
19   IExpression,
20   ClassType,
21   IOperator,
22   Closure,
23   HashMap,
24   Numeric,
25   String,
26   Ubyte,
27   Bool,
28   Null,
29   Array
30 }
31 
32 class Value {
33   ValueType type;
34 
35   private {
36     double  numeric_value;
37     string  string_value;
38     bool    bool_value;
39     ubyte   ubyte_value;
40     Value[] array_value;
41     ImmediateValue imv_value;
42     SymbolValue    sym_value;
43     IExpression    ie_value;
44     ClassType      class_value;
45     IOperator      io_value;
46     Closure        closure_value;
47     Value[string]  hashmap_value;
48   }
49 
50   this()               { this.type = ValueType.Null; }
51   this(ValueType type) { this.type = type; }
52   this(T)(T value) if (isNumeric!T) { this.opAssign(value); }
53   this(string value)  { this.opAssign(value); }
54   this(bool value)    { this.opAssign(value); }
55   this(ubyte value)   {
56     this.init;
57     this.ubyte_value = value;
58     this.type        = ValueType.Ubyte;
59   }
60   this(Value[] value) { this.opAssign(value); }
61   this(ImmediateValue value) {
62     this.init;
63     this.imv_value = value;
64     this.type      = ValueType.ImmediateValue; }
65   this(SymbolValue value) {
66     this.init;
67     this.sym_value = value;
68     this.type      = ValueType.SymbolValue; }
69   this(IExpression value)    { this.opAssign(value); }
70   this(ClassType value)      { this.opAssign(value); }
71   this(IOperator value)      { this.opAssign(value); }
72   this(Closure value)        { this.opAssign(value); }
73   this(Value[string] value)  { this.opAssign(value); }
74 
75   double  getNumeric() { enforce(this.type == ValueType.Numeric);
76                          return this.numeric_value; }
77   string  getString()  { enforce(this.type == ValueType.String || this.type == ValueType.SymbolValue);
78                          return this.type == ValueType.String ? this.string_value : this.sym_value.value; }
79   bool    getBool()    { enforce(this.type == ValueType.Bool);
80                          return this.bool_value; }
81   ubyte   getUbyte()   { enforce(this.type == ValueType.Ubyte);
82                          return this.ubyte_value; }
83   auto    getNull()    { throw new Error("Can't get from NULL value"); }
84   Value[] getArray()   { enforce(this.type == ValueType.Array);
85                          return this.array_value; }
86   ImmediateValue getImmediateValue() { enforce(this.type == ValueType.ImmediateValue);
87                                        return this.imv_value; }
88   SymbolValue    getSymbolValue()    { enforce(this.type == ValueType.SymbolValue);
89                                        return this.sym_value; }
90   IExpression    getIExpression()    { enforce(this.type == ValueType.IExpression);
91                                        return this.ie_value; }
92   ClassType      getClassType()      { enforce(this.type == ValueType.ClassType);
93                                        return this.class_value; }
94   IOperator      getIOperator()      { enforce(this.type == ValueType.IOperator);
95                                        return this.io_value; }
96   Closure        getClosure()        { enforce(this.type == ValueType.Closure);
97                                        return this.closure_value; }
98   Value[string]  getHashMap()        { enforce(this.type == ValueType.HashMap);
99                                        return this.hashmap_value; }
100 
101   void opAssign(T)(T value) if (isNumeric!T) {
102     this.init;
103     this.numeric_value = value;
104     this.type = ValueType.Numeric;
105   }
106 
107   void opAssign(T)(T value) if (is(T == string)) {
108     this.init;
109     this.string_value = value;
110     this.type         = ValueType.String;
111   }
112 
113   void opAssign(bool value) {
114     this.init;
115     this.bool_value = value;
116     this.type       = ValueType.Bool;
117   }
118 
119   void opAssign(T)(T[] value) if (is(T == Value)) {
120     this.init;
121     this.array_value = value;
122     this.type        = ValueType.Array;
123   }
124 
125   void opAssign(T)(T[] value) if (!is(T == Value) && !is(T == immutable(char))) {
126     this.init;
127     this.array_value = [];
128 
129     foreach (e; value) this.array_value ~= new Value(e);
130 
131     this.type        = ValueType.Array;
132   }
133 
134   void opAssign(IExpression value) {
135     this.init;
136     this.ie_value = value;
137     this.type     = ValueType.IExpression;
138   }
139 
140   void opAssign(ClassType value) {
141     this.init;
142     this.class_value = value;
143     this.type        = ValueType.ClassType;
144   }
145 
146   void opAssign(IOperator value) {
147     this.init;
148     this.io_value = value;
149     this.type     = ValueType.IOperator;
150   }
151 
152   void opAssign(Closure value) {
153     this.init;
154     this.closure_value = value;
155     this.type          = ValueType.Closure;
156   }
157 
158   void opAssign(Value[string] value) {
159     this.init;
160     this.hashmap_value = value;
161     this.type          = ValueType.HashMap; 
162   }
163 
164   override string toString() {
165     final switch(this.type) with (ValueType) {
166       case Numeric: return this.numeric_value.to!string;
167       case String:  return this.string_value;
168       case Bool:    return this.bool_value.to!string;
169       case Ubyte:   return this.ubyte_value.to!string;
170       case Null:    return "null";
171       case Array:   return "[" ~ this.array_value.map!(value => value.toString).array.join(", ") ~ "]";
172       case HashMap: return this.hashmap_value.to!string;
173       case ImmediateValue: return this.imv_value.toString;
174       case SymbolValue:    return this.sym_value.value;
175       case IExpression:    return this.ie_value.stringof;
176       case ClassType:      return this.class_value.stringof;
177       case IOperator:      return this.io_value.stringof;
178       case Closure:        return this.closure_value.stringof;
179     }
180   }
181 
182   void addTo(Value value) {
183     enforce(this.type == value.type && value.type == ValueType.Numeric);
184     this.numeric_value += value.getNumeric;
185   }
186 
187   void subTo(Value value) {
188     enforce(this.type == value.type && value.type == ValueType.Numeric);
189     this.numeric_value -= value.getNumeric;
190   }
191 
192   void mulTo(Value value) {
193     enforce(this.type == value.type && value.type == ValueType.Numeric);
194     this.numeric_value *= value.getNumeric;
195   }
196 
197   void divTo(Value value) {
198     enforce(this.type == value.type && value.type == ValueType.Numeric);
199     this.numeric_value /= value.getNumeric;
200   }
201 
202   void modTo(Value value) {
203     enforce(this.type == value.type && value.type == ValueType.Numeric);
204     this.numeric_value %= value.getNumeric;
205   }
206 
207   Value opBinary(string op)(Value value) if (op == "+") {
208     enforce(value.type == ValueType.Numeric);
209     return new Value(this.numeric_value + value.getNumeric);
210   }
211 
212   Value opBinary(string op)(Value value) if (op == "-") {
213     enforce(value.type == ValueType.Numeric);
214 
215     return new Value(this.numeric_value - value.getNumeric);
216   }
217 
218   Value opBinary(string op)(Value value) if (op == "*") {
219     enforce(value.type == ValueType.Numeric);
220     return new Value(this.numeric_value * value.getNumeric);
221   }
222 
223   Value opBinary(string op)(Value value) if (op == "/") {
224     enforce(value.type == ValueType.Numeric);
225     return new Value(this.numeric_value / value.getNumeric);
226   }
227 
228   Value opBinary(string op)(Value value) if (op == "%") {
229     enforce(value.type == ValueType.Numeric);
230     return new Value(this.numeric_value % value.getNumeric);
231   }
232 
233   void init() {
234     if (this.type != ValueType.Null) {
235       if (this.type == ValueType.Numeric) { this.numeric_value = 0;  }
236       if (this.type == ValueType.String)  { this.string_value  = ""; }
237       if (this.type == ValueType.Array)   { this.array_value   = []; }
238       if (this.type == ValueType.Bool)    { this.bool_value    = false; }
239       if (this.type == ValueType.Ubyte)   { this.ubyte_value   = 0;  }
240       if (this.type == ValueType.ImmediateValue) { this.imv_value = null; }
241       if (this.type == ValueType.SymbolValue)    { this.sym_value = null; }
242       if (this.type == ValueType.IExpression)    { this.ie_value  = null; }
243       if (this.type == ValueType.ClassType)      { this.class_value   = null; }
244       if (this.type == ValueType.IOperator)      { this.io_value      = null; }
245       if (this.type == ValueType.Closure)        { this.closure_value = null; }
246       if (this.type == ValueType.HashMap)        { this.hashmap_value = null; }
247 
248       this.type = ValueType.Null;
249     }
250   }
251 
252   Value opIndex() {
253     enforce(this.type == ValueType.Array);
254 
255     return new Value;
256   }
257 
258   Value opIndex(size_t idx) {
259     enforce(this.type == ValueType.Array);
260 
261     if (!(idx < this.array_value.length)) {
262       throw new Error("Out of index of the Array, orded - " ~ idx.to!string ~ " but length of the array is " ~ this.array_value.length.to!string);
263     }
264 
265     return this.array_value[idx];
266   }
267 
268   Value opIndex(Value value) {
269     enforce(this.type == ValueType.HashMap);
270 
271     if (value.getString !in this.hashmap_value) {
272       throw new Error("No such a key in the hash, key - " ~ value.toString ~ ", hash - " ~ this.hashmap_value.stringof);
273     }
274 
275     return this.hashmap_value[value.getString];
276   }
277 
278   override bool opEquals(Object _value) {
279     if ((cast(Value)_value) is null) {
280       throw new Error("Can not compare between incompatibility");
281     }
282 
283     Value value = cast(Value)_value;
284 
285     if (this.type != value.type) {
286       throw new Error("Can not compare between incompatibility type " ~ this.type.to!string ~ " and " ~ value.type.to!string);
287     }
288 
289     final switch(this.type) with (ValueType) {
290       case ImmediateValue:
291         throw new Error("Can't compare with ImmediateValue");
292       case SymbolValue:
293         return this.sym_value.value == value.getSymbolValue.value;
294       case IExpression:
295         throw new Error("Can't compare with IExpression");
296       case ClassType:
297         throw new Error("Can't compare with ClassType");
298       case IOperator:
299         throw new Error("Can't compare with IOperator");
300       case Closure:
301         throw new Error("Can't compare with Closure");
302       case HashMap:
303         throw new Error("Can't compare with HashMap");
304       case Numeric:
305         return this.numeric_value == value.numeric_value;
306       case String:
307         return this.string_value  == value.string_value;
308       case Bool:
309         return this.bool_value    == value.bool_value;
310       case Ubyte:
311         return this.ubyte_value   == value.ubyte_value;
312       case Null:
313         throw new Error("Can't compare with Null");
314       case Array:
315         Value[] a = this.getArray,
316                 b = value.getArray;
317 
318         if (a.length != b.length) {
319           return false;
320         }
321 
322         foreach (idx; 0..(a.length)) {
323           if (a[idx].opCmp(b[idx]) != 0) { return false; }
324         }
325 
326         return true;
327     }
328   }
329 
330   override int opCmp(Object _value) {
331     if ((cast(Value)_value) is null) {
332       throw new Error("Can not compare between incompatibility");
333     }
334 
335     Value value = cast(Value)_value;
336 
337     if (this.type != value.type) {
338       throw new Error("Can not compare between incompatibility type " ~ this.type.to!string ~ " and " ~ value.type.to!string);
339     }
340 
341     final switch(this.type) with (ValueType) {
342       case ImmediateValue:
343         throw new Error("Can't compare with ImmediateValue");
344       case SymbolValue:
345         auto c = this.sym_value.value,
346              d = value.getSymbolValue.value;
347         if (c == d) { return 0; }
348         if (c < d)  { return -1; }
349         return 1;
350       case IExpression:
351         throw new Error("Can't compare with IExpression");
352       case ClassType:
353         throw new Error("Can't compare with ClassType");
354       case IOperator:
355         throw new Error("Can't compare with IOperator");
356       case Closure:
357         throw new Error("Can't compare with Closure");
358       case HashMap:
359         throw new Error("Can't compare with HashMap");
360       case Numeric:
361         auto c = this.numeric_value,
362              d = value.numeric_value;
363 
364         if (c == d) { return 0;  }
365         if (c < d)  { return -1; }
366         return 1;
367       case String:
368         auto c = this.string_value,
369              d = value.string_value;
370 
371         if (c == d) { return 0;  }
372         if (c < d)  { return -1; }
373         return 1;
374       case Ubyte:
375         auto c = this.ubyte_value,
376              d = value.ubyte_value;
377 
378         if (c == d) { return 0;  }
379         if (c < d)  { return -1; }
380         return 1;
381       case Bool:
382         throw new Error("Can't compare with Bool");
383       case Null:
384         throw new Error("Can't compare with Null");
385       case Array:
386         Value[] a = this.getArray,
387                 b = value.getArray;
388 
389         if (a.length != b.length) {
390           throw new Error("Can't compare between different size array");
391         }
392 
393         foreach (idx; 0..(a.length)) {
394           if (a[idx].opCmp(b[idx]) != 0) { return 1; }
395         }
396 
397         return 0;
398     }
399   }
400 
401   Value dup() {
402     final switch (this.type) with (ValueType) {
403       case ImmediateValue:
404         return new Value(this.imv_value);
405       case SymbolValue:
406         return new Value(this.sym_value);
407       case IExpression:
408         return new Value(this.ie_value);
409       case ClassType:
410         return new Value(this.class_value);
411       case IOperator:
412         return new Value(this.io_value);
413       case Closure:
414         return new Value(this.closure_value);
415       case HashMap:
416         return new Value(this.hashmap_value);
417       case Numeric:
418         return new Value(this.numeric_value);
419       case String:
420         return new Value(this.string_value);
421       case Bool:
422         return new Value(this.bool_value);
423       case Ubyte:
424         return new Value(this.ubyte_value);
425       case Null:
426         return new Value;
427       case Array:
428         return new Value(this.array_value.dup);
429     }
430   }
431 }