Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2665 Fixed bias 0 issue, PushExpressionFrequencyAnalyzer, Fixed probability for ERC settings, Fixed enable/disable instructions, Added expression descriptions

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