公式解析器和估值器都没有使用表达式树,而是使用栈。
FormulaEvaluator公式估值器类
文件:FormulaEvaluator.js
// JScript source code // ----------- FormulaEvaluator ----------- function FormulaEvaluator() { this.DeriveFrom(new Object()); this.set_ClassName("FormulaEvaluator"); this._parser = new FormulaParser(); this._expression = null; this._operands = []; this._tokens = []; // Evaluates an expression and return the result from the operands stack. this._evaluateExpression = function(tokens) { var token; while (tokens.length > 0) { token = tokens.pop(); // Operand? if (token.HasAncestor("OperandBase")) this._operands.push(token); else {// Operator if (token.HasAncestor("OperatorBase")) { switch ($T(token.get_Type())) { case "UnaryType": this._evaluateUnary(token, tokens); break; case "BinaryType": this._evaluateBinary(token, tokens); break; case "FunctionType": this._evaluateFunction(token, tokens); break; default: throw new Exception(this, "_evaluateExpression", "Unsupported operator type:" + $T(token)); } } // if // Other unknown. else throw new Exception(this, "_evaluateExpression", "Unknown token:" + $T(token)); } // if ... else } // while $ASSERT(this._operands.length == 1); if (this._operands.length != 1) throw new Exception(this, "_evaluateExpression", "Failed to evaluate expression: No result"); // result return this._operands.pop(); }; // Evaluate a binary operator. this._evaluateBinary = function(operator, tokens) { this._operands.push(operator.Evaluate(this._operands)); }; // Evaluate an unary operator. this._evaluateUnary = function(operator, tokens) { this._operands.push(operator.Evaluate(this._operands)); }; // Evaluate a function. this._evaluateFunction = function(func, tokens) { var argNum = this._operands.pop().get_Value(); var requiredArgNum = func.get_RequiredArgumentsNum(); // Check arguments number requirements if (requiredArgNum > -1) { if (argNum > requiredArgNum) throw new Exception( this, "_evaluateFunction", "Too many arguments, needs " + requiredArgNum + ", but gets " + argNum); else if (argNum < requiredArgNum) throw new Exception( this, "_evaluateFunction", func.get_FunctionName() + " needs " + requiredArgNum + " arguments, but gets only " + argNum); } var args = []; for(var i = 0; i < argNum; i++) args.push(this._operands.pop()); this._operands.push(func.Evaluate(args.reverse())); }; // Attempt to convert initial FunctinUnknown to registered function. this._convertFunction = function(expression) { var func; for(var i = 0; i < this._expression.Tokens.length; i++) { if (this._expression.Tokens[i].HasAncestor("FunctionBase")) { func = $GetRegisterFunction(this._expression.Tokens[i].get_FunctionName()); if (func != null) this._expression.Tokens[i] = func; } } }; }; FormulaEvaluator.prototype = { // Set formula and initialize the expression. set_Formula: function(formula) { var exp = this._parser.Parse(formula); this._expression = exp; this._convertFunction(this._expression); }, // Access original formula content. get_OriginalFormula: function() { return this._expression.OriginalFormula; }, // Access formated formula. get_Formula: function() { return this._expression.Formula; }, // Evaluate the formula if specified or evaluate original expression. Evaluate: function(formula) { if (formula != null) this.set_Formula(formula); this._operands = []; this._tokens = []; // Always perform evaluation on a copy of expression. // It should be reversed for evaluation. // You can output the expression before and after to compare // the differences. for(var i = this._expression.Tokens.length - 1; i >= 0 ; i--) this._tokens.push(this._expression.Tokens[i]); return this._evaluateExpression(this._tokens); }, // function Evaluate // Unit test. Test: function() { $Debug.WriteLine("=============== " + this.get_ClassName() + " ===================="); var formula = [ "=1", "=1+2", "=1+2+3", "=-1", "=true", "=false", "=1+2-3+4*5+6/7", "=(1+2-3+4)*5", "=Sum(1, 2)", "=Average(1, 2, 3)", "=ADEV(1, 2)", "=1 + 2 + 3 * 4 + 5 / 6 + Sum(7, 8, Average(9, 10, 11))" ]; var evaluator = new FormulaEvaluator(); for(var i = 0; i < formula.length; i++) { try { $Debug.WriteLine("Evaluating:" + formula[i]); evaluator.set_Formula(formula[i]); $Debug.WriteLine("Original Formula:" + evaluator.get_OriginalFormula()); $Debug.WriteLine("Formula:" + evaluator.get_Formula()); var result = evaluator.Evaluate(); $Debug.WriteLine("Result=" + result.get_Value() + "/t[" + $T(result) + "]"); } catch(e) { $Debug.WriteLine("Failed to evaluate " + formula[i] + "/nError:" + e.description); } } // for } // Test };