/// <summary> /// Creates a Node containing the specified Operator and argument. /// This will automatically mark this Node as a TYPE_EXPRESSION /// </summary> /// <param name="_operator">the string representing an operator</param> /// <param name="_arg1">the argument to the specified operator</param> internal Node(Operator op, Node arg1) { this.arg1 = arg1; this.op = op; this.args = 1; this.type = NodeType.Expression; }
/// <summary> /// Parses an infix String expression and creates a parse tree of Node's. /// </summary> /// <remarks> /// This is the heart of the parser, it takes a normal infix expression and creates /// a tree datastructure we can easily recurse when evaluating. /// </remarks> /// <param name="exp">the infix string expression to process</param> /// <returns>A tree datastructure of Node objects representing the expression</returns> private Node ParseInfix(string exp) { int i, ma, len; string farg, sarg, fop; Node tree = null; farg = sarg = fop = ""; ma = i = 0; len = exp.Length; if (len == 0) { throw new ParserException("Wrong number of arguments to operator"); } else if (exp[0] == '(' && ((ma = match(exp, 0)) == (len - 1))) { return (ParseInfix(exp.Substring(1, ma - 1))); } else if (isVariable(exp)) { // If built in constant put in value otherwise the variable if (constants.ContainsKey(exp)) return new Node((double)constants[exp]); else return new Node(exp); } else if (isConstant(exp)) { try { return (new Node(Double.Parse(exp, NumberStyles.Any, culture))); } catch (FormatException) { throw new ParserException("Syntax error-> " + exp + " (not using regional decimal separator?)"); } } while (i < len) { if ((fop = getOp(exp, i)) == null) { farg = arg(null, exp, i); fop = getOp(exp, i + farg.Length); if (fop == null) throw new Exception("Missing operator"); if (isTwoArgOp(fop)) { sarg = arg(fop, exp, i + farg.Length + fop.Length); if (sarg.Equals("")) throw new Exception("Wrong number of arguments to operator " + fop); tree = new Node(operators[fop], ParseInfix(farg), ParseInfix(sarg)); i += farg.Length + fop.Length + sarg.Length; } else { if (farg.Equals("")) throw new Exception("Wrong number of arguments to operator " + fop); tree = new Node(operators[fop], ParseInfix(farg)); i += farg.Length + fop.Length; } } else { if (isTwoArgOp(fop)) { farg = arg(fop, exp, i + fop.Length); if (farg.Equals("")) throw new Exception("Wrong number of arguments to operator " + fop); if (tree == null) { if (fop.Equals("+") || fop.Equals("-")) { tree = new Node(0D); } else throw new Exception("Wrong number of arguments to operator " + fop); } tree = new Node(operators[fop], tree, ParseInfix(farg)); i += farg.Length + fop.Length; } else { farg = arg(fop, exp, i + fop.Length); if (farg.Equals("")) throw new Exception("Wrong number of arguments to operator " + fop); tree = new Node(operators[fop], ParseInfix(farg)); i += farg.Length + fop.Length; } } } return tree; }
/// <summary> /// Retrieves the value associated with the variable in the node /// </summary> /// <param name="node">node of type NodeType.Variable</param> /// <returns>value associated with variable</returns> private Value GetValue(Node node) { Value value; if (node.Value != null && node.Value.Type != ValueType.Invalid) { // Value already in node value = node.Value; } else { if (!htbl.TryGetValue(node.Variable, out value)) { throw new ParserException("No value associated with " + node.Variable); } // Save value reference in the node for next time node.Value = value; } return value; }
/// <summary> /// Traverses and evaluates the datastructure /// </summary> /// <param name="tree">tree as a structure of Node(s)</param> /// <returns>resulting double value</returns> internal double EvalTree(Node tree) { string tmp; if (tree.Type == NodeType.Value) { return (tree.Value.ToDouble()); } else if (tree.Type == NodeType.Variable) { // get value associated with variable Value value = GetValue(tree); if (value.Type == ValueType.String && cachedExpressions.ContainsKey(value.ToString(culture))) // cached expression { return EvalExpression(cachedExpressions[value.ToString(culture)]); } else if (value.Type == ValueType.Constant) // constant value { return value.ToDouble(culture); } else { // apparently a nested expression, parse and cache htbl[tree.Variable] = new StringValue() { Value = value.ToString(culture) }; tmp = value.ToString(culture); Expression expression = treeParser.Parse(tmp); cachedExpressions.Add(tmp, expression); return EvalExpression(expression); } } return tree.Operator.Eval(this, tree.FirstArgument, tree.SecondArgument); }