Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2665 simplifier, push solution results view, performance improvements, small bug fixes, ui fixes

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