Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2665 BenchmarkSuite, all examples, partially tested, VectorExpressions added

File size: 31.5 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  using Interpreter;
13  using Stack;
14
15  /// <summary>
16  ///     Recursively invokes the interpreter on the program on top of the CODE stack. After evaluation the
17  ///     CODE stack is popped; normally this pops the program that was just executed, but if the expression itself
18  ///     manipulates the stack then this final pop may end up popping something else.
19  /// </summary>
20  [PushExpression(StackTypes.Code, "CODE.DO", StackTypes.Exec)]
21  public class CodeDoExpression : StatelessExpression {
22    public override bool Eval(IInternalPushInterpreter interpreter) {
23      // not enough arguments on stack
24      if (interpreter.CodeStack.Count == 0) return false;
25
26      var codePopExpression = ExpressionTable.GetStatelessExpression<CodePopExpression>();
27      interpreter.ExecStack.Push(codePopExpression, interpreter.CodeStack.Top);
28
29      return true;
30    }
31  }
32
33  /// <summary>
34  ///     Like CODE.DO but pops the stack before, rather than after, the recursive execution
35  /// </summary>
36  [PushExpression(StackTypes.Code, "CODE.DO*", StackTypes.Exec)]
37  public class CodeDoXExpression : StatelessExpression {
38    public override bool Eval(IInternalPushInterpreter interpreter) {
39      // not enough arguments on stack
40      if (interpreter.CodeStack.Count == 0) return false;
41
42      var expression = interpreter.CodeStack.Pop();
43      interpreter.ExecStack.Push(expression);
44
45      return true;
46    }
47  }
48
49  /// <summary>
50  ///     Does nothing.
51  /// </summary>
52  [PushExpression(StackTypes.Code, "CODE.NOOP")]
53  public class CodeNoopExpression : StatelessExpression {
54    public override bool Eval(IInternalPushInterpreter interpreter) {
55      return false;
56    }
57  }
58
59  /// <summary>
60  ///     Specifies that the next expression submitted for execution will instead be pushed literally onto the CODE stack.
61  ///     This can be implemented by moving the top item on the EXEC stack onto the CODE stack.
62  /// </summary>
63  [PushExpression(StackTypes.Code, "CODE.QUOTE", StackTypes.Exec)]
64  public class CodeQuoteExpression : StatelessExpression {
65    public override bool Eval(IInternalPushInterpreter interpreter) {
66      // not enough arguments on stack
67      if (interpreter.ExecStack.Count == 0) return false;
68
69      var expression = interpreter.ExecStack.Pop();
70      interpreter.CodeStack.Push(expression);
71
72      return true;
73    }
74  }
75
76  /// <summary>
77  ///     If the top item of the BOOLEAN stack is TRUE this recursively executes the second item of the CODE stack;
78  ///     otherwise it recursively executes the first item of the CODE stack. Either way both elements of the CODE stack
79  ///     (and the BOOLEAN value upon which the decision was made) are popped.
80  /// </summary>
81  [PushExpression(StackTypes.Code, "CODE.IF", StackTypes.Exec | StackTypes.Boolean)]
82  public class CodeIfExpression : StatelessExpression {
83    public override bool Eval(IInternalPushInterpreter interpreter) {
84      // not enough arguments on stack
85      if (interpreter.BooleanStack.Count == 0 ||
86          interpreter.CodeStack.Count < 2)
87        return false;
88
89      var condition = interpreter.BooleanStack.Pop();
90      var first = interpreter.CodeStack[1];
91      var second = interpreter.CodeStack.Top;
92
93      interpreter.CodeStack.Remove(2);
94      interpreter.ExecStack.Push(condition ? first : second);
95
96      return true;
97    }
98  }
99
100  /// <summary>
101  ///     Pushes the result of appending the top two pieces of code. If one of the pieces of code is a single instruction or
102  ///     literal (that is, something not surrounded by parentheses) then it is surrounded by parentheses first.
103  /// </summary>
104  [PushExpression(StackTypes.Code, "CODE.APPEND")]
105  public class CodeAppendExpression : StatelessExpression {
106    public override bool Eval(IInternalPushInterpreter interpreter) {
107      if (interpreter.CodeStack.Count < 2) return false;
108
109      var first = interpreter.CodeStack.Top;
110      var second = interpreter.CodeStack[1];
111
112      PushProgram firstProgram = null;
113      PushProgram secondProgram = null;
114
115      if (first.IsProgram) {
116        firstProgram = (PushProgram)first;
117
118        if (firstProgram.Depth > interpreter.Configuration.MaxDepth) return false;
119
120        if (second.IsProgram) {
121          secondProgram = (PushProgram)second;
122
123          if (secondProgram.Depth > interpreter.Configuration.MaxDepth ||
124              firstProgram.Count + secondProgram.Count > interpreter.Configuration.MaxPointsInProgram)
125            return false;
126        } else if (firstProgram.Count + 1 > interpreter.Configuration.MaxPointsInProgram) return false;
127      } else if (second.IsProgram) {
128        secondProgram = (PushProgram)second;
129
130        if (secondProgram.Depth > interpreter.Configuration.MaxDepth
131            || secondProgram.Count + 1 > interpreter.Configuration.MaxPointsInProgram) return false;
132      } else if (interpreter.Configuration.MaxPointsInProgram <= 2) {
133        return false;
134      }
135
136      interpreter.CodeStack.Pop();
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(IInternalPushInterpreter 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(IInternalPushInterpreter 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(IInternalPushInterpreter 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(IInternalPushInterpreter 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      var expressions = interpreter.PoolContainer.ExpressionListPool.Get();
245      var first = interpreter.CodeStack.Pop();
246
247      if (first.IsProgram) {
248        expressions.AddRange(((PushProgram)first).Expressions);
249      } else {
250        expressions.Add(first);
251      }
252
253      expressions.Add(interpreter.CodeStack.Top);
254      var result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, expressions);
255      interpreter.CodeStack.SetTop(result);
256
257      return true;
258    }
259  }
260
261  /// <summary>
262  ///     Pushes the "container" of the second CODE stack item within the first CODE stack item onto the CODE stack.
263  ///     If second item contains the first anywhere (i.e. in any nested list) then the container is the smallest sub-list
264  ///     that contains but is not equal to the first instance. For example, if the top piece of code is "( B ( C ( A ) ) ( D
265  ///     ( A ) ) )"
266  ///     and the second piece of code is "( A )" then this pushes ( C ( A ) ). Pushes an empty list if there is no such
267  ///     container.
268  /// </summary>
269  [PushExpression(StackTypes.Code, "CODE.CONTAINER")]
270  public class CodeContainerExpression : StatelessExpression {
271    public override bool Eval(IInternalPushInterpreter interpreter) {
272      if ((interpreter.CodeStack.Count < 2) ||
273          (interpreter.CodeStack[1].GetType() !=
274           typeof(PushProgram))) return false;
275
276      var target = interpreter.CodeStack.Pop();
277      var source = interpreter.CodeStack.Top;
278
279      var container = GetContainer(source as PushProgram, target);
280      var result = container ?? PushProgram.Empty;
281
282      interpreter.CodeStack.SetTop(result);
283      return true;
284    }
285
286    private static PushProgram GetContainer(PushProgram current, Expression target) {
287      if (current == target)
288        return null;
289
290      var subContainer =
291          current.Expressions.Where(e => e.IsProgram)
292              .Select(e => GetContainer(e as PushProgram, target))
293              .Where(e => e != null);
294
295      var execExpandExpressions = subContainer as IList<PushProgram> ?? subContainer.ToList();
296      return execExpandExpressions.Any()
297          ? execExpandExpressions.OrderBy(c => c.Expressions.Count).LastOrDefault()
298          : current.Expressions.Contains(target)
299              ? current
300              : null;
301    }
302  }
303
304  /// <summary>
305  ///     Pushes TRUE on the BOOLEAN stack if the second CODE stack item contains the first CODE stack
306  ///     item anywhere (e.g. in a sub-list).
307  /// </summary>
308  [PushExpression(StackTypes.Code, "CODE.CONTAINS", StackTypes.Boolean)]
309  public class CodeContainsExpression : StatelessExpression {
310    public override bool Eval(IInternalPushInterpreter interpreter) {
311      if (interpreter.CodeStack.Count < 2 ||
312         !interpreter.CodeStack[1].IsProgram)
313        return false;
314
315      var second = (PushProgram)interpreter.CodeStack[1];
316      var first = interpreter.CodeStack.Top;
317      interpreter.CodeStack.Remove(2);
318
319      var contains = second.Expressions.Contains(first);
320      interpreter.BooleanStack.Push(contains);
321      return true;
322    }
323  }
324
325  /// <summary>
326  ///     Pushes the definition associated with the top NAME on the NAME stack (if any) onto the CODE stack.
327  ///     This extracts the definition for inspection/manipulation, rather than for immediate execution (although it
328  ///     may then be executed with a call to CODE.DO or a similar instruction).
329  /// </summary>
330  [PushExpression(StackTypes.Code, "CODE.DEFINITION", StackTypes.Name)]
331  public class CodeDefinitionExpression : StatelessExpression {
332    public override bool Eval(IInternalPushInterpreter interpreter) {
333      if ((interpreter.NameStack.Count == 0) ||
334          !interpreter.CustomExpressions.ContainsKey(interpreter.NameStack.Top)) return false;
335
336      var name = interpreter.NameStack.Pop();
337      var definition = interpreter.CustomExpressions[name];
338
339      interpreter.CodeStack.Push(definition);
340
341      return true;
342    }
343  }
344
345  /// <summary>
346  ///     Pushes a measure of the discrepancy between the top two CODE stack items onto the INTEGER stack. This will be zero
347  ///     if the top two items are equivalent, and will be higher the 'more different' the items are from one another. The calculation
348  ///     is as follows:
349  ///     <list types="bullet">
350  ///         <item>
351  ///             <description>
352  ///                 Construct a list of all of the unique items in both of the lists(where uniqueness is determined by equalp).
353  ///                 Sub-lists and atoms all count as items.
354  ///             </description>
355  ///         </item>
356  ///         <item>
357  ///             <description>Initialize the result to zero.</description>
358  ///         </item>
359  ///         <item>
360  ///             <description>
361  ///                 For each unique item increment the result by the difference between the number of occurrences of the
362  ///                 item in the two pieces of code.
363  ///             </description>
364  ///         </item>
365  ///         <item>
366  ///             <description>Push the result.</description>
367  ///         </item>
368  ///     </list>
369  /// </summary>
370  [PushExpression(StackTypes.Code, "CODE.DISCREPANCY", StackTypes.Integer)]
371  public class CodeDiscrepancyExpression : StatelessExpression {
372    public override bool Eval(IInternalPushInterpreter interpreter) {
373      if (interpreter.CodeStack.Count < 2) return false;
374
375      var second = interpreter.CodeStack[1];
376      var first = interpreter.CodeStack.Top;
377      interpreter.CodeStack.Remove(2);
378
379      var firstItems = new Dictionary<int, int>();
380      var secondItems = new Dictionary<int, int>();
381
382      DetermineUniqueItems(second, secondItems);
383      DetermineUniqueItems(first, firstItems);
384
385      var discrepancy = GetDiscrepancy(firstItems, secondItems);
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(IInternalPushInterpreter 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(IInternalPushInterpreter 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(IInternalPushInterpreter 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(IInternalPushInterpreter 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(IInternalPushInterpreter 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(IInternalPushInterpreter 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[1];
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(IInternalPushInterpreter 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(IInternalPushInterpreter interpreter) {
581      if (interpreter.CodeStack.Count < 2 ||
582         (interpreter.CodeStack.Top.IsProgram && ((PushProgram)interpreter.CodeStack.Top).Depth == interpreter.Configuration.MaxDepth) ||
583         (interpreter.CodeStack[1].IsProgram && ((PushProgram)interpreter.CodeStack[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(IInternalPushInterpreter interpreter) {
608      if (interpreter.CodeStack.Count < 2) return false;
609
610      var first = interpreter.CodeStack[1];
611      var second = interpreter.CodeStack.Top;
612      interpreter.CodeStack.Remove(2);
613
614      var contains = second.IsProgram
615          ? ((PushProgram)second).Expressions.Contains(first)
616          : second.Equals(first);
617
618      interpreter.BooleanStack.Push(contains);
619
620      return true;
621    }
622  }
623
624  /// <summary>
625  ///     Pushes the nth element of the expression on top of the CODE stack (which is coerced to a list first if necessary).
626  ///     If the expression is an empty list then the result is an empty list. N is taken from the INTEGER stack and is taken
627  ///     modulo the length of the expression into which it is indexing.
628  /// </summary>
629  [PushExpression(StackTypes.Code, "CODE.NTH", StackTypes.Integer)]
630  public class CodeNthExpression : StatelessExpression {
631    public override bool Eval(IInternalPushInterpreter interpreter) {
632      if ((interpreter.CodeStack.Count == 0) || (interpreter.IntegerStack.Count == 0)) return false;
633
634      var n = interpreter.IntegerStack.Pop();
635      var expression = interpreter.CodeStack.Top;
636
637      Expression nthExpression;
638
639      if (expression.IsProgram) {
640        var subExpression = (PushProgram)expression;
641
642        if (subExpression.IsEmpty) {
643          nthExpression = PushProgram.Empty;
644        } else {
645          var index = (int)(subExpression.Expressions.Count - 1 - Math.Abs(n % subExpression.Expressions.Count));
646
647          nthExpression = subExpression.Expressions[index];
648        }
649      } else {
650        nthExpression = expression;
651      }
652
653      interpreter.CodeStack.SetTop(nthExpression);
654
655      return true;
656    }
657  }
658
659  /// <summary>
660  ///     Pushes the nth "CDR" (in the Lisp sense) of the expression on top of the CODE stack (which is coerced to a list
661  ///     first if necessary). If the expression is an empty list then the result is an empty list. N is taken from the
662  ///     INTEGER
663  ///     stack and is taken modulo the length of the expression into which it is indexing. A "CDR" of a list is the list
664  ///     without
665  ///     its first element.
666  /// </summary>
667  [PushExpression(StackTypes.Code, "CODE.NTHCDR", StackTypes.Integer)]
668  public class CodeNthCdrExpression : StatelessExpression {
669    public override bool Eval(IInternalPushInterpreter interpreter) {
670      if ((interpreter.CodeStack.Count == 0) || (interpreter.IntegerStack.Count == 0)) return false;
671
672      var n = interpreter.IntegerStack.Pop();
673      var expression = interpreter.CodeStack.Top;
674
675      Expression nthExpression;
676
677      if (expression.IsProgram) {
678        var subExpressions = ((PushProgram)expression).Expressions;
679
680        if (subExpressions.Count < 2) {
681          nthExpression = PushProgram.Empty;
682        } else {
683          var index = (int)(subExpressions.Count - 2 - Math.Abs(n % (subExpressions.Count - 1)));
684
685          nthExpression = subExpressions[index];
686        }
687      } else {
688        nthExpression = expression;
689      }
690
691      interpreter.CodeStack.SetTop(nthExpression);
692
693      return true;
694    }
695  }
696
697  /// <summary>
698  ///     Pushes TRUE onto the BOOLEAN stack if the top item of the CODE stack is an empty list, or FALSE otherwise.
699  /// </summary>
700  [PushExpression(StackTypes.Code, "CODE.NULL", StackTypes.Boolean)]
701  public class CodeNullExpression : StatelessExpression {
702    public override bool Eval(IInternalPushInterpreter interpreter) {
703      if (interpreter.CodeStack.Count == 0) return false;
704
705      var top = interpreter.CodeStack.Pop();
706      var result = top.IsProgram && ((PushProgram)top).IsEmpty;
707      interpreter.BooleanStack.Push(result);
708
709      return true;
710    }
711  }
712
713  /// <summary>
714  ///     Pushes onto the INTEGER stack the position of the second item on the CODE stack within the first item
715  ///     (which is coerced to a list if necessary). Pushes -1 if no match is found.
716  /// </summary>
717  [PushExpression(StackTypes.Code, "CODE.POSITION", StackTypes.Integer)]
718  public class CodePositionExpression : StatelessExpression {
719    public override bool Eval(IInternalPushInterpreter interpreter) {
720      if (interpreter.CodeStack.Count < 2) return false;
721
722      var second = interpreter.CodeStack[1];
723      var first = interpreter.CodeStack.Top;
724      interpreter.CodeStack.Remove(2);
725
726      var position = -1;
727      if (first.IsProgram) {
728        var program = (PushProgram)first;
729        position = program.IndexOf(second);
730      } else if (first.Equals(second)) {
731        position = 0;
732      }
733
734      interpreter.IntegerStack.Push(position);
735
736      return true;
737    }
738  }
739
740  /// <summary>
741  ///     Pushes the number of "points" in the top piece of CODE onto the INTEGER stack. Each instruction, literal,
742  ///     and pair of parentheses counts as a point.
743  /// </summary>
744  [PushExpression(StackTypes.Code, "CODE.SIZE", StackTypes.Integer)]
745  public class CodeSizeExpression : StatelessExpression {
746    public override bool Eval(IInternalPushInterpreter interpreter) {
747      if (interpreter.CodeStack.Count == 0) return false;
748
749      var expression = interpreter.CodeStack.Pop();
750      var points = expression.IsProgram
751          ? ((PushProgram)expression).Expressions.Count
752          : 1;
753
754      interpreter.IntegerStack.Push(points);
755
756      return true;
757    }
758  }
759
760  /// <summary>
761  ///     Pushes the result of substituting the third item on the code stack for the second item in the first item.
762  ///     As of this writing this is implemented only in the Lisp implementation, within which it relies on the Lisp "subst"
763  ///     function. As such, there are several problematic possibilities; for example "dotted-lists" can result in certain
764  ///     cases with empty-list arguments. If any of these problematic possibilities occurs the stack is left unchanged.
765  /// </summary>
766  [PushExpression(StackTypes.Code, "CODE.SUBST")]
767  public class CodeSubstitutionExpression : StatelessExpression {
768    public override bool Eval(IInternalPushInterpreter interpreter) {
769      if ((interpreter.CodeStack.Count < 3) ||
770          (interpreter.CodeStack.Top.GetType() != typeof(PushProgram)))
771        return false;
772
773      var third = interpreter.CodeStack[2];
774      var second = interpreter.CodeStack[1];
775      var first = interpreter.CodeStack.Top as PushProgram;
776      interpreter.CodeStack.Remove(2);
777
778      var firstExpressions = first.Expressions;
779      var newExpressions = interpreter.PoolContainer.ExpressionListPool.Get();
780
781      for (var i = 0; i < firstExpressions.Count; i++) {
782        var expression = firstExpressions[i].Equals(third)
783                           ? second
784                           /* no cloning needed here because first is removed and therefore newExpression is the only container of sub expressions */
785                           : firstExpressions[i];
786
787        newExpressions.Add(expression);
788      }
789
790      var result = PushProgram.Create(interpreter.PoolContainer.PushProgramPool, newExpressions);
791
792      interpreter.CodeStack.SetTop(result);
793
794      return true;
795    }
796  }
797}
Note: See TracBrowser for help on using the repository browser.