Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2665 Added IsNoop to Expression, Made Expressions storable, Fixed Debugger, Fixed and improved problem data and result visualisation, Added custom ErcOption view, Added problem difficulty to problem data name

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