private bool DoRPN(TParsedTokenListBuilder TokenBuilder, TNameRecordList Names, byte[] RPN, int atPos, int afPos, ref bool HasSubtotal, ref bool HasAggregate) { int tPos = atPos; int fPos = afPos; int ArrayPos = fPos; TTokenOffset TokenOffset = new TTokenOffset(); while (tPos < fPos) { TokenOffset.Add(tPos, TokenBuilder.Count); byte RealToken = RPN[tPos]; ptg BaseToken = TBaseParsedToken.CalcBaseToken((ptg)RealToken); TUnsupportedFormulaErrorType ErrType = TUnsupportedFormulaErrorType.FormulaTooComplex; string ErrName = null; if (!Evaluate(TokenBuilder, TokenOffset, Names, BaseToken, (ptg)RealToken, RPN, ref tPos, ref ArrayPos, ref ErrType, ref ErrName, ref HasSubtotal, ref HasAggregate)) { TokenBuilder.Clear(); return(false); } tPos++; } //while TokenOffset.Add(tPos, TokenBuilder.Count); //eof FixGotosAndMemTokens(TokenBuilder, TokenOffset); return(true); }
private void FixGotosAndMemTokens(TParsedTokenListBuilder TokenBuilder, TTokenOffset TokenOffset) { for (int i = TokenBuilder.Count - 1; i >= 0; i--) { TAttrGotoToken tk = TokenBuilder[i] as TAttrGotoToken; if (tk != null) { tk.PositionOfNextPtg = TokenOffset[tk.PositionOfNextPtg]; continue; } TAttrOptChooseToken ochoose = TokenBuilder[i] as TAttrOptChooseToken; if (ochoose != null) { for (int z = 0; z < ochoose.PositionOfNextPtg.Length; z++) { ochoose.PositionOfNextPtg[z] = TokenOffset[ochoose.PositionOfNextPtg[z]]; } continue; } TSimpleMemToken mem = TokenBuilder[i] as TSimpleMemToken; if (mem != null) { mem.PositionOfNextPtg = TokenOffset[mem.PositionOfNextPtg]; } } }
public virtual void Parse() { LastRefOp = -1; FHasErrors = false; FParsedDataBuilder = new TParsedTokenListBuilder(); try { Go(); FParsedData = FParsedDataBuilder.ToParsedTokenList(); FParsedData.TextLenght = FormulaText.Length; } finally { FParsedDataBuilder = null; } //Try to decode what we encoded //something like "= >" will be encoded nicely, but will crash when decoded /*try * { * FParsedData.ResetPositionToLast(); * FParsedData.Flush(); * if (!FParsedData.Bof())FlxMessages.ThrowException(FlxErr.ErrFormulaInvalid, FormulaText); * } * catch (Exception) * { * FlxMessages.ThrowException(FlxErr.ErrFormulaInvalid,FormulaText); * }*/ }
protected static void AddParsedRefN(TParsedTokenListBuilder TokenBuilder, ptg aId, int Rw1, int grBit1) { bool RowAbs; bool ColAbs; int Row; int Col; GetRelativeRowAndCol(Rw1, grBit1, out RowAbs, out ColAbs, out Row, out Col); Push(TokenBuilder, new TRefNToken(aId, Row, Col, RowAbs, ColAbs, true)); }
private void AddParsedTable(TParsedTokenListBuilder TokenBuilder, int Row, int Col) { if (IsFmlaObject) { Push(TokenBuilder, new TTableObjToken(Row, Col)); } else { Push(TokenBuilder, new TTableToken(Row, Col)); } }
protected static void AddParsedAreaN(TParsedTokenListBuilder TokenBuilder, ptg aId, int Rw1, int Rw2, int grBit1, int grBit2) { bool RowAbs1; bool ColAbs1; int Row1; int Col1; GetRelativeRowAndCol(Rw1, grBit1, out RowAbs1, out ColAbs1, out Row1, out Col1); bool RowAbs2; bool ColAbs2; int Row2; int Col2; GetRelativeRowAndCol(Rw2, grBit2, out RowAbs2, out ColAbs2, out Row2, out Col2); Push(TokenBuilder, new TAreaNToken(aId, Row1, Col1, RowAbs1, ColAbs1, Row2, Col2, RowAbs2, ColAbs2, true)); }
protected override void AddParsedOp(TOperator op) { TBaseParsedToken OpToken = TParsedTokenListBuilder.GetParsedOp(op); if (OpToken is TUnsupportedToken) { DoError(FlxErr.ErrFormulaInvalid, 2); } else { Push(OpToken); } }
protected static void AddParsedSep(TParsedTokenListBuilder TokenBuilder, ptg b) { switch (b) { case ptg.Isect: Push(TokenBuilder, TISectToken.Instance); break; case ptg.Union: Push(TokenBuilder, TUnionToken.Instance); break; case ptg.Range: Push(TokenBuilder, TRangeToken.Instance); break; default: Push(TokenBuilder, new TUnsupportedToken(2, b)); break; } }
public TParsedTokenList ParseRPN(TNameRecordList Names, int aRow, int aCol, byte[] Data, int atPos, int fmlaLen, bool aRelative3dRanges, out bool HasSubtotal, out bool HasAggregate, bool aIsFmlaObject) { HasSubtotal = false; HasAggregate = false; Relative3dRanges = aRelative3dRanges; IsFmlaObject = aIsFmlaObject; TParsedTokenListBuilder TokenBuilder = new TParsedTokenListBuilder(); if (!DoRPN(TokenBuilder, Names, Data, atPos, atPos + fmlaLen, ref HasSubtotal, ref HasAggregate)) { XlsMessages.ThrowException(XlsErr.ErrBadFormula, aRow + 1, aCol + 1, 0); } return(TokenBuilder.ToParsedTokenList()); }
private void AddParsed3dRef(TParsedTokenListBuilder TokenBuilder, ptg RealToken, int ExternSheet, int Rw1, int grBit1) { if (Relative3dRanges) { bool RowAbs1; bool ColAbs1; int Row1; int Col1; GetRelativeRowAndCol(Rw1, grBit1, out RowAbs1, out ColAbs1, out Row1, out Col1); Push(TokenBuilder, new TRef3dNToken(RealToken, ExternSheet, Row1, Col1, RowAbs1, ColAbs1, true)); } else { Push(TokenBuilder, new TRef3dToken(RealToken, ExternSheet, Biff8Utils.ExpandBiff8Row(Rw1), Biff8Utils.ExpandBiff8Col(grBit1 & ColMask), (grBit1 & 0x8000) == 0, (grBit1 & 0x4000) == 0)); } }
private void CheckFutureFunction(TParsedTokenListBuilder TokenBuilder, ref int np, TNameRecordList Names, ref TCellFunctionData fd2, TTokenOffset TokenOffset, ref bool HasAggregate) { //We need to recursively read parameters in back order to find out the name, which is stored as far as possible from the function :( TParsedTokenList ParsedList = TokenBuilder.ToParsedTokenList(); ParsedList.ResetPositionToLast(); for (int i = 0; i < np; i++) //np is +1. But we will move below the name, then inc 1, so we know there isn't a "neutral" token like parent or memarea, intead of the real thing. { ParsedList.Flush(); } ParsedList.MoveBack(); TNameToken bp = ParsedList.ForwardPop() as TNameToken; if (bp is TNameXToken) { return; //This name isn't an internal 2007 name. } if (bp == null) { return; } if (bp.NameIndex <= 0 || bp.NameIndex > Names.Count) { return; } string FunctionName = Names[bp.NameIndex - 1].Name; if (FunctionName.StartsWith("_xlfn.", StringComparison.InvariantCultureIgnoreCase)) { TCellFunctionData fn = TXlsFunction.GetData(FunctionName.Substring("_xlfn.".Length)); if (fn != null) { if (fn.Index == (int)TFutureFunctions.Aggregate) { HasAggregate = true; } fd2 = fn; int tPos = ParsedList.SavePosition(); TokenBuilder.RemoveAt(tPos); TokenOffset.RemoveToken(tPos); np--; } } }
protected override void AddParsedFunction(TCellFunctionData Func, byte ArgCount) { ptg FmlaPtg; if (Func.MinArgCount != Func.MaxArgCount || Func.FutureInXls) { FmlaPtg = GetRealPtg(ptg.FuncVar, Func.ReturnType); } else { FmlaPtg = GetRealPtg(ptg.Func, Func.ReturnType); } TBaseParsedToken FmlaToken = TParsedTokenListBuilder.GetParsedFormula(FmlaPtg, Func, ArgCount); Push(FmlaToken); }
private bool ProcessAttr(TParsedTokenListBuilder TokenBuilder, byte[] RPN, int tPos, ref int AttrLen) { AttrLen = 3; if ((RPN[tPos] & 0x10) == 0x10) { //optimized sum Push(TokenBuilder, TAttrSumToken.Instance); return(true); } if ((RPN[tPos] & 0x40) == 0x40) //Spaces. As we can have volatile spaces, this should go before the check for volatile. { Push(TokenBuilder, new TAttrSpaceToken((FormulaAttr)RPN[tPos + 1], RPN[tPos + 2], (RPN[tPos] & 0x1) == 0x1)); return(true); } if ((RPN[tPos] & 0x1) == 0x1) //volatile { Push(TokenBuilder, TAttrVolatileToken.Instance); return(true); } if ((RPN[tPos] & 0x2) == 0x2) //Optimized if { //It works the same by ignoring this and the optif tokens, but it is slower. //On a normal if you have: [cond], [trueexpr], [falseexpr], [if] //so, it doesn't matter condition is false or true, both truexpr and falsexpr will be evaluated. //On an optimized if you have: [Cond], [AttrIf], [true], [Goto],[false], [Goto], [if] // |---------------|---------> // |--------------------> // //So, you evaluate Cond. Next token is AttrIf, it Cond is true if will continue, if false jump before [false] //Now evaluate true or false, and the goto goes after if. Push(TokenBuilder, new TAttrOptIfToken(tPos + AttrLen + BitOps.GetWord(RPN, tPos + 1)));//temporarily store the stream offset in the token, it will be fixed by FixTokens return(true); } if ((RPN[tPos] & 0x04) == 0x04) { //This works like the optimized if. //For example: // = Choose(Index,3/0,4) //would be parsed as: // Index, AttrChoose, 3, 0, /, Goto, 4, Goto, Choose. // // Then we can get from the index the direct position of the token, and after it there is a goto to the end. AttrLen += (BitOps.GetWord(RPN, tPos + 1) + 1) * 2; Push(TokenBuilder, new TAttrOptChooseToken(GetOptChoose(RPN, tPos + 1, tPos + 3))); //distance goes from start of jump table. return(true); } if ((RPN[tPos] & 0x8) == 0x8 || RPN[tPos] == 0x0) //Goto. 0x0 is not documented, but might happen and means goto. { //Goto can't really be implemented with our engine. //The Excel engine has a stack and travels from left to right. Say you have the RPN 5,7,+: //Excel would push 5 and 7 to the stack, then the "+" would pop those values and push 12. //We use a recursive approach where the stack is implicit, so we start by "+", and it recusively gets //its 2 arguments. Bt if we add a jmp before the 5, we are never going to see it this way. Excel would. //It doesn't really matter since gotos are optimizations and can be ignored. Push(TokenBuilder, new TAttrGotoToken(1 + tPos + AttrLen + BitOps.GetWord(RPN, tPos + 1))); //temporarily store the stream offset in the token, it will be fixed by FixTokens return(true); } //return false; return(true); }
private bool Evaluate(TParsedTokenListBuilder TokenBuilder, TTokenOffset TokenOffset, TNameRecordList Names, ptg BaseToken, ptg RealToken, byte[] RPN, ref int tPos, ref int ArrayPos, ref TUnsupportedFormulaErrorType ErrType, ref string ErrName, ref bool HasSubtotal, ref bool HasAggregate) { switch (BaseToken) { case ptg.Exp: AddParsedExp(TokenBuilder, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 3)); tPos += 4; break; case ptg.Tbl: AddParsedTable(TokenBuilder, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 3)); tPos += 4; break; case ptg.Add: case ptg.Sub: case ptg.Mul: case ptg.Div: case ptg.Power: case ptg.Concat: case ptg.LT: case ptg.LE: case ptg.EQ: case ptg.GE: case ptg.GT: case ptg.NE: case ptg.Uminus: case ptg.Percent: case ptg.Uplus: AddParsedOp(TokenBuilder, (TOperator)BaseToken); break; case ptg.MissArg: AddMissingArg(TokenBuilder); break; case ptg.Isect: AddParsedSep(TokenBuilder, BaseToken); break; case ptg.Union: AddParsedSep(TokenBuilder, BaseToken); break; case ptg.Range: AddParsedSep(TokenBuilder, BaseToken); break; case ptg.Paren: Push(TokenBuilder, TParenToken.Instance); break; case ptg.Str: long sl = 0; string Result = null; StrOps.GetSimpleString(false, RPN, tPos + 1, false, 0, ref Result, ref sl); AddParsed(TokenBuilder, Result); tPos += (int)sl; break; case ptg.Attr: int AttrLen = 0; if (!ProcessAttr(TokenBuilder, RPN, tPos + 1, ref AttrLen)) { return(false); } tPos += AttrLen; break; case ptg.Sheet: return(false); case ptg.EndSheet: return(false); case ptg.Err: AddParsed(TokenBuilder, (TFlxFormulaErrorValue)RPN[tPos + 1]); tPos++; break; case ptg.Bool: AddParsed(TokenBuilder, RPN[tPos + 1] == 1); tPos++; break; case ptg.Int: AddParsed16(TokenBuilder, BitOps.GetWord(RPN, tPos + 1)); tPos += 2; break; case ptg.Num: AddParsed(TokenBuilder, BitConverter.ToDouble(RPN, tPos + 1)); tPos += 8; break; case ptg.Array: Push(TokenBuilder, GetArrayDataToken(RealToken, RPN, ref ArrayPos)); tPos += 7; break; case ptg.Func: bool Result1; int index = BitOps.GetWord(RPN, tPos + 1); TCellFunctionData fd = TXlsFunction.GetData(index, out Result1); if (!Result1) { return(false); } Debug.Assert(fd.MinArgCount == fd.MaxArgCount, "On a fixed formula the min count of arguments should be the same as the max"); AddParsedFormula(TokenBuilder, RealToken, fd, (byte)fd.MinArgCount); tPos += 2; break; case ptg.FuncVar: bool Result2; int index2 = BitOps.GetWord(RPN, tPos + 2); TCellFunctionData fd2 = TXlsFunction.GetData(index2, out Result2); if (!Result2) { return(false); } if (fd2.Index == 344) // SubTotal { HasSubtotal = true; } int np = RPN[tPos + 1] & 0x7F; if (fd2.Index == 255) { CheckFutureFunction(TokenBuilder, ref np, Names, ref fd2, TokenOffset, ref HasAggregate); } AddParsedFormula(TokenBuilder, RealToken, fd2, (byte)np); tPos += 3; break; case ptg.Name: Push(TokenBuilder, new TNameToken(RealToken, BitOps.GetWord(RPN, tPos + 1))); tPos += 4; break; case ptg.NameX: Push(TokenBuilder, new TNameXToken(RealToken, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 3))); tPos += 6; break; case ptg.RefErr: case ptg.Ref: AddParsedRef(TokenBuilder, RealToken, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 2 + 1)); tPos += 4; break; case ptg.RefN: AddParsedRefN(TokenBuilder, RealToken, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 2 + 1)); tPos += 4; break; case ptg.AreaErr: case ptg.Area: AddParsedArea(TokenBuilder, RealToken, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 2 + 1), BitOps.GetWord(RPN, tPos + 4 + 1), BitOps.GetWord(RPN, tPos + 6 + 1)); tPos += 8; break; case ptg.AreaN: AddParsedAreaN(TokenBuilder, RealToken, BitOps.GetWord(RPN, tPos + 1), BitOps.GetWord(RPN, tPos + 2 + 1), BitOps.GetWord(RPN, tPos + 4 + 1), BitOps.GetWord(RPN, tPos + 6 + 1)); tPos += 8; break; case ptg.MemArea: { int ArrayLen; Push(TokenBuilder, new TMemAreaToken(RealToken, GetMemControl(RPN, ArrayPos, out ArrayLen), tPos + 1 + 6 + BitOps.GetWord(RPN, tPos + 1 + 4))); ArrayPos += ArrayLen; tPos += 6; break; //this is an optimization, but we don't need it. } case ptg.MemErr: { Push(TokenBuilder, new TMemErrToken(RealToken, (TFlxFormulaErrorValue)RPN[tPos + 1], tPos + 1 + 6 + BitOps.GetWord(RPN, tPos + 1 + 4))); tPos += 6; break; //this is an optimization, but we don't need it. } case ptg.MemNoMem: { Push(TokenBuilder, new TMemNoMemToken(RealToken, tPos + 1 + 6 + BitOps.GetWord(RPN, tPos + 1 + 4))); tPos += 6; break; //this is an optimization, but we don't need it. } case ptg.MemFunc: { Push(TokenBuilder, new TMemFuncToken(RealToken, tPos + 1 + 2 + BitOps.GetWord(RPN, tPos + 1))); tPos += 2; break; //this is an optimization, but we don't need it. } case ptg.MemAreaN: { Push(TokenBuilder, new TMemAreaNToken(RealToken, tPos + 1 + 2 + BitOps.GetWord(RPN, tPos + 1))); tPos += 2; break; //this is an optimization, but we don't need it. } case ptg.MemNoMemN: { Push(TokenBuilder, new TMemNoMemNToken(RealToken, tPos + 1 + 2 + BitOps.GetWord(RPN, tPos + 1))); tPos += 2; break; //this is an optimization, but we don't need it. } case ptg.Ref3dErr: case ptg.Ref3d: { int grBit1 = BitOps.GetWord(RPN, tPos + 4 + 1); int ExternSheet = BitOps.GetWord(RPN, tPos + 1); int Row1 = BitOps.GetWord(RPN, tPos + 2 + 1); AddParsed3dRef(TokenBuilder, RealToken, ExternSheet, Row1, grBit1); tPos += 6; break; } case ptg.Area3dErr: case ptg.Area3d: { int ExternSheet = BitOps.GetWord(RPN, tPos + 1); int Row1 = BitOps.GetWord(RPN, tPos + 2 + 1); int Row2 = BitOps.GetWord(RPN, tPos + 4 + 1); int grBit1 = BitOps.GetWord(RPN, tPos + 6 + 1); int grBit2 = BitOps.GetWord(RPN, tPos + 8 + 1); AddParsed3dArea(TokenBuilder, RealToken, ExternSheet, Row1, Row2, grBit1, grBit2); tPos += 10; break; } default: return(false); } return(true); }
protected static void AddParsed16(TParsedTokenListBuilder TokenBuilder, int t) { Push(TokenBuilder, new TIntDataToken(t)); }
private static void AddParsedExp(TParsedTokenListBuilder TokenBuilder, int Row, int Col) { Push(TokenBuilder, new TExp_Token(Row, Col)); }
protected static void AddParsed(TParsedTokenListBuilder TokenBuilder, double d) { Push(TokenBuilder, new TNumDataToken(d)); }
protected static void AddParsed(TParsedTokenListBuilder TokenBuilder, TFlxFormulaErrorValue err) { Push(TokenBuilder, new TErrDataToken(err)); }
protected static void AddParsedOp(TParsedTokenListBuilder TokenBuilder, TOperator op) { Push(TokenBuilder, TParsedTokenListBuilder.GetParsedOp(op)); }
protected static void AddParsedArea(TParsedTokenListBuilder TokenBuilder, ptg aId, int Rw1, int Rw2, int grBit1, int grBit2) { Push(TokenBuilder, new TAreaToken(aId, Biff8Utils.ExpandBiff8Row(Rw1), Biff8Utils.ExpandBiff8Col(grBit1 & ColMask), (grBit1 & 0x8000) == 0, (grBit1 & 0x4000) == 0, Biff8Utils.ExpandBiff8Row(Rw2), Biff8Utils.ExpandBiff8Col(grBit2 & ColMask), (grBit2 & 0x8000) == 0, (grBit2 & 0x4000) == 0)); }
protected static void AddParsed(TParsedTokenListBuilder TokenBuilder, string s) { Push(TokenBuilder, new TStrDataToken(s, null, false)); }
protected static void AddMissingArg(TParsedTokenListBuilder TokenBuilder) { Push(TokenBuilder, TMissingArgDataToken.Instance); }
protected static void Push(TParsedTokenListBuilder TokenBuilder, TBaseParsedToken obj) { TokenBuilder.Add(obj); }
protected static void AddParsedFormula(TParsedTokenListBuilder TokenBuilder, ptg FmlaPtg, TCellFunctionData Func, byte ArgCount) { TBaseParsedToken FmlaToken = TParsedTokenListBuilder.GetParsedFormula(FmlaPtg, Func, ArgCount); Push(TokenBuilder, FmlaToken); //Always push unsupported. }
protected static void AddParsed(TParsedTokenListBuilder TokenBuilder, bool b) { Push(TokenBuilder, new TBoolDataToken(b)); }