Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 18175 was 18175, checked in by gkronber, 2 years ago

#3140: merged r18136:18138,r18153,r18165:18174 from trunk to branch

File size: 25.8 KB
RevLine 
[14024]1#region License Information
2/* HeuristicLab
[17180]3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[14024]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;
[14350]28using HeuristicLab.Common;
[14024]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 }
[14826]35  /// Variables names and variable values can be set under quotes "" or '' because variable names might contain spaces.
36  ///   Variable = ident | " ident " | ' ident '
[14024]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.
[14826]39  ///
40  ///
41  /// S             = Expr EOF
[18175]42  /// Expr          = Term { '+' Term | '-' Term }
[14826]43  /// Term          = Fact { '*' Fact | '/' Fact }
[16356]44  /// Fact          = SimpleFact [ '^' SimpleFact ]
45  /// SimpleFact    = '(' Expr ')'
46  ///                 | '{' Expr '}'
47  ///                 | 'LAG' '(' varId ',' ['+' | '-' ] number ')
48  ///                 | funcId '(' ArgList ')'
49  ///                 | VarExpr
50  ///                 | number
[18175]51  ///                 | ['+' | '-'] SimpleFact
[14826]52  /// ArgList       = Expr { ',' Expr }
53  /// VarExpr       = varId OptFactorPart
[18175]54  /// OptFactorPart = [ ('=' varVal | '[' ['+' | '-' ]  number {',' ['+' | '-' ]  number } ']' ) ]
[14826]55  /// varId         =  ident | ' ident ' | " ident "
56  /// varVal        =  ident | ' ident ' | " ident "
57  /// ident         =  '_' | letter { '_' | letter | digit }
[14024]58  /// </summary>
[14026]59  public sealed class InfixExpressionParser {
[18093]60    private enum TokenType { Operator, Identifier, Number, LeftPar, RightPar, LeftBracket, RightBracket, LeftAngleBracket, RightAngleBracket, Comma, Eq, End, NA };
[14024]61    private class Token {
62      internal double doubleVal;
63      internal string strVal;
64      internal TokenType TokenType;
65    }
66
[17902]67    private class SymbolComparer : IEqualityComparer<ISymbol>, IComparer<ISymbol> {
[14024]68      public int Compare(ISymbol x, ISymbol y) {
69        return x.Name.CompareTo(y.Name);
70      }
71
72      public bool Equals(ISymbol x, ISymbol y) {
[17902]73        return x.GetType() == y.GetType();
[14024]74      }
75
76      public int GetHashCode(ISymbol obj) {
[17902]77        return obj.GetType().GetHashCode();
[14024]78      }
79    }
80    // format name <-> symbol
81    // the lookup table is also used in the corresponding formatter
82    internal static readonly BidirectionalLookup<string, ISymbol>
[17902]83      knownSymbols = new BidirectionalLookup<string, ISymbol>(StringComparer.InvariantCulture, new SymbolComparer());
[14024]84
[18100]85    private Number number = new Number();
[18175]86    private Constant minusOne = new Constant() { Value = -1 };
[14024]87    private Variable variable = new Variable();
[14826]88    private BinaryFactorVariable binaryFactorVar = new BinaryFactorVariable();
89    private FactorVariable factorVar = new FactorVariable();
[14024]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()},
[16356]102        { "^", new Power() },
103        { "ABS", new Absolute() },
[14024]104        { "EXP", new Exponential()},
105        { "LOG", new Logarithm()},
[16359]106        { "POW", new Power() },
[14024]107        { "ROOT", new Root()},
108        { "SQR", new Square() },
109        { "SQRT", new SquareRoot() },
[16356]110        { "CUBE", new Cube() },
111        { "CUBEROOT", new CubeRoot() },
[14024]112        { "SIN",new Sine()},
113        { "COS", new Cosine()},
114        { "TAN", new Tangent()},
[16656]115        { "TANH", new HyperbolicTangent()},
[14024]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()},
[16360]131        { "AQ", new AnalyticQuotient() },
[14024]132        { "MEAN", new Average()},
133        { "IF", new IfThenElse()},
[14347]134        { "GT", new GreaterThan()},
135        { "LT", new LessThan()},
[14024]136        { "AND", new And()},
137        { "OR", new Or()},
138        { "NOT", new Not()},
139        { "XOR", new Xor()},
140        { "DIFF", new Derivative()},
[14350]141        { "LAG", new LaggedVariable() },
[14024]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) {
[18175]165        while (pos < str.Length && char.IsWhiteSpace(str[pos])) pos++;
[14024]166        if (pos >= str.Length) {
167          yield return new Token { TokenType = TokenType.End, strVal = "" };
168          yield break;
169        }
170        if (char.IsDigit(str[pos])) {
[18175]171          // read number (=> read until white space or other symbol)
[14024]172          var sb = new StringBuilder();
173          sb.Append(str[pos]);
174          pos++;
175          while (pos < str.Length && !char.IsWhiteSpace(str[pos])
[14319]176            && (str[pos] != '+' || str[pos - 1] == 'e' || str[pos - 1] == 'E')     // continue reading exponents
[14024]177            && (str[pos] != '-' || str[pos - 1] == 'e' || str[pos - 1] == 'E')
[14319]178            && str[pos] != '*'
[14024]179            && str[pos] != '/'
[16356]180            && str[pos] != '^'
[14347]181            && str[pos] != ')'
[14826]182            && str[pos] != ']'
[16356]183            && str[pos] != '}'
[18100]184            && str[pos] != ','
185            && str[pos] != '>') {
[14024]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 = "*" };
[16356]243        } else if (str[pos] == '^') {
244          pos++;
245          yield return new Token { TokenType = TokenType.Operator, strVal = "^" };
[14024]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 = ")" };
[14826]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 = "]" };
[16356]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 = "}" };
[14826]264        } else if (str[pos] == '=') {
265          pos++;
266          yield return new Token { TokenType = TokenType.Eq, strVal = "=" };
[14347]267        } else if (str[pos] == ',') {
268          pos++;
269          yield return new Token { TokenType = TokenType.Comma, strVal = "," };
[18093]270        } else if (str[pos] == '<') {
271          pos++;
[18175]272          yield return new Token { TokenType = TokenType.LeftAngleBracket, strVal = "<" };
[18093]273        } else if (str[pos] == '>') {
274          pos++;
[18175]275          yield return new Token { TokenType = TokenType.RightAngleBracket, strVal = ">" };
[14319]276        } else {
277          throw new ArgumentException("Invalid character: " + str[pos]);
[14024]278        }
279      }
280    }
[14826]281    /// S             = Expr EOF
[14024]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    }
[14826]291
[18175]292    /// Expr          = Term { '+' Term | '-' Term }
[14024]293    private ISymbolicExpressionTreeNode ParseExpr(Queue<Token> tokens) {
[18175]294      // build tree from bottom to top and left to right
295      // a + b - c => ((a + b) - c)
296      // a - b - c => ((a - b) - c)
297      // and then flatten as far as possible
298      var left = ParseTerm(tokens);
299
[14024]300      var next = tokens.Peek();
301      while (next.strVal == "+" || next.strVal == "-") {
302        switch (next.strVal) {
303          case "+": {
304              tokens.Dequeue();
[18175]305              var right = ParseTerm(tokens);
306              var op = GetSymbol("+").CreateTreeNode();
307              op.AddSubtree(left);
308              op.AddSubtree(right);
309              left = op;
[14024]310              break;
311            }
312          case "-": {
313              tokens.Dequeue();
[18175]314              var right = ParseTerm(tokens);
315              var op = GetSymbol("-").CreateTreeNode();
316              op.AddSubtree(left);
317              op.AddSubtree(right);
318              left = op;
[14024]319              break;
320            }
321        }
322        next = tokens.Peek();
323      }
324
[18175]325      FoldLeftRecursive(left);
326      return left;
[14024]327    }
328
329    private ISymbol GetSymbol(string tok) {
330      var symb = knownSymbols.GetByFirst(tok).FirstOrDefault();
331      if (symb == null) throw new ArgumentException(string.Format("Unknown token {0} found.", tok));
332      return symb;
333    }
334
[14826]335    /// Term          = Fact { '*' Fact | '/' Fact }
[14024]336    private ISymbolicExpressionTreeNode ParseTerm(Queue<Token> tokens) {
[18175]337      // build tree from bottom to top and left to right
338      // a / b * c => ((a / b) * c)
339      // a / b / c => ((a / b) / c)
340      // and then flatten as far as possible
[14024]341
[18175]342      var left = ParseFact(tokens);
343
[14024]344      var next = tokens.Peek();
345      while (next.strVal == "*" || next.strVal == "/") {
346        switch (next.strVal) {
347          case "*": {
348              tokens.Dequeue();
[18175]349              var right = ParseFact(tokens);
350
351              var op = GetSymbol("*").CreateTreeNode();
352              op.AddSubtree(left);
353              op.AddSubtree(right);
354              left = op;
[14024]355              break;
356            }
357          case "/": {
358              tokens.Dequeue();
[18175]359              var right = ParseFact(tokens);
360              var op = GetSymbol("/").CreateTreeNode();
361              op.AddSubtree(left);
362              op.AddSubtree(right);
363              left = op;
[14024]364              break;
365            }
366        }
367
368        next = tokens.Peek();
369      }
[18175]370      // remove all nodes where the child op is the same as the parent op
371      // (a * b) * c) => (a * b * c)
372      // (a / b) / c) => (a / b / c)
373
374      FoldLeftRecursive(left);
375      return left;
376    }
377
378    private void FoldLeftRecursive(ISymbolicExpressionTreeNode parent) {
379      if (parent.SubtreeCount > 1) {
380        var child = parent.GetSubtree(0);
381        FoldLeftRecursive(child);
382        if (parent.Symbol == child.Symbol && IsAssociative(parent.Symbol)) {
383          parent.RemoveSubtree(0);
384          for (int i = 0; i < child.SubtreeCount; i++) {
385            parent.InsertSubtree(i, child.GetSubtree(i));
386          }
387        }
[14024]388      }
389    }
390
[16356]391    // Fact = SimpleFact ['^' SimpleFact]
392    private ISymbolicExpressionTreeNode ParseFact(Queue<Token> tokens) {
393      var expr = ParseSimpleFact(tokens);
394      var next = tokens.Peek();
395      if (next.TokenType == TokenType.Operator && next.strVal == "^") {
396        tokens.Dequeue(); // skip;
397
398        var p = GetSymbol("^").CreateTreeNode();
399        p.AddSubtree(expr);
400        p.AddSubtree(ParseSimpleFact(tokens));
401        expr = p;
402      }
403      return expr;
404    }
405
406
407    /// SimpleFact   = '(' Expr ')'
408    ///                 | '{' Expr '}'
409    ///                 | 'LAG' '(' varId ',' ['+' | '-' ] number ')'
410    ///                 | funcId '(' ArgList ')
411    ///                 | VarExpr
[18175]412    ///                 | '<' 'num' [ '=' [ '+' | '-' ] number ] '>'
[16356]413    ///                 | number
[18175]414    ///                 | ['+' | '-' ] SimpleFact
[14826]415    /// ArgList       = Expr { ',' Expr }
416    /// VarExpr       = varId OptFactorPart
417    /// OptFactorPart = [ ('=' varVal | '[' ['+' | '-' ] number {',' ['+' | '-' ] number } ']' ) ]
418    /// varId         =  ident | ' ident ' | " ident "
419    /// varVal        =  ident | ' ident ' | " ident "
420    /// ident         =  '_' | letter { '_' | letter | digit }
[16356]421    private ISymbolicExpressionTreeNode ParseSimpleFact(Queue<Token> tokens) {
[14024]422      var next = tokens.Peek();
423      if (next.TokenType == TokenType.LeftPar) {
[16356]424        var initPar = tokens.Dequeue(); // match par type
[14024]425        var expr = ParseExpr(tokens);
426        var rPar = tokens.Dequeue();
427        if (rPar.TokenType != TokenType.RightPar)
[16356]428          throw new ArgumentException("expected closing parenthesis");
429        if (initPar.strVal == "(" && rPar.strVal == "}")
430          throw new ArgumentException("expected closing )");
431        if (initPar.strVal == "{" && rPar.strVal == ")")
432          throw new ArgumentException("expected closing }");
[14024]433        return expr;
434      } else if (next.TokenType == TokenType.Identifier) {
435        var idTok = tokens.Dequeue();
436        if (tokens.Peek().TokenType == TokenType.LeftPar) {
[14826]437          // function identifier or LAG
[18175]438          return ParseFunctionOrLaggedVariable(tokens, idTok);
439        } else {
440          return ParseVariable(tokens, idTok);
441        }
442      } else if (next.TokenType == TokenType.LeftAngleBracket) {
443        // '<' 'num' [ '=' ['+'|'-'] number ] '>'
444        return ParseNumber(tokens);
445      } else if (next.TokenType == TokenType.Operator && (next.strVal == "-" || next.strVal == "+")) {
446        // ['+' | '-' ] SimpleFact
447        if (tokens.Dequeue().strVal == "-") {
448          var arg = ParseSimpleFact(tokens);
449          if (arg is NumberTreeNode numNode) {
450            numNode.Value *= -1;
451            return numNode;
452          } else if (arg is ConstantTreeNode constNode) {
453            var constSy = new Constant { Value = -constNode.Value };
454            return constSy.CreateTreeNode();
455          } else if (arg is VariableTreeNode varNode) {
456            varNode.Weight *= -1;
457            return varNode;
[14350]458          } else {
[18175]459            var mul = GetSymbol("*").CreateTreeNode();
460            var neg = minusOne.CreateTreeNode();
461            mul.AddSubtree(neg);
462            mul.AddSubtree(arg);
463            return mul;
[14350]464          }
[18175]465        } else {
466          return ParseSimpleFact(tokens);
467        }
468      } else if (next.TokenType == TokenType.Number) {
469        // number
470        var numTok = tokens.Dequeue();
471        var constSy = new Constant { Value = numTok.doubleVal };
472        return constSy.CreateTreeNode();
473      } else {
474        throw new ArgumentException(string.Format("unexpected token in expression {0}", next.strVal));
475      }
476    }
[14347]477
[18175]478    private ISymbolicExpressionTreeNode ParseNumber(Queue<Token> tokens) {
479      // we distinguish parameters and constants. The values of parameters can be changed.
480      // a parameter is written as '<' 'num' [ '=' ['+'|'-'] number ] '>' with an optional initialization
481      Token numberTok = null;
482      var leftAngleBracket = tokens.Dequeue();
483      if (leftAngleBracket.TokenType != TokenType.LeftAngleBracket)
484        throw new ArgumentException("opening bracket < expected");
[14024]485
[18175]486      var idTok = tokens.Dequeue();
487      if (idTok.TokenType != TokenType.Identifier || idTok.strVal.ToLower() != "num")
488        throw new ArgumentException("string 'num' expected");
[16356]489
[18175]490      var numNode = (NumberTreeNode)number.CreateTreeNode();
[14826]491
[18175]492      if (tokens.Peek().TokenType == TokenType.Eq) {
493        tokens.Dequeue(); // skip "="
494        var next = tokens.Peek();
495        if (next.strVal != "+" && next.strVal != "-" && next.TokenType != TokenType.Number)
496          throw new ArgumentException("Expected '+', '-' or number.");
497
498        var sign = 1.0;
499        if (next.strVal == "+" || next.strVal == "-") {
500          if (tokens.Dequeue().strVal == "-") sign = -1.0;
[14024]501        }
[18175]502        if (tokens.Peek().TokenType != TokenType.Number) {
503          throw new ArgumentException("Expected number.");
504        }
505        numberTok = tokens.Dequeue();
506        numNode.Value = sign * numberTok.doubleVal;
507      }
[18093]508
[18175]509      var rightAngleBracket = tokens.Dequeue();
510      if (rightAngleBracket.TokenType != TokenType.RightAngleBracket)
511        throw new ArgumentException("closing bracket > expected");
[18100]512
[18175]513      return numNode;
514    }
[18100]515
[18175]516    private ISymbolicExpressionTreeNode ParseVariable(Queue<Token> tokens, Token idTok) {
517      // variable
518      if (tokens.Peek().TokenType == TokenType.Eq) {
519        // binary factor
520        tokens.Dequeue(); // skip Eq
521        var valTok = tokens.Dequeue();
522        if (valTok.TokenType != TokenType.Identifier) throw new ArgumentException("expected identifier");
523        var binFactorNode = (BinaryFactorVariableTreeNode)binaryFactorVar.CreateTreeNode();
524        binFactorNode.Weight = 1.0;
525        binFactorNode.VariableName = idTok.strVal;
526        binFactorNode.VariableValue = valTok.strVal;
527        return binFactorNode;
528      } else if (tokens.Peek().TokenType == TokenType.LeftBracket) {
529        // factor variable
530        var factorVariableNode = (FactorVariableTreeNode)factorVar.CreateTreeNode();
531        factorVariableNode.VariableName = idTok.strVal;
532
533        tokens.Dequeue(); // skip [
534        var weights = new List<double>();
535        // at least one weight is necessary
536        var sign = 1.0;
537        if (tokens.Peek().TokenType == TokenType.Operator) {
538          var opToken = tokens.Dequeue();
539          if (opToken.strVal == "+") sign = 1.0;
540          else if (opToken.strVal == "-") sign = -1.0;
541          else throw new ArgumentException();
[18100]542        }
[18175]543        if (tokens.Peek().TokenType != TokenType.Number) throw new ArgumentException("number expected");
544        var weightTok = tokens.Dequeue();
545        weights.Add(sign * weightTok.doubleVal);
546        while (tokens.Peek().TokenType == TokenType.Comma) {
547          // skip comma
548          tokens.Dequeue();
549          if (tokens.Peek().TokenType == TokenType.Operator) {
550            var opToken = tokens.Dequeue();
551            if (opToken.strVal == "+") sign = 1.0;
552            else if (opToken.strVal == "-") sign = -1.0;
553            else throw new ArgumentException();
554          }
555          weightTok = tokens.Dequeue();
556          if (weightTok.TokenType != TokenType.Number) throw new ArgumentException("number expected");
557          weights.Add(sign * weightTok.doubleVal);
558        }
559        var rightBracketToken = tokens.Dequeue();
560        if (rightBracketToken.TokenType != TokenType.RightBracket) throw new ArgumentException("closing bracket ] expected");
561        factorVariableNode.Weights = weights.ToArray();
562        return factorVariableNode;
563      } else {
564        // variable
565        var varNode = (VariableTreeNode)variable.CreateTreeNode();
566        varNode.Weight = 1.0;
567        varNode.VariableName = idTok.strVal;
568        return varNode;
569      }
570    }
[18100]571
[18175]572    private ISymbolicExpressionTreeNode ParseFunctionOrLaggedVariable(Queue<Token> tokens, Token idTok) {
573      var funcId = idTok.strVal.ToUpperInvariant();
574
575      var funcNode = GetSymbol(funcId).CreateTreeNode();
576      var lPar = tokens.Dequeue();
577      if (lPar.TokenType != TokenType.LeftPar)
578        throw new ArgumentException("expected (");
579
580      // handle 'lag' specifically
581      if (funcNode.Symbol is LaggedVariable) {
582        ParseLaggedVariable(tokens, funcNode);
[14024]583      } else {
[18175]584        // functions
585        var args = ParseArgList(tokens);
586        // check number of arguments
587        if (funcNode.Symbol.MinimumArity > args.Length || funcNode.Symbol.MaximumArity < args.Length) {
588          throw new ArgumentException(string.Format("Symbol {0} requires between {1} and  {2} arguments.", funcId,
589            funcNode.Symbol.MinimumArity, funcNode.Symbol.MaximumArity));
590        }
591        foreach (var arg in args) funcNode.AddSubtree(arg);
[14024]592      }
[18175]593
594      var rPar = tokens.Dequeue();
595      if (rPar.TokenType != TokenType.RightPar)
596        throw new ArgumentException("expected )");
597
598
599      return funcNode;
[14024]600    }
[14347]601
[18175]602    private static void ParseLaggedVariable(Queue<Token> tokens, ISymbolicExpressionTreeNode funcNode) {
603      var varId = tokens.Dequeue();
604      if (varId.TokenType != TokenType.Identifier) throw new ArgumentException("Identifier expected. Format for lagged variables: \"lag(x, -1)\"");
605      var comma = tokens.Dequeue();
606      if (comma.TokenType != TokenType.Comma) throw new ArgumentException("',' expected, Format for lagged variables: \"lag(x, -1)\"");
607      double sign = 1.0;
608      if (tokens.Peek().strVal == "+" || tokens.Peek().strVal == "-") {
609        // read sign
610        var signTok = tokens.Dequeue();
611        if (signTok.strVal == "-") sign = -1.0;
612      }
613      var lagToken = tokens.Dequeue();
614      if (lagToken.TokenType != TokenType.Number) throw new ArgumentException("Number expected, Format for lagged variables: \"lag(x, -1)\"");
615      if (!lagToken.doubleVal.IsAlmost(Math.Round(lagToken.doubleVal)))
616        throw new ArgumentException("Time lags must be integer values");
617      var laggedVarNode = funcNode as LaggedVariableTreeNode;
618      laggedVarNode.VariableName = varId.strVal;
619      laggedVarNode.Lag = (int)Math.Round(sign * lagToken.doubleVal);
620      laggedVarNode.Weight = 1.0;
621    }
622
[14347]623    // ArgList = Expr { ',' Expr }
624    private ISymbolicExpressionTreeNode[] ParseArgList(Queue<Token> tokens) {
625      var exprList = new List<ISymbolicExpressionTreeNode>();
626      exprList.Add(ParseExpr(tokens));
627      while (tokens.Peek().TokenType != TokenType.RightPar) {
628        var comma = tokens.Dequeue();
629        if (comma.TokenType != TokenType.Comma) throw new ArgumentException("expected ',' ");
630        exprList.Add(ParseExpr(tokens));
631      }
632      return exprList.ToArray();
633    }
[18175]634
635    private bool IsAssociative(ISymbol sy) {
636      return sy == GetSymbol("+") || sy == GetSymbol("-") ||
637             sy == GetSymbol("*") || sy == GetSymbol("/") ||
638             sy == GetSymbol("AND") || sy == GetSymbol("OR") || sy == GetSymbol("XOR");
639    }
[14024]640  }
641}
Note: See TracBrowser for help on using the repository browser.