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 }