Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3136_Structural_GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/InfixExpressionParser.cs @ 18157

Last change on this file since 18157 was 18157, checked in by dpiringe, 2 years ago

#3136

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