Free cookie consent management tool by TermsFeed Policy Generator

source: addons/HeuristicLab.PushGP/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/CodeExpressions.cs @ 16958

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

#2665 Testet Problems, Testet error functions, Small fixes, Created HL files

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