Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2665 PooledPushProgram reduces memory usage and increases performance

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