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