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 }