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