Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2895_PushGP_GenealogyAnalysis/HeuristicLab.Problems.ProgramSynthesis/Push/Expressions/CodeExpressions.cs @ 16724

Last change on this file since 16724 was 15771, checked in by bburlacu, 7 years ago

#2895: Add solution skeleton for PushGP with genealogy analysis.

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