1 module orelang.Engine; 2 3 /** 4 * Premitive Interfaces and Value Classes 5 */ 6 import orelang.expression.ImmediateValue, 7 orelang.expression.CallOperator, 8 orelang.expression.IExpression, 9 orelang.expression.ClassType, 10 orelang.operator.IOperator, 11 orelang.Closure, 12 orelang.Value; 13 14 /** 15 * variables 16 */ 17 import orelang.operator.DatetimeOperators, 18 orelang.operator.IsHashMapOperator, 19 orelang.operator.TranspileOperator, 20 orelang.operator.HashMapOperators, 21 orelang.operator.DigestOperators, 22 orelang.operator.DynamicOperator, 23 orelang.operator.ForeachOperator, 24 orelang.operator.StringOperators, 25 orelang.operator.ArrayOperators, 26 orelang.operator.DebugOperators, 27 orelang.operator.ClassOperators, 28 orelang.operator.DeffunOperator, 29 orelang.operator.DefineOperator, 30 orelang.operator.DefvarOperator, 31 orelang.operator.GetfunOperator, 32 orelang.operator.IsListOperator, 33 orelang.operator.IsNullOperator, 34 orelang.operator.LambdaOperator, 35 orelang.operator.LengthOperator, 36 orelang.operator.RemoveOperator, 37 orelang.operator.SetIdxOperator, 38 orelang.operator.StdioOperators, 39 orelang.operator.AliasOperator, 40 orelang.operator.ConvOperators, 41 orelang.operator.CurlOperators, 42 orelang.operator.EqualOperator, 43 orelang.operator.FileOperators, 44 orelang.operator.LogicOperator, 45 orelang.operator.PathOperators, 46 orelang.operator.PrintOperator, 47 orelang.operator.TimesOperator, 48 orelang.operator.UntilOperator, 49 orelang.operator.UUIDOperators, 50 orelang.operator.WhileOperator, 51 orelang.operator.AsIVOperator, 52 orelang.operator.CondOperator, 53 orelang.operator.ConsOperator, 54 orelang.operator.EvalOperator, 55 orelang.operator.FoldOperator, 56 orelang.operator.LoadOperator, 57 orelang.operator.SortOperator, 58 orelang.operator.StepOperator, 59 orelang.operator.TypeOperator, 60 orelang.operator.UriOperators, 61 orelang.operator.WhenOperator, 62 orelang.operator.AddOperator, 63 orelang.operator.CarOperator, 64 orelang.operator.CdrOperator, 65 orelang.operator.DivOperator, 66 orelang.operator.GetOperator, 67 orelang.operator.LetOperator, 68 orelang.operator.MapOperator, 69 orelang.operator.MulOperator, 70 orelang.operator.ModOperator, 71 orelang.operator.SetOperator, 72 orelang.operator.SeqOperator, 73 orelang.operator.SubOperator, 74 orelang.operator.IfOperator; 75 import orelang.operator.FileClass; 76 77 import std.exception; 78 79 /** 80 * Lazed Associative Array 81 * 82 * For instance; 83 * assocArray["key"] = new ValueType 84 * The above code create a new instance of ValueType with some consts(memory amount and time). 85 * However it likely to be a bobottleneck if the value isn't needed. 86 * Then this class provides lazed associative array, this class willn't create an instance until the value become needed. 87 * In other words, this is sort of lazy evaluation for performance. 88 */ 89 class LazedAssocArray(T) { 90 /** 91 * Flags, indicates whether the instance of the key is already created 92 */ 93 bool[string] called; 94 /** 95 * This variable holds the instance as a value of hashmap. 96 */ 97 T[string] storage; 98 /** 99 * This variable holds the constructor calling delegate to make the instance which will be called when the isntance become needed. 100 */ 101 T delegate()[string] constructors; 102 bool[string] alwaysNew; 103 104 alias storage this; 105 106 /** 107 * This function works like: 108 * // laa is an instance of LazedAssocArray 109 * laa["key"] = new T; 110 * with following way: 111 * laa.insert!("key", "new T"); 112 * 113 * This function uses string-mixin for handling "new T", becase D can't allow make an alias of the expr liek `new T` 114 */ 115 void insert(string key, string value, bool always = false)() { 116 constructors[key] = mixin("delegate T () { return " ~ value ~ ";}"); 117 called[key] = false; 118 119 if (always) { 120 alwaysNew[key] = true; 121 } 122 } 123 124 void insert(string key, T delegate() value, bool always = false)() { 125 constructors[key] = value; 126 called[key] = false; 127 128 if (always) { 129 alwaysNew[key] = true; 130 } 131 } 132 133 void insert(string key, T function() value, bool always = false)() { 134 constructors[key] = () => value(); 135 called[key] = false; 136 137 if (always) { 138 alwaysNew[key] = true; 139 } 140 } 141 142 void insert(string key, T delegate() value, bool always = false) { 143 constructors[key] = value; 144 called[key] = false; 145 146 if (always) { 147 alwaysNew[key] = true; 148 } 149 } 150 151 void insert(string key, T function() value, bool always = false) { 152 constructors[key] = () => value(); 153 called[key] = false; 154 155 if (always) { 156 alwaysNew[key] = true; 157 } 158 } 159 160 /** 161 * Set the value with the key. 162 * This function works like: 163 * laa["key"] = value; 164 * with 165 * laa.set("key", value) 166 */ 167 void set(string key, T value) { 168 storage[key] = value; 169 called[key] = true; 170 } 171 172 /** 173 * Make an alias of the key 174 */ 175 void link(string alternative, string key) { 176 const flag = called[key]; 177 called[alternative] = flag; 178 179 if (flag) { 180 storage[alternative] = storage[key]; 181 } else { 182 constructors[alternative] = constructors[key]; 183 } 184 } 185 186 /** 187 * An overloaded function of opIndexAssing 188 * This function hooks: laa["key"] = value; event but this function might be no use 189 */ 190 void opIndexAssing(T value, string key) { 191 storage[key] = value; 192 called[key] = true; 193 } 194 195 /** 196 * An overloaded function of opIndex 197 * This function hooks: laa["key"] event. 198 */ 199 T opIndex(string key) { 200 if (key in called && key in alwaysNew) { 201 return constructors[key](); 202 } 203 204 if (!called[key]) { 205 T newT = constructors[key](); 206 207 storage[key] = newT; 208 called[key] = true; 209 210 return newT; 211 } 212 213 return storage[key]; 214 } 215 } 216 217 /** 218 * Script Engine of ChickenClisp 219 */ 220 class Engine { 221 enum ConstructorMode { 222 CLONE 223 } 224 225 // Debug flags for Engine 226 bool debug_get_expression = false; 227 228 /** 229 * This holds variables and operators. 230 * You can distinguish A VALUE of the child of this from whether a varibale or an operator. 231 */ 232 LazedAssocArray!Value variables; 233 234 bool sync_storage; 235 236 /** 237 * Default Constructor 238 */ 239 this() { 240 this.variables = new LazedAssocArray!Value; 241 242 // Arithmetic operations 243 this.variables.insert!("+", q{new Value(cast(IOperator)(new AddOperator))}); 244 this.variables.insert!("-", q{new Value(cast(IOperator)(new SubOperator))}); 245 this.variables.insert!("*", q{new Value(cast(IOperator)(new MulOperator))}); 246 this.variables.insert!("/", q{new Value(cast(IOperator)(new DivOperator))}); 247 this.variables.insert!("%", q{new Value(cast(IOperator)(new ModOperator))}); 248 249 // Comparison operators 250 this.variables.insert!("=", q{new Value(cast(IOperator)(new EqualOperator))}); 251 this.variables.insert!("<", q{new Value(cast(IOperator)(new LessOperator))}); 252 this.variables.insert!(">", q{new Value(cast(IOperator)(new GreatOperator))}); 253 this.variables.insert!("<=", q{new Value(cast(IOperator)(new LEqOperator))}); 254 this.variables.insert!(">=", q{new Value(cast(IOperator)(new GEqOperator))}); 255 256 // Varibale/Function operators 257 this.variables.insert!("def", q{new Value(cast(IOperator)(new DeffunOperator))}); 258 this.variables.insert!("set", q{new Value(cast(IOperator)(new SetOperator))}); 259 this.variables.insert!("set-p", q{new Value(cast(IOperator)(new SetPOperator))}); 260 this.variables.set("get", new Value(cast(IOperator)(new GetOperator))); 261 this.variables.insert!("let", q{new Value(cast(IOperator)(new LetOperator))}); 262 this.variables.insert!("as-iv", q{new Value(cast(IOperator)(new AsIVOperator))}); 263 this.variables.insert!("define", q{new Value(cast(IOperator)(new DefineOperator))}); 264 this.variables.insert!("def-var", q{new Value(cast(IOperator)(new DefvarOperator))}); 265 this.variables.insert!("get-fun", q{new Value(cast(IOperator)(new GetfunOperator))}); 266 this.variables.insert!("set-idx", q{new Value(cast(IOperator)(new SetIdxOperator))}); 267 268 // Loop operators 269 this.variables.insert!("step", q{new Value(cast(IOperator)(new StepOperator))}); 270 this.variables.insert!("times", q{new Value(cast(IOperator)(new TimesOperator))}); 271 this.variables.insert!("until", q{new Value(cast(IOperator)(new UntilOperator))}); 272 this.variables.insert!("while", q{new Value(cast(IOperator)(new WhileOperator))}); 273 274 // Logic operators 275 this.variables.insert!("!", q{new Value(cast(IOperator)(new NotOperator))}); 276 this.variables.insert!("&&", q{new Value(cast(IOperator)(new AndOperator))}); 277 this.variables.insert!("||", q{new Value(cast(IOperator)(new OrOperator))}); 278 279 // I/O operators 280 this.variables.insert!("print", q{new Value(cast(IOperator)(new PrintOperator))}); 281 this.variables.insert!("println", q{new Value(cast(IOperator)(new PrintlnOperator))}); 282 283 // Condition operators 284 this.variables.insert!("if", q{new Value(cast(IOperator)(new IfOperator))}); 285 this.variables.insert!("cond", q{new Value(cast(IOperator)(new CondOperator))}); 286 this.variables.insert!("when", q{new Value(cast(IOperator)(new WhenOperator))}); 287 288 // Functional operators 289 this.variables.insert!("lambda", q{new Value(cast(IOperator)(new LambdaOperator))}); 290 this.variables.insert!("map", q{new Value(cast(IOperator)(new MapOperator))}); 291 this.variables.insert!("for-each", q{new Value(cast(IOperator)(new ForeachOperator))}); 292 this.variables.insert!("fold", q{new Value(cast(IOperator)(new FoldOperator))}); 293 294 // List operators 295 this.variables.insert!("car", q{new Value(cast(IOperator)(new CarOperator))}); 296 this.variables.insert!("cdr", q{new Value(cast(IOperator)(new CdrOperator))}); 297 this.variables.insert!("seq", q{new Value(cast(IOperator)(new SeqOperator))}); 298 this.variables.insert!("cons", q{new Value(cast(IOperator)(new ConsOperator))}); 299 this.variables.insert!("sort", q{new Value(cast(IOperator)(new SortOperator))}); 300 this.variables.insert!("list?", q{new Value(cast(IOperator)(new IsListOperator))}); 301 this.variables.insert!("remove", q{new Value(cast(IOperator)(new RemoveOperator))}); 302 this.variables.insert!("length", q{new Value(cast(IOperator)(new LengthOperator))}); 303 304 // HashMap operators 305 this.variables.insert!("new-hash", q{new Value(cast(IOperator)(new NewHashOperator))}); 306 this.variables.insert!("make-hash", q{new Value(cast(IOperator)(new MakeHashOperator))}); 307 this.variables.insert!("hash-set-value", q{new Value(cast(IOperator)(new HashSetValueOperator))}); 308 this.variables.insert!("hash-get-value", q{new Value(cast(IOperator)(new HashGetValueOperator))}); 309 this.variables.insert!("hash-get-keys", q{new Value(cast(IOperator)(new HashGetKeysOperator))}); 310 this.variables.insert!("hash-get-values", q{new Value(cast(IOperator)(new HashGetValuesOperator))}); 311 312 // String operators 313 this.variables.insert!("string-concat", q{new Value(cast(IOperator)(new StringConcatOperator))}); 314 this.variables.insert!("string-join", q{new Value(cast(IOperator)(new StringJoinOperator))}); 315 this.variables.insert!("string-split", q{new Value(cast(IOperator)(new StringSplitOperator))}); 316 this.variables.insert!("string-length", q{new Value(cast(IOperator)(new StringLengthOperator))}); 317 this.variables.insert!("string-slice", q{new Value(cast(IOperator)(new StringSliceOperator))}); 318 this.variables.insert!("as-string", q{new Value(cast(IOperator)(new AsStringOperator))}); 319 this.variables.insert!("string-repeat", q{new Value(cast(IOperator)(new StringRepeatOperator))}); 320 this.variables.insert!("string-chomp", q{new Value(cast(IOperator)(new StringChompOperator))}); 321 322 // Conversion operators 323 this.variables.insert!("number-to-string", q{new Value(cast(IOperator)(new numberToStringOperator))}); 324 this.variables.insert!("char-to-number", q{new Value(cast(IOperator)(new charToNumberOperator))}); 325 this.variables.insert!("float-to-integer", q{new Value(cast(IOperator)(new floatToIntegerOperator))}); 326 this.variables.insert!("ubytes-to-string", q{new Value(cast(IOperator)(new ubytesToStringOperator))}); 327 this.variables.insert!("ubytes-to-integers", q{new Value(cast(IOperator)(new ubytesToIntegersOperator))}); 328 329 // Array Operators 330 this.variables.insert!("array-new", q{new Value(cast(IOperator)(new ArrayNewOperator))}); 331 this.variables.insert!("array-get-n", q{new Value(cast(IOperator)(new ArrayGetNOperator))}); 332 this.variables.insert!("array-set-n", q{new Value(cast(IOperator)(new ArraySetNOperator))}); 333 this.variables.insert!("array-slice", q{new Value(cast(IOperator)(new ArraySliceOperator))}); 334 this.variables.insert!("array-append", q{new Value(cast(IOperator)(new ArrayAppendOperator))}); 335 this.variables.insert!("array-concat", q{new Value(cast(IOperator)(new ArrayConcatOperator))}); 336 this.variables.insert!("array-length", q{new Value(cast(IOperator)(new ArrayLengthOperator))}); 337 this.variables.insert!("array-flatten", q{new Value(cast(IOperator)(new ArrayFlattenOperator))}); 338 this.variables.insert!("array-reverse", q{new Value(cast(IOperator)(new ArrayReverseOperator))}); 339 340 // Utility operators 341 this.variables.insert!("eval", q{new Value(cast(IOperator)(new EvalOperator))}); 342 this.variables.insert!("load", q{new Value(cast(IOperator)(new LoadOperator))}); 343 this.variables.insert!("type", q{new Value(cast(IOperator)(new TypeOperator))}); 344 this.variables.insert!("alias", q{new Value(cast(IOperator)(new AliasOperator))}); 345 this.variables.insert!("is-null?", q{new Value(cast(IOperator)(new IsNullOperator))}); 346 this.variables.insert!("is-hash?", q{new Value(cast(IOperator)(new IsHashMapOperator))}); 347 this.variables.insert!("transpile", q{new Value(cast(IOperator)(new TranspileOperator))}); 348 349 // Curl Operators 350 this.variables.insert!("curl-download", q{new Value(cast(IOperator)(new CurlDownloadOperator))}); 351 this.variables.insert!("curl-upload", q{new Value(cast(IOperator)(new CurlUploadOperator))}); 352 this.variables.insert!("curl-get", q{new Value(cast(IOperator)(new CurlGetOperator))}); 353 this.variables.insert!("curl-get-string", q{new Value(cast(IOperator)(new CurlGetStringOperator))}); 354 this.variables.insert!("curl-post", q{new Value(cast(IOperator)(new CurlPostOperator))}); 355 this.variables.insert!("curl-post-string", q{new Value(cast(IOperator)(new CurlPostStringOperator))}); 356 357 // Uri Operators 358 this.variables.insert!("url-encode-component", q{new Value(cast(IOperator)(new UrlEncodeComponentOperator))}); 359 360 // UUID Operators 361 this.variables.insert!("random-uuid", q{new Value(cast(IOperator)(new RandomUUIDOperator))}); 362 363 // Datetime Operators 364 this.variables.insert!("get-current-unixtime", q{new Value(cast(IOperator)(new GetCurrentUNIXTime))}); 365 366 // Digest Operators 367 this.variables.insert!("hmac-sha1", q{new Value(cast(IOperator)(new HMACSHA1Operator))}); 368 369 // Debug Operators 370 this.variables.insert!("dump-variables", q{new Value(cast(IOperator)(new DumpVaribalesOperator))}); 371 this.variables.insert!("peek-closure", q{new Value(cast(IOperator)(new PeekClosureOperator))}); 372 this.variables.insert!("call-closure", q{new Value(cast(IOperator)(new CallClosureOperator))}); 373 this.variables.insert!("toggle-ge-dbg", q{new Value(cast(IOperator)(new ToggleGEDebugOperator))}); 374 375 // Class Operators 376 this.variables.insert!("class", q{new Value(cast(IOperator)(new ClassOperator))}); 377 this.variables.insert!("new", q{new Value(cast(IOperator)(new NewOperator))}); 378 379 // Path Operators 380 this.variables.insert!("path-exists", q{new Value(cast(IOperator)(new PathExistsOperator))}); 381 this.variables.insert!("path-is-dir", q{new Value(cast(IOperator)(new PathIsDirOperator))}); 382 this.variables.insert!("path-is-file", q{new Value(cast(IOperator)(new PathIsFileOperator))}); 383 384 // File Operators 385 this.variables.insert!("remove-file", q{new Value(cast(IOperator)(new RemoveFileOperator))}); 386 this.variables.insert!("remove-dir", q{new Value(cast(IOperator)(new RemoveDirOperator))}); 387 this.variables.insert!("get-cwd", q{new Value(cast(IOperator)(new GetcwdOperator))}); 388 this.variables.insert!("get-size", q{new Value(cast(IOperator)(new GetsizeOperator))}); 389 390 // STDIO Operators 391 this.variables.insert!("readln", q{new Value(cast(IOperator)(new ReadlnOperator))}); 392 393 // Aliases 394 this.variables.link("not", "!"); 395 this.variables.link("and", "&&"); 396 this.variables.link("or", "||"); 397 this.variables.link("begin", "step"); 398 399 // Classes 400 this.variables.insert("FileClass", () => new Value(cast(ClassType)new FileClass(this)), true); 401 } 402 403 /** 404 * Constructor to make a clone 405 */ 406 this(ConstructorMode mode) {} 407 408 /** 409 * Super Class for a cloned object 410 */ 411 private Engine _super; 412 413 Engine peekSuper() { 414 return this._super; 415 } 416 417 /** 418 * Clone this object 419 */ 420 Engine clone() { 421 Engine newEngine = new Engine(ConstructorMode.CLONE); 422 423 newEngine._super = this; 424 425 newEngine.variables = new LazedAssocArray!Value; 426 427 if (!sync_storage) { 428 newEngine.variables.called = this.variables.called.dup; 429 newEngine.variables.constructors = this.variables.constructors; 430 newEngine.variables.storage = this.variables.storage.dup; 431 } else { 432 newEngine.variables.called = this.variables.called; 433 newEngine.variables.constructors = this.variables.constructors; 434 newEngine.variables.storage = this.variables.storage; 435 } 436 437 return newEngine; 438 } 439 440 /** 441 * Define new variable 442 */ 443 public Value defineVariable(string name, Value value) { 444 this.variables.set(name, value.dup); 445 446 return value; 447 } 448 449 /** 450 * Set a value into certain variable 451 */ 452 public Value setVariable(string name, Value value) { 453 Engine engine = this; 454 455 while (true) { 456 if (name in engine.variables.called) { 457 engine.variables.set(name, value.dup); 458 return value; 459 } else if (engine._super !is null) { 460 engine = engine._super; 461 } else { 462 engine.defineVariable(name, value); 463 } 464 } 465 } 466 467 /** 468 * Get a value from variables table 469 */ 470 public Value getVariable(string name) { 471 Engine engine = this; 472 473 while (true) { 474 if (name in engine.variables.called) { 475 return engine.variables[name]; 476 } else if (engine._super !is null) { 477 engine = engine._super; 478 } else { 479 return new Value; 480 } 481 } 482 } 483 484 /** 485 * Evalute Object 486 */ 487 public Value eval(Value script) { 488 Value ret = new Value(this.getExpression(script)); 489 490 if (ret.type == ValueType.IOperator) { 491 return ret; 492 } 493 494 enforce(ret.type == ValueType.IExpression); 495 496 ret = ret.getIExpression.eval(this); 497 498 if (ret.type == ValueType.IOperator) { 499 return new Value(new Closure(this, ret.getIOperator)); 500 } else { 501 return ret; 502 } 503 } 504 505 /** 506 * getExpression 507 * Build Script Tree 508 */ 509 public IExpression getExpression(Value script) { 510 if (debug_get_expression) { 511 import std.stdio; 512 writeln("[getExpression] script -> ", script); 513 } 514 515 if (script.type == ValueType.ImmediateValue) { 516 return script.getImmediateValue; 517 } 518 519 if (script.type == ValueType.Array) { 520 Value[] scriptList = script.getArray; 521 522 if (scriptList[0].type == ValueType.Array) { 523 Value op = this.variables[scriptList[0][0].getString]; 524 if (op.type == ValueType.Closure) { 525 Closure closure = op.getClosure; 526 Engine engine = closure.engine; 527 IOperator operator = closure.operator; 528 529 Closure cls = operator.call(engine, scriptList[0].getArray[1..$]).getClosure; 530 531 if (scriptList.length == 2) { 532 return new ImmediateValue(cls.eval(scriptList[1..$])); 533 } else { 534 return new ImmediateValue(cls.eval([])); 535 } 536 } else if (op.type == ValueType.IOperator) { 537 CallOperator ret = new CallOperator( 538 this.variables[scriptList[0][0].getString].getIOperator, 539 scriptList[0].getArray[1..$]); 540 Value tmp = ret.eval(this); 541 542 if (tmp.type == ValueType.Closure) { 543 return new ImmediateValue(tmp.getClosure.eval(scriptList[1..$])); 544 } else if (tmp.type == ValueType.IOperator) { 545 return new ImmediateValue(tmp.getIOperator.call(this, scriptList[1..$])); 546 } else if (tmp.type == ValueType.ClassType) { 547 ClassType cls = tmp.getClassType; 548 return new ImmediateValue(cls.call(cls._engine, scriptList[1..$])); 549 } 550 } else { 551 throw new Error("Invalid Operator was given!"); 552 } 553 } 554 555 Value tmp = this.variables[scriptList[0].getString]; 556 557 if (tmp.type == ValueType.IOperator) { 558 IOperator op = tmp.getIOperator; 559 return new CallOperator(op, scriptList[1..$]); 560 } else if (tmp.type == ValueType.Closure) { 561 return new CallOperator(tmp.getClosure.operator, scriptList[1..$]); 562 } else if (tmp.type == ValueType.ClassType) { 563 //import std.stdio; writeln("ClassType!"); 564 //writeln("scriptList -> ", scriptList); 565 ClassType cls = tmp.getClassType; 566 return new ImmediateValue(cls.call(cls._engine, scriptList[1..$])); 567 } else { 568 throw new Error("Invalid Operator was given!"); 569 } 570 } else { 571 if (script.type == ValueType.SymbolValue || script.type == ValueType.String) { 572 if (script.type == ValueType.SymbolValue) { 573 Value tmp; 574 tmp = this.getVariable(script.getString).dup; 575 576 if (tmp.type != ValueType.Null) { 577 return new ImmediateValue(tmp); 578 } 579 } else { 580 return new ImmediateValue(new Value(script.getString)); 581 } 582 } 583 584 return new ImmediateValue(script); 585 } 586 } 587 588 public bool variableDefined(string name) { 589 return this.getVariable(name).type != ValueType.Null; 590 } 591 }