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 }