public ParseNode(Ptg token, ParseNode[] children) { _token = token; _children = children; _isIf = IsIf(token); int tokenCount = 1; for (int i = 0; i < children.Length; i++) { tokenCount += children[i].TokenCount; } if (_isIf) { // there will be 2 or 3 extra tAttr Tokens according To whether the false param is present tokenCount += children.Length; } _tokenCount = tokenCount; }
/** * Traverses the supplied formula parse tree, calling <tt>Ptg.SetClass()</tt> for each non-base * Token To Set its operand class. */ public void TransformFormula(ParseNode rootNode) { byte rootNodeOperandClass; switch (_formulaType) { case FormulaType.CELL: rootNodeOperandClass = Ptg.CLASS_VALUE; break; case FormulaType.DATAVALIDATION_LIST: rootNodeOperandClass = Ptg.CLASS_REF; break; default: throw new Exception("Incomplete code - formula type (" + _formulaType + ") not supported yet"); } TransformNode(rootNode, rootNodeOperandClass, false); }
/** * Traverses the supplied formula parse tree, calling <c>Ptg.SetClass()</c> for each non-base * Token To Set its operand class. */ public void TransformFormula(ParseNode rootNode) { byte rootNodeOperandClass; switch (_formulaType) { case FormulaType.Cell: rootNodeOperandClass = Ptg.CLASS_VALUE; break; case FormulaType.Array: rootNodeOperandClass = Ptg.CLASS_ARRAY; break; case FormulaType.NamedRange: case FormulaType.DataValidationList: rootNodeOperandClass = Ptg.CLASS_REF; break; default: throw new Exception("Incomplete code - formula type (" + _formulaType + ") not supported yet"); } TransformNode(rootNode, rootNodeOperandClass, false); }
//{--------------------------------------------------------------} //{ Parse and Translate an Assignment Statement } /* procedure Assignment; var Name: string[8]; begin Name := GetName; Match('='); Expression; end; **/ /** * API call To execute the parsing of the formula * */ private void Parse() { pointer = 0; GetChar(); _rootNode = UnionExpression(); if (pointer <= formulaLength) { String msg = "Unused input [" + formulaString.Substring(pointer - 1) + "] after attempting To Parse the formula [" + formulaString + "]"; throw new FormulaParseException(msg); } }
/** Parse and Translate an Expression */ private ParseNode AdditiveExpression() { ParseNode result = Term(); while (true) { SkipWhite(); Ptg operator1; switch (look) { case '+': Match('+'); operator1 = AddPtg.instance; break; case '-': Match('-'); operator1 = SubtractPtg.instance; break; default: return result; // finished with Additive expression } ParseNode other = Term(); result = new ParseNode(operator1, result, other); } }
private ParseNode ConcatExpression() { ParseNode result = AdditiveExpression(); while (true) { SkipWhite(); if (look != '&') { break; // finished with concat expression } Match('&'); ParseNode other = AdditiveExpression(); result = new ParseNode(ConcatPtg.instance, result, other); } return result; }
/** * @param callerForceArrayFlag <c>true</c> if one of the current node's parents is a * function Ptg which Has been Changed from default 'V' To 'A' type (due To requirements on * the function return value). */ private void TransformNode(ParseNode node, byte desiredOperandClass, bool callerForceArrayFlag) { Ptg token = node.GetToken(); ParseNode[] children = node.GetChildren(); bool IsSimpleValueFunc = IsSimpleValueFunction(token); if (IsSimpleValueFunc) { bool localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY; for (int i = 0; i < children.Length; i++) { TransformNode(children[i], desiredOperandClass, localForceArray); } SetSimpleValueFuncClass((AbstractFunctionPtg)token, desiredOperandClass, callerForceArrayFlag); return; } if (IsSingleArgSum(token)) { // Need to process the argument of SUM with transformFunctionNode below // so make a dummy FuncVarPtg for that call. token = FuncVarPtg.SUM; // Note - the tAttrSum token (node.getToken()) is a base // token so does not need to have its operand class set } if (token is ValueOperatorPtg || token is ControlPtg || token is MemFuncPtg || token is MemAreaPtg || token is UnionPtg) { // Value Operator Ptgs and Control are base Tokens, so Token will be unchanged // but any child nodes are processed according To desiredOperandClass and callerForceArrayFlag // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1" // All direct operands of value operators that are initially 'R' type will // be converted To 'V' type. byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass; for (int i = 0; i < children.Length; i++) { TransformNode(children[i], localDesiredOperandClass, callerForceArrayFlag); } return; } if (token is AbstractFunctionPtg) { TransformFunctionNode((AbstractFunctionPtg)token, children, desiredOperandClass, callerForceArrayFlag); return; } if (children.Length > 0) { //if (token == RangePtg.instance) if (token is OperationPtg) { // TODO is any Token transformation required under the various ref operators? return; } throw new InvalidOperationException("Node should not have any children"); } if (token.IsBaseToken) { // nothing To do return; } token.PtgClass = (TransformClass(token.PtgClass, desiredOperandClass, callerForceArrayFlag)); }
private void TransformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children, byte desiredOperandClass, bool callerForceArrayFlag) { bool localForceArrayFlag; byte defaultReturnOperandClass = afp.DefaultOperandClass; if (callerForceArrayFlag) { switch (defaultReturnOperandClass) { case Ptg.CLASS_REF: if (desiredOperandClass == Ptg.CLASS_REF) { afp.PtgClass = (Ptg.CLASS_REF); } else { afp.PtgClass = (Ptg.CLASS_ARRAY); } localForceArrayFlag = false; break; case Ptg.CLASS_ARRAY: afp.PtgClass = (Ptg.CLASS_ARRAY); localForceArrayFlag = false; break; case Ptg.CLASS_VALUE: afp.PtgClass = (Ptg.CLASS_ARRAY); localForceArrayFlag = true; break; default: throw new InvalidOperationException("Unexpected operand class (" + defaultReturnOperandClass + ")"); } } else { if (defaultReturnOperandClass == desiredOperandClass) { localForceArrayFlag = false; // an alternative would have been To for non-base Ptgs To Set their operand class // from their default, but this would require the call in many subclasses because // the default OC is not known until the end of the constructor afp.PtgClass = (defaultReturnOperandClass); } else { switch (desiredOperandClass) { case Ptg.CLASS_VALUE: // always OK To Set functions To return 'value' afp.PtgClass = (Ptg.CLASS_VALUE); localForceArrayFlag = false; break; case Ptg.CLASS_ARRAY: switch (defaultReturnOperandClass) { case Ptg.CLASS_REF: afp.PtgClass = (Ptg.CLASS_REF); // afp.SetClass(Ptg.CLASS_ARRAY); break; case Ptg.CLASS_VALUE: afp.PtgClass = (Ptg.CLASS_ARRAY); break; default: throw new InvalidOperationException("Unexpected operand class (" + defaultReturnOperandClass + ")"); } localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE); break; case Ptg.CLASS_REF: switch (defaultReturnOperandClass) { case Ptg.CLASS_ARRAY: afp.PtgClass=(Ptg.CLASS_ARRAY); break; case Ptg.CLASS_VALUE: afp.PtgClass=(Ptg.CLASS_VALUE); break; default: throw new InvalidOperationException("Unexpected operand class (" + defaultReturnOperandClass + ")"); } localForceArrayFlag = false; break; default: throw new InvalidOperationException("Unexpected operand class (" + desiredOperandClass + ")"); } } } for (int i = 0; i < children.Length; i++) { ParseNode child = children[i]; byte paramOperandClass = afp.GetParameterClass(i); TransformNode(child, paramOperandClass, localForceArrayFlag); } }
/** * @param currentParsePosition used to format a potential error message */ private void CheckValidRangeOperand(String sideName, int currentParsePosition, ParseNode pn) { if (!IsValidRangeOperand(pn)) { throw new FormulaParseException("The " + sideName + " of the range operator ':' at position " + currentParsePosition + " is not a proper reference."); } }
private static ParseNode AugmentWithMemPtg(ParseNode root) { Ptg memPtg; if (NeedsMemFunc(root)) { memPtg = new MemFuncPtg(root.EncodedSize); } else { memPtg = new MemAreaPtg(root.EncodedSize); } return new ParseNode(memPtg, root); }
/** * Generates the variable Function ptg for the formula. * * For IF Formulas, Additional PTGs are Added To the Tokens * @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code> * @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, Ptg 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); System.Array.Copy(args, 0, allArgs, 1, numArgs); return new ParseNode(FuncVarPtg.Create(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; if (funcIx == FunctionMetadataRegistry.FUNCTION_INDEX_SUM && args.Length == 1) { // Excel encodes the sum of a single argument as tAttrSum // POI does the same for consistency, but this is not critical return new ParseNode(AttrPtg.GetSumSingle(), args); // The code below would encode tFuncVar(SUM) which seems to do no harm } ValidateNumArgs(args.Length, fm); AbstractFunctionPtg retval; if (IsVarArgs) { retval = FuncVarPtg.Create(name, (byte)numArgs); } else { retval = FuncPtg.Create(funcIx); } return new ParseNode(retval, args); }
/** * Collects the array of <c>Ptg</c> Tokens for the specified tree. */ public static Ptg[] ToTokenArray(ParseNode rootNode) { TokenCollector temp = new TokenCollector(rootNode.TokenCount); rootNode.CollectPtgs(temp); return temp.GetResult(); }
public ParseNode(Ptg token, ParseNode child0, ParseNode child1) : this(token, new ParseNode[] { child0, child1, }) { }
/** * Generates the variable Function ptg for the formula. * * For IF Formulas, Additional PTGs are Added To the Tokens * @param name a {@link NamePtg} or {@link NameXPtg} or <c>null</c> * @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, Ptg 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); System.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); }
private ParseNode UnionExpression() { ParseNode result = ComparisonExpression(); bool hasUnions = false; while (true) { SkipWhite(); switch (look) { case ',': GetChar(); hasUnions = true; ParseNode other = ComparisonExpression(); result = new ParseNode(UnionPtg.instance, result, other); continue; } if (hasUnions) { return AugmentWithMemPtg(result); } return result; } }
/** Parse and Translate a Math Factor */ private ParseNode PowerFactor() { ParseNode result = PercentFactor(); while (true) { SkipWhite(); if (look != '^') { return result; } Match('^'); ParseNode other = PercentFactor(); result = new ParseNode(PowerPtg.instance, result, other); } }
private ParseNode ParseRangeExpression() { ParseNode result = ParseRangeable(); bool hasRange = false; while (look == ':') { int pos = pointer; GetChar(); ParseNode nextPart = ParseRangeable(); // Note - no range simplification here. An expr like "A1:B2:C3:D4:E5" should be // grouped into area ref pairs like: "(A1:B2):(C3:D4):E5" // Furthermore, Excel doesn't seem to simplify // expressions like "Sheet1!A1:Sheet1:B2" into "Sheet1!A1:B2" CheckValidRangeOperand("LHS", pos, result); CheckValidRangeOperand("RHS", pos, nextPart); ParseNode[] children = { result, nextPart, }; result = new ParseNode(RangePtg.instance, children); hasRange = true; } if (hasRange) { return AugmentWithMemPtg(result); } return result; }
private ParseNode PercentFactor() { ParseNode result = ParseSimpleFactor(); while (true) { SkipWhite(); if (look != '%') { return result; } Match('%'); result = new ParseNode(PercentPtg.instance, result); } }
/** * From OOO doc: "Whenever one operand of the reference subexpression is a function, * a defined name, a 3D reference, or an external reference (and no error occurs), * a tMemFunc token is used" * */ private static bool NeedsMemFunc(ParseNode root) { Ptg token = root.GetToken(); if (token is AbstractFunctionPtg) { return true; } if (token is IExternSheetReferenceToken) { // 3D refs return true; } if (token is NamePtg || token is NameXPtg) { // 3D refs return true; } if (token is OperationPtg || token is ParenthesisPtg) { // expect RangePtg, but perhaps also UnionPtg, IntersectionPtg etc foreach (ParseNode child in root.GetChildren()) { if (NeedsMemFunc(child)) { return true; } } return false; } if (token is OperandPtg) { return false; } if (token is OperationPtg) { return true; } return false; }
/** Parse and Translate a Math Term */ private ParseNode Term() { ParseNode result = PowerFactor(); while (true) { SkipWhite(); Ptg operator1; switch (look) { case '*': Match('*'); operator1 = MultiplyPtg.instance; break; case '/': Match('/'); operator1 = DividePtg.instance; break; default: return result; // finished with Term } ParseNode other = PowerFactor(); result = new ParseNode(operator1, result, other); } }
/** * @return false if sub-expression represented the specified ParseNode definitely * cannot appear on either side of the range (':') operator */ private bool IsValidRangeOperand(ParseNode a) { Ptg tkn = a.GetToken(); // Note - order is important for these instance-of checks if (tkn is OperandPtg) { // notably cell refs and area refs return true; } // next 2 are special cases of OperationPtg if (tkn is AbstractFunctionPtg) { AbstractFunctionPtg afp = (AbstractFunctionPtg)tkn; byte returnClass = afp.DefaultOperandClass; return Ptg.CLASS_REF == returnClass; } if (tkn is ValueOperatorPtg) { return false; } if (tkn is OperationPtg) { return true; } // one special case of ControlPtg if (tkn is ParenthesisPtg) { // parenthesis Ptg should have only one child return IsValidRangeOperand(a.GetChildren()[0]); } // one special case of ScalarConstantPtg if (tkn == ErrPtg.REF_INVALID) { return true; } // All other ControlPtgs and ScalarConstantPtgs cannot be used with ':' return false; }
private ParseNode ComparisonExpression() { ParseNode result = ConcatExpression(); while (true) { SkipWhite(); switch (look) { case '=': case '>': case '<': Ptg comparisonToken = GetComparisonToken(); ParseNode other = ConcatExpression(); result = new ParseNode(comparisonToken, result, other); continue; } return result; // finished with predicate expression } }
/** * @param callerForceArrayFlag <c>true</c> if one of the current node's parents is a * function Ptg which Has been Changed from default 'V' To 'A' type (due To requirements on * the function return value). */ private void TransformNode(ParseNode node, byte desiredOperandClass, bool callerForceArrayFlag) { Ptg token = node.GetToken(); ParseNode[] children = node.GetChildren(); bool IsSimpleValueFunc = IsSimpleValueFunction(token); if (IsSimpleValueFunc) { bool localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY; for (int i = 0; i < children.Length; i++) { TransformNode(children[i], desiredOperandClass, localForceArray); } SetSimpleValueFuncClass((AbstractFunctionPtg)token, desiredOperandClass, callerForceArrayFlag); return; } if (token is ValueOperatorPtg || token is ControlPtg || token is MemFuncPtg || token is MemAreaPtg || token is UnionPtg) { // Value Operator Ptgs and Control are base Tokens, so Token will be unchanged // but any child nodes are processed according To desiredOperandClass and callerForceArrayFlag // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1" // All direct operands of value operators that are initially 'R' type will // be converted To 'V' type. byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass; for (int i = 0; i < children.Length; i++) { TransformNode(children[i], localDesiredOperandClass, callerForceArrayFlag); } return; } if (token is AbstractFunctionPtg) { TransformFunctionNode((AbstractFunctionPtg)token, children, desiredOperandClass, callerForceArrayFlag); return; } if (children.Length > 0) { //if (token == RangePtg.instance) if(token is OperationPtg) { // TODO is any Token transformation required under the various ref operators? return; } throw new InvalidOperationException("Node should not have any children"); } if (token.IsBaseToken) { // nothing To do return; } token.PtgClass = (TransformClass(token.PtgClass, desiredOperandClass, callerForceArrayFlag)); }
private void TransformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children, byte desiredOperandClass, bool callerForceArrayFlag) { bool localForceArrayFlag; byte defaultReturnOperandClass = afp.DefaultOperandClass; if (callerForceArrayFlag) { switch (defaultReturnOperandClass) { case Ptg.CLASS_REF: if (desiredOperandClass == Ptg.CLASS_REF) { afp.PtgClass = (Ptg.CLASS_REF); } else { afp.PtgClass = (Ptg.CLASS_ARRAY); } localForceArrayFlag = false; break; case Ptg.CLASS_ARRAY: afp.PtgClass = (Ptg.CLASS_ARRAY); localForceArrayFlag = false; break; case Ptg.CLASS_VALUE: afp.PtgClass = (Ptg.CLASS_ARRAY); localForceArrayFlag = true; break; default: throw new InvalidOperationException("Unexpected operand class (" + defaultReturnOperandClass + ")"); } } else { if (defaultReturnOperandClass == desiredOperandClass) { localForceArrayFlag = false; // an alternative would have been To for non-base Ptgs To Set their operand class // from their default, but this would require the call in many subclasses because // the default OC is not known until the end of the constructor afp.PtgClass = (defaultReturnOperandClass); } else { switch (desiredOperandClass) { case Ptg.CLASS_VALUE: // always OK To Set functions To return 'value' afp.PtgClass = (Ptg.CLASS_VALUE); localForceArrayFlag = false; break; case Ptg.CLASS_ARRAY: switch (defaultReturnOperandClass) { case Ptg.CLASS_REF: afp.PtgClass = (Ptg.CLASS_REF); // afp.SetClass(Ptg.CLASS_ARRAY); break; case Ptg.CLASS_VALUE: afp.PtgClass = (Ptg.CLASS_ARRAY); break; default: throw new InvalidOperationException("Unexpected operand class (" + defaultReturnOperandClass + ")"); } localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE); break; case Ptg.CLASS_REF: switch (defaultReturnOperandClass) { case Ptg.CLASS_ARRAY: afp.PtgClass = (Ptg.CLASS_ARRAY); break; case Ptg.CLASS_VALUE: afp.PtgClass = (Ptg.CLASS_VALUE); break; default: throw new InvalidOperationException("Unexpected operand class (" + defaultReturnOperandClass + ")"); } localForceArrayFlag = false; break; default: throw new InvalidOperationException("Unexpected operand class (" + desiredOperandClass + ")"); } } } for (int i = 0; i < children.Length; i++) { ParseNode child = children[i]; byte paramOperandClass = afp.GetParameterClass(i); TransformNode(child, paramOperandClass, localForceArrayFlag); } }