/** * Generates the variable function ptg for the formula. * <p> * For IF Formulas, Additional PTGs are Added To the Tokens * @param name * @param numArgs * @return Ptg a null Is returned if we're in an IF formula, it needs extreme manipulation and Is handled in this function */ private ParseNode GetFunction(String name, NamePtg namePtg, ParseNode[] args) { FunctionMetadata fm = FunctionMetadataRegistry.GetFunctionByName(name.ToUpper()); int numArgs = args.Length; if (fm == null) { if (namePtg == null) { throw new InvalidOperationException("NamePtg must be supplied for external functions"); } // must be external function ParseNode[] allArgs = new ParseNode[numArgs + 1]; allArgs[0] = new ParseNode(namePtg); Array.Copy(args, 0, allArgs, 1, numArgs); return new ParseNode(new FuncVarPtg(name, (byte)(numArgs + 1)), allArgs); } if (namePtg != null) { throw new InvalidOperationException("NamePtg no applicable To internal functions"); } bool IsVarArgs = !fm.HasFixedArgsLength; int funcIx = fm.Index; ValidateNumArgs(args.Length, fm); AbstractFunctionPtg retval; if (IsVarArgs) { retval = new FuncVarPtg(name, (byte)numArgs); } else { retval = new FuncPtg(funcIx); } return new ParseNode(retval, args); }
// visibility raised for testing /* package */ public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) { Stack<ValueEval> stack = new Stack<ValueEval>(); for (int i = 0, iSize = ptgs.Length; i < iSize; i++) { // since we don't know how To handle these yet :( Ptg ptg = ptgs[i]; if (ptg is AttrPtg) { AttrPtg attrPtg = (AttrPtg)ptg; if (attrPtg.IsSum) { // Excel prefers To encode 'SUM()' as a tAttr Token, but this evaluator // expects the equivalent function Token byte nArgs = 1; // tAttrSum always Has 1 parameter ptg = new FuncVarPtg("SUM", nArgs); } if (attrPtg.IsOptimizedChoose) { ValueEval arg0 = stack.Pop(); int[] jumpTable = attrPtg.JumpTable; int dist; int nChoices = jumpTable.Length; try { int switchIndex = Choose.EvaluateFirstArg(arg0, ec.RowIndex, ec.ColumnIndex); if (switchIndex<1 || switchIndex > nChoices) { stack.Push(ErrorEval.VALUE_INVALID); dist = attrPtg.ChooseFuncOffset + 4; // +4 for tFuncFar(CHOOSE) } else { dist = jumpTable[switchIndex-1]; } } catch (EvaluationException e) { stack.Push(e.GetErrorEval()); dist = attrPtg.ChooseFuncOffset + 4; // +4 for tFuncFar(CHOOSE) } // Encoded dist for tAttrChoose includes size of jump table, but // countTokensToBeSkipped() does not (it counts whole tokens). dist -= nChoices*2+2; // subtract jump table size i+= CountTokensToBeSkipped(ptgs, i, dist); continue; } if (attrPtg.IsOptimizedIf) { ValueEval arg0 = stack.Pop(); bool evaluatedPredicate; try { evaluatedPredicate = If.EvaluateFirstArg(arg0, ec.RowIndex, ec.ColumnIndex); } catch (EvaluationException e) { stack.Push(e.GetErrorEval()); int dist = attrPtg.Data; i+= CountTokensToBeSkipped(ptgs, i, dist); attrPtg = (AttrPtg) ptgs[i]; dist = attrPtg.Data+1; i+= CountTokensToBeSkipped(ptgs, i, dist); continue; } if (evaluatedPredicate) { // nothing to skip - true param folows } else { int dist = attrPtg.Data; i+= CountTokensToBeSkipped(ptgs, i, dist); Ptg nextPtg = ptgs[i+1]; if (ptgs[i] is AttrPtg && nextPtg is FuncVarPtg) { // this is an if statement without a false param (as opposed to MissingArgPtg as the false param) i++; stack.Push(BoolEval.FALSE); } } continue; } if (attrPtg.IsSkip) { int dist = attrPtg.Data+1; i+= CountTokensToBeSkipped(ptgs, i, dist); if (stack.Peek() == MissingArgEval.instance) { stack.Pop(); stack.Push(BlankEval.instance); } continue; } } if (ptg is ControlPtg) { // skip Parentheses, Attr, etc continue; } if (ptg is MemFuncPtg) { // can ignore, rest of Tokens for this expression are in OK RPN order continue; } if (ptg is MemErrPtg) { continue; } ValueEval opResult; if (ptg is OperationPtg) { OperationPtg optg = (OperationPtg)ptg; if (optg is UnionPtg) { continue; } int numops = optg.NumberOfOperands; ValueEval[] ops = new ValueEval[numops]; // storing the ops in reverse order since they are popping for (int j = numops - 1; j >= 0; j--) { ValueEval p = (ValueEval)stack.Pop(); ops[j] = p; } // logDebug("Invoke " + operation + " (nAgs=" + numops + ")"); opResult = OperationEvaluatorFactory.Evaluate(optg, ops, ec); } else { opResult = GetEvalForPtg(ptg, ec); } if (opResult == null) { throw new Exception("Evaluation result must not be null"); } // logDebug("push " + opResult); stack.Push(opResult); } ValueEval value = ((ValueEval)stack.Pop()); if (stack.Count != 0) { throw new InvalidOperationException("evaluation stack not empty"); } value = DereferenceValue(value, ec.RowIndex, ec.ColumnIndex); if (value == BlankEval.instance) { // Note Excel behaviour here. A blank value is converted To zero. return NumberEval.ZERO; // Formulas _never_ evaluate To blank. If a formula appears To have evaluated To // blank, the actual value is empty string. This can be verified with ISBLANK(). } return value; }
// visibility raised for testing /* package */ public ValueEval EvaluateFormula(int sheetIndex, int srcRowNum, int srcColNum, Ptg[] ptgs, EvaluationTracker tracker) { Stack stack = new Stack(); for (int i = 0, iSize = ptgs.Length; i < iSize; i++) { // since we don't know how To handle these yet :( Ptg ptg = ptgs[i]; if (ptg is AttrPtg) { AttrPtg attrPtg = (AttrPtg)ptg; if (attrPtg.IsSum) { // Excel prefers To encode 'SUM()' as a tAttr Token, but this evaluator // expects the equivalent function Token byte nArgs = 1; // tAttrSum always Has 1 parameter ptg = new FuncVarPtg("SUM", nArgs); } } if (ptg is ControlPtg) { // skip Parentheses, Attr, etc continue; } if (ptg is MemFuncPtg) { // can ignore, rest of Tokens for this expression are in OK RPN order continue; } if (ptg is MemErrPtg) { continue; } Eval opResult; if (ptg is OperationPtg) { OperationPtg optg = (OperationPtg)ptg; if (optg is UnionPtg) { continue; } OperationEval operation = OperationEvaluatorFactory.Create(optg); int numops = operation.NumberOfOperands; Eval[] ops = new Eval[numops]; // storing the ops in reverse order since they are popping for (int j = numops - 1; j >= 0; j--) { Eval p = (Eval)stack.Pop(); ops[j] = p; } // logDebug("Invoke " + operation + " (nAgs=" + numops + ")"); opResult = InvokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum); if (opResult == MissingArgEval.instance) { opResult = BlankEval.INSTANCE; } } else { opResult = GetEvalForPtg(ptg, sheetIndex, tracker); } if (opResult == null) { throw new Exception("Evaluation result must not be null"); } // logDebug("push " + opResult); stack.Push(opResult); } ValueEval value = ((ValueEval)stack.Pop()); if (stack.Count != 0) { throw new InvalidOperationException("evaluation stack not empty"); } value = DereferenceValue(value, srcRowNum, srcColNum); if (value == BlankEval.INSTANCE) { // Note Excel behaviour here. A blank value Is converted To zero. return NumberEval.ZERO; // Formulas _never_ evaluate To blank. If a formula appears To have evaluated To // blank, the actual value Is empty string. This can be verified with ISBLANK(). } return value; }