Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TimeSeries/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeInterpreter.cs @ 7461

Last change on this file since 7461 was 7268, checked in by gkronber, 13 years ago

#1081: merged r7214:7266 from trunk into time series branch.

File size: 22.1 KB
RevLine 
[5571]1#region License Information
2/* HeuristicLab
[7268]3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[5571]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
[7120]24using System.Linq;
[5571]25using HeuristicLab.Common;
26using HeuristicLab.Core;
[6740]27using HeuristicLab.Data;
[5571]28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
[6740]29using HeuristicLab.Parameters;
[5571]30using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
31
32namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
33  [StorableClass]
34  [Item("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.")]
[7120]35  public sealed class SymbolicDataAnalysisExpressionTreeInterpreter : ParameterizedNamedItem,
36    ISymbolicDataAnalysisExpressionTreeInterpreter, ISymbolicTimeSeriesPrognosisExpressionTreeInterpreter {
[5749]37    private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic";
38    #region private classes
[5571]39    private class InterpreterState {
40      private double[] argumentStack;
41      private int argumentStackPointer;
42      private Instruction[] code;
43      private int pc;
44      public int ProgramCounter {
45        get { return pc; }
46        set { pc = value; }
47      }
[5987]48      internal InterpreterState(Instruction[] code, int argumentStackSize) {
[5571]49        this.code = code;
50        this.pc = 0;
[5987]51        if (argumentStackSize > 0) {
52          this.argumentStack = new double[argumentStackSize];
53        }
[5571]54        this.argumentStackPointer = 0;
55      }
56
57      internal void Reset() {
58        this.pc = 0;
59        this.argumentStackPointer = 0;
60      }
61
62      internal Instruction NextInstruction() {
63        return code[pc++];
64      }
65      private void Push(double val) {
66        argumentStack[argumentStackPointer++] = val;
67      }
68      private double Pop() {
69        return argumentStack[--argumentStackPointer];
70      }
71
72      internal void CreateStackFrame(double[] argValues) {
73        // push in reverse order to make indexing easier
74        for (int i = argValues.Length - 1; i >= 0; i--) {
75          argumentStack[argumentStackPointer++] = argValues[i];
76        }
77        Push(argValues.Length);
78      }
79
80      internal void RemoveStackFrame() {
81        int size = (int)Pop();
82        argumentStackPointer -= size;
83      }
84
85      internal double GetStackFrameValue(ushort index) {
86        // layout of stack:
87        // [0]   <- argumentStackPointer
88        // [StackFrameSize = N + 1]
89        // [Arg0] <- argumentStackPointer - 2 - 0
90        // [Arg1] <- argumentStackPointer - 2 - 1
91        // [...]
92        // [ArgN] <- argumentStackPointer - 2 - N
93        // <Begin of stack frame>
94        return argumentStack[argumentStackPointer - index - 2];
95      }
96    }
97    private class OpCodes {
98      public const byte Add = 1;
99      public const byte Sub = 2;
100      public const byte Mul = 3;
101      public const byte Div = 4;
102
103      public const byte Sin = 5;
104      public const byte Cos = 6;
105      public const byte Tan = 7;
106
107      public const byte Log = 8;
108      public const byte Exp = 9;
109
110      public const byte IfThenElse = 10;
111
112      public const byte GT = 11;
113      public const byte LT = 12;
114
115      public const byte AND = 13;
116      public const byte OR = 14;
117      public const byte NOT = 15;
118
119
120      public const byte Average = 16;
121
122      public const byte Call = 17;
123
124      public const byte Variable = 18;
125      public const byte LagVariable = 19;
126      public const byte Constant = 20;
127      public const byte Arg = 21;
128
129      public const byte Power = 22;
130      public const byte Root = 23;
131      public const byte TimeLag = 24;
132      public const byte Integral = 25;
133      public const byte Derivative = 26;
134
135      public const byte VariableCondition = 27;
136    }
[5749]137    #endregion
[5571]138
139    private Dictionary<Type, byte> symbolToOpcode = new Dictionary<Type, byte>() {
140      { typeof(Addition), OpCodes.Add },
141      { typeof(Subtraction), OpCodes.Sub },
142      { typeof(Multiplication), OpCodes.Mul },
143      { typeof(Division), OpCodes.Div },
144      { typeof(Sine), OpCodes.Sin },
145      { typeof(Cosine), OpCodes.Cos },
146      { typeof(Tangent), OpCodes.Tan },
147      { typeof(Logarithm), OpCodes.Log },
148      { typeof(Exponential), OpCodes.Exp },
149      { typeof(IfThenElse), OpCodes.IfThenElse },
150      { typeof(GreaterThan), OpCodes.GT },
151      { typeof(LessThan), OpCodes.LT },
152      { typeof(And), OpCodes.AND },
153      { typeof(Or), OpCodes.OR },
154      { typeof(Not), OpCodes.NOT},
155      { typeof(Average), OpCodes.Average},
156      { typeof(InvokeFunction), OpCodes.Call },
157      { typeof(HeuristicLab.Problems.DataAnalysis.Symbolic.Variable), OpCodes.Variable },
158      { typeof(LaggedVariable), OpCodes.LagVariable },
159      { typeof(Constant), OpCodes.Constant },
160      { typeof(Argument), OpCodes.Arg },
161      { typeof(Power),OpCodes.Power},
162      { typeof(Root),OpCodes.Root},
163      { typeof(TimeLag), OpCodes.TimeLag},
164      { typeof(Integral), OpCodes.Integral},
165      { typeof(Derivative), OpCodes.Derivative},
166      { typeof(VariableCondition),OpCodes.VariableCondition}
167    };
168
169    public override bool CanChangeName {
170      get { return false; }
171    }
172    public override bool CanChangeDescription {
173      get { return false; }
174    }
175
[5749]176    #region parameter properties
177    public IValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter {
178      get { return (IValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
179    }
180    #endregion
181
182    #region properties
183    public BoolValue CheckExpressionsWithIntervalArithmetic {
184      get { return CheckExpressionsWithIntervalArithmeticParameter.Value; }
185      set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; }
186    }
187    #endregion
188
189
[5571]190    [StorableConstructor]
191    private SymbolicDataAnalysisExpressionTreeInterpreter(bool deserializing) : base(deserializing) { }
192    private SymbolicDataAnalysisExpressionTreeInterpreter(SymbolicDataAnalysisExpressionTreeInterpreter original, Cloner cloner) : base(original, cloner) { }
193    public override IDeepCloneable Clone(Cloner cloner) {
194      return new SymbolicDataAnalysisExpressionTreeInterpreter(this, cloner);
195    }
196
197    public SymbolicDataAnalysisExpressionTreeInterpreter()
[5749]198      : base("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.") {
199      Parameters.Add(new ValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
[5571]200    }
201
202    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, IEnumerable<int> rows) {
[7154]203      return GetSymbolicExpressionTreeValues(tree, dataset, new string[] { "#NOTHING#" }, rows);
[7120]204    }
205
[7154]206    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, string[] targetVariables, IEnumerable<int> rows) {
207      return GetSymbolicExpressionTreeValues(tree, dataset, targetVariables, rows, 1);
208    }
209
210
211    // for each row for each horizon for each target variable one value
212    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, string[] targetVariables, IEnumerable<int> rows, int horizon) {
[5749]213      if (CheckExpressionsWithIntervalArithmetic.Value)
214        throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
[5571]215      var compiler = new SymbolicExpressionTreeCompiler();
216      Instruction[] code = compiler.Compile(tree, MapSymbolToOpCode);
[5987]217      int necessaryArgStackSize = 0;
[5571]218      for (int i = 0; i < code.Length; i++) {
219        Instruction instr = code[i];
[6860]220        if (instr.opCode == OpCodes.Variable) {
[5571]221          var variableTreeNode = instr.dynamicNode as VariableTreeNode;
[6740]222          instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
[5897]223          code[i] = instr;
[5571]224        } else if (instr.opCode == OpCodes.LagVariable) {
[6740]225          var laggedVariableTreeNode = instr.dynamicNode as LaggedVariableTreeNode;
226          instr.iArg0 = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
[5897]227          code[i] = instr;
[6860]228        } else if (instr.opCode == OpCodes.VariableCondition) {
[5571]229          var variableConditionTreeNode = instr.dynamicNode as VariableConditionTreeNode;
[6740]230          instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
[5987]231        } else if (instr.opCode == OpCodes.Call) {
232          necessaryArgStackSize += instr.nArguments + 1;
[5571]233        }
234      }
[5987]235      var state = new InterpreterState(code, necessaryArgStackSize);
[5571]236
[7120]237      int nComponents = tree.Root.GetSubtree(0).SubtreeCount;
238      // produce a n-step forecast for each target variable for all rows
239      var cachedPrognosedValues = new Dictionary<string, double[]>();
240      foreach (var targetVariable in targetVariables)
241        cachedPrognosedValues[targetVariable] = new double[horizon];
[5571]242      foreach (var rowEnum in rows) {
243        int row = rowEnum;
[7120]244        foreach (var horizonRow in Enumerable.Range(row, horizon)) {
245          int localRow = horizonRow; // create a local variable for the ref parameter
[7154]246          for (int c = 0; c < nComponents; c++) {
247            var prog = Evaluate(dataset, ref localRow, row - 1, state, cachedPrognosedValues);
248            yield return prog;
249            cachedPrognosedValues[targetVariables[c]][horizonRow - row] = prog;
250          }
[7120]251
252          state.Reset();
253        }
[5571]254      }
255    }
256
[7120]257    private double Evaluate(Dataset dataset, ref int row, int lastObservedRow, InterpreterState state, Dictionary<string, double[]> cachedPrognosedValues) {
[5571]258      Instruction currentInstr = state.NextInstruction();
259      switch (currentInstr.opCode) {
260        case OpCodes.Add: {
[7120]261            double s = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]262            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]263              s += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]264            }
265            return s;
266          }
267        case OpCodes.Sub: {
[7120]268            double s = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]269            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]270              s -= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]271            }
272            if (currentInstr.nArguments == 1) s = -s;
273            return s;
274          }
275        case OpCodes.Mul: {
[7120]276            double p = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]277            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]278              p *= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]279            }
280            return p;
281          }
282        case OpCodes.Div: {
[7120]283            double p = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]284            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]285              p /= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]286            }
287            if (currentInstr.nArguments == 1) p = 1.0 / p;
288            return p;
289          }
290        case OpCodes.Average: {
[7120]291            double sum = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]292            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]293              sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]294            }
295            return sum / currentInstr.nArguments;
296          }
297        case OpCodes.Cos: {
[7120]298            return Math.Cos(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]299          }
300        case OpCodes.Sin: {
[7120]301            return Math.Sin(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]302          }
303        case OpCodes.Tan: {
[7120]304            return Math.Tan(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]305          }
306        case OpCodes.Power: {
[7120]307            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
308            double y = Math.Round(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]309            return Math.Pow(x, y);
310          }
311        case OpCodes.Root: {
[7120]312            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
313            double y = Math.Round(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]314            return Math.Pow(x, 1 / y);
315          }
316        case OpCodes.Exp: {
[7120]317            return Math.Exp(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]318          }
319        case OpCodes.Log: {
[7120]320            return Math.Log(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
[5571]321          }
322        case OpCodes.IfThenElse: {
[7120]323            double condition = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]324            double result;
325            if (condition > 0.0) {
[7120]326              result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); SkipInstructions(state);
[5571]327            } else {
[7120]328              SkipInstructions(state); result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]329            }
330            return result;
331          }
332        case OpCodes.AND: {
[7120]333            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]334            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]335              if (result > 0.0) result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]336              else {
[6732]337                SkipInstructions(state);
[5571]338              }
339            }
[6732]340            return result > 0.0 ? 1.0 : -1.0;
[5571]341          }
342        case OpCodes.OR: {
[7120]343            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]344            for (int i = 1; i < currentInstr.nArguments; i++) {
[7120]345              if (result <= 0.0) result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]346              else {
[6732]347                SkipInstructions(state);
[5571]348              }
349            }
350            return result > 0.0 ? 1.0 : -1.0;
351          }
352        case OpCodes.NOT: {
[7120]353            return Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues) > 0.0 ? -1.0 : 1.0;
[5571]354          }
355        case OpCodes.GT: {
[7120]356            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
357            double y = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]358            if (x > y) return 1.0;
359            else return -1.0;
360          }
361        case OpCodes.LT: {
[7120]362            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
363            double y = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]364            if (x < y) return 1.0;
365            else return -1.0;
366          }
367        case OpCodes.TimeLag: {
368            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
369            row += timeLagTreeNode.Lag;
[7120]370            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]371            row -= timeLagTreeNode.Lag;
372            return result;
373          }
374        case OpCodes.Integral: {
375            int savedPc = state.ProgramCounter;
376            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
377            double sum = 0.0;
378            for (int i = 0; i < Math.Abs(timeLagTreeNode.Lag); i++) {
379              row += Math.Sign(timeLagTreeNode.Lag);
[7120]380              sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]381              state.ProgramCounter = savedPc;
382            }
383            row -= timeLagTreeNode.Lag;
[7120]384            sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]385            return sum;
386          }
387
388        //mkommend: derivate calculation taken from:
389        //http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/
390        //one sided smooth differentiatior, N = 4
391        // y' = 1/8h (f_i + 2f_i-1, -2 f_i-3 - f_i-4)
392        case OpCodes.Derivative: {
393            int savedPc = state.ProgramCounter;
[7120]394            double f_0 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row--;
[5571]395            state.ProgramCounter = savedPc;
[7120]396            double f_1 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row -= 2;
[5571]397            state.ProgramCounter = savedPc;
[7120]398            double f_3 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row--;
[5571]399            state.ProgramCounter = savedPc;
[7120]400            double f_4 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]401            row += 4;
402
403            return (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
404          }
405        case OpCodes.Call: {
406            // evaluate sub-trees
407            double[] argValues = new double[currentInstr.nArguments];
408            for (int i = 0; i < currentInstr.nArguments; i++) {
[7120]409              argValues[i] = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]410            }
411            // push on argument values on stack
412            state.CreateStackFrame(argValues);
413
414            // save the pc
415            int savedPc = state.ProgramCounter;
416            // set pc to start of function 
[6740]417            state.ProgramCounter = (ushort)currentInstr.iArg0;
[5571]418            // evaluate the function
[7120]419            double v = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]420
421            // delete the stack frame
422            state.RemoveStackFrame();
423
424            // restore the pc => evaluation will continue at point after my subtrees 
425            state.ProgramCounter = savedPc;
426            return v;
427          }
428        case OpCodes.Arg: {
[6740]429            return state.GetStackFrameValue((ushort)currentInstr.iArg0);
[5571]430          }
431        case OpCodes.Variable: {
[5923]432            if (row < 0 || row >= dataset.Rows)
433              return double.NaN;
[6740]434            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
[7120]435            if (row <= lastObservedRow || !cachedPrognosedValues.ContainsKey(variableTreeNode.VariableName)) return ((IList<double>)currentInstr.iArg0)[row] * variableTreeNode.Weight;
436            else return cachedPrognosedValues[variableTreeNode.VariableName][row - lastObservedRow - 1] * variableTreeNode.Weight;
[5571]437          }
438        case OpCodes.LagVariable: {
[6740]439            var laggedVariableTreeNode = (LaggedVariableTreeNode)currentInstr.dynamicNode;
[5571]440            int actualRow = row + laggedVariableTreeNode.Lag;
[5923]441            if (actualRow < 0 || actualRow >= dataset.Rows)
442              return double.NaN;
[7120]443            if (actualRow <= lastObservedRow || !cachedPrognosedValues.ContainsKey(laggedVariableTreeNode.VariableName)) return ((IList<double>)currentInstr.iArg0)[actualRow] * laggedVariableTreeNode.Weight;
444            else return cachedPrognosedValues[laggedVariableTreeNode.VariableName][actualRow - lastObservedRow - 1] * laggedVariableTreeNode.Weight;
[5571]445          }
446        case OpCodes.Constant: {
[5897]447            var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode;
448            return constTreeNode.Value;
[5571]449          }
450
451        //mkommend: this symbol uses the logistic function f(x) = 1 / (1 + e^(-alpha * x) )
452        //to determine the relative amounts of the true and false branch see http://en.wikipedia.org/wiki/Logistic_function
453        case OpCodes.VariableCondition: {
[5923]454            if (row < 0 || row >= dataset.Rows)
455              return double.NaN;
[5571]456            var variableConditionTreeNode = (VariableConditionTreeNode)currentInstr.dynamicNode;
[7120]457            double variableValue;
458            if (row <= lastObservedRow || !cachedPrognosedValues.ContainsKey(variableConditionTreeNode.VariableName))
459              variableValue = ((IList<double>)currentInstr.iArg0)[row];
460            else
461              variableValue = cachedPrognosedValues[variableConditionTreeNode.VariableName][row - lastObservedRow - 1];
462
[5897]463            double x = variableValue - variableConditionTreeNode.Threshold;
[5571]464            double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
465
[7120]466            double trueBranch = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
467            double falseBranch = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
[5571]468
469            return trueBranch * p + falseBranch * (1 - p);
470          }
471        default: throw new NotSupportedException();
472      }
473    }
474
475    private byte MapSymbolToOpCode(ISymbolicExpressionTreeNode treeNode) {
[6860]476      if (symbolToOpcode.ContainsKey(treeNode.Symbol.GetType()))
477        return symbolToOpcode[treeNode.Symbol.GetType()];
478      else
479        throw new NotSupportedException("Symbol: " + treeNode.Symbol);
[5571]480    }
481
482    // skips a whole branch
483    private void SkipInstructions(InterpreterState state) {
484      int i = 1;
485      while (i > 0) {
486        i += state.NextInstruction().nArguments;
487        i--;
488      }
489    }
490  }
491}
Note: See TracBrowser for help on using the repository browser.