Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PushGP/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/CodeExpressions.cs @ 15017

Last change on this file since 15017 was 15017, checked in by pkimmesw, 7 years ago

#2665 Fixed Benchmark Problem Definition, Converted LoopExpressions to stateless expressions, Added several unit test to ensure funcionality, Fixed UI bugs

File size: 37.7 KB
Line 
1/// <summary>
2/// For explicit code manipulation and execution. May also be used as a general list data types.
3/// This types must always be present, as the top level interpreter will push any code to be executed on the
4/// CODE stack prior to execution. However, one may turn off all CODE instructions if code manipulation is not needed.
5/// </summary>
6
7namespace HeuristicLab.Problems.ProgramSynthesis.Push.Expressions {
8  using System;
9  using System.Collections.Generic;
10  using System.Linq;
11  using Attributes;
12
13  using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
14  using HeuristicLab.Problems.ProgramSynthesis.Push.Extensions;
15
16  using Interpreter;
17  using Stack;
18
19  /// <summary>
20  ///     Recursively invokes the interpreter on the program on top of the CODE stack. After evaluation the
21  ///     CODE stack is popped; normally this pops the program that was just executed, but if the expression itself
22  ///     manipulates the stack then this final pop may end up popping something else.
23  /// </summary>
24  [PushExpression(StackTypes.Code, "CODE.DO", StackTypes.Exec)]
25  [StorableClass]
26  public class CodeDoExpression : StatelessExpression {
27    public CodeDoExpression() { }
28    [StorableConstructor]
29    public CodeDoExpression(bool deserializing) : base(deserializing) { }
30
31    public override bool IsNoop(IInternalPushInterpreter interpreter) {
32      return interpreter.CodeStack.IsEmpty;
33    }
34
35    public override void Eval(IInternalPushInterpreter interpreter) {
36      var codePopExpression = ExpressionTable.GetStatelessExpression<CodePopExpression>();
37      interpreter.ExecStack.Push(codePopExpression, interpreter.CodeStack.Top);
38    }
39  }
40
41  /// <summary>
42  ///     Like CODE.DO but pops the stack before, rather than after, the recursive execution
43  /// </summary>
44  [PushExpression(StackTypes.Code, "CODE.DO*", StackTypes.Exec)]
45  [StorableClass]
46  public class CodeDoXExpression : StatelessExpression {
47    public CodeDoXExpression() { }
48    [StorableConstructor]
49    public CodeDoXExpression(bool deserializing) : base(deserializing) { }
50
51    public override bool IsNoop(IInternalPushInterpreter interpreter) {
52      return interpreter.CodeStack.IsEmpty;
53    }
54
55    public override void Eval(IInternalPushInterpreter interpreter) {
56      var expression = interpreter.CodeStack.Pop();
57      interpreter.ExecStack.Push(expression);
58    }
59  }
60
61  /// <summary>
62  ///     Does nothing.
63  /// </summary>
64  [PushExpression(StackTypes.Code, "CODE.NOOP")]
65  [StorableClass]
66  public class CodeNoopExpression : StatelessExpression {
67    public CodeNoopExpression() { }
68    [StorableConstructor]
69    public CodeNoopExpression(bool deserializing) : base(deserializing) { }
70
71    public override bool IsNoop(IInternalPushInterpreter interpreter) {
72      return true;
73    }
74
75    public override void Eval(IInternalPushInterpreter interpreter) {
76      // do nothing
77    }
78  }
79
80  /// <summary>
81  ///     Specifies that the next expression submitted for execution will instead be pushed literally onto the CODE stack.
82  ///     This can be implemented by moving the top item on the EXEC stack onto the CODE stack.
83  /// </summary>
84  [PushExpression(StackTypes.Code, "CODE.QUOTE", StackTypes.Exec)]
85  [StorableClass]
86  public class CodeQuoteExpression : StatelessExpression {
87    public CodeQuoteExpression() { }
88    [StorableConstructor]
89    public CodeQuoteExpression(bool deserializing) : base(deserializing) { }
90
91    public override bool IsNoop(IInternalPushInterpreter interpreter) {
92      return interpreter.ExecStack.IsEmpty;
93    }
94
95    public override void Eval(IInternalPushInterpreter interpreter) {
96      var expression = interpreter.ExecStack.Pop();
97      interpreter.CodeStack.Push(expression);
98    }
99  }
100
101  /// <summary>
102  ///     If the top item of the BOOLEAN stack is TRUE this recursively executes the second item of the CODE stack;
103  ///     otherwise it recursively executes the first item of the CODE stack. Either way both elements of the CODE stack
104  ///     (and the BOOLEAN value upon which the decision was made) are popped.
105  /// </summary>
106  [PushExpression(StackTypes.Code, "CODE.IF", StackTypes.Exec | StackTypes.Boolean)]
107  [StorableClass]
108  public class CodeIfExpression : StatelessExpression {
109    public CodeIfExpression() { }
110    [StorableConstructor]
111    public CodeIfExpression(bool deserializing) : base(deserializing) { }
112
113    public override bool IsNoop(IInternalPushInterpreter interpreter) {
114      return interpreter.BooleanStack.IsEmpty || interpreter.CodeStack.Count < 2;
115    }
116
117    public override void Eval(IInternalPushInterpreter interpreter) {
118      var condition = interpreter.BooleanStack.Pop();
119      var first = interpreter.CodeStack[1];
120      var second = interpreter.CodeStack.Top;
121
122      interpreter.CodeStack.Remove(2);
123      interpreter.ExecStack.Push(condition ? first : second);
124    }
125  }
126
127  /// <summary>
128  ///     Pushes the result of appending the top two pieces of code. If one of the pieces of code is a single instruction or
129  ///     literal (that is, something not surrounded by parentheses) then it is surrounded by parentheses first.
130  /// </summary>
131  [PushExpression(StackTypes.Code, "CODE.APPEND")]
132  [StorableClass]
133  public class CodeAppendExpression : StatelessExpression {
134    public CodeAppendExpression() { }
135    [StorableConstructor]
136    public CodeAppendExpression(bool deserializing) : base(deserializing) { }
137
138    public override bool IsNoop(IInternalPushInterpreter interpreter) {
139      return interpreter.CodeStack.Count < 2;
140    }
141
142    public override void Eval(IInternalPushInterpreter interpreter) {
143      var first = interpreter.CodeStack.Top;
144      var second = interpreter.CodeStack[1];
145
146      PushProgram firstProgram = null;
147      PushProgram secondProgram = null;
148
149      if (first.IsProgram) {
150        firstProgram = (PushProgram)first;
151
152        if (firstProgram.Depth > interpreter.Configuration.MaxDepth) return;
153
154        if (second.IsProgram) {
155          secondProgram = (PushProgram)second;
156
157          if (secondProgram.Depth > interpreter.Configuration.MaxDepth ||
158              firstProgram.Count + secondProgram.Count > interpreter.Configuration.MaxPointsInProgram)
159            return;
160        } else if (firstProgram.Count + 1 > interpreter.Configuration.MaxPointsInProgram) return;
161      } else if (second.IsProgram) {
162        secondProgram = (PushProgram)second;
163
164        if (secondProgram.Depth > interpreter.Configuration.MaxDepth
165            || secondProgram.Count + 1 > interpreter.Configuration.MaxPointsInProgram) return;
166      } else if (interpreter.Configuration.MaxPointsInProgram <= 2) {
167        return;
168      }
169
170      interpreter.CodeStack.Pop();
171
172      PushProgram result;
173
174      if (firstProgram != null) {
175        result = secondProgram != null
176          ? PushProgram.Merge(interpreter.PoolContainer.PushProgramPool, interpreter.PoolContainer.ExpressionListPool, secondProgram, firstProgram)
177          : PushProgram.Merge(interpreter.PoolContainer.PushProgramPool, interpreter.PoolContainer.ExpressionListPool, second, firstProgram);
178      } else if (secondProgram != null) {
179        result = PushProgram.Merge(interpreter.PoolContainer.PushProgramPool, interpreter.PoolContainer.ExpressionListPool, first, secondProgram);
180      } else {
181        var expressions = interpreter.PoolContainer.ExpressionListPool.Get();
182        expressions.Add(second);
183        expressions.Add(first);
184
185        result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, expressions);
186      }
187
188      interpreter.CodeStack.Top = result;
189    }
190  }
191
192  /// <summary>
193  ///     Pushes TRUE onto the BOOLEAN stack if the top piece of code is a single instruction or a literal,
194  ///     and FALSE otherwise (that is, if it is something surrounded by parentheses).
195  /// </summary>
196  [PushExpression(StackTypes.Code, "CODE.ATOM", StackTypes.Boolean)]
197  [StorableClass]
198  public class CodeAtomExpression : StatelessExpression {
199    public CodeAtomExpression() { }
200    [StorableConstructor]
201    public CodeAtomExpression(bool deserializing) : base(deserializing) { }
202
203    public override bool IsNoop(IInternalPushInterpreter interpreter) {
204      return interpreter.CodeStack.IsEmpty;
205    }
206
207    public override void Eval(IInternalPushInterpreter interpreter) {
208      var expression = interpreter.CodeStack.Pop();
209      var isExpandExpression = expression.IsProgram;
210
211      interpreter.BooleanStack.Push(!isExpandExpression);
212    }
213  }
214
215  /// <summary>
216  ///     Pushes the first item of the list on top of the CODE stack. For example, if the top piece of code is "( A B )"
217  ///     then this pushes "A" (after popping the argument). If the code on top of the stack is not a list then this has no
218  ///     effect.
219  ///     The name derives from the similar Lisp function; a more generic name would be "FIRST".
220  /// </summary>
221  [PushExpression(StackTypes.Code, "CODE.CAR")]
222  [StorableClass]
223  public class CodeCarExpression : StatelessExpression {
224    public CodeCarExpression() { }
225    [StorableConstructor]
226    public CodeCarExpression(bool deserializing) : base(deserializing) { }
227
228    public override bool IsNoop(IInternalPushInterpreter interpreter) {
229      return interpreter.CodeStack.IsEmpty ||
230            !interpreter.CodeStack.Top.IsProgram ||
231            ((PushProgram)interpreter.CodeStack.Top).IsEmpty;
232    }
233
234    public override void Eval(IInternalPushInterpreter interpreter) {
235      var expand = (PushProgram)interpreter.CodeStack.Top;
236      var first = expand.Expressions[expand.Expressions.Count - 1];
237
238      interpreter.CodeStack.Top = first;
239    }
240  }
241
242  /// <summary>
243  ///     Pushes a version of the list from the top of the CODE stack without its first element.
244  ///     For example, if the top piece of code is "( A B )" then this pushes "( B )" (after popping the argument).
245  ///     If the code on top of the stack is not a list then this pushes the empty list ("( )"). The name derives from
246  ///     the similar Lisp function; a more generic name would be "REST".
247  /// </summary>
248  [PushExpression(StackTypes.Code, "CODE.CDR")]
249  [StorableClass]
250  public class CodeCdrExpression : StatelessExpression {
251    public CodeCdrExpression() { }
252    [StorableConstructor]
253    public CodeCdrExpression(bool deserializing) : base(deserializing) { }
254
255    public override bool IsNoop(IInternalPushInterpreter interpreter) {
256      return interpreter.CodeStack.IsEmpty ||
257             (interpreter.CodeStack.Top.IsProgram && ((PushProgram)interpreter.CodeStack.Top).IsEmpty);
258    }
259
260    public override void Eval(IInternalPushInterpreter interpreter) {
261      PushProgram result;
262      var top = interpreter.CodeStack.Top;
263
264      if (top.IsProgram) {
265        var program = (PushProgram)top;
266        var expressions = program.CopyExpressions(interpreter.PoolContainer.ExpressionListPool);
267        expressions.RemoveAt(expressions.Count - 1);
268
269        result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, expressions as IReadOnlyList<Expression>);
270      } else result = PushProgram.Empty;
271
272
273      interpreter.CodeStack.Top = result;
274    }
275  }
276
277  /// <summary>
278  ///     Pushes the result of "consing" (in the Lisp sense) the second stack item onto the first stack item
279  ///     (which is coerced to a list if necessary). For example, if the top piece of code is "( A B )" and the
280  ///     second piece of code is "X" then this pushes "( X A B )" (after popping the argument).
281  /// </summary>
282  [PushExpression(StackTypes.Code, "CODE.CONS")]
283  [StorableClass]
284  public class CodeConsExpression : StatelessExpression {
285    public CodeConsExpression() { }
286    [StorableConstructor]
287    public CodeConsExpression(bool deserializing) : base(deserializing) { }
288
289    public override bool IsNoop(IInternalPushInterpreter interpreter) {
290      return interpreter.CodeStack.Count < 2 ||
291            (interpreter.CodeStack.Top.IsProgram &&
292            ((PushProgram)interpreter.CodeStack.Top).Expressions.Count + 1 > interpreter.Configuration.MaxPointsInProgram);
293    }
294
295    public override void Eval(IInternalPushInterpreter interpreter) {
296      var expressions = interpreter.PoolContainer.ExpressionListPool.Get();
297      var first = interpreter.CodeStack.Pop();
298
299      if (first.IsProgram) {
300        expressions.AddRange(((PushProgram)first).Expressions);
301      } else {
302        expressions.Add(first);
303      }
304
305      expressions.Add(interpreter.CodeStack.Top);
306      var result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, expressions);
307      interpreter.CodeStack.Top = result;
308    }
309  }
310
311  /// <summary>
312  ///     Pushes the "container" of the second CODE stack item within the first CODE stack item onto the CODE stack.
313  ///     If second item contains the first anywhere (i.e. in any nested list) then the container is the smallest sub-list
314  ///     that contains but is not equal to the first instance. For example, if the top piece of code is "( B ( C ( A ) ) ( D
315  ///     ( A ) ) )"
316  ///     and the second piece of code is "( A )" then this pushes ( C ( A ) ). Pushes an empty list if there is no such
317  ///     container.
318  /// </summary>
319  [PushExpression(StackTypes.Code, "CODE.CONTAINER")]
320  [StorableClass]
321  public class CodeContainerExpression : StatelessExpression {
322    public CodeContainerExpression() { }
323    [StorableConstructor]
324    public CodeContainerExpression(bool deserializing) : base(deserializing) { }
325
326    public override bool IsNoop(IInternalPushInterpreter interpreter) {
327      return (interpreter.CodeStack.Count < 2) ||
328             (interpreter.CodeStack[1].GetType() != typeof(PushProgram));
329    }
330
331    public override void Eval(IInternalPushInterpreter interpreter) {
332      var target = interpreter.CodeStack.Pop();
333      var source = interpreter.CodeStack.Top;
334
335      var container = GetContainer(source as PushProgram, target);
336      var result = container ?? PushProgram.Empty;
337
338      interpreter.CodeStack.Top = result;
339    }
340
341    private static PushProgram GetContainer(PushProgram current, Expression target) {
342      if (Equals(current, target))
343        return null;
344
345      var subContainer =
346          current.Expressions.Where(e => e.IsProgram)
347              .Select(e => GetContainer(e as PushProgram, target))
348              .Where(e => e != null);
349
350      var execExpandExpressions = subContainer as IList<PushProgram> ?? subContainer.ToList();
351      return execExpandExpressions.Any()
352          ? execExpandExpressions.OrderBy(c => c.Expressions.Count).LastOrDefault()
353          : current.Expressions.Contains(target)
354              ? current
355              : null;
356    }
357  }
358
359  /// <summary>
360  ///     Pushes TRUE on the BOOLEAN stack if the second CODE stack item contains the first CODE stack
361  ///     item anywhere (e.g. in a sub-list).
362  /// </summary>
363  [PushExpression(StackTypes.Code, "CODE.CONTAINS", StackTypes.Boolean)]
364  [StorableClass]
365  public class CodeContainsExpression : StatelessExpression {
366    public CodeContainsExpression() { }
367    [StorableConstructor]
368    public CodeContainsExpression(bool deserializing) : base(deserializing) { }
369
370    public override bool IsNoop(IInternalPushInterpreter interpreter) {
371      return interpreter.CodeStack.Count < 2 || !interpreter.CodeStack[1].IsProgram;
372    }
373
374    public override void Eval(IInternalPushInterpreter interpreter) {
375      var second = (PushProgram)interpreter.CodeStack[1];
376      var first = interpreter.CodeStack.Top;
377      interpreter.CodeStack.Remove(2);
378
379      var contains = second.Expressions.Contains(first);
380      interpreter.BooleanStack.Push(contains);
381    }
382  }
383
384  /// <summary>
385  ///     Pushes the definition associated with the top NAME on the NAME stack (if any) onto the CODE stack.
386  ///     This extracts the definition for inspection/manipulation, rather than for immediate execution (although it
387  ///     may then be executed with a call to CODE.DO or a similar instruction).
388  /// </summary>
389  [PushExpression(StackTypes.Code, "CODE.DEFINITION", StackTypes.Name)]
390  [StorableClass]
391  public class CodeDefinitionExpression : StatelessExpression {
392    public CodeDefinitionExpression() { }
393    [StorableConstructor]
394    public CodeDefinitionExpression(bool deserializing) : base(deserializing) { }
395
396    public override bool IsNoop(IInternalPushInterpreter interpreter) {
397      return interpreter.NameStack.IsEmpty ||
398            !interpreter.CustomExpressions.ContainsKey(interpreter.NameStack.Top);
399    }
400
401    public override void Eval(IInternalPushInterpreter interpreter) {
402      var name = interpreter.NameStack.Pop();
403      var definition = interpreter.CustomExpressions[name];
404
405      interpreter.CodeStack.Push(definition);
406    }
407  }
408
409  /// <summary>
410  ///     Pushes a measure of the discrepancy between the top two CODE stack items onto the INTEGER stack. This will be zero
411  ///     if the top two items are equivalent, and will be higher the 'more different' the items are from one another. The calculation
412  ///     is as follows:
413  ///     <list types="bullet">
414  ///         <item>
415  ///             <description>
416  ///                 Construct a list of all of the unique items in both of the lists(where uniqueness is determined by equalp).
417  ///                 Sub-lists and atoms all count as items.
418  ///             </description>
419  ///         </item>
420  ///         <item>
421  ///             <description>Initialize the result to zero.</description>
422  ///         </item>
423  ///         <item>
424  ///             <description>
425  ///                 For each unique item increment the result by the difference between the number of occurrences of the
426  ///                 item in the two pieces of code.
427  ///             </description>
428  ///         </item>
429  ///         <item>
430  ///             <description>Push the result.</description>
431  ///         </item>
432  ///     </list>
433  /// </summary>
434  [PushExpression(StackTypes.Code, "CODE.DISCREPANCY", StackTypes.Integer)]
435  [StorableClass]
436  public class CodeDiscrepancyExpression : StatelessExpression {
437    public CodeDiscrepancyExpression() { }
438    [StorableConstructor]
439    public CodeDiscrepancyExpression(bool deserializing) : base(deserializing) { }
440
441    public override bool IsNoop(IInternalPushInterpreter interpreter) {
442      return interpreter.CodeStack.Count < 2;
443    }
444
445    public override void Eval(IInternalPushInterpreter interpreter) {
446      var second = interpreter.CodeStack[1];
447      var first = interpreter.CodeStack.Top;
448      interpreter.CodeStack.Remove(2);
449
450      var firstItems = new Dictionary<int, int>();
451      var secondItems = new Dictionary<int, int>();
452
453      DetermineUniqueItems(second, secondItems);
454      DetermineUniqueItems(first, firstItems);
455
456      var discrepancy = GetDiscrepancy(firstItems, secondItems);
457      interpreter.IntegerStack.Push(discrepancy);
458    }
459
460    private int GetDiscrepancy(IDictionary<int, int> first, IDictionary<int, int> second) {
461      var discrepancy = 0;
462      var keys = first.Keys.Concat(second.Keys).Distinct();
463
464      foreach (var key in keys)
465        if (first.ContainsKey(key) && second.ContainsKey(key))
466          discrepancy += Math.Abs(first[key] - second[key]);
467        else if (first.ContainsKey(key)) discrepancy += first[key];
468        else discrepancy += second[key];
469
470      return discrepancy;
471    }
472
473    private void DetermineUniqueItems(Expression source, IDictionary<int, int> items) {
474      if (source.IsProgram) {
475        var program = source as PushProgram;
476        var expressions = program.Expressions;
477
478        for (var i = 0; i < expressions.Count; i++) {
479          var id = expressions[i].GetHashCode();
480          if (!items.ContainsKey(id)) items.Add(id, 1);
481          else items[id]++;
482        }
483      } else {
484        items.Add(source.GetHashCode(), 1);
485      }
486    }
487  }
488
489  /// <summary>
490  ///     Pushes the sub-expression of the top item of the CODE stack that is indexed by the top item of the INTEGER stack.
491  ///     The indexing here counts "points," where each parenthesized expression and each literal/instruction is considered a
492  ///     point,
493  ///     and it proceeds in depth first order. The entire piece of code is at index 0; if it is a list then the first item
494  ///     in the list
495  ///     is at index 1, etc. The integer used as the index is taken modulo the number of points in the overall expression
496  ///     (and its
497  ///     absolute value is taken in case it is negative) to ensure that it is within the meaningful range.
498  /// </summary>
499  [PushExpression(StackTypes.Code, "CODE.EXTRACT", StackTypes.Integer)]
500  [StorableClass]
501  public class CodeExtractExpression : StatelessExpression {
502    public CodeExtractExpression() { }
503    [StorableConstructor]
504    public CodeExtractExpression(bool deserializing) : base(deserializing) { }
505
506    public override bool IsNoop(IInternalPushInterpreter interpreter) {
507      return interpreter.IntegerStack.IsEmpty ||
508             interpreter.CodeStack.IsEmpty ||
509            !interpreter.CodeStack.Top.IsProgram;
510    }
511
512    public override void Eval(IInternalPushInterpreter interpreter) {
513      var expression = (PushProgram)interpreter.CodeStack.Top;
514      var index = interpreter.IntegerStack.Pop().AsInt(expression.TotalCount);
515      var result = expression.GetFromTree(index);
516
517      interpreter.CodeStack.Top = result;
518    }
519  }
520
521  /// <summary>
522  ///     Pops the BOOLEAN stack and pushes the popped item (TRUE or FALSE) onto the CODE stack.
523  /// </summary>
524  [PushExpression(StackTypes.Code, "CODE.FROMBOOLEAN", StackTypes.Boolean)]
525  [StorableClass]
526  public class CodeFromBooleanExpression : StatelessExpression {
527    public CodeFromBooleanExpression() { }
528    [StorableConstructor]
529    public CodeFromBooleanExpression(bool deserializing) : base(deserializing) { }
530
531    public override bool IsNoop(IInternalPushInterpreter interpreter) {
532      return interpreter.BooleanStack.IsEmpty;
533    }
534
535    public override void Eval(IInternalPushInterpreter interpreter) {
536      var value = interpreter.BooleanStack.Pop();
537      var pool = interpreter.PoolContainer.GetStatefulExpressionPool<BooleanPushExpression>();
538      var expression = BooleanPushExpression.Create(pool, value);
539
540      interpreter.CodeStack.Push(expression);
541    }
542  }
543
544  /// <summary>
545  ///     Pops the FLOAT stack and pushes the popped item onto the CODE stack.
546  /// </summary>
547  [PushExpression(StackTypes.Code, "CODE.FROMFLOAT", StackTypes.Float)]
548  [StorableClass]
549  public class CodeFromFloatExpression : StatelessExpression {
550    public CodeFromFloatExpression() { }
551    [StorableConstructor]
552    public CodeFromFloatExpression(bool deserializing) : base(deserializing) { }
553
554    public override bool IsNoop(IInternalPushInterpreter interpreter) {
555      return interpreter.FloatStack.IsEmpty;
556    }
557
558    public override void Eval(IInternalPushInterpreter interpreter) {
559      var value = interpreter.FloatStack.Pop();
560      var pool = interpreter.PoolContainer.GetStatefulExpressionPool<FloatPushExpression>();
561      var expression = FloatPushExpression.Create(pool, value);
562
563      interpreter.CodeStack.Push(expression);
564    }
565  }
566
567  /// <summary>
568  ///     Pops the INTEGER stack and pushes the popped integer onto the CODE stack.
569  /// </summary>
570  [PushExpression(StackTypes.Code, "CODE.FROMINTEGER", StackTypes.Integer)]
571  [StorableClass]
572  public class CodeFromIntegerExpression : StatelessExpression {
573    public CodeFromIntegerExpression() { }
574    [StorableConstructor]
575    public CodeFromIntegerExpression(bool deserializing) : base(deserializing) { }
576
577    public override bool IsNoop(IInternalPushInterpreter interpreter) {
578      return interpreter.IntegerStack.IsEmpty;
579    }
580
581    public override void Eval(IInternalPushInterpreter interpreter) {
582      var value = interpreter.IntegerStack.Pop();
583      var pool = interpreter.PoolContainer.GetStatefulExpressionPool<IntegerPushExpression>();
584      var expression = IntegerPushExpression.Create(pool, value);
585
586      interpreter.CodeStack.Push(expression);
587    }
588  }
589
590  /// <summary>
591  ///     Pops the NAME stack and pushes the popped item onto the CODE stack.
592  /// </summary>
593  [PushExpression(StackTypes.Code, "CODE.FROMNAME", StackTypes.Name)]
594  [StorableClass]
595  public class CodeFromNameExpression : StatelessExpression {
596    public CodeFromNameExpression() { }
597    [StorableConstructor]
598    public CodeFromNameExpression(bool deserializing) : base(deserializing) { }
599
600    public override bool IsNoop(IInternalPushInterpreter interpreter) {
601      return interpreter.NameStack.IsEmpty;
602    }
603
604    public override void Eval(IInternalPushInterpreter interpreter) {
605      var value = interpreter.NameStack.Pop();
606      var expression = new NameDefineXExecExpression(value);
607
608      interpreter.CodeStack.Push(expression);
609    }
610  }
611
612  /// <summary>
613  ///     Pushes the result of inserting the second item of the CODE stack into the first item, at the position indexed
614  ///     by the top item of the INTEGER stack (and replacing whatever was there formerly).
615  /// </summary>
616  [PushExpression(StackTypes.Code, "CODE.CODEINSERT", StackTypes.Integer)]
617  [StorableClass]
618  public class CodeInsertExpression : StatelessExpression {
619    public CodeInsertExpression() { }
620    [StorableConstructor]
621    public CodeInsertExpression(bool deserializing) : base(deserializing) { }
622
623    public override bool IsNoop(IInternalPushInterpreter interpreter) {
624      return interpreter.IntegerStack.IsEmpty ||
625             interpreter.CodeStack.Count < 2 ||
626            !interpreter.CodeStack.Top.IsProgram;
627    }
628
629    public override void Eval(IInternalPushInterpreter interpreter) {
630      var source = interpreter.CodeStack[1];
631      var target = (PushProgram)interpreter.CodeStack.Pop();
632
633      IList<Expression> newExpressions;
634      if (!target.IsEmpty) {
635        var index = interpreter.IntegerStack.Pop().AsInt(target.Expressions.Count);
636        index = target.Expressions.Count - 1 - index;
637        newExpressions = target.CopyExpressions(interpreter.PoolContainer.ExpressionListPool);
638
639        newExpressions[index] = source.IsProgram
640            ? ((PushProgram)source).Copy(interpreter.PoolContainer.PushProgramPool)
641            : source;
642      } else {
643        newExpressions = interpreter.PoolContainer.ExpressionListPool.Get();
644        newExpressions.Add(source);
645      }
646
647      var result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, newExpressions as IReadOnlyList<Expression>);
648      interpreter.CodeStack.Top = result;
649    }
650  }
651
652  /// <summary>
653  ///     Pushes the length of the top item on the CODE stack onto the INTEGER stack.
654  ///     If the top item is not a list then this pushes a 1. If the top item is a list then this pushes the
655  ///     number of items in the top level of the list; that is, nested lists contribute only 1 to this count, no
656  ///     matter what they contain.
657  /// </summary>
658  [PushExpression(StackTypes.Code, "CODE.LENGTH", StackTypes.Integer)]
659  [StorableClass]
660  public class CodeLengthExpression : StatelessExpression {
661    public CodeLengthExpression() { }
662    [StorableConstructor]
663    public CodeLengthExpression(bool deserializing) : base(deserializing) { }
664
665    public override bool IsNoop(IInternalPushInterpreter interpreter) {
666      return interpreter.CodeStack.IsEmpty;
667    }
668
669    public override void Eval(IInternalPushInterpreter interpreter) {
670      var expression = interpreter.CodeStack.Pop();
671      var count = 1;
672
673      if (expression.IsProgram)
674        count = ((PushProgram)expression).Expressions.Count;
675
676      interpreter.IntegerStack.Push(count);
677    }
678  }
679
680  /// <summary>
681  ///     Pushes a list of the top two items of the CODE stack onto the CODE stack.
682  /// </summary>
683  [PushExpression(StackTypes.Code, "CODE.LIST")]
684  [StorableClass]
685  public class CodeListExpression : StatelessExpression {
686    public CodeListExpression() { }
687    [StorableConstructor]
688    public CodeListExpression(bool deserializing) : base(deserializing) { }
689
690    public override bool IsNoop(IInternalPushInterpreter interpreter) {
691      return interpreter.CodeStack.Count < 2 ||
692            (interpreter.CodeStack.Top.IsProgram && ((PushProgram)interpreter.CodeStack.Top).Depth == interpreter.Configuration.MaxDepth) ||
693            (interpreter.CodeStack[1].IsProgram && ((PushProgram)interpreter.CodeStack[1]).Depth == interpreter.Configuration.MaxDepth);
694    }
695
696    public override void Eval(IInternalPushInterpreter interpreter) {
697      var first = interpreter.CodeStack.Pop();
698      var second = interpreter.CodeStack.Top;
699
700      var expressions = interpreter.PoolContainer.ExpressionListPool.Get();
701      expressions.Add(second);
702      expressions.Add(first);
703
704      var expandExpression = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, expressions);
705      interpreter.CodeStack.Top = expandExpression;
706    }
707  }
708
709  /// <summary>
710  ///     Pushes TRUE onto the BOOLEAN stack if the second item of the CODE stack is a member of the first item
711  ///     (which is coerced to a list if necessary). Pushes FALSE onto the BOOLEAN stack otherwise.
712  /// </summary>
713  [PushExpression(StackTypes.Code, "CODE.MEMBER", StackTypes.Boolean)]
714  [StorableClass]
715  public class CodeMemberExpression : StatelessExpression {
716    public CodeMemberExpression() { }
717    [StorableConstructor]
718    public CodeMemberExpression(bool deserializing) : base(deserializing) { }
719
720    public override bool IsNoop(IInternalPushInterpreter interpreter) {
721      return interpreter.CodeStack.Count < 2;
722    }
723
724    public override void Eval(IInternalPushInterpreter interpreter) {
725      var first = interpreter.CodeStack[1];
726      var second = interpreter.CodeStack.Top;
727      interpreter.CodeStack.Remove(2);
728
729      var contains = second.IsProgram
730          ? ((PushProgram)second).Expressions.Contains(first)
731          : second.Equals(first);
732
733      interpreter.BooleanStack.Push(contains);
734    }
735  }
736
737  /// <summary>
738  ///     Pushes the nth element of the expression on top of the CODE stack (which is coerced to a list first if necessary).
739  ///     If the expression is an empty list then the result is an empty list. N is taken from the INTEGER stack and is taken
740  ///     modulo the length of the expression into which it is indexing.
741  /// </summary>
742  [PushExpression(StackTypes.Code, "CODE.NTH", StackTypes.Integer)]
743  [StorableClass]
744  public class CodeNthExpression : StatelessExpression {
745    public CodeNthExpression() { }
746    [StorableConstructor]
747    public CodeNthExpression(bool deserializing) : base(deserializing) { }
748
749    public override bool IsNoop(IInternalPushInterpreter interpreter) {
750      return interpreter.CodeStack.IsEmpty || interpreter.IntegerStack.IsEmpty;
751    }
752
753    public override void Eval(IInternalPushInterpreter interpreter) {
754      var n = interpreter.IntegerStack.Pop();
755      var expression = interpreter.CodeStack.Top;
756
757      Expression nthExpression;
758
759      if (expression.IsProgram) {
760        var subExpression = (PushProgram)expression;
761
762        if (subExpression.IsEmpty) {
763          nthExpression = PushProgram.Empty;
764        } else {
765          var index = (subExpression.Expressions.Count - 1 - n.AsInt(subExpression.Expressions.Count));
766
767          nthExpression = subExpression.Expressions[index];
768        }
769      } else {
770        nthExpression = expression;
771      }
772
773      interpreter.CodeStack.Top = nthExpression;
774    }
775  }
776
777  /// <summary>
778  ///     Pushes the nth "CDR" (in the Lisp sense) of the expression on top of the CODE stack (which is coerced to a list
779  ///     first if necessary). If the expression is an empty list then the result is an empty list. N is taken from the
780  ///     INTEGER
781  ///     stack and is taken modulo the length of the expression into which it is indexing. A "CDR" of a list is the list
782  ///     without
783  ///     its first element.
784  /// </summary>
785  [PushExpression(StackTypes.Code, "CODE.NTHCDR", StackTypes.Integer)]
786  [StorableClass]
787  public class CodeNthCdrExpression : StatelessExpression {
788    public CodeNthCdrExpression() { }
789    [StorableConstructor]
790    public CodeNthCdrExpression(bool deserializing) : base(deserializing) { }
791
792    public override bool IsNoop(IInternalPushInterpreter interpreter) {
793      return interpreter.CodeStack.IsEmpty || interpreter.IntegerStack.IsEmpty;
794    }
795
796    public override void Eval(IInternalPushInterpreter interpreter) {
797      var n = interpreter.IntegerStack.Pop();
798      var expression = interpreter.CodeStack.Top;
799
800      Expression nthExpression;
801
802      if (expression.IsProgram) {
803        var subExpressions = ((PushProgram)expression).Expressions;
804
805        if (subExpressions.Count < 2) {
806          nthExpression = PushProgram.Empty;
807        } else {
808          var index = subExpressions.Count - 2 - n.AsInt(subExpressions.Count - 1);
809
810          nthExpression = subExpressions[index];
811        }
812      } else {
813        nthExpression = expression;
814      }
815
816      interpreter.CodeStack.Top = nthExpression;
817    }
818  }
819
820  /// <summary>
821  ///     Pushes TRUE onto the BOOLEAN stack if the top item of the CODE stack is an empty list, or FALSE otherwise.
822  /// </summary>
823  [PushExpression(StackTypes.Code, "CODE.NULL", StackTypes.Boolean)]
824  [StorableClass]
825  public class CodeNullExpression : StatelessExpression {
826    public CodeNullExpression() { }
827    [StorableConstructor]
828    public CodeNullExpression(bool deserializing) : base(deserializing) { }
829
830    public override bool IsNoop(IInternalPushInterpreter interpreter) {
831      return interpreter.CodeStack.IsEmpty;
832    }
833
834    public override void Eval(IInternalPushInterpreter interpreter) {
835      var top = interpreter.CodeStack.Pop();
836      var result = top.IsProgram && ((PushProgram)top).IsEmpty;
837      interpreter.BooleanStack.Push(result);
838    }
839  }
840
841  /// <summary>
842  ///     Pushes onto the INTEGER stack the position of the second item on the CODE stack within the first item
843  ///     (which is coerced to a list if necessary). Pushes -1 if no match is found.
844  /// </summary>
845  [PushExpression(StackTypes.Code, "CODE.POSITION", StackTypes.Integer)]
846  [StorableClass]
847  public class CodePositionExpression : StatelessExpression {
848    public CodePositionExpression() { }
849    [StorableConstructor]
850    public CodePositionExpression(bool deserializing) : base(deserializing) { }
851
852    public override bool IsNoop(IInternalPushInterpreter interpreter) {
853      return interpreter.CodeStack.Count < 2;
854    }
855
856    public override void Eval(IInternalPushInterpreter interpreter) {
857      var second = interpreter.CodeStack[1];
858      var first = interpreter.CodeStack.Top;
859      interpreter.CodeStack.Remove(2);
860
861      var position = -1;
862      if (first.IsProgram) {
863        var program = (PushProgram)first;
864        position = program.IndexOf(second);
865      } else if (first.Equals(second)) {
866        position = 0;
867      }
868
869      interpreter.IntegerStack.Push(position);
870    }
871  }
872
873  /// <summary>
874  ///     Pushes the number of "points" in the top piece of CODE onto the INTEGER stack. Each instruction, literal,
875  ///     and pair of parentheses counts as a point.
876  /// </summary>
877  [PushExpression(StackTypes.Code, "CODE.SIZE", StackTypes.Integer)]
878  [StorableClass]
879  public class CodeSizeExpression : StatelessExpression {
880    public CodeSizeExpression() { }
881    [StorableConstructor]
882    public CodeSizeExpression(bool deserializing) : base(deserializing) { }
883
884    public override bool IsNoop(IInternalPushInterpreter interpreter) {
885      return interpreter.CodeStack.IsEmpty;
886    }
887
888    public override void Eval(IInternalPushInterpreter interpreter) {
889      var expression = interpreter.CodeStack.Pop();
890      var points = expression.IsProgram
891          ? ((PushProgram)expression).Expressions.Count
892          : 1;
893
894      interpreter.IntegerStack.Push(points);
895    }
896  }
897
898  /// <summary>
899  ///     Pushes the result of substituting the third item on the code stack for the second item in the first item.
900  ///     As of this writing this is implemented only in the Lisp implementation, within which it relies on the Lisp "subst"
901  ///     function. As such, there are several problematic possibilities; for example "dotted-lists" can result in certain
902  ///     cases with empty-list arguments. If any of these problematic possibilities occurs the stack is left unchanged.
903  /// </summary>
904  [PushExpression(StackTypes.Code, "CODE.SUBST")]
905  [StorableClass]
906  public class CodeSubstitutionExpression : StatelessExpression {
907    public CodeSubstitutionExpression() { }
908    [StorableConstructor]
909    public CodeSubstitutionExpression(bool deserializing) : base(deserializing) { }
910
911    public override bool IsNoop(IInternalPushInterpreter interpreter) {
912      return (interpreter.CodeStack.Count < 3) ||
913             (interpreter.CodeStack.Top.GetType() != typeof(PushProgram));
914    }
915
916    public override void Eval(IInternalPushInterpreter interpreter) {
917      var third = interpreter.CodeStack[2];
918      var second = interpreter.CodeStack[1];
919      var first = interpreter.CodeStack.Top as PushProgram;
920      interpreter.CodeStack.Remove(2);
921
922      var firstExpressions = first.Expressions;
923      var newExpressions = interpreter.PoolContainer.ExpressionListPool.Get();
924
925      for (var i = 0; i < firstExpressions.Count; i++) {
926        var expression = firstExpressions[i].Equals(third)
927                           ? second
928                           /* no cloning needed here because first is removed and therefore newExpression is the only container of sub expressions */
929                           : firstExpressions[i];
930
931        newExpressions.Add(expression);
932      }
933
934      var result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, newExpressions);
935      interpreter.CodeStack.Top = result;
936    }
937  }
938}
Note: See TracBrowser for help on using the repository browser.