fpspreadsheet: Add reading of rpn formulas.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3082 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-05-23 13:16:01 +00:00
parent a66603ab66
commit 49e99a74b5
6 changed files with 564 additions and 185 deletions

View File

@ -31,6 +31,8 @@ begin
// Create the spreadsheet // Create the spreadsheet
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
MyWorkbook.ReadFormulas := true;
MyWorkbook.ReadFromFile(InputFilename, sfExcel8); MyWorkbook.ReadFromFile(InputFilename, sfExcel8);
MyWorksheet := MyWorkbook.GetFirstWorksheet; MyWorksheet := MyWorkbook.GetFirstWorksheet;

View File

@ -68,7 +68,8 @@ type
TFEKind = ( TFEKind = (
{ Basic operands } { Basic operands }
fekCell, fekCellRef, fekCellRange, fekNum, fekString, fekBool, fekMissingArg, fekCell, fekCellRef, fekCellRange, fekNum, fekInteger, fekString, fekBool,
fekErr, fekMissingArg,
{ Basic operations } { Basic operations }
fekAdd, fekSub, fekDiv, fekMul, fekPercent, fekPower, fekUMinus, fekUPlus, fekAdd, fekSub, fekDiv, fekMul, fekPercent, fekPower, fekUMinus, fekUPlus,
fekConcat, // string concatenation fekConcat, // string concatenation
@ -138,7 +139,9 @@ type
errIllegalRef, // #REF! errIllegalRef, // #REF!
errWrongName, // #NAME? errWrongName, // #NAME?
errOverflow, // #NUM! errOverflow, // #NUM!
errArgNotAvail // #N/A errArgNotAvail, // #N/A
// --- no Excel errors --
errFormulaNotSupported
); );
{@@ List of possible formatting fields } {@@ List of possible formatting fields }
@ -402,7 +405,8 @@ type
procedure WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean); procedure WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean);
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
procedure WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue); procedure WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue); overload;
procedure WriteErrorValue(ACell: PCell; AValue: TErrorValue); overload;
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2; AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2;
@ -487,6 +491,7 @@ type
FFontList: TFPList; FFontList: TFPList;
FBuiltinFontCount: Integer; FBuiltinFontCount: Integer;
FPalette: array of TsColorValue; FPalette: array of TsColorValue;
FReadFormulas: Boolean;
{ Internal methods } { Internal methods }
procedure RemoveWorksheetsCallback(data, arg: pointer); procedure RemoveWorksheetsCallback(data, arg: pointer);
public public
@ -537,6 +542,7 @@ type
and support a single encoding for the whole document, like Excel 2 to 5 } and support a single encoding for the whole document, like Excel 2 to 5 }
property Encoding: TsEncoding read FEncoding write FEncoding; property Encoding: TsEncoding read FEncoding write FEncoding;
property FileFormat: TsSpreadsheetFormat read FFormat; property FileFormat: TsSpreadsheetFormat read FFormat;
property ReadFormulas: Boolean read FReadFormulas write FReadFormulas;
end; end;
@ -708,6 +714,7 @@ type
} }
function CreateRPNFormula(AItem: PRPNItem): TsRPNFormula; function CreateRPNFormula(AItem: PRPNItem): TsRPNFormula;
procedure DestroyRPNFormula(AItem: PRPNItem);
function RPNBool(AValue: Boolean; function RPNBool(AValue: Boolean;
ANext: PRPNItem): PRPNItem; ANext: PRPNItem): PRPNItem;
@ -723,6 +730,8 @@ type
ANext: PRPNItem): PRPNItem; overload; ANext: PRPNItem): PRPNItem; overload;
function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags; function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
ANext: PRPNItem): PRPNItem; overload; ANext: PRPNItem): PRPNItem; overload;
function RPNErr(AErrCode: Byte; ANext: PRPNItem): PRPNItem;
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
function RPNMissingArg(ANext: PRPNItem): PRPNItem; function RPNMissingArg(ANext: PRPNItem): PRPNItem;
function RPNNumber(AValue: Double; ANext: PRPNItem): PRPNItem; function RPNNumber(AValue: Double; ANext: PRPNItem): PRPNItem;
function RPNString(AValue: String; ANext: PRPNItem): PRPNItem; function RPNString(AValue: String; ANext: PRPNItem): PRPNItem;
@ -767,6 +776,7 @@ resourcestring
lpErrWrongName = '#NAME?'; lpErrWrongName = '#NAME?';
lpErrOverflow = '#NUM!'; lpErrOverflow = '#NUM!';
lpErrArgNotAvail = '#N/A'; lpErrArgNotAvail = '#N/A';
lpErrFormulaNotSupported = '<FORMULA?>';
var var
{@@ {@@
@ -1310,13 +1320,14 @@ begin
Result := IfThen(BoolValue, lpTRUE, lpFALSE); Result := IfThen(BoolValue, lpTRUE, lpFALSE);
cctError: cctError:
case TErrorValue(StatusValue and $0F) of case TErrorValue(StatusValue and $0F) of
errEmptyIntersection: Result := lpErrEmptyIntersection; errEmptyIntersection : Result := lpErrEmptyIntersection;
errDivideByZero : Result := lpErrDivideByZero; errDivideByZero : Result := lpErrDivideByZero;
errWrongType : Result := lpErrWrongType; errWrongType : Result := lpErrWrongType;
errIllegalRef : Result := lpErrIllegalRef; errIllegalRef : Result := lpErrIllegalRef;
errWrongName : Result := lpErrWrongName; errWrongName : Result := lpErrWrongName;
errOverflow : Result := lpErrOverflow; errOverflow : Result := lpErrOverflow;
errArgNotAvail : Result := lpErrArgNotAvail; errArgNotAvail : Result := lpErrArgNotAvail;
errFormulaNotSupported: Result := lpErrFormulaNotSupported;
end; end;
else else
Result := ''; Result := '';
@ -1609,13 +1620,17 @@ end;
@param AValue The error code value @param AValue The error code value
} }
procedure TsWorksheet.WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue); procedure TsWorksheet.WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue);
var
ACell: PCell;
begin begin
ACell := GetCell(ARow, ACol); WriteErrorValue(GetCell(ARow, ACol), AValue);
end;
procedure TsWorksheet.WriteErrorValue(ACell: PCell; AValue: TErrorValue);
begin
if ACell <> nil then begin
ACell^.ContentType := cctError; ACell^.ContentType := cctError;
ACell^.StatusValue := (ACell^.StatusValue and $F0) or ord(AValue); ACell^.StatusValue := (ACell^.StatusValue and $F0) or ord(AValue);
ChangedCell(ARow, ACol); ChangedCell(ACell^.Row, ACell^.Col);
end;
end; end;
{@@ {@@
@ -3533,6 +3548,28 @@ begin
Result^.Next := ANext; Result^.Next := ANext;
end; end;
{@@
Creates an entry in the RPN array with an error value.
}
function RPNErr(AErrCode: Byte; ANext: PRPNItem): PRPNItem;
begin
Result := NewRPNItem;
Result^.FE.ElementKind := fekErr;
Result^.FE.IntValue := AErrCode;
Result^.Next := ANext;
end;
{@@
Creates an entry in the RPN array for a 2-byte unsigned integer
}
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
begin
Result := NewRPNItem;
Result^.FE.ElementKind := fekInteger;
Result^.FE.IntValue := AValue;
Result^.Next := ANext;
end;
{@@ {@@
Creates an entry in the RPN array for a missing argument in of function call. Creates an entry in the RPN array for a missing argument in of function call.
} }
@ -3623,6 +3660,17 @@ begin
end; end;
end; end;
procedure DestroyRPNFormula(AItem: PRPNItem);
var
nextitem: PRPNItem;
begin
while AItem <> nil do begin
nextitem := AItem^.Next;
DisposeRPNItem(AItem);
AItem := nextitem;
end;
end;
initialization initialization
MakeLEPalette(@DEFAULT_PALETTE, Length(DEFAULT_PALETTE)); MakeLEPalette(@DEFAULT_PALETTE, Length(DEFAULT_PALETTE));

View File

@ -73,6 +73,8 @@ type
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override; procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
procedure ReadRowInfo(AStream: TStream); override; procedure ReadRowInfo(AStream: TStream); override;
function ReadRPNFunc(AStream: TStream): Word; override;
function ReadRPNTokenArraySize(AStream: TStream): Word; override;
procedure ReadStringRecord(AStream: TStream); override; procedure ReadStringRecord(AStream: TStream); override;
procedure ReadWindow2(AStream: TStream); override; procedure ReadWindow2(AStream: TStream); override;
procedure ReadXF(AStream: TStream); procedure ReadXF(AStream: TStream);
@ -156,11 +158,6 @@ const
INT_EXCEL_ID_IXFE = $0044; INT_EXCEL_ID_IXFE = $0044;
INT_EXCEL_ID_FONTCOLOR = $0045; INT_EXCEL_ID_FONTCOLOR = $0045;
{ Cell Addresses constants }
MASK_EXCEL_ROW = $3FFF;
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
{ BOF record constants } { BOF record constants }
INT_EXCEL_SHEET = $0010; INT_EXCEL_SHEET = $0010;
INT_EXCEL_CHART = $0020; INT_EXCEL_CHART = $0020;
@ -647,6 +644,23 @@ begin
end; end;
end; end;
{ Reads the identifier for an RPN function with fixed argument count.
Valid for BIFF2-BIFF3. }
function TsSpreadBIFF2Reader.ReadRPNFunc(AStream: TStream): Word;
var
b: Byte;
begin
b := AStream.ReadByte;
Result := b;
end;
{ Helper funtion for reading of the size of the token array of an RPN formula.
Is overridden because BIFF2 uses 1 byte only. }
function TsSpreadBIFF2Reader.ReadRPNTokenArraySize(AStream: TStream): Word;
begin
Result := AStream.ReadByte;
end;
{ Reads a STRING record which contains the result of string formula. } { Reads a STRING record which contains the result of string formula. }
procedure TsSpreadBIFF2Reader.ReadStringRecord(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadStringRecord(AStream: TStream);
var var

View File

@ -219,11 +219,6 @@ const
{ Excel record IDs } { Excel record IDs }
// see: in xlscommon // see: in xlscommon
{ Cell Addresses constants }
MASK_EXCEL_ROW = $3FFF;
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
{ BOF record constants } { BOF record constants }
INT_BOF_BIFF5_VER = $0500; INT_BOF_BIFF5_VER = $0500;
INT_BOF_WORKBOOK_GLOBALS= $0005; INT_BOF_WORKBOOK_GLOBALS= $0005;

View File

@ -86,7 +86,12 @@ type
procedure ReadLabelSST(const AStream: TStream); procedure ReadLabelSST(const AStream: TStream);
// procedure ReadNumber() --> xlscommon // procedure ReadNumber() --> xlscommon
procedure ReadRichString(const AStream: TStream); procedure ReadRichString(const AStream: TStream);
procedure ReadRPNCellAddress(AStream: TStream; var ARow, ACol: Cardinal;
var AFlags: TsRelFlags); override;
procedure ReadRPNCellRangeAddress(AStream: TStream;
var ARow1, ACol1, ARow2, ACol2: Cardinal; var AFlags: TsRelFlags); override;
procedure ReadSST(const AStream: TStream); procedure ReadSST(const AStream: TStream);
function ReadString_8bitLen(AStream: TStream): String; override;
procedure ReadStringRecord(AStream: TStream); override; procedure ReadStringRecord(AStream: TStream); override;
procedure ReadXF(const AStream: TStream); procedure ReadXF(const AStream: TStream);
public public
@ -219,7 +224,6 @@ const
INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3; INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3;
{ Cell Addresses constants } { Cell Addresses constants }
MASK_EXCEL_ROW = $3FFF;
MASK_EXCEL_COL_BITS_BIFF8=$00FF; MASK_EXCEL_COL_BITS_BIFF8=$00FF;
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation, MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation! MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
@ -1658,6 +1662,53 @@ begin
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
end; end;
{ Reads the cell address used in an RPN formula element. Evaluates the corresponding
bits to distinguish between absolute and relative addresses.
Overriding the implementation in xlscommon. }
procedure TsSpreadBIFF8Reader.ReadRPNCellAddress(AStream: TStream;
var ARow, ACol: Cardinal; var AFlags: TsRelFlags);
var
c: word;
begin
// Read row index (2 bytes)
ARow := WordLEToN(AStream.ReadWord);
// Read column index; it contains info on absolute/relative address
c := WordLEToN(AStream.ReadWord);
// Extract column index
ACol := c and MASK_EXCEL_COL_BITS_BIFF8;
// Extract info on absolute/relative addresses.
AFlags := [];
if (c and MASK_EXCEL_RELATIVE_COL = 1) then Include(AFlags, rfRelCol);
if (c and MASK_EXCEL_RELATIVE_ROW = 1) then Include(AFlags, rfRelRow);
end;
{ Reads a cell range address used in an RPN formula element.
Evaluates the corresponding bits to distinguish between absolute and
relative addresses.
Overriding the implementation in xlscommon. }
procedure TsSpreadBIFF8Reader.ReadRPNCellRangeAddress(AStream: TStream;
var ARow1, ACol1, ARow2, ACol2: Cardinal; var AFlags: TsRelFlags);
var
c1, c2: word;
begin
// Read row index of first and last rows (2 bytes, each)
ARow1 := WordLEToN(AStream.ReadWord);
ARow2 := WordLEToN(AStream.ReadWord);
// Read column index of first and last columns; they contain info on
// absolute/relative address
c1 := WordLEToN(AStream.ReadWord);
c2 := WordLEToN(AStream.ReadWord);
// Extract column index of rist and last columns
ACol1 := c1 and MASK_EXCEL_COL_BITS_BIFF8;
ACol2 := c2 and MASK_EXCEL_COL_BITS_BIFF8;
// Extract info on absolute/relative addresses.
AFlags := [];
if (c1 and MASK_EXCEL_RELATIVE_COL = 1) then Include(AFlags, rfRelCol);
if (c1 and MASK_EXCEL_RELATIVE_ROW = 1) then Include(AFlags, rfRelRow);
if (c2 and MASK_EXCEL_RELATIVE_COL = 1) then Include(AFlags, rfRelCol2);
if (c2 and MASK_EXCEL_RELATIVE_ROW = 1) then Include(AFlags, rfRelRow2);
end;
procedure TsSpreadBIFF8Reader.ReadSST(const AStream: TStream); procedure TsSpreadBIFF8Reader.ReadSST(const AStream: TStream);
var var
Items: DWORD; Items: DWORD;
@ -1742,6 +1793,15 @@ begin
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
end; end;
{ Helper function for reading a string with 8-bit length. }
function TsSpreadBIFF8Reader.ReadString_8bitLen(AStream: TStream): String;
var
s: widestring;
begin
s := ReadWideString(AStream, true);
Result := s;
end;
procedure TsSpreadBIFF8Reader.ReadStringRecord(AStream: TStream); procedure TsSpreadBIFF8Reader.ReadStringRecord(AStream: TStream);
var var
s: String; s: String;

View File

@ -83,8 +83,9 @@ const
{ Constant Operand Tokens, 3.8} { Constant Operand Tokens, 3.8}
INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand
INT_EXCEL_TOKEN_TSTR = $17; //string INT_EXCEL_TOKEN_TSTR = $17; //string
INT_EXCEL_TOKEN_TERR = $1C; //error value
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
INT_EXCEL_TOKEN_TINT = $1E; //integer INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer
INT_EXCEL_TOKEN_TNUM = $1F; //floating-point INT_EXCEL_TOKEN_TNUM = $1F; //floating-point
{ Operand Tokens } { Operand Tokens }
@ -309,6 +310,13 @@ const
MASK_XF_VERT_ALIGN_BOTTOM = $20; MASK_XF_VERT_ALIGN_BOTTOM = $20;
MASK_XF_VERT_ALIGN_JUSTIFIED = $30; MASK_XF_VERT_ALIGN_JUSTIFIED = $30;
{ Cell Addresses constants, valid for BIFF2-BIFF5 }
MASK_EXCEL_ROW = $3FFF;
MASK_EXCEL_RELATIVE_COL = $4000;
MASK_EXCEL_RELATIVE_ROW = $8000;
{ Note: The assignment of the RELATIVE_COL and _ROW masks is according to
Microsoft's documentation, but opposite to the OpenOffice documentation. }
{ Error codes } { Error codes }
ERR_INTERSECTION_EMPTY = $00; // #NULL! ERR_INTERSECTION_EMPTY = $00; // #NULL!
ERR_DIVIDE_BY_ZERO = $07; // #DIV/0! ERR_DIVIDE_BY_ZERO = $07; // #DIV/0!
@ -399,6 +407,17 @@ type
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); virtual; procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); virtual;
// Read row info // Read row info
procedure ReadRowInfo(AStream: TStream); virtual; procedure ReadRowInfo(AStream: TStream); virtual;
// Read the array of RPN tokens of a formula
procedure ReadRPNCellAddress(AStream: TStream; var ARow, ACol: Cardinal;
var AFlags: TsRelFlags); virtual;
procedure ReadRPNCellRangeAddress(AStream: TStream;
var ARow1, ACol1, ARow2, ACol2: Cardinal; var AFlags: TsRelFlags); virtual;
function ReadRPNFunc(AStream: TStream): Word; virtual;
function ReadRPNTokenArray(AStream: TStream; var AFormula: TsRPNFormula): Boolean;
function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
// Helper function for reading a string with 8-bit length
function ReadString_8bitLen(AStream: TStream): String; virtual;
// Read STRING record (result of string formula) // Read STRING record (result of string formula)
procedure ReadStringRecord(AStream: TStream); virtual; procedure ReadStringRecord(AStream: TStream); virtual;
// Read WINDOW2 record (gridlines, sheet headers) // Read WINDOW2 record (gridlines, sheet headers)
@ -478,6 +497,164 @@ implementation
uses uses
StrUtils, fpsNumFormatParser; 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 }
const
TokenIDs: array[fekCell..fekOpSum, 0..1] 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}
// 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, <>}
// 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}
// 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}
// 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}
// 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}
// 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}
// 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}
// 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}
// 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}
// Other operations
(0, INT_EXCEL_TOKEN_TATTR) {fekOpSum}
);
function ConvertExcelDateTimeToDateTime( function ConvertExcelDateTimeToDateTime(
const AExcelDateNum: Double; ADateMode: TDateMode): TDateTime; const AExcelDateNum: Double; ADateMode: TDateMode): TDateTime;
begin begin
@ -876,9 +1053,10 @@ var
ARow, ACol: Cardinal; ARow, ACol: Cardinal;
XF: WORD; XF: WORD;
ResultFormula: Double; ResultFormula: Double;
RPNFormula: TsRPNFormula;
Data: array [0..7] of BYTE; Data: array [0..7] of BYTE;
Flags: WORD; Flags: WORD;
FormulaSize: BYTE; // FormulaSize: BYTE;
i: Integer; i: Integer;
dt: TDateTime; dt: TDateTime;
nf: TsNumberFormat; nf: TsNumberFormat;
@ -887,6 +1065,8 @@ var
nfs: String; nfs: String;
resultStr: String; resultStr: String;
err: TErrorValue; err: TErrorValue;
ok: Boolean;
cell: PCell;
begin begin
{ BIFF Record header } { BIFF Record header }
@ -904,6 +1084,7 @@ begin
AStream.ReadDWord; AStream.ReadDWord;
{ Formula size } { Formula size }
(*
FormulaSize := WordLEtoN(AStream.ReadWord); FormulaSize := WordLEtoN(AStream.ReadWord);
{ Formula data, output as debug info } { Formula data, output as debug info }
@ -914,6 +1095,7 @@ begin
//RPN data not used by now //RPN data not used by now
AStream.Position := AStream.Position + FormulaSize; AStream.Position := AStream.Position + FormulaSize;
*)
// Now determine the type of the formula result // Now determine the type of the formula result
if (Data[6] = $FF) and (Data[7] = $FF) then if (Data[6] = $FF) and (Data[7] = $FF) then
@ -953,6 +1135,13 @@ begin
FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd, ncs); FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd, ncs);
end; end;
{ Formula token array }
if FWorkbook.ReadFormulas then begin
cell := FWorksheet.FindCell(ARow, ACol);
ok := ReadRPNTokenArray(AStream, cell^.RPNFormulaValue);
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
end;
{Add attributes} {Add attributes}
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
end; end;
@ -1168,6 +1357,230 @@ begin
// changed manually. // changed manually.
end; end;
{ Reads the cell address used in an RPN formula element. Evaluates the corresponding
bits to distinguish between absolute and relative addresses.
Implemented here for BIFF2-BIFF5. BIFF8 must be overridden. }
procedure TsSpreadBIFFReader.ReadRPNCellAddress(AStream: TStream;
var ARow, ACol: Cardinal; var AFlags: TsRelFlags);
var
r: word;
begin
// 2 bytes for row (including absolute/relative info)
r := WordLEToN(AStream.ReadWord);
// 1 byte for column index
ACol := AStream.ReadByte;
// Extract row index
ARow := r and MASK_EXCEL_ROW;
// Extract absolute/relative flags
AFlags := [];
if (r and MASK_EXCEL_RELATIVE_COL = 1) then Include(AFlags, rfRelCol);
if (r and MASK_EXCEL_RELATIVE_ROW = 1) then Include(AFlags, rfRelRow);
end;
{ Reads the cell address used in an RPN formula element. Evaluates the corresponding
bits to distinguish between absolute and relative addresses.
Implemented here for BIFF2-BIFF5. BIFF8 must be overridden. }
procedure TsSpreadBIFFReader.ReadRPNCellRangeAddress(AStream: TStream;
var ARow1, ACol1, ARow2, ACol2: Cardinal; var AFlags: TsRelFlags);
var
r1, r2: word;
begin
// 2 bytes, each, for first and last row (including absolute/relative info)
r1 := WordLEToN(AStream.ReadWord);
r2 := WordLEToN(AStream.ReadWord);
// 1 byte each for fist and last column index
ACol1 := AStream.ReadByte;
ACol2 := AStream.ReadByte;
// Extract row index of first and last row
ARow1 := r1 and MASK_EXCEL_ROW;
ARow2 := r2 and MASK_EXCEL_ROW;
// Extract absolute/relative flags
AFlags := [];
if (r1 and MASK_EXCEL_RELATIVE_COL = 1) then Include(AFlags, rfRelCol);
if (r2 and MASK_EXCEL_RELATIVE_COL = 1) then Include(AFlags, rfRelCol2);
if (r1 and MASK_EXCEL_RELATIVE_ROW = 1) then Include(AFlags, rfRelRow);
if (r2 and MASK_EXCEL_RELATIVE_ROW = 1) then Include(AFlags, rfRelRow2);
end;
{ Reads the identifier for an RPN function with fixed argument count.
Valid for BIFF4-BIFF8. Override in BIFF2-BIFF3 }
function TsSpreadBIFFReader.ReadRPNFunc(AStream: TStream): Word;
begin
Result := WordLEToN(AStream.ReadWord);
end;
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
var AFormula: TsRPNFormula): Boolean;
var
n: Word;
p0: Int64;
token: Byte;
rpnItem: PRPNItem;
supported: boolean;
wordVal: Word; // 2 byte unsigned integer
dblVal: Double; // IEEE 8 byte floating point number
flags: TsRelFlags;
r, c, r2, c2: Cardinal;
fek: TFEKind;
func: Word;
b: Byte;
found: Boolean;
begin
rpnItem := nil;
n := ReadRPNTokenArraySize(AStream);
p0 := AStream.Position;
supported := true;
while (AStream.Position < p0 + n) and supported do begin
token := AStream.ReadByte;
case token of
INT_EXCEL_TOKEN_TREFV:
begin;
ReadRPNCellAddress(AStream, r, c, flags);
rpnItem := RPNCellValue(r, c, flags, rpnItem);
end;
INT_EXCEL_TOKEN_TREFR:
begin
ReadRPNCellAddress(AStream, r, c, flags);
rpnItem := RPNCellRef(r, c, flags, rpnItem);
end;
INT_EXCEL_TOKEN_TREFA:
begin
ReadRPNCellRangeAddress(AStream, r, c, r2, c2, flags);
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
end;
INT_EXCEL_TOKEN_TMISSARG:
rpnItem := RPNMissingArg(rpnItem);
INT_EXCEL_TOKEN_TSTR:
rpnItem := RPNString(ReadString_8BitLen(AStream), rpnItem);
INT_EXCEL_TOKEN_TERR:
rpnItem := RPNErr(AStream.ReadByte, rpnItem);
INT_EXCEL_TOKEN_TBOOL:
rpnItem := RPNBool(AStream.ReadByte=1, rpnItem);
INT_EXCEL_TOKEN_TINT:
rpnItem := RPNInteger(WordLEToN(AStream.ReadWord), rpnItem);
INT_EXCEL_TOKEN_TNUM:
begin
AStream.ReadBuffer(dblVal, 8);
rpnItem := RPNNumber(dblVal, rpnItem);
end;
INT_EXCEL_TOKEN_FUNC_R,
INT_EXCEL_TOKEN_FUNC_V,
INT_EXCEL_TOKEN_FUNC_A:
// functions with fixed argument count
begin
func := ReadRPNFunc(AStream);
found := false;
for fek in TFEKind do begin
if (TokenIDs[fek, 1] = func) and (TokenIDs[fek, 0] = 1) then begin
rpnItem := RPNFunc(fek, rpnItem);
found := true;
break;
end;
end;
if not found then
supported := false;
end;
INT_EXCEL_TOKEN_FUNCVAR_R,
INT_EXCEL_TOKEN_FUNCVAR_V,
INT_EXCEL_TOKEN_FUNCVAR_A:
// functions with variable argument count
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
rpnItem := RPNFunc(fek, b, rpnItem);
found := true;
break;
end;
if not found then
supported := false;
end
else
found := false;
for fek in TFEKind do
if (TokenIDs[fek, 1] = token) and (TokenIDs[fek, 0] = 0) then begin
rpnItem := RPNFunc(fek, rpnItem);
found := true;
break;
end;
if not found then
supported := false;
(*
// binary tokens
INT_EXCEL_TOKEN_TADD:
rpnItem := RPNFunc(fekAdd, rpnItem);
INT_EXCEL_TOKEN_TSUB:
rpnItem := RPNFunc(fekSub, rpnItem);
INT_EXCEL_TOKEN_TMUL:
rpnItem := RPNFunc(fekMul, rpnItem);
INT_EXCEL_TOKEN_TDIV:
rpnItem := RPNFunc(fekDiv, rpnItem);
INT_EXCEL_TOKEN_TPOWER:
rpnItem := RPNFunc(fekPower, rpnItem);
INT_EXCEL_TOKEN_TCONCAT:
rpnItem := RPNFunc(fekConcat, rpnItem);
INT_EXCEL_TOKEN_TLT:
rpnItem := RPNFunc(fekLess, rpnItem);
INT_EXCEL_TOKEN_TLE:
rpnItem := RPNFunc(fekLessEqual, rpnItem);
INT_EXCEL_TOKEN_TEQ:
rpnItem := RPNFunc(fekEqual, rpnItem);
INT_EXCEL_TOKEN_TGE:
rpnItem := RPNFunc(fekGreaterEqual, rpnItem);
INT_EXCEL_TOKEN_TGT:
rpnItem := RPNFunc(fekGreater, rpnItem);
INT_EXCEL_TOKEN_TNE:
rpnItem := RPNFunc(fekNotEqual, rpnItem);
// Unary operations
INT_EXCEL_TOKEN_TUPLUS:
rpnItem := RPNFunc(fekUPlus, rpnItem);
INT_EXCEL_TOKEN_TUMINUS:
rpnItem := RPNFunc(fekUMinus, rpnItem);
INT_EXCEL_TOKEN_TPERCENT:
rpnItem := RPNFunc(fekPercent, rpnItem);
// Operands (--> 3.8)
else
supported := false; *)
end;
end;
if not supported then begin
DestroyRPNFormula(rpnItem);
SetLength(AFormula, 0);
Result := false;
end
else begin
AFormula := CreateRPNFormula(rpnItem);
Result := true;
end;
end;
{ Helper funtion for reading of the size of the token array of an RPN formula.
Is implemented here for BIFF3-BIFF8 where the size is a 2-byte value.
Needs to be rewritten for BIFF2 using a 1-byte size. }
function TsSpreadBIFFReader.ReadRPNTokenArraySize(AStream: TStream): Word;
begin
Result := WordLEToN(AStream.ReadWord);
end;
{ Helper function for reading a string with 8-bit length. Here, we implement the
version for ansistrings since it is valid for all BIFF versions except BIFF8
where it has to overridden. }
function TsSpreadBIFFReader.ReadString_8bitLen(AStream: TStream): String;
var
len: Byte;
s: ansistring;
begin
len := AStream.ReadByte;
SetLength(s, len);
AStream.ReadBuffer(s[1], len);
Result := s;
end;
{ Reads a STRING record. It immediately precedes a FORMULA record which has a { Reads a STRING record. It immediately precedes a FORMULA record which has a
string result. The read value is applied to the FIncompleteCell. string result. The read value is applied to the FIncompleteCell.
Must be overridden because the implementation depends on BIFF version. } Must be overridden because the implementation depends on BIFF version. }
@ -1256,159 +1669,6 @@ end;
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID( function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
AElementKind: TFEKind; out ASecondaryID: Word): Word; AElementKind: TFEKind; out ASecondaryID: Word): Word;
const
{ Explanation of first index:
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 }
TokenIDs: array[fekCell..fekOpSum, 0..1] 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_TSTR), {fekString}
(0, INT_EXCEL_TOKEN_TBOOL), {fekBool}
(0, 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, <>}
// 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}
// 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}
// 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}
// 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}
// 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}
// 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}
// 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}
// 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}
// Other operations
(0, INT_EXCEL_TOKEN_TATTR) {fekOpSum}
);
begin begin
case TokenIDs[AElementKind, 0] of case TokenIDs[AElementKind, 0] of
0: begin 0: begin