Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3140_NumberSymbol/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/InfixExpressionParser.cs @ 18118

Last change on this file since 18118 was 18118, checked in by chaider, 2 years ago

#3140

  • Changed to set value of Constant symbol instead direct of ConstantTreeNode in InfixExpressionParser
File size: 24.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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;
24using System.Globalization;
25using System.Linq;
26using System.Text;
27using HeuristicLab.Collections;
28using HeuristicLab.Common;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
30
31namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
32  /// <summary>
33  /// Parses mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3)
34  /// Identifier format (functions or variables): '_' | letter { '_' | letter | digit }
35  /// Variables names and variable values can be set under quotes "" or '' because variable names might contain spaces.
36  ///   Variable = ident | " ident " | ' ident '
37  /// It is also possible to use functions e.g. log("x1") or real-valued constants e.g. 3.1415 .
38  /// Variable names are case sensitive. Function names are not case sensitive.
39  ///
40  ///
41  /// S             = Expr EOF
42  /// Expr          = ['-' | '+'] Term { '+' Term | '-' Term }
43  /// Term          = Fact { '*' Fact | '/' Fact }
44  /// Fact          = SimpleFact [ '^' SimpleFact ]
45  /// SimpleFact    = '(' Expr ')'
46  ///                 | '{' Expr '}'
47  ///                 | 'LAG' '(' varId ',' ['+' | '-' ] number ')
48  ///                 | funcId '(' ArgList ')'
49  ///                 | VarExpr
50  ///                 | number
51  /// ArgList       = Expr { ',' Expr }
52  /// VarExpr       = varId OptFactorPart
53  /// OptFactorPart = [ ('=' varVal | '[' ['+' | '-' ] number {',' ['+' | '-' ] number } ']' ) ]
54  /// varId         =  ident | ' ident ' | " ident "
55  /// varVal        =  ident | ' ident ' | " ident "
56  /// ident         =  '_' | letter { '_' | letter | digit }
57  /// </summary>
58  public sealed class InfixExpressionParser {
59    private enum TokenType { Operator, Identifier, Number, LeftPar, RightPar, LeftBracket, RightBracket, LeftAngleBracket, RightAngleBracket, Comma, Eq, End, NA };
60    private class Token {
61      internal double doubleVal;
62      internal string strVal;
63      internal TokenType TokenType;
64    }
65
66    private class SymbolComparer : IEqualityComparer<ISymbol>, IComparer<ISymbol> {
67      public int Compare(ISymbol x, ISymbol y) {
68        return x.Name.CompareTo(y.Name);
69      }
70
71      public bool Equals(ISymbol x, ISymbol y) {
72        return x.GetType() == y.GetType();
73      }
74
75      public int GetHashCode(ISymbol obj) {
76        return obj.GetType().GetHashCode();
77      }
78    }
79    // format name <-> symbol
80    // the lookup table is also used in the corresponding formatter
81    internal static readonly BidirectionalLookup<string, ISymbol>
82      knownSymbols = new BidirectionalLookup<string, ISymbol>(StringComparer.InvariantCulture, new SymbolComparer());
83
84    private Number number = new Number();
85    private Constant constant = new Constant();
86    private Variable variable = new Variable();
87    private BinaryFactorVariable binaryFactorVar = new BinaryFactorVariable();
88    private FactorVariable factorVar = new FactorVariable();
89
90    private ProgramRootSymbol programRootSymbol = new ProgramRootSymbol();
91    private StartSymbol startSymbol = new StartSymbol();
92
93    static InfixExpressionParser() {
94      // populate bidirectional lookup
95      var dict = new Dictionary<string, ISymbol>
96      {
97        { "+", new Addition()},
98        { "/", new Division()},
99        { "*", new Multiplication()},
100        { "-", new Subtraction()},
101        { "^", new Power() },
102        { "ABS", new Absolute() },
103        { "EXP", new Exponential()},
104        { "LOG", new Logarithm()},
105        { "POW", new Power() },
106        { "ROOT", new Root()},
107        { "SQR", new Square() },
108        { "SQRT", new SquareRoot() },
109        { "CUBE", new Cube() },
110        { "CUBEROOT", new CubeRoot() },
111        { "SIN",new Sine()},
112        { "COS", new Cosine()},
113        { "TAN", new Tangent()},
114        { "TANH", new HyperbolicTangent()},
115        { "AIRYA", new AiryA()},
116        { "AIRYB", new AiryB()},
117        { "BESSEL", new Bessel()},
118        { "COSINT", new CosineIntegral()},
119        { "SININT", new SineIntegral()},
120        { "HYPCOSINT", new HyperbolicCosineIntegral()},
121        { "HYPSININT", new HyperbolicSineIntegral()},
122        { "FRESNELSININT", new FresnelSineIntegral()},
123        { "FRESNELCOSINT", new FresnelCosineIntegral()},
124        { "NORM", new Norm()},
125        { "ERF", new Erf()},
126        { "GAMMA", new Gamma()},
127        { "PSI", new Psi()},
128        { "DAWSON", new Dawson()},
129        { "EXPINT", new ExponentialIntegralEi()},
130        { "AQ", new AnalyticQuotient() },
131        { "MEAN", new Average()},
132        { "IF", new IfThenElse()},
133        { "GT", new GreaterThan()},
134        { "LT", new LessThan()},
135        { "AND", new And()},
136        { "OR", new Or()},
137        { "NOT", new Not()},
138        { "XOR", new Xor()},
139        { "DIFF", new Derivative()},
140        { "LAG", new LaggedVariable() },
141      };
142
143
144      foreach (var kvp in dict) {
145        knownSymbols.Add(kvp.Key, kvp.Value);
146      }
147    }
148
149    public ISymbolicExpressionTree Parse(string str) {
150      ISymbolicExpressionTreeNode root = programRootSymbol.CreateTreeNode();
151      ISymbolicExpressionTreeNode start = startSymbol.CreateTreeNode();
152      var allTokens = GetAllTokens(str).ToArray();
153      ISymbolicExpressionTreeNode mainBranch = ParseS(new Queue<Token>(allTokens));
154
155      // only a main branch was given => insert the main branch into the default tree template
156      root.AddSubtree(start);
157      start.AddSubtree(mainBranch);
158      return new SymbolicExpressionTree(root);
159    }
160
161    private IEnumerable<Token> GetAllTokens(string str) {
162      int pos = 0;
163      while (true) {
164        while (pos < str.Length && Char.IsWhiteSpace(str[pos])) pos++;
165        if (pos >= str.Length) {
166          yield return new Token { TokenType = TokenType.End, strVal = "" };
167          yield break;
168        }
169        if (char.IsDigit(str[pos])) {
170          // read number (=> read until white space or operator or comma)
171          var sb = new StringBuilder();
172          sb.Append(str[pos]);
173          pos++;
174          while (pos < str.Length && !char.IsWhiteSpace(str[pos])
175            && (str[pos] != '+' || str[pos - 1] == 'e' || str[pos - 1] == 'E')     // continue reading exponents
176            && (str[pos] != '-' || str[pos - 1] == 'e' || str[pos - 1] == 'E')
177            && str[pos] != '*'
178            && str[pos] != '/'
179            && str[pos] != '^'
180            && str[pos] != ')'
181            && str[pos] != ']'
182            && str[pos] != '}'
183            && str[pos] != ','
184            && str[pos] != '>') {
185            sb.Append(str[pos]);
186            pos++;
187          }
188          double dblVal;
189          if (double.TryParse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out dblVal))
190            yield return new Token { TokenType = TokenType.Number, strVal = sb.ToString(), doubleVal = dblVal };
191          else yield return new Token { TokenType = TokenType.NA, strVal = sb.ToString() };
192        } else if (char.IsLetter(str[pos]) || str[pos] == '_') {
193          // read ident
194          var sb = new StringBuilder();
195          sb.Append(str[pos]);
196          pos++;
197          while (pos < str.Length &&
198            (char.IsLetter(str[pos]) || str[pos] == '_' || char.IsDigit(str[pos]))) {
199            sb.Append(str[pos]);
200            pos++;
201          }
202          yield return new Token { TokenType = TokenType.Identifier, strVal = sb.ToString() };
203        } else if (str[pos] == '"') {
204          // read to next "
205          pos++;
206          var sb = new StringBuilder();
207          while (pos < str.Length && str[pos] != '"') {
208            sb.Append(str[pos]);
209            pos++;
210          }
211          if (pos < str.Length && str[pos] == '"') {
212            pos++; // skip "
213            yield return new Token { TokenType = TokenType.Identifier, strVal = sb.ToString() };
214          } else
215            yield return new Token { TokenType = TokenType.NA };
216
217        } else if (str[pos] == '\'') {
218          // read to next '
219          pos++;
220          var sb = new StringBuilder();
221          while (pos < str.Length && str[pos] != '\'') {
222            sb.Append(str[pos]);
223            pos++;
224          }
225          if (pos < str.Length && str[pos] == '\'') {
226            pos++; // skip '
227            yield return new Token { TokenType = TokenType.Identifier, strVal = sb.ToString() };
228          } else
229            yield return new Token { TokenType = TokenType.NA };
230        } else if (str[pos] == '+') {
231          pos++;
232          yield return new Token { TokenType = TokenType.Operator, strVal = "+" };
233        } else if (str[pos] == '-') {
234          pos++;
235          yield return new Token { TokenType = TokenType.Operator, strVal = "-" };
236        } else if (str[pos] == '/') {
237          pos++;
238          yield return new Token { TokenType = TokenType.Operator, strVal = "/" };
239        } else if (str[pos] == '*') {
240          pos++;
241          yield return new Token { TokenType = TokenType.Operator, strVal = "*" };
242        } else if (str[pos] == '^') {
243          pos++;
244          yield return new Token { TokenType = TokenType.Operator, strVal = "^" };
245        } else if (str[pos] == '(') {
246          pos++;
247          yield return new Token { TokenType = TokenType.LeftPar, strVal = "(" };
248        } else if (str[pos] == ')') {
249          pos++;
250          yield return new Token { TokenType = TokenType.RightPar, strVal = ")" };
251        } else if (str[pos] == '[') {
252          pos++;
253          yield return new Token { TokenType = TokenType.LeftBracket, strVal = "[" };
254        } else if (str[pos] == ']') {
255          pos++;
256          yield return new Token { TokenType = TokenType.RightBracket, strVal = "]" };
257        } else if (str[pos] == '{') {
258          pos++;
259          yield return new Token { TokenType = TokenType.LeftPar, strVal = "{" };
260        } else if (str[pos] == '}') {
261          pos++;
262          yield return new Token { TokenType = TokenType.RightPar, strVal = "}" };
263        } else if (str[pos] == '=') {
264          pos++;
265          yield return new Token { TokenType = TokenType.Eq, strVal = "=" };
266        } else if (str[pos] == ',') {
267          pos++;
268          yield return new Token { TokenType = TokenType.Comma, strVal = "," };
269        } else if (str[pos] == '<') {
270          pos++;
271          yield return new Token {TokenType = TokenType.LeftAngleBracket, strVal = "<"};
272        } else if (str[pos] == '>') {
273          pos++;
274          yield return new Token {TokenType = TokenType.RightAngleBracket, strVal = ">"};
275        } else {
276          throw new ArgumentException("Invalid character: " + str[pos]);
277        }
278      }
279    }
280    /// S             = Expr EOF
281    private ISymbolicExpressionTreeNode ParseS(Queue<Token> tokens) {
282      var expr = ParseExpr(tokens);
283
284      var endTok = tokens.Dequeue();
285      if (endTok.TokenType != TokenType.End)
286        throw new ArgumentException(string.Format("Expected end of expression (got {0})", endTok.strVal));
287
288      return expr;
289    }
290
291    /// Expr          = ['-' | '+'] Term { '+' Term | '-' Term }
292    private ISymbolicExpressionTreeNode ParseExpr(Queue<Token> tokens) {
293      var next = tokens.Peek();
294      var posTerms = new List<ISymbolicExpressionTreeNode>();
295      var negTerms = new List<ISymbolicExpressionTreeNode>();
296      bool negateFirstTerm = false;
297      if (next.TokenType == TokenType.Operator && (next.strVal == "+" || next.strVal == "-")) {
298        tokens.Dequeue();
299        if (next.strVal == "-")
300          negateFirstTerm = true;
301      }
302      var t = ParseTerm(tokens);
303      if (negateFirstTerm) negTerms.Add(t);
304      else posTerms.Add(t);
305
306      next = tokens.Peek();
307      while (next.strVal == "+" || next.strVal == "-") {
308        switch (next.strVal) {
309          case "+": {
310              tokens.Dequeue();
311              var term = ParseTerm(tokens);
312              posTerms.Add(term);
313              break;
314            }
315          case "-": {
316              tokens.Dequeue();
317              var term = ParseTerm(tokens);
318              negTerms.Add(term);
319              break;
320            }
321        }
322        next = tokens.Peek();
323      }
324
325      var sum = GetSymbol("+").CreateTreeNode();
326      foreach (var posTerm in posTerms) sum.AddSubtree(posTerm);
327      if (negTerms.Any()) {
328        if (negTerms.Count == 1) {
329          var sub = GetSymbol("-").CreateTreeNode();
330          sub.AddSubtree(negTerms.Single());
331          sum.AddSubtree(sub);
332        } else {
333          var sumNeg = GetSymbol("+").CreateTreeNode();
334          foreach (var negTerm in negTerms) sumNeg.AddSubtree(negTerm);
335
336          var constNode = (NumberTreeNode)number.CreateTreeNode();
337          constNode.Value = -1.0;
338          var prod = GetSymbol("*").CreateTreeNode();
339          prod.AddSubtree(constNode);
340          prod.AddSubtree(sumNeg);
341
342          sum.AddSubtree(prod);
343        }
344      }
345      if (sum.SubtreeCount == 1) return sum.Subtrees.First();
346      else return sum;
347    }
348
349    private ISymbol GetSymbol(string tok) {
350      var symb = knownSymbols.GetByFirst(tok).FirstOrDefault();
351      if (symb == null) throw new ArgumentException(string.Format("Unknown token {0} found.", tok));
352      return symb;
353    }
354
355    /// Term          = Fact { '*' Fact | '/' Fact }
356    private ISymbolicExpressionTreeNode ParseTerm(Queue<Token> tokens) {
357      var factors = new List<ISymbolicExpressionTreeNode>();
358      var firstFactor = ParseFact(tokens);
359      factors.Add(firstFactor);
360
361      var next = tokens.Peek();
362      while (next.strVal == "*" || next.strVal == "/") {
363        switch (next.strVal) {
364          case "*": {
365              tokens.Dequeue();
366              var fact = ParseFact(tokens);
367              factors.Add(fact);
368              break;
369            }
370          case "/": {
371              tokens.Dequeue();
372              var invFact = ParseFact(tokens);
373              var divNode = GetSymbol("/").CreateTreeNode(); // 1/x
374              divNode.AddSubtree(invFact);
375              factors.Add(divNode);
376              break;
377            }
378        }
379
380        next = tokens.Peek();
381      }
382      if (factors.Count == 1) return factors.First();
383      else {
384        var prod = GetSymbol("*").CreateTreeNode();
385        foreach (var f in factors) prod.AddSubtree(f);
386        return prod;
387      }
388    }
389
390    // Fact = SimpleFact ['^' SimpleFact]
391    private ISymbolicExpressionTreeNode ParseFact(Queue<Token> tokens) {
392      var expr = ParseSimpleFact(tokens);
393      var next = tokens.Peek();
394      if (next.TokenType == TokenType.Operator && next.strVal == "^") {
395        tokens.Dequeue(); // skip;
396
397        var p = GetSymbol("^").CreateTreeNode();
398        p.AddSubtree(expr);
399        p.AddSubtree(ParseSimpleFact(tokens));
400        expr = p;
401      }
402      return expr;
403    }
404
405
406    /// SimpleFact   = '(' Expr ')'
407    ///                 | '{' Expr '}'
408    ///                 | 'LAG' '(' varId ',' ['+' | '-' ] number ')'
409    ///                 | funcId '(' ArgList ')
410    ///                 | VarExpr
411    ///                 | number
412    /// ArgList       = Expr { ',' Expr }
413    /// VarExpr       = varId OptFactorPart
414    /// OptFactorPart = [ ('=' varVal | '[' ['+' | '-' ] number {',' ['+' | '-' ] number } ']' ) ]
415    /// varId         =  ident | ' ident ' | " ident "
416    /// varVal        =  ident | ' ident ' | " ident "
417    /// ident         =  '_' | letter { '_' | letter | digit }
418    private ISymbolicExpressionTreeNode ParseSimpleFact(Queue<Token> tokens) {
419      var next = tokens.Peek();
420      if (next.TokenType == TokenType.LeftPar) {
421        var initPar = tokens.Dequeue(); // match par type
422        var expr = ParseExpr(tokens);
423        var rPar = tokens.Dequeue();
424        if (rPar.TokenType != TokenType.RightPar)
425          throw new ArgumentException("expected closing parenthesis");
426        if (initPar.strVal == "(" && rPar.strVal == "}")
427          throw new ArgumentException("expected closing )");
428        if (initPar.strVal == "{" && rPar.strVal == ")")
429          throw new ArgumentException("expected closing }");
430        return expr;
431      } else if (next.TokenType == TokenType.Identifier) {
432        var idTok = tokens.Dequeue();
433        if (tokens.Peek().TokenType == TokenType.LeftPar) {
434          // function identifier or LAG
435          var funcId = idTok.strVal.ToUpperInvariant();
436
437          var funcNode = GetSymbol(funcId).CreateTreeNode();
438          var lPar = tokens.Dequeue();
439          if (lPar.TokenType != TokenType.LeftPar)
440            throw new ArgumentException("expected (");
441
442          // handle 'lag' specifically
443          if (funcNode.Symbol is LaggedVariable) {
444            var varId = tokens.Dequeue();
445            if (varId.TokenType != TokenType.Identifier) throw new ArgumentException("Identifier expected. Format for lagged variables: \"lag(x, -1)\"");
446            var comma = tokens.Dequeue();
447            if (comma.TokenType != TokenType.Comma) throw new ArgumentException("',' expected, Format for lagged variables: \"lag(x, -1)\"");
448            double sign = 1.0;
449            if (tokens.Peek().strVal == "+" || tokens.Peek().strVal == "-") {
450              // read sign
451              var signTok = tokens.Dequeue();
452              if (signTok.strVal == "-") sign = -1.0;
453            }
454            var lagToken = tokens.Dequeue();
455            if (lagToken.TokenType != TokenType.Number) throw new ArgumentException("Number expected, Format for lagged variables: \"lag(x, -1)\"");
456            if (!lagToken.doubleVal.IsAlmost(Math.Round(lagToken.doubleVal)))
457              throw new ArgumentException("Time lags must be integer values");
458            var laggedVarNode = funcNode as LaggedVariableTreeNode;
459            laggedVarNode.VariableName = varId.strVal;
460            laggedVarNode.Lag = (int)Math.Round(sign * lagToken.doubleVal);
461            laggedVarNode.Weight = 1.0;
462          } else {
463            // functions
464            var args = ParseArgList(tokens);
465            // check number of arguments
466            if (funcNode.Symbol.MinimumArity > args.Length || funcNode.Symbol.MaximumArity < args.Length) {
467              throw new ArgumentException(string.Format("Symbol {0} requires between {1} and  {2} arguments.", funcId,
468                funcNode.Symbol.MinimumArity, funcNode.Symbol.MaximumArity));
469            }
470            foreach (var arg in args) funcNode.AddSubtree(arg);
471          }
472
473          var rPar = tokens.Dequeue();
474          if (rPar.TokenType != TokenType.RightPar)
475            throw new ArgumentException("expected )");
476
477
478          return funcNode;
479        } else {
480          // variable
481          if (tokens.Peek().TokenType == TokenType.Eq) {
482            // binary factor
483            tokens.Dequeue(); // skip Eq
484            var valTok = tokens.Dequeue();
485            if (valTok.TokenType != TokenType.Identifier) throw new ArgumentException("expected identifier");
486            var binFactorNode = (BinaryFactorVariableTreeNode)binaryFactorVar.CreateTreeNode();
487            binFactorNode.Weight = 1.0;
488            binFactorNode.VariableName = idTok.strVal;
489            binFactorNode.VariableValue = valTok.strVal;
490            return binFactorNode;
491          } else if (tokens.Peek().TokenType == TokenType.LeftBracket) {
492            // factor variable
493            var factorVariableNode = (FactorVariableTreeNode)factorVar.CreateTreeNode();
494            factorVariableNode.VariableName = idTok.strVal;
495
496            tokens.Dequeue(); // skip [
497            var weights = new List<double>();
498            // at least one weight is necessary
499            var sign = 1.0;
500            if (tokens.Peek().TokenType == TokenType.Operator) {
501              var opToken = tokens.Dequeue();
502              if (opToken.strVal == "+") sign = 1.0;
503              else if (opToken.strVal == "-") sign = -1.0;
504              else throw new ArgumentException();
505            }
506            if (tokens.Peek().TokenType != TokenType.Number) throw new ArgumentException("number expected");
507            var weightTok = tokens.Dequeue();
508            weights.Add(sign * weightTok.doubleVal);
509            while (tokens.Peek().TokenType == TokenType.Comma) {
510              // skip comma
511              tokens.Dequeue();
512              if (tokens.Peek().TokenType == TokenType.Operator) {
513                var opToken = tokens.Dequeue();
514                if (opToken.strVal == "+") sign = 1.0;
515                else if (opToken.strVal == "-") sign = -1.0;
516                else throw new ArgumentException();
517              }
518              weightTok = tokens.Dequeue();
519              if (weightTok.TokenType != TokenType.Number) throw new ArgumentException("number expected");
520              weights.Add(sign * weightTok.doubleVal);
521            }
522            var rightBracketToken = tokens.Dequeue();
523            if (rightBracketToken.TokenType != TokenType.RightBracket) throw new ArgumentException("closing bracket ] expected");
524            factorVariableNode.Weights = weights.ToArray();
525            return factorVariableNode;
526          } else {
527            // variable
528            var varNode = (VariableTreeNode)variable.CreateTreeNode();
529            varNode.Weight = 1.0;
530            varNode.VariableName = idTok.strVal;
531            return varNode;
532          }
533        }
534      } else if (next.TokenType == TokenType.LeftAngleBracket) {
535        Token numberTok = null;
536        var leftAngleBracket = tokens.Dequeue();
537        if (leftAngleBracket.TokenType != TokenType.LeftAngleBracket)
538          throw new ArgumentException("opening bracket < expected");
539
540        var idTok = tokens.Dequeue();
541        if (idTok.TokenType != TokenType.Identifier || idTok.strVal.ToLower() != "num")
542          throw new ArgumentException("string 'num' expected");
543
544        if (tokens.Peek().TokenType == TokenType.Eq) {
545          var equalTok = tokens.Dequeue();
546          if (tokens.Peek().TokenType != TokenType.Number)
547            throw new ArgumentException("No value for number specified.");
548
549          numberTok = tokens.Dequeue();
550        }
551
552        var rightAngleBracket = tokens.Dequeue();
553        if (rightAngleBracket.TokenType != TokenType.RightAngleBracket)
554          throw new ArgumentException("closing bracket > expected");
555        var numNode = (NumberTreeNode)number.CreateTreeNode();
556        if (numberTok != null) numNode.Value = numberTok.doubleVal;
557        return numNode;
558      } else if (next.TokenType == TokenType.Number) {
559        var numTok = tokens.Dequeue();
560        var constSy = new Constant {Value = numTok.doubleVal};
561        return constSy.CreateTreeNode();
562        /*var constNode = (ConstantTreeNode)constant.CreateTreeNode();
563        constNode.Value = numTok.doubleVal;
564        return constNode;*/
565      } else {
566        throw new ArgumentException(string.Format("unexpected token in expression {0}", next.strVal));
567      }
568    }
569
570    // ArgList = Expr { ',' Expr }
571    private ISymbolicExpressionTreeNode[] ParseArgList(Queue<Token> tokens) {
572      var exprList = new List<ISymbolicExpressionTreeNode>();
573      exprList.Add(ParseExpr(tokens));
574      while (tokens.Peek().TokenType != TokenType.RightPar) {
575        var comma = tokens.Dequeue();
576        if (comma.TokenType != TokenType.Comma) throw new ArgumentException("expected ',' ");
577        exprList.Add(ParseExpr(tokens));
578      }
579      return exprList.ToArray();
580    }
581  }
582}
Note: See TracBrowser for help on using the repository browser.