From 81093f647f6f4e8fa4250fcb30e84ee8c5641cb2 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sat, 24 May 2014 17:11:05 +0000 Subject: [PATCH] fpspreadsheet: Improved reading of rpn formulas: replace "monster case instruction" for function parameter count by lookup table. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3088 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpspreadsheet.pas | 226 ++++++++++++--- components/fpspreadsheet/tests/rpntests.inc | 2 +- components/fpspreadsheet/xlscommon.pas | 298 ++++++++++---------- 3 files changed, 336 insertions(+), 190 deletions(-) diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 90449eb3a..c1471d119 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -61,19 +61,24 @@ type See http://www.techonthenet.com/excel/formulas/ for an explanation of meaning and parameters of each formula - NOTE: When adding or rearranging items make sure to keep the TokenID table - in TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID, unit xlscommon, - in sync !!! + NOTE: When adding or rearranging items: + - make sure that the subtypes TOperandTokens, TBasicOperationTokens and TFuncTokens + are complete + - make sure to keep the FEProps table in sync + - make sure to keep the TokenID table + in TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID, unit xlscommon, + in sync } TFEKind = ( { Basic operands } fekCell, fekCellRef, fekCellRange, fekNum, fekInteger, fekString, fekBool, - fekErr, fekMissingArg, fekParen, + fekErr, fekMissingArg, { Basic operations } fekAdd, fekSub, fekDiv, fekMul, fekPercent, fekPower, fekUMinus, fekUPlus, fekConcat, // string concatenation fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual, + fekParen, { Built-in/Worksheet Functions} // math fekABS, fekACOS, fekACOSH, fekASIN, fekASINH, fekATAN, fekATANH, @@ -90,7 +95,7 @@ type fekMAX, fekMEDIAN, fekMIN, fekPERMUT, fekPOISSON, fekPRODUCT, fekSTDEV, fekSTDEVP, fekSUM, fekSUMIF, fekSUMSQ, fekVAR, fekVARP, // financial - fekFV, fekNPER, fekPV, fekPMT, fekRATE, + fekFV, fekNPER, fekPMT, fekPV, fekRATE, // logical fekAND, fekFALSE, fekIF, fekNOT, fekOR, fekTRUE, // string @@ -106,6 +111,10 @@ type fekOpSUM {Unary sum operation. Note: CANNOT be used for summing sell contents; use fekSUM} ); + TOperandTokens = fekCell..fekMissingArg; + TBasicOperationTokens = fekAdd..fekParen; + TFuncTokens = fekAbs..fekOpSum; + TsRelFlag = (rfRelRow, rfRelCol, rfRelRow2, rfRelCol2); TsRelFlags = set of TsRelFlag; @@ -746,6 +755,7 @@ type function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem; overload; function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem; overload; + function FixedParamCount(AElementKind: TFEKind): Boolean; var GsSpreadFormats: array of TsSpreadFormatData; @@ -775,6 +785,8 @@ resourcestring lpNoValidNumberFormatString = 'No valid number format string.'; lpNoValidDateTimeFormatString = 'No valid date/time format string.'; lpIllegalNumberFormat = 'Illegal number format.'; + lpSpecifyNumberOfParams = 'Specify number of parameters for function %s'; + lpIncorrectParamCount = 'Funtion %s requires at least %d and at most %d parameters.'; lpTRUE = 'TRUE'; lpFALSE = 'FALSE'; lpErrEmptyIntersection = '#NULL!'; @@ -844,6 +856,154 @@ var 'wheat' // $16 ); + +{ Properties of formula elements } + +type + TFEProp = record Symbol: String; MinParams, MaxParams: Byte; end; + +const + FEProps: array[TFEKind] of TFEProp = ( + { Operands } + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCell + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellRef + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellRange + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellNum + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellInteger + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellString + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellBool + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellErr + (Symbol:''; MinParams:-1; MaxParams:-1), // fekCellMissingArg + { Basic operations } + (Symbol:'+'; MinParams:2; MaxParams:2), // fekAdd + (Symbol:'-'; MinParams:2; MaxParams:2), // fekSub + (Symbol:'*'; MinParams:2; MaxParams:2), // fekDiv + (Symbol:'/'; MinParams:2; MaxParams:2), // fekMul + (Symbol:'%'; MinParams:1; MaxParams:1), // fekPercent + (Symbol:'^'; MinParams:2; MaxParams:2), // fekPower + (Symbol:'-'; MinParams:1; MaxParams:1), // fekUMinus + (Symbol:'+'; MinParams:1; MaxParams:1), // fekUPlus + (Symbol:'&'; MinParams:2; MaxParams:2), // fekConcat (string concatenation) + (Symbol:'='; MinParams:2; MaxParams:2), // fekEqual + (Symbol:'>'; MinParams:2; MaxParams:2), // fekGreater + (Symbol:'>='; MinParams:2; MaxParams:2), // fekGreaterEqual + (Symbol:'<'; MinParams:2; MaxParams:2), // fekLess + (Symbol:'<='; MinParams:2; MaxParams:2), // fekLessEqual + (Symbol:'<>'; MinParams:2; MaxParams:2), // fekNotEqual + (Symbol:''; MinParams:1; MaxParams:1), // fekParen + { math } + (Symbol:'ABS'; MinParams:1; MaxParams:1), // fekABS + (Symbol:'ACOS'; MinParams:1; MaxParams:1), // fekACOS + (Symbol:'ACOSH'; MinParams:1; MaxParams:1), // fekACOSH + (Symbol:'ASIN'; MinParams:1; MaxParams:1), // fekASIN + (Symbol:'ASINH'; MinParams:1; MaxParams:1), // fekASINH + (Symbol:'ATAN'; MinParams:1; MaxParams:1), // fekATAN + (Symbol:'ATANH'; MinParams:1; MaxParams:1), // fekATANH, + (Symbol:'COS'; MinParams:1; MaxParams:1), // fekCOS + (Symbol:'COSH'; MinParams:1; MaxParams:1), // fekCOSH + (Symbol:'DEGREES'; MinParams:1; MaxParams:1), // fekDEGREES + (Symbol:'EXP'; MinParams:1; MaxParams:1), // fekEXP + (Symbol:'INT'; MinParams:1; MaxParams:1), // fekINT + (Symbol:'LN'; MinParams:1; MaxParams:1), // fekLN + (Symbol:'LOG'; MinParams:1; MaxParams:2), // fekLOG, + (Symbol:'LOG10'; MinParams:1; MaxParams:1), // fekLOG10 + (Symbol:'PI'; MinParams:0; MaxParams:0), // fekPI + (Symbol:'RADIANS'; MinParams:1; MaxParams:1), // fekRADIANS + (Symbol:'RAND'; MinParams:0; MaxParams:0), // fekRAND + (Symbol:'ROUND'; MinParams:2; MaxParams:2), // fekROUND, + (Symbol:'SIGN'; MinParams:1; MaxParams:1), // fekSIGN + (Symbol:'SIN'; MinParams:1; MaxParams:1), // fekSIN + (Symbol:'SINH'; MinParams:1; MaxParams:1), // fekSINH + (Symbol:'SQRT'; MinParams:1; MaxParams:1), // fekSQRT, + (Symbol:'TAN'; MinParams:1; MaxParams:1), // fekTAN + (Symbol:'TANH'; MinParams:1; MaxParams:1), // fekTANH, + { date/time } + (Symbol:'DATE'; MinParams:3; MaxParams:3), // fekDATE + (Symbol:'DATEDIF'; MinParams:3; MaxParams:3), // fekDATEDIF + (Symbol:'DATEVALUE'; MinParams:1; MaxParams:1), // fekDATEVALUE + (Symbol:'DAY'; MinParams:1; MaxParams:1), // fekDAY + (Symbol:'HOUR'; MinParams:1; MaxParams:1), // fekHOUR + (Symbol:'MINUTE'; MinParams:1; MaxParams:1), // fekMINUTE + (Symbol:'MONTH'; MinParams:1; MaxParams:1), // fekMONTH + (Symbol:'NOW'; MinParams:0; MaxParams:0), // fekNOW + (Symbol:'SECOND'; MinParams:1; MaxParams:1), // fekSECOND + (Symbol:'TIME'; MinParams:3; MaxParams:3), // fekTIME + (Symbol:'TIMEVALUE'; MinParams:1; MaxParams:1), // fekTIMEVALUE + (Symbol:'TODAY'; MinParams:0; MaxParams:0), // fekTODAY + (Symbol:'WEEKDAY'; MinParams:1; MaxParams:2), // fekWEEKDAY + (Symbol:'YEAR'; MinParams:1; MaxParams:1), // fekYEAR + { statistical } + (Symbol:'AVEDEV'; MinParams:1; MaxParams:30), // fekAVEDEV + (Symbol:'AVERAGE'; MinParams:1; MaxParams:30), // fekAVERAGE + (Symbol:'BETADIST'; MinParams:3; MaxParams:5), // fekBETADIST + (Symbol:'BETAINV'; MinParams:3; MaxParams:5), // fekBETAINV + (Symbol:'BINOMDIST'; MinParams:4; MaxParams:4), // fekBINOMDIST + (Symbol:'CHIDIST'; MinParams:2; MaxParams:2), // fekCHIDIST + (Symbol:'CHIINV'; MinParams:2; MaxParams:2), // fekCHIINV + (Symbol:'COUNT'; MinParams:0; MaxParams:30), // fekCOUNT + (Symbol:'COUNTA'; MinParams:0; MaxParams:30), // fekCOUNTA + (Symbol:'COUNTBLANK';MinParams:1; MaxParams:1), // fekCOUNTBLANK + (Symbol:'COUNTIF'; MinParams:2; MaxParams:2), // fekCOUNTIF + (Symbol:'MAX'; MinParams:1; MaxParams:30), // fekMAX + (Symbol:'MEDIAN'; MinParams:1; MaxParams:30), // fekMEDIAN + (Symbol:'MIN'; MinParams:1; MaxParams:30), // fekMIN + (Symbol:'PERMUT'; MinParams:2; MaxParams:2), // fekPERMUT + (Symbol:'POISSON'; MinParams:3; MaxParams:3), // fekPOISSON + (Symbol:'PRODUCT'; MinParams:0; MaxParams:30), // fekPRODUCT + (Symbol:'STDEV'; MinParams:1; MaxParams:30), // fekSTDEV + (Symbol:'STDEVP'; MinParams:1; MaxParams:30), // fekSTDEVP + (Symbol:'SUM'; MinParams:0; MaxParams:30), // fekSUM + (Symbol:'SUMIF'; MinParams:2; MaxParams:3), // fekSUMIF + (Symbol:'SUMSQ'; MinParams:0; MaxParams:30), // fekSUMSQ + (Symbol:'VAR'; MinParams:1; MaxParams:30), // fekVAR + (Symbol:'VARP'; MinParams:1; MaxParams:30), // fekVARP + { financial } + (Symbol:'FV'; MinParams:3; MaxParams:5), // fekFV + (Symbol:'NPER'; MinParams:3; MaxParams:5), // fekNPER + (Symbol:'PMT'; MinParams:3; MaxParams:5), // fekPMT + (Symbol:'PV'; MinParams:3; MaxParams:5), // fekPV + (Symbol:'RATE'; MinParams:3; MaxParams:6), // fekRATE + { logical } + (Symbol:'AND'; MinParams:0; MaxParams:30), // fekAND + (Symbol:'FALSE'; MinParams:0; MaxParams:0), // fekFALSE + (Symbol:'IF'; MinParams:2; MaxParams:3), // fekIF + (Symbol:'NOT'; MinParams:1; MaxParams:1), // fekNOT + (Symbol:'OR'; MinParams:1; MaxParams:30), // fekOR + (Symbol:'TRUE'; MinParams:0; MaxParams:0), // fekTRUE + { string } + (Symbol:'CHAR'; MinParams:1; MaxParams:1), // fekCHAR + (Symbol:'CODE'; MinParams:1; MaxParams:1), // fekCODE + (Symbol:'LEFT'; MinParams:1; MaxParams:2), // fekLEFT + (Symbol:'LOWER'; MinParams:1; MaxParams:1), // fekLOWER + (Symbol:'MID'; MinParams:3; MaxParams:3), // fekMID + (Symbol:'PROPER'; MinParams:1; MaxParams:1), // fekPROPER + (Symbol:'REPLACE'; MinParams:4; MaxParams:4), // fekREPLACE + (Symbol:'RIGHT'; MinParams:1; MaxParams:2), // fekRIGHT + (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4), // fekSUBSTITUTE + (Symbol:'TRIM'; MinParams:1; MaxParams:1), // fekTRIM + (Symbol:'UPPER'; MinParams:1; MaxParams:1), // fekUPPER + { lookup/reference } + (Symbol:'COLUMN'; MinParams:0; MaxParams:1), // fekCOLUMN + (Symbol:'COLUMNS'; MinParams:1; MaxParams:1), // fekCOLUMNS + (Symbol:'ROW'; MinParams:0; MaxParams:1), // fekROW + (Symbol:'ROWS'; MinParams:1; MaxParams:1), // fekROWS + { info } + (Symbol:'CELL'; MinParams:1; MaxParams:2), // fekCELLINFO + (Symbol:'INFO'; MinParams:1; MaxParams:1), // fekINFO + (Symbol:'ISBLANK'; MinParams:1; MaxParams:1), // fekIsBLANK + (Symbol:'ISERR'; MinParams:1; MaxParams:1), // fekIsERR + (Symbol:'ISERROR'; MinParams:1; MaxParams:1), // fekIsERROR + (Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1), // fekIsLOGICAL + (Symbol:'ISNA'; MinParams:1; MaxParams:1), // fekIsNA + (Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1), // fekIsNONTEXT + (Symbol:'ISNUMBER'; MinParams:1; MaxParams:1), // fekIsNUMBER + (Symbol:'ISREF'; MinParams:1; MaxParams:1), // fekIsRef + (Symbol:'ISTEXT'; MinParams:1; MaxParams:1), // fekIsTEXT + (Symbol:'VALUE'; MinParams:1; MaxParams:1), // fekValue + { Other operations } + (Symbol:'SUM'; MinParams:1; MaxParams:1) // fekOpSUM (Unary sum operation). Note: CANNOT be used for summing sell contents; use fekSUM} + ); + {@@ Registers a new reader/writer pair for a format } @@ -3761,38 +3921,10 @@ end; } function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem; begin - if ord(AToken) < ord(fekAdd) then - raise Exception.Create('No basic tokens allowed here.'); - Result := NewRPNItem; - Result^.FE.ElementKind := AToken; - - case AToken of - fekFALSE, fekNOW, fekPI, fekRAND, fekTODAY, fekTRUE: - Result^.FE.ParamsNum := 0; - - fekABS, fekACOS, fekACOSH, fekASIN, fekASINH, fekATAN, fekATANH, - fekCHAR, fekCODE, fekCOLUMNS, fekCOUNTBLANK, fekCOS, fekCOSH, - fekDATEVALUE, fekDAY, fekDEGREES, fekEXP, fekHOUR, fekINFO, fekINT, - fekIsBLANK, fekIsERR, fekIsERROR, fekIsLOGICAL, fekIsNA, fekIsNONTEXT, - fekIsTEXT, fekIsNUMBER, fekIsRef, fekLN, fekLOG10, fekLOWER, fekMINUTE, - fekMONTH, fekNOT, fekOpSUM, fekPercent, fekPROPER, fekRADIANS, fekROWS, - fekSECOND, fekSIGN, fekSIN, fekSINH, fekSQRT, fekTAN, fekTANH, - fekTIMEVALUE, fekTRIM, fekUMinus, fekUPlus, fekUPPER, fekValue, - fekWEEKDAY, fekYEAR: - Result^.FE.ParamsNum := 1; - - fekAdd, fekCHIDIST, fekCHIINV, fekConcat, fekCOUNTIF, fekDiv, - fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, - fekMul, fekNotEqual, fekPERMUT, fekPower, fekSub, fekROUND: - Result^.FE.ParamsNum := 2; - - fekDATE, fekDATEDIF, fekMID, fekPOISSON, fekTIME: - Result^.FE.ParamsNum := 3; - - fekBINOMDIST, fekREPLACE: - Result^.FE.ParamsNum := 4; - end; + if FEProps[AToken].MinParams <> FEProps[AToken].MaxParams then + raise Exception.CreateFmt(lpSpecifyNumberOfParams, [FEProps[AToken].Symbol]); + Result := RPNFunc(AToken, FEProps[AToken].MinParams, ANext); Result^.Next := ANext; end; @@ -3803,8 +3935,27 @@ end; } function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem; begin - Result := RPNFunc(AToken, ANext); + if ord(AToken) < ord(fekAdd) then + raise Exception.Create('No basic tokens allowed here.'); + + if (ANumParams < FEProps[AToken].MinParams) or (ANumParams > FEProps[AToken].MaxParams) then + raise Exception.CreateFmt(lpIncorrectParamCount, [ + FEProps[AToken].Symbol, FEProps[AToken].MinParams, FEProps[AToken].MaxParams + ]); + + Result := NewRPNItem; + Result^.FE.ElementKind := AToken; Result^.FE.ParamsNum := ANumParams; + Result^.Next := ANext; +end; + +{@@ + Returns if the function defined by the token requires a fixed number of parameter. +} +function FixedParamCount(AElementKind: TFEKind): Boolean; +begin + Result := (FEProps[AElementKind].MinParams = FEProps[AElementKind].MaxParams) + and (FEProps[AElementKind].MinParams >= 0); end; {@@ @@ -3850,6 +4001,7 @@ begin end; end; + initialization MakeLEPalette(@DEFAULT_PALETTE, Length(DEFAULT_PALETTE)); diff --git a/components/fpspreadsheet/tests/rpntests.inc b/components/fpspreadsheet/tests/rpntests.inc index d43764101..304f48404 100644 --- a/components/fpspreadsheet/tests/rpntests.inc +++ b/components/fpspreadsheet/tests/rpntests.inc @@ -875,7 +875,7 @@ begin Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( RPNNumber(value, RPNNumber(2, - RPNFunc(fekLOG, + RPNFunc(fekLOG, 2, nil))))); Worksheet.WriteNumber(Row, 2, logn(2.0, value)); end; diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 9149fcf72..c8d18c025 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -499,161 +499,157 @@ uses StrUtils, fpsNumFormatParser; { Helper table for rpn formulas: - Assignment of FormulaElementKinds (fekXXXX) to EXCEL_TOKEN IDs. The table - contains additional inforation in the first column: - 0 --> primary token (basic operands and operations) - 1 --> secondary token of a function with a fixed parameter count - 2 --> secondary token of a function with a variable parameter count } + Assignment of FormulaElementKinds (fekXXXX) to EXCEL_TOKEN IDs. } const - TokenIDs: array[fekCell..fekOpSum, 0..1] of Word = ( + TokenIDs: array[TFEKind] of Word = ( // Basic operands - (0, INT_EXCEL_TOKEN_TREFV), {fekCell} - (0, INT_EXCEL_TOKEN_TREFR), {fekCellRef} - (0, INT_EXCEL_TOKEN_TAREA_R), {fekCellRange} - (0, INT_EXCEL_TOKEN_TNUM), {fekNum} - (0, INT_EXCEL_TOKEN_TINT), {fekInteger} - (0, INT_EXCEL_TOKEN_TSTR), {fekString} - (0, INT_EXCEL_TOKEN_TBOOL), {fekBool} - (0, INT_EXCEL_TOKEN_TERR), {fekErr} - (0, INT_EXCEL_TOKEN_TMISSARG), {fekMissArg, missing argument} - (0, INT_EXCEL_TOKEN_TPAREN), {Operator in parenthesis} + INT_EXCEL_TOKEN_TREFV, {fekCell} + INT_EXCEL_TOKEN_TREFR, {fekCellRef} + INT_EXCEL_TOKEN_TAREA_R, {fekCellRange} + INT_EXCEL_TOKEN_TNUM, {fekNum} + INT_EXCEL_TOKEN_TINT, {fekInteger} + INT_EXCEL_TOKEN_TSTR, {fekString} + INT_EXCEL_TOKEN_TBOOL, {fekBool} + INT_EXCEL_TOKEN_TERR, {fekErr} + INT_EXCEL_TOKEN_TMISSARG, {fekMissArg, missing argument} // Basic operations - (0, INT_EXCEL_TOKEN_TADD), {fekAdd, +} - (0, INT_EXCEL_TOKEN_TSUB), {fekSub, -} - (0, INT_EXCEL_TOKEN_TDIV), {fekDiv, /} - (0, INT_EXCEL_TOKEN_TMUL), {fekMul, *} - (0, INT_EXCEL_TOKEN_TPERCENT), {fekPercent, %} - (0, INT_EXCEL_TOKEN_TPOWER), {fekPower, ^} - (0, INT_EXCEL_TOKEN_TUMINUS), {fekUMinus, -} - (0, INT_EXCEL_TOKEN_TUPLUS), {fekUPlus, +} - (0, INT_EXCEL_TOKEN_TCONCAT), {fekConcat, &, for strings} - (0, INT_EXCEL_TOKEN_TEQ), {fekEqual, =} - (0, INT_EXCEL_TOKEN_TGT), {fekGreater, >} - (0, INT_EXCEL_TOKEN_TGE), {fekGreaterEqual, >=} - (0, INT_EXCEL_TOKEN_TLT), {fekLess <} - (0, INT_EXCEL_TOKEN_TLE), {fekLessEqual, <=} - (0, INT_EXCEL_TOKEN_TNE), {fekNotEqual, <>} + INT_EXCEL_TOKEN_TADD, {fekAdd, +} + INT_EXCEL_TOKEN_TSUB, {fekSub, -} + INT_EXCEL_TOKEN_TDIV, {fekDiv, /} + INT_EXCEL_TOKEN_TMUL, {fekMul, *} + INT_EXCEL_TOKEN_TPERCENT, {fekPercent, %} + INT_EXCEL_TOKEN_TPOWER, {fekPower, ^} + INT_EXCEL_TOKEN_TUMINUS, {fekUMinus, -} + INT_EXCEL_TOKEN_TUPLUS, {fekUPlus, +} + INT_EXCEL_TOKEN_TCONCAT, {fekConcat, &, for strings} + INT_EXCEL_TOKEN_TEQ, {fekEqual, =} + INT_EXCEL_TOKEN_TGT, {fekGreater, >} + INT_EXCEL_TOKEN_TGE, {fekGreaterEqual, >=} + INT_EXCEL_TOKEN_TLT, {fekLess <} + INT_EXCEL_TOKEN_TLE, {fekLessEqual, <=} + INT_EXCEL_TOKEN_TNE, {fekNotEqual, <>} + INT_EXCEL_TOKEN_TPAREN, {Operator in parenthesis} // Math functions - (1, INT_EXCEL_SHEET_FUNC_ABS), {fekABS} - (1, INT_EXCEL_SHEET_FUNC_ACOS), {fekACOS} - (1, INT_EXCEL_SHEET_FUNC_ACOSH), {fekACOSH} - (1, INT_EXCEL_SHEET_FUNC_ASIN), {fekASIN} - (1, INT_EXCEL_SHEET_FUNC_ASINH), {fekASINH} - (1, INT_EXCEL_SHEET_FUNC_ATAN), {fekATAN} - (1, INT_EXCEL_SHEET_FUNC_ATANH), {fekATANH} - (1, INT_EXCEL_SHEET_FUNC_COS), {fekCOS} - (1, INT_EXCEL_SHEET_FUNC_COSH), {fekCOSH} - (1, INT_EXCEL_SHEET_FUNC_DEGREES), {fekDEGREES} - (1, INT_EXCEL_SHEET_FUNC_EXP), {fekEXP} - (1, INT_EXCEL_SHEET_FUNC_INT), {fekINT} - (1, INT_EXCEL_SHEET_FUNC_LN), {fekLN} - (1, INT_EXCEL_SHEET_FUNC_LOG), {fekLOG} - (1, INT_EXCEL_SHEET_FUNC_LOG10), {fekLOG10} - (1, INT_EXCEL_SHEET_FUNC_PI), {fekPI} - (1, INT_EXCEL_SHEET_FUNC_RADIANS), {fekRADIANS} - (1, INT_EXCEL_SHEET_FUNC_RAND), {fekRAND} - (1, INT_EXCEL_SHEET_FUNC_ROUND), {fekROUND} - (1, INT_EXCEL_SHEET_FUNC_SIGN), {fekSIGN} - (1, INT_EXCEL_SHEET_FUNC_SIN), {fekSIN} - (1, INT_EXCEL_SHEET_FUNC_SINH), {fekSINH} - (1, INT_EXCEL_SHEET_FUNC_SQRT), {fekSQRT} - (1, INT_EXCEL_SHEET_FUNC_TAN), {fekTAN} - (1, INT_EXCEL_SHEET_FUNC_TANH), {fekTANH} + INT_EXCEL_SHEET_FUNC_ABS, {fekABS} + INT_EXCEL_SHEET_FUNC_ACOS, {fekACOS} + INT_EXCEL_SHEET_FUNC_ACOSH, {fekACOSH} + INT_EXCEL_SHEET_FUNC_ASIN, {fekASIN} + INT_EXCEL_SHEET_FUNC_ASINH, {fekASINH} + INT_EXCEL_SHEET_FUNC_ATAN, {fekATAN} + INT_EXCEL_SHEET_FUNC_ATANH, {fekATANH} + INT_EXCEL_SHEET_FUNC_COS, {fekCOS} + INT_EXCEL_SHEET_FUNC_COSH, {fekCOSH} + INT_EXCEL_SHEET_FUNC_DEGREES, {fekDEGREES} + INT_EXCEL_SHEET_FUNC_EXP, {fekEXP} + INT_EXCEL_SHEET_FUNC_INT, {fekINT} + INT_EXCEL_SHEET_FUNC_LN, {fekLN} + INT_EXCEL_SHEET_FUNC_LOG, {fekLOG} + INT_EXCEL_SHEET_FUNC_LOG10, {fekLOG10} + INT_EXCEL_SHEET_FUNC_PI, {fekPI} + INT_EXCEL_SHEET_FUNC_RADIANS, {fekRADIANS} + INT_EXCEL_SHEET_FUNC_RAND, {fekRAND} + INT_EXCEL_SHEET_FUNC_ROUND, {fekROUND} + INT_EXCEL_SHEET_FUNC_SIGN, {fekSIGN} + INT_EXCEL_SHEET_FUNC_SIN, {fekSIN} + INT_EXCEL_SHEET_FUNC_SINH, {fekSINH} + INT_EXCEL_SHEET_FUNC_SQRT, {fekSQRT} + INT_EXCEL_SHEET_FUNC_TAN, {fekTAN} + INT_EXCEL_SHEET_FUNC_TANH, {fekTANH} // Date/time functions - (1, INT_EXCEL_SHEET_FUNC_DATE), {fekDATE} - (1, INT_EXCEL_SHEET_FUNC_DATEDIF), {fekDATEDIF} - (1, INT_EXCEL_SHEET_FUNC_DATEVALUE), {fekDATEVALUE} - (1, INT_EXCEL_SHEET_FUNC_DAY), {fekDAY} - (1, INT_EXCEL_SHEET_FUNC_HOUR), {fekHOUR} - (1, INT_EXCEL_SHEET_FUNC_MINUTE), {fekMINUTE} - (1, INT_EXCEL_SHEET_FUNC_MONTH), {fekMONTH} - (1, INT_EXCEL_SHEET_FUNC_NOW), {fekNOW} - (1, INT_EXCEL_SHEET_FUNC_SECOND), {fekSECOND} - (1, INT_EXCEL_SHEET_FUNC_TIME), {fekTIME} - (1, INT_EXCEL_SHEET_FUNC_TIMEVALUE), {fekTIMEVALUE} - (1, INT_EXCEL_SHEET_FUNC_TODAY), {fekTODAY} - (2, INT_EXCEL_SHEET_FUNC_WEEKDAY), {fekWEEKDAY} - (1, INT_EXCEL_SHEET_FUNC_YEAR), {fekYEAR} + INT_EXCEL_SHEET_FUNC_DATE, {fekDATE} + INT_EXCEL_SHEET_FUNC_DATEDIF, {fekDATEDIF} + INT_EXCEL_SHEET_FUNC_DATEVALUE, {fekDATEVALUE} + INT_EXCEL_SHEET_FUNC_DAY, {fekDAY} + INT_EXCEL_SHEET_FUNC_HOUR, {fekHOUR} + INT_EXCEL_SHEET_FUNC_MINUTE, {fekMINUTE} + INT_EXCEL_SHEET_FUNC_MONTH, {fekMONTH} + INT_EXCEL_SHEET_FUNC_NOW, {fekNOW} + INT_EXCEL_SHEET_FUNC_SECOND, {fekSECOND} + INT_EXCEL_SHEET_FUNC_TIME, {fekTIME} + INT_EXCEL_SHEET_FUNC_TIMEVALUE, {fekTIMEVALUE} + INT_EXCEL_SHEET_FUNC_TODAY, {fekTODAY} + INT_EXCEL_SHEET_FUNC_WEEKDAY, {fekWEEKDAY} + INT_EXCEL_SHEET_FUNC_YEAR, {fekYEAR} // Statistical functions - (2, INT_EXCEL_SHEET_FUNC_AVEDEV), {fekAVEDEV} - (2, INT_EXCEL_SHEET_FUNC_AVERAGE), {fekAVERAGE} - (2, INT_EXCEL_SHEET_FUNC_BETADIST), {fekBETADIST} - (2, INT_EXCEL_SHEET_FUNC_BETAINV), {fekBETAINV} - (1, INT_EXCEL_SHEET_FUNC_BINOMDIST), {fekBINOMDIST} - (1, INT_EXCEL_SHEET_FUNC_CHIDIST), {fekCHIDIST} - (1, INT_EXCEL_SHEET_FUNC_CHIINV), {fekCHIINV} - (2, INT_EXCEL_SHEET_FUNC_COUNT), {fekCOUNT} - (2, INT_EXCEL_SHEET_FUNC_COUNTA), {fekCOUNTA} - (1, INT_EXCEL_SHEET_FUNC_COUNTBLANK),{fekCOUNTBLANK} - (2, INT_EXCEL_SHEET_FUNC_COUNTIF), {fekCOUNTIF} - (2, INT_EXCEL_SHEET_FUNC_MAX), {fekMAX} - (2, INT_EXCEL_SHEET_FUNC_MEDIAN), {fekMEDIAN} - (2, INT_EXCEL_SHEET_FUNC_MIN), {fekMIN} - (1, INT_EXCEL_SHEET_FUNC_PERMUT), {fekPERMUT} - (1, INT_EXCEL_SHEET_FUNC_POISSON), {fekPOISSON} - (2, INT_EXCEL_SHEET_FUNC_PRODUCT), {fekPRODUCT} - (2, INT_EXCEL_SHEET_FUNC_STDEV), {fekSTDEV} - (2, INT_EXCEL_SHEET_FUNC_STDEVP), {fekSTDEVP} - (2, INT_EXCEL_SHEET_FUNC_SUM), {fekSUM} - (2, INT_EXCEL_SHEET_FUNC_SUMIF), {fekSUMIF} - (2, INT_EXCEL_SHEET_FUNC_SUMSQ), {fekSUMSQ} - (2, INT_EXCEL_SHEET_FUNC_VAR), {fekVAR} - (2, INT_EXCEL_SHEET_FUNC_VARP), {fekVARP} + INT_EXCEL_SHEET_FUNC_AVEDEV, {fekAVEDEV} + INT_EXCEL_SHEET_FUNC_AVERAGE, {fekAVERAGE} + INT_EXCEL_SHEET_FUNC_BETADIST, {fekBETADIST} + INT_EXCEL_SHEET_FUNC_BETAINV, {fekBETAINV} + INT_EXCEL_SHEET_FUNC_BINOMDIST, {fekBINOMDIST} + INT_EXCEL_SHEET_FUNC_CHIDIST, {fekCHIDIST} + INT_EXCEL_SHEET_FUNC_CHIINV, {fekCHIINV} + INT_EXCEL_SHEET_FUNC_COUNT, {fekCOUNT} + INT_EXCEL_SHEET_FUNC_COUNTA, {fekCOUNTA} + INT_EXCEL_SHEET_FUNC_COUNTBLANK,{fekCOUNTBLANK} + INT_EXCEL_SHEET_FUNC_COUNTIF, {fekCOUNTIF} + INT_EXCEL_SHEET_FUNC_MAX, {fekMAX} + INT_EXCEL_SHEET_FUNC_MEDIAN, {fekMEDIAN} + INT_EXCEL_SHEET_FUNC_MIN, {fekMIN} + INT_EXCEL_SHEET_FUNC_PERMUT, {fekPERMUT} + INT_EXCEL_SHEET_FUNC_POISSON, {fekPOISSON} + INT_EXCEL_SHEET_FUNC_PRODUCT, {fekPRODUCT} + INT_EXCEL_SHEET_FUNC_STDEV, {fekSTDEV} + INT_EXCEL_SHEET_FUNC_STDEVP, {fekSTDEVP} + INT_EXCEL_SHEET_FUNC_SUM, {fekSUM} + INT_EXCEL_SHEET_FUNC_SUMIF, {fekSUMIF} + INT_EXCEL_SHEET_FUNC_SUMSQ, {fekSUMSQ} + INT_EXCEL_SHEET_FUNC_VAR, {fekVAR} + INT_EXCEL_SHEET_FUNC_VARP, {fekVARP} // Financial functions - (2, INT_EXCEL_SHEET_FUNC_FV), {fekFV} - (2, INT_EXCEL_SHEET_FUNC_NPER), {fekNPER} - (2, INT_EXCEL_SHEET_FUNC_PV), {fekPV} - (2, INT_EXCEL_SHEET_FUNC_PMT), {fekPMT} - (2, INT_EXCEL_SHEET_FUNC_RATE), {fekRATE} + INT_EXCEL_SHEET_FUNC_FV, {fekFV} + INT_EXCEL_SHEET_FUNC_NPER, {fekNPER} + INT_EXCEL_SHEET_FUNC_PMT, {fekPMT} + INT_EXCEL_SHEET_FUNC_PV, {fekPV} + INT_EXCEL_SHEET_FUNC_RATE, {fekRATE} // Logical functions - (2, INT_EXCEL_SHEET_FUNC_AND), {fekAND} - (1, INT_EXCEL_SHEET_FUNC_FALSE), {fekFALSE} - (2, INT_EXCEL_SHEET_FUNC_IF), {fekIF} - (1, INT_EXCEL_SHEET_FUNC_NOT), {fekNOT} - (2, INT_EXCEL_SHEET_FUNC_OR), {fekOR} - (1, INT_EXCEL_SHEET_FUNC_TRUE), {fekTRUE} + INT_EXCEL_SHEET_FUNC_AND, {fekAND} + INT_EXCEL_SHEET_FUNC_FALSE, {fekFALSE} + INT_EXCEL_SHEET_FUNC_IF, {fekIF} + INT_EXCEL_SHEET_FUNC_NOT, {fekNOT} + INT_EXCEL_SHEET_FUNC_OR, {fekOR} + INT_EXCEL_SHEET_FUNC_TRUE, {fekTRUE} // String functions - (1, INT_EXCEL_SHEET_FUNC_CHAR), {fekCHAR} - (1, INT_EXCEL_SHEET_FUNC_CODE), {fekCODE} - (2, INT_EXCEL_SHEET_FUNC_LEFT), {fekLEFT} - (1, INT_EXCEL_SHEET_FUNC_LOWER), {fekLOWER} - (1, INT_EXCEL_SHEET_FUNC_MID), {fekMID} - (1, INT_EXCEL_SHEET_FUNC_PROPER), {fekPROPER} - (1, INT_EXCEL_SHEET_FUNC_REPLACE), {fekREPLACE} - (2, INT_EXCEL_SHEET_FUNC_RIGHT), {fekRIGHT} - (2, INT_EXCEL_SHEET_FUNC_SUBSTITUTE),{fekSUBSTITUTE} - (1, INT_EXCEL_SHEET_FUNC_TRIM), {fekTRIM} - (1, INT_EXCEL_SHEET_FUNC_UPPER), {fekUPPER} + INT_EXCEL_SHEET_FUNC_CHAR, {fekCHAR} + INT_EXCEL_SHEET_FUNC_CODE, {fekCODE} + INT_EXCEL_SHEET_FUNC_LEFT, {fekLEFT} + INT_EXCEL_SHEET_FUNC_LOWER, {fekLOWER} + INT_EXCEL_SHEET_FUNC_MID, {fekMID} + INT_EXCEL_SHEET_FUNC_PROPER, {fekPROPER} + INT_EXCEL_SHEET_FUNC_REPLACE, {fekREPLACE} + INT_EXCEL_SHEET_FUNC_RIGHT, {fekRIGHT} + INT_EXCEL_SHEET_FUNC_SUBSTITUTE,{fekSUBSTITUTE} + INT_EXCEL_SHEET_FUNC_TRIM, {fekTRIM} + INT_EXCEL_SHEET_FUNC_UPPER, {fekUPPER} // lookup/reference functions - (2, INT_EXCEL_SHEET_FUNC_COLUMN), {fekCOLUMN} - (1, INT_EXCEL_SHEET_FUNC_COLUMNS), {fekCOLUMNS} - (2, INT_EXCEL_SHEET_FUNC_ROW), {fekROW} - (1, INT_EXCEL_SHEET_FUNC_ROWS), {fekROWS} + INT_EXCEL_SHEET_FUNC_COLUMN, {fekCOLUMN} + INT_EXCEL_SHEET_FUNC_COLUMNS, {fekCOLUMNS} + INT_EXCEL_SHEET_FUNC_ROW, {fekROW} + INT_EXCEL_SHEET_FUNC_ROWS, {fekROWS} // Info functions - (2, INT_EXCEL_SHEET_FUNC_CELL), {fekCELLINFO} - (1, INT_EXCEL_SHEET_FUNC_INFO), {fekINFO} - (1, INT_EXCEL_SHEET_FUNC_ISBLANK), {fekIsBLANK} - (1, INT_EXCEL_SHEET_FUNC_ISERR), {fekIsERR} - (1, INT_EXCEL_SHEET_FUNC_ISERROR), {fekIsERROR} - (1, INT_EXCEL_SHEET_FUNC_ISLOGICAL), {fekIsLOGICAL} - (1, INT_EXCEL_SHEET_FUNC_ISNA), {fekIsNA} - (1, INT_EXCEL_SHEET_FUNC_ISNONTEXT), {fekIsNONTEXT} - (1, INT_EXCEL_SHEET_FUNC_ISNUMBER), {fekIsNUMBER} - (1, INT_EXCEL_SHEET_FUNC_ISREF), {fekIsREF} - (1, INT_EXCEL_SHEET_FUNC_ISTEXT), {fekIsTEXT} - (1, INT_EXCEL_SHEET_FUNC_VALUE), {fekValue} + INT_EXCEL_SHEET_FUNC_CELL, {fekCELLINFO} + INT_EXCEL_SHEET_FUNC_INFO, {fekINFO} + INT_EXCEL_SHEET_FUNC_ISBLANK, {fekIsBLANK} + INT_EXCEL_SHEET_FUNC_ISERR, {fekIsERR} + INT_EXCEL_SHEET_FUNC_ISERROR, {fekIsERROR} + INT_EXCEL_SHEET_FUNC_ISLOGICAL, {fekIsLOGICAL} + INT_EXCEL_SHEET_FUNC_ISNA, {fekIsNA} + INT_EXCEL_SHEET_FUNC_ISNONTEXT, {fekIsNONTEXT} + INT_EXCEL_SHEET_FUNC_ISNUMBER, {fekIsNUMBER} + INT_EXCEL_SHEET_FUNC_ISREF, {fekIsREF} + INT_EXCEL_SHEET_FUNC_ISTEXT, {fekIsTEXT} + INT_EXCEL_SHEET_FUNC_VALUE, {fekValue} // Other operations - (0, INT_EXCEL_TOKEN_TATTR) {fekOpSum} + INT_EXCEL_TOKEN_TATTR {fekOpSum} ); @@ -1475,8 +1471,8 @@ begin begin func := ReadRPNFunc(AStream); found := false; - for fek in TFEKind do begin - if (TokenIDs[fek, 1] = func) and (TokenIDs[fek, 0] = 1) then begin + for fek in TFuncTokens do begin + if (TokenIDs[fek] = func) and FixedParamCount(fek) then begin rpnItem := RPNFunc(fek, rpnItem); found := true; break; @@ -1494,8 +1490,8 @@ begin b := AStream.ReadByte; func := ReadRPNFunc(AStream); found := false; - for fek in TFEKind do - if (TokenIDs[fek, 1] = func) and (TokenIDs[fek, 0] = 2) then begin + for fek in TFuncTokens do + if (TokenIDs[fek] = func) and not FixedParamCount(fek) then begin rpnItem := RPNFunc(fek, b, rpnItem); found := true; break; @@ -1506,8 +1502,8 @@ begin else found := false; - for fek in TFEKind do - if (TokenIDs[fek, 1] = token) and (TokenIDs[fek, 0] = 0) then begin + for fek in TBasicOperationTokens do + if (TokenIDs[fek] = token) then begin rpnItem := RPNFunc(fek, rpnItem); found := true; break; @@ -1638,19 +1634,17 @@ end; function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID( AElementKind: TFEKind; out ASecondaryID: Word): Word; begin - case TokenIDs[AElementKind, 0] of - 0: begin - Result := TokenIDs[AElementKind, 1]; - ASecondaryID := 0; - end; - 1: begin - Result := INT_EXCEL_TOKEN_FUNC_V; - ASecondaryID := TokenIDs[AElementKind, 1] - end; - 2: begin - Result := INT_EXCEL_TOKEN_FUNCVAR_V; - ASecondaryID := TokenIDs[AElementKind, 1] - end; + if (AElementKind >= Low(TFuncTokens)) and (AElementKind <= High(TFuncTokens)) + then begin + if FixedParamCount(AElementKind) then + Result := INT_EXCEL_TOKEN_FUNC_V + else + Result := INT_EXCEL_TOKEN_FUNCVAR_V; + ASecondaryID := TokenIDs[AElementKind]; + end + else begin + Result := TokenIDs[AElementKind]; + ASecondaryID := 0; end; end;