You've already forked lazarus-ccr
+ Add support for more/easier RPN formulas in XLS
- Allow relative cell references in RPN formulas - Simplify generation of RPN formulas by using nested function calls - Add writing of cell ranges for formulas in BIFF2/BIFF5 - Simplification of code e.g. by replacing the huge case instruction in TsSpreadBiffWriter.FormulaElementKindToExcelTokenID by a look-up table. Fixes mantis issue #25718; patch by wp; thanks a lot! git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2931 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -14,7 +14,7 @@ unit fpspreadsheet;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, fpimage, AVL_Tree, avglvltree, lconvencoding;
|
Classes, SysUtils, fpimage, AVL_Tree, avglvltree, lconvencoding, fpsutils;
|
||||||
|
|
||||||
type
|
type
|
||||||
TsSpreadsheetFormat = (sfExcel2, sfExcel3, sfExcel4, sfExcel5, sfExcel8,
|
TsSpreadsheetFormat = (sfExcel2, sfExcel3, sfExcel4, sfExcel5, sfExcel8,
|
||||||
@ -56,15 +56,51 @@ type
|
|||||||
DoubleValue: double;
|
DoubleValue: double;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ Expanded formula. Used by backend modules. Provides more information then the text only }
|
{@@ Expanded formula. Used by backend modules. Provides more information than the text only
|
||||||
|
|
||||||
|
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 !!!
|
||||||
|
}
|
||||||
|
|
||||||
TFEKind = (
|
TFEKind = (
|
||||||
{ Basic operands }
|
{ Basic operands }
|
||||||
fekCell, fekCellRange, fekNum,
|
fekCell, fekCellRef, fekCellRange, fekNum, fekString, fekBool, fekMissingArg,
|
||||||
{ Basic operations }
|
{ Basic operations }
|
||||||
fekAdd, fekSub, fekDiv, fekMul,
|
fekAdd, fekSub, fekDiv, fekMul, fekPercent, fekPower, fekUMinus, fekUPlus,
|
||||||
|
fekConcat, // string concatenation
|
||||||
|
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
|
||||||
{ Built-in/Worksheet Functions}
|
{ Built-in/Worksheet Functions}
|
||||||
fekABS, fekDATE, fekROUND, fekTIME,
|
// math
|
||||||
|
fekABS, fekACOS, fekACOSH, fekASIN, fekASINH, fekATAN, fekATANH,
|
||||||
|
fekCOS, fekCOSH, fekDEGREES, fekEXP, fekINT, fekLN, fekLOG,
|
||||||
|
fekLOG10, fekPI, fekRADIANS, fekRAND, fekROUND,
|
||||||
|
fekSIGN, fekSIN, fekSINH, fekSQRT,
|
||||||
|
fekTAN, fekTANH,
|
||||||
|
// date/time
|
||||||
|
fekDATE, fekDATEDIF, fekDATEVALUE, fekDAY, fekHOUR, fekMINUTE, fekMONTH,
|
||||||
|
fekNOW, fekSECOND, fekTIME, fekTIMEVALUE, fekTODAY, fekWEEKDAY, fekYEAR,
|
||||||
|
// statistical
|
||||||
|
fekAVEDEV, fekAVERAGE, fekBETADIST, fekBETAINV, fekBINOMDIST, fekCHIDIST,
|
||||||
|
fekCHIINV, fekCOUNT, fekCOUNTA, fekCOUNTBLANK, fekCOUNTIF,
|
||||||
|
fekMAX, fekMEDIAN, fekMIN, fekPERMUT, fekPOISSON, fekPRODUCT,
|
||||||
|
fekSTDEV, fekSTDEVP, fekSUM, fekSUMIF, fekSUMSQ, fekVAR, fekVARP,
|
||||||
|
// financial
|
||||||
|
fekFV, fekNPER, fekPV, fekPMT, fekRATE,
|
||||||
|
// logical
|
||||||
|
fekAND, fekFALSE, fekIF, fekNOT, fekOR, fekTRUE,
|
||||||
|
// string
|
||||||
|
fekCHAR, fekCODE, fekLEFT, fekLOWER, fekMID, fekPROPER, fekREPLACE, fekRIGHT,
|
||||||
|
fekSUBSTITUTE, fekTRIM, fekUPPER,
|
||||||
|
// lookup/reference
|
||||||
|
fekCOLUMN, fekCOLUMNS, fekROW, fekROWS,
|
||||||
|
// info
|
||||||
|
fekCELLINFO, fekINFO, fekIsBLANK, fekIsERR, fekIsERROR,
|
||||||
|
fekIsLOGICAL, fekIsNA, fekIsNONTEXT, fekIsNUMBER, fekIsRef, fekIsTEXT,
|
||||||
|
fekValue,
|
||||||
{ Other operations }
|
{ Other operations }
|
||||||
fekOpSUM
|
fekOpSUM
|
||||||
);
|
);
|
||||||
@ -72,9 +108,13 @@ type
|
|||||||
TsFormulaElement = record
|
TsFormulaElement = record
|
||||||
ElementKind: TFEKind;
|
ElementKind: TFEKind;
|
||||||
Row, Row2: Word; // zero-based
|
Row, Row2: Word; // zero-based
|
||||||
Col, Col2: Byte; // zero-based
|
Col, Col2: Word; // zero-based
|
||||||
Param1, Param2: Word; // Extra parameters
|
Param1, Param2: Word; // Extra parameters
|
||||||
DoubleValue: double;
|
DoubleValue: double;
|
||||||
|
IntValue: Word;
|
||||||
|
StringValue: String;
|
||||||
|
RelFlags: TsRelFlags; // store info on relative/absolute addresses
|
||||||
|
ParamsNum: Byte;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TsExpandedFormula = array of TsFormulaElement;
|
TsExpandedFormula = array of TsFormulaElement;
|
||||||
@ -378,6 +418,54 @@ type
|
|||||||
Format: TsSpreadsheetFormat;
|
Format: TsSpreadsheetFormat;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ Helper for simplification of RPN formula creation }
|
||||||
|
PRPNItem = ^TRPNItem;
|
||||||
|
TRPNItem = record
|
||||||
|
FE: TsFormulaElement;
|
||||||
|
Next: PRPNItem;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Simple creation an RPNFormula array to be used in fpspreadsheet.
|
||||||
|
For each formula element, use one of the RPNxxxx functions implemented here.
|
||||||
|
They are designed to be nested into each other. Terminate the chain by
|
||||||
|
using nil.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
The RPN formula for the string expression "$A1+2" can be created as follows:
|
||||||
|
|
||||||
|
var
|
||||||
|
f: TsRPNFormula;
|
||||||
|
|
||||||
|
f := CreateRPNFormula(
|
||||||
|
RPNCellValue('A1',
|
||||||
|
RPNNumber(2,
|
||||||
|
RPNFunc(fekAdd,
|
||||||
|
nil))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreateRPNFormula(AItem: PRPNItem): TsRPNFormula;
|
||||||
|
|
||||||
|
function RPNBool(AValue: Boolean;
|
||||||
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
function RPNCellValue(ACellAddress: String;
|
||||||
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNCellValue(ARow, ACol: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNCellRef(ACellAddress: String;
|
||||||
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNCellRef(ARow, ACol: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNCellRange(ACellRangeAddress: String;
|
||||||
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNMissingArg(ANext: PRPNItem): PRPNItem;
|
||||||
|
function RPNNumber(AValue: Double; ANext: PRPNItem): PRPNItem;
|
||||||
|
function RPNString(AValue: String; ANext: PRPNItem): PRPNItem;
|
||||||
|
function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
|
||||||
var
|
var
|
||||||
GsSpreadFormats: array of TsSpreadFormatData;
|
GsSpreadFormats: array of TsSpreadFormatData;
|
||||||
|
|
||||||
@ -1874,6 +1962,220 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ Simplified creation of RPN formulas }
|
||||||
|
|
||||||
|
function NewRPNItem: PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := GetMem(SizeOf(TRPNItem));
|
||||||
|
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
||||||
|
Result^.FE.StringValue := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DisposeRPNItem(AItem: PRPNItem);
|
||||||
|
begin
|
||||||
|
if AItem <> nil then
|
||||||
|
FreeMem(AItem, SizeOf(TRPNItem));
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates a boolean value entry in the RPN array.
|
||||||
|
}
|
||||||
|
function RPNBool(AValue: Boolean; ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekBool;
|
||||||
|
if AValue then Result^.FE.DoubleValue := 1.0 else Result^.FE.DoubleValue := 0.0;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a cell value, specifed by its
|
||||||
|
address, e.g. 'A1'. Takes care of absolute and relative cell addresses.
|
||||||
|
}
|
||||||
|
function RPNCellValue(ACellAddress: String; ANext: PRPNItem): PRPNItem;
|
||||||
|
var
|
||||||
|
r,c: Integer;
|
||||||
|
flags: TsRelFlags;
|
||||||
|
begin
|
||||||
|
if not ParseCellString(ACellAddress, r, c, flags) then
|
||||||
|
raise Exception.CreateFmt('"%s" is not a valid cell address.', [ACellAddress]);
|
||||||
|
Result := RPNCellValue(r,c, flags, ANext);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a cell value, specifed by its
|
||||||
|
row and column index and a flag containing information on relative addresses.
|
||||||
|
}
|
||||||
|
function RPNCellValue(ARow, ACol: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekCell;
|
||||||
|
Result^.FE.Row := ARow;
|
||||||
|
Result^.FE.Col := ACol;
|
||||||
|
Result^.FE.RelFlags := AFlags;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a cell reference, specifed by its
|
||||||
|
address, e.g. 'A1'. Takes care of absolute and relative cell addresses.
|
||||||
|
"Cell reference" means that all properties of the cell can be handled.
|
||||||
|
Note that most Excel formulas with cells require the cell value only
|
||||||
|
(--> RPNCellValue)
|
||||||
|
}
|
||||||
|
function RPNCellRef(ACellAddress: String; ANext: PRPNItem): PRPNItem;
|
||||||
|
var
|
||||||
|
r,c: Integer;
|
||||||
|
flags: TsRelFlags;
|
||||||
|
begin
|
||||||
|
if not ParseCellString(ACellAddress, r, c, flags) then
|
||||||
|
raise Exception.CreateFmt('"%s" is not a valid cell address.', [ACellAddress]);
|
||||||
|
Result := RPNCellRef(r,c, flags, ANext);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a cell reference, specifed by its
|
||||||
|
row and column index and flags containing information on relative addresses.
|
||||||
|
"Cell reference" means that all properties of the cell can be handled.
|
||||||
|
Note that most Excel formulas with cells require the cell value only
|
||||||
|
(--> RPNCellValue)
|
||||||
|
}
|
||||||
|
function RPNCellRef(ARow, ACol: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekCellRef;
|
||||||
|
Result^.FE.Row := ARow;
|
||||||
|
Result^.FE.Col := ACol;
|
||||||
|
Result^.FE.RelFlags := AFlags;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a range of cells, specified by an
|
||||||
|
Excel-style address, e.g. A1:G5. As in Excel, use a $ sign to indicate
|
||||||
|
absolute addresses.
|
||||||
|
}
|
||||||
|
function RPNCellRange(ACellRangeAddress: String; ANext: PRPNItem): PRPNItem;
|
||||||
|
var
|
||||||
|
r1,c1, r2,c2: Integer;
|
||||||
|
flags: TsRelFlags;
|
||||||
|
begin
|
||||||
|
if not ParseCellRangeString(ACellRangeAddress, r1,c1, r2,c2, flags) then
|
||||||
|
raise Exception.CreateFmt('"%s" is not a valid cell range address.', [ACellRangeAddress]);
|
||||||
|
Result := RPNCellRange(r1,c1, r2,c2, flags, ANext);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a range of cells, specified by the
|
||||||
|
row/column indexes of the top/left and bottom/right corners of the block.
|
||||||
|
The flags indicate relative indexes.
|
||||||
|
}
|
||||||
|
function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
|
||||||
|
ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekCellRange;
|
||||||
|
Result^.FE.Row := ARow;
|
||||||
|
Result^.FE.Col := ACol;
|
||||||
|
Result^.FE.Row2 := ARow2;
|
||||||
|
Result^.FE.Col2 := ACol2;
|
||||||
|
Result^.FE.RelFlags := AFlags;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a missing argument in of function call.
|
||||||
|
}
|
||||||
|
function RPNMissingArg(ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekMissingArg;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a number. Integers and floating-point
|
||||||
|
values can be used likewise.
|
||||||
|
}
|
||||||
|
function RPNNumber(AValue: Double; ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekNum;
|
||||||
|
Result^.FE.DoubleValue := AValue;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for a string.
|
||||||
|
}
|
||||||
|
function RPNString(AValue: String; ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := NewRPNItem;
|
||||||
|
Result^.FE.ElementKind := fekString;
|
||||||
|
Result^.FE.StringValue := AValue;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for an Excel function or operation
|
||||||
|
specified by its TokenID (--> TFEKind). Note that array elements for all
|
||||||
|
needed parameters must have been created before.
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
Result^.Next := ANext;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an entry in the RPN array for an Excel function or operation
|
||||||
|
specified by its TokenID (--> TFEKind). Specify the number of parameters used.
|
||||||
|
They must have been created before.
|
||||||
|
}
|
||||||
|
function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem;
|
||||||
|
begin
|
||||||
|
Result := RPNFunc(AToken, ANext);
|
||||||
|
Result^.FE.ParamsNum := ANumParams;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Creates an RPN formula by a single call using nested RPN items.
|
||||||
|
}
|
||||||
|
function CreateRPNFormula(AItem: PRPNItem): TsRPNFormula;
|
||||||
|
var
|
||||||
|
item: PRPNItem;
|
||||||
|
nextitem: PRPNItem;
|
||||||
|
n: Integer;
|
||||||
|
begin
|
||||||
|
// Determine count of RPN elements
|
||||||
|
n := 0;
|
||||||
|
item := AItem;
|
||||||
|
while item <> nil do begin
|
||||||
|
inc(n);
|
||||||
|
item := item^.Next;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Set array length of TsRPNFormula result
|
||||||
|
SetLength(Result, n);
|
||||||
|
|
||||||
|
// Copy FormulaElements to result and free temporary RPNItems
|
||||||
|
item := AItem;
|
||||||
|
n := 0;
|
||||||
|
while item <> nil do begin
|
||||||
|
nextitem := item^.Next;
|
||||||
|
Result[n] := item^.FE;
|
||||||
|
inc(n);
|
||||||
|
DisposeRPNItem(item);
|
||||||
|
item := nextitem;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
|
|
||||||
SetLength(GsSpreadFormats, 0);
|
SetLength(GsSpreadFormats, 0);
|
||||||
|
@ -14,6 +14,9 @@ uses
|
|||||||
type
|
type
|
||||||
TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection);
|
TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection);
|
||||||
|
|
||||||
|
TsRelFlag = (rfRelRow, rfRelCol, rfRelRow2, rfRelCol2);
|
||||||
|
TsRelFlags = set of TsRelFlag;
|
||||||
|
|
||||||
const
|
const
|
||||||
// Date formatting string for unambiguous date/time display as strings
|
// Date formatting string for unambiguous date/time display as strings
|
||||||
// Can be used for text output when date/time cell support is not available
|
// Can be used for text output when date/time cell support is not available
|
||||||
@ -35,8 +38,13 @@ function WideStringLEToN(const AValue: WideString): WideString;
|
|||||||
function ParseIntervalString(const AStr: string;
|
function ParseIntervalString(const AStr: string;
|
||||||
var AFirstCellRow, AFirstCellCol, ACount: Integer;
|
var AFirstCellRow, AFirstCellCol, ACount: Integer;
|
||||||
var ADirection: TsSelectionDirection): Boolean;
|
var ADirection: TsSelectionDirection): Boolean;
|
||||||
|
function ParseCellRangeString(const AStr: string;
|
||||||
|
var AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer;
|
||||||
|
var AFlags: TsRelFlags): Boolean;
|
||||||
function ParseCellString(const AStr: string;
|
function ParseCellString(const AStr: string;
|
||||||
var ACellRow, ACellCol: Integer): Boolean;
|
var ACellRow, ACellCol: Integer; var AFlags: TsRelFlags): Boolean; overload;
|
||||||
|
function ParseCellString(const AStr: string;
|
||||||
|
var ACellRow, ACellCol: Integer): Boolean; overload;
|
||||||
function ParseCellRowString(const AStr: string;
|
function ParseCellRowString(const AStr: string;
|
||||||
var AResult: Integer): Boolean;
|
var AResult: Integer): Boolean;
|
||||||
function ParseCellColString(const AStr: string;
|
function ParseCellColString(const AStr: string;
|
||||||
@ -148,11 +156,17 @@ function ParseIntervalString(const AStr: string;
|
|||||||
var AFirstCellRow, AFirstCellCol, ACount: Integer;
|
var AFirstCellRow, AFirstCellCol, ACount: Integer;
|
||||||
var ADirection: TsSelectionDirection): Boolean;
|
var ADirection: TsSelectionDirection): Boolean;
|
||||||
var
|
var
|
||||||
Cells: TStringList;
|
//Cells: TStringList;
|
||||||
LastCellRow, LastCellCol: Integer;
|
LastCellRow, LastCellCol: Integer;
|
||||||
|
p: Integer;
|
||||||
|
s1, s2: String;
|
||||||
begin
|
begin
|
||||||
Result := True;
|
Result := True;
|
||||||
|
|
||||||
|
{ Simpler:
|
||||||
|
use "pos" instead of the TStringList overhead.
|
||||||
|
And: the StringList is not free'ed here
|
||||||
|
|
||||||
// First get the cells
|
// First get the cells
|
||||||
Cells := TStringList.Create;
|
Cells := TStringList.Create;
|
||||||
ExtractStrings([':'],[], PChar(AStr), Cells);
|
ExtractStrings([':'],[], PChar(AStr), Cells);
|
||||||
@ -162,6 +176,19 @@ begin
|
|||||||
if not Result then Exit;
|
if not Result then Exit;
|
||||||
Result := ParseCellString(Cells[1], LastCellRow, LastCellCol);
|
Result := ParseCellString(Cells[1], LastCellRow, LastCellCol);
|
||||||
if not Result then Exit;
|
if not Result then Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First find the position of the colon and split into parts
|
||||||
|
p := pos(':', AStr);
|
||||||
|
if p = 0 then exit(false);
|
||||||
|
s1 := copy(AStr, 1, p-1);
|
||||||
|
s2 := copy(AStr, p+1, Length(AStr));
|
||||||
|
|
||||||
|
// Then parse each of them
|
||||||
|
Result := ParseCellString(s1, AFirstCellRow, AFirstCellCol);
|
||||||
|
if not Result then Exit;
|
||||||
|
Result := ParseCellString(s2, LastCellRow, LastCellCol);
|
||||||
|
if not Result then Exit;
|
||||||
|
|
||||||
if AFirstCellRow = LastCellRow then
|
if AFirstCellRow = LastCellRow then
|
||||||
begin
|
begin
|
||||||
@ -176,6 +203,42 @@ begin
|
|||||||
else Exit(False);
|
else Exit(False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@
|
||||||
|
Parses strings like A5:C10 into a range selection information.
|
||||||
|
Return also information on relative/absolute cells.
|
||||||
|
}
|
||||||
|
function ParseCellRangeString(const AStr: string;
|
||||||
|
var AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer;
|
||||||
|
var AFlags: TsRelFlags): Boolean;
|
||||||
|
var
|
||||||
|
p: Integer;
|
||||||
|
s: String;
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
|
||||||
|
// First find the colon
|
||||||
|
p := pos(':', AStr);
|
||||||
|
if p = 0 then exit(false);
|
||||||
|
|
||||||
|
// Analyze part after the colon
|
||||||
|
s := copy(AStr, p+1, Length(AStr));
|
||||||
|
Result := ParseCellString(s, ALastCellRow, ALastCellCol, AFlags);
|
||||||
|
if not Result then exit;
|
||||||
|
if (rfRelRow in AFlags) then begin
|
||||||
|
Include(AFlags, rfRelRow2);
|
||||||
|
Exclude(AFlags, rfRelRow);
|
||||||
|
end;
|
||||||
|
if (rfRelCol in AFlags) then begin
|
||||||
|
Include(AFlags, rfRelCol2);
|
||||||
|
Exclude(AFlags, rfRelCol);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Analyze part before the colon
|
||||||
|
s := copy(AStr, 1, p-1);
|
||||||
|
Result := ParseCellString(s, AFirstCellRow, AFirstCellCol, AFlags);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
Parses a cell string, like 'A1' into zero-based column and row numbers
|
Parses a cell string, like 'A1' into zero-based column and row numbers
|
||||||
|
|
||||||
@ -184,13 +247,17 @@ end;
|
|||||||
0 - Reading Column part 1 (necesserely needs a letter)
|
0 - Reading Column part 1 (necesserely needs a letter)
|
||||||
1 - Reading Column part 2, but could be the first number as well
|
1 - Reading Column part 2, but could be the first number as well
|
||||||
2 - Reading Row
|
2 - Reading Row
|
||||||
|
|
||||||
|
'AFlags' indicates relative addresses.
|
||||||
}
|
}
|
||||||
function ParseCellString(const AStr: string; var ACellRow, ACellCol: Integer): Boolean;
|
function ParseCellString(const AStr: string; var ACellRow, ACellCol: Integer;
|
||||||
|
var AFlags: TsRelFlags): Boolean;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
state: Integer;
|
state: Integer;
|
||||||
Col, Row: string;
|
Col, Row: string;
|
||||||
lChar: Char;
|
lChar: Char;
|
||||||
|
isAbs: Boolean;
|
||||||
const
|
const
|
||||||
cLetters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
cLetters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
||||||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z'];
|
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z'];
|
||||||
@ -201,11 +268,20 @@ begin
|
|||||||
state := 0;
|
state := 0;
|
||||||
Col := '';
|
Col := '';
|
||||||
Row := '';
|
Row := '';
|
||||||
|
AFlags := [rfRelCol, rfRelRow];
|
||||||
|
isAbs := false;
|
||||||
|
|
||||||
// Separates the string into a row and a col
|
// Separates the string into a row and a col
|
||||||
for i := 0 to Length(AStr) - 1 do
|
for i := 1 to Length(AStr) do
|
||||||
begin
|
begin
|
||||||
lChar := AStr[i + 1];
|
lChar := AStr[i];
|
||||||
|
|
||||||
|
if lChar = '$' then begin
|
||||||
|
if isAbs then
|
||||||
|
exit(false);
|
||||||
|
isAbs := true;
|
||||||
|
continue;
|
||||||
|
end;
|
||||||
|
|
||||||
case state of
|
case state of
|
||||||
|
|
||||||
@ -214,6 +290,9 @@ begin
|
|||||||
if lChar in cLetters then
|
if lChar in cLetters then
|
||||||
begin
|
begin
|
||||||
Col := lChar;
|
Col := lChar;
|
||||||
|
if isAbs then
|
||||||
|
Exclude(AFlags, rfRelCol);
|
||||||
|
isAbs := false;
|
||||||
state := 1;
|
state := 1;
|
||||||
end
|
end
|
||||||
else Exit(False);
|
else Exit(False);
|
||||||
@ -221,10 +300,14 @@ begin
|
|||||||
|
|
||||||
1:
|
1:
|
||||||
begin
|
begin
|
||||||
if lChar in cLetters then Col := Col + lChar
|
if lChar in cLetters then
|
||||||
|
Col := Col + lChar
|
||||||
else if lChar in cDigits then
|
else if lChar in cDigits then
|
||||||
begin
|
begin
|
||||||
Row := lChar;
|
Row := lChar;
|
||||||
|
if isAbs then
|
||||||
|
Exclude(AFlags, rfRelRow);
|
||||||
|
isAbs := false;
|
||||||
state := 2;
|
state := 2;
|
||||||
end
|
end
|
||||||
else Exit(False);
|
else Exit(False);
|
||||||
@ -244,6 +327,16 @@ begin
|
|||||||
ParseCellColString(Col, ACellCol);
|
ParseCellColString(Col, ACellCol);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ for compatibility with old version which does not return flags for relative
|
||||||
|
cell addresses }
|
||||||
|
function ParseCellString(const AStr: string;
|
||||||
|
var ACellRow, ACellCol: Integer): Boolean;
|
||||||
|
var
|
||||||
|
flags: TsRelFlags;
|
||||||
|
begin
|
||||||
|
ParseCellString(AStr, ACellRow, ACellCol, flags);
|
||||||
|
end;
|
||||||
|
|
||||||
function ParseCellRowString(const AStr: string; var AResult: Integer): Boolean;
|
function ParseCellRowString(const AStr: string; var AResult: Integer): Boolean;
|
||||||
begin
|
begin
|
||||||
try
|
try
|
||||||
@ -267,6 +360,12 @@ begin
|
|||||||
AResult := (Ord(AStr[1]) - Ord('A') + 1) * INT_NUM_LETTERS
|
AResult := (Ord(AStr[1]) - Ord('A') + 1) * INT_NUM_LETTERS
|
||||||
+ Ord(AStr[2]) - Ord('A');
|
+ Ord(AStr[2]) - Ord('A');
|
||||||
end
|
end
|
||||||
|
else if Length(AStr) = 3 then
|
||||||
|
begin
|
||||||
|
AResult := (Ord(AStr[1]) - Ord('A') + 1) * INT_NUM_LETTERS * INT_NUM_LETTERS
|
||||||
|
+ (Ord(AStr[2]) - Ord('A') + 1) * INT_NUM_LETTERS
|
||||||
|
+ Ord(AStr[3]) - Ord('A');
|
||||||
|
end
|
||||||
else Exit(False);
|
else Exit(False);
|
||||||
|
|
||||||
Result := True;
|
Result := True;
|
||||||
|
@ -50,7 +50,7 @@ type
|
|||||||
procedure TearDown; override;
|
procedure TearDown; override;
|
||||||
published
|
published
|
||||||
// Current fpspreadsheet does not yet have support for new RPN formulas
|
// Current fpspreadsheet does not yet have support for new RPN formulas
|
||||||
{.$DEFINE FPSPREAD_HAS_NEWRPNSUPPORT}
|
{$DEFINE FPSPREAD_HAS_NEWRPNSUPPORT}
|
||||||
{$IFDEF FPSPREAD_HAS_NEWRPNSUPPORT}
|
{$IFDEF FPSPREAD_HAS_NEWRPNSUPPORT}
|
||||||
// As described in bug 25718: Feature request & patch: Implementation of writing more functions
|
// As described in bug 25718: Feature request & patch: Implementation of writing more functions
|
||||||
// Writes all rpn formulas. Use Excel or Open/LibreOffice to check validity.
|
// Writes all rpn formulas. Use Excel or Open/LibreOffice to check validity.
|
||||||
|
Binary file not shown.
@ -57,9 +57,8 @@ type
|
|||||||
|
|
||||||
{ TsSpreadBIFF2Writer }
|
{ TsSpreadBIFF2Writer }
|
||||||
|
|
||||||
TsSpreadBIFF2Writer = class(TsCustomSpreadWriter)
|
TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter)
|
||||||
private
|
private
|
||||||
function FEKindToExcelID(AElement: TFEKind; var AParamsNum, AFuncNum: Byte): Byte;
|
|
||||||
procedure WriteCellFormatting(AStream: TStream; ACell: PCell);
|
procedure WriteCellFormatting(AStream: TStream; ACell: PCell);
|
||||||
public
|
public
|
||||||
{ General writing methods }
|
{ General writing methods }
|
||||||
@ -86,8 +85,8 @@ const
|
|||||||
|
|
||||||
{ Cell Addresses constants }
|
{ Cell Addresses constants }
|
||||||
MASK_EXCEL_ROW = $3FFF;
|
MASK_EXCEL_ROW = $3FFF;
|
||||||
MASK_EXCEL_RELATIVE_ROW = $4000;
|
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||||
MASK_EXCEL_RELATIVE_COL = $8000;
|
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
||||||
|
|
||||||
{ BOF record constants }
|
{ BOF record constants }
|
||||||
INT_EXCEL_SHEET = $0010;
|
INT_EXCEL_SHEET = $0010;
|
||||||
@ -96,47 +95,6 @@ const
|
|||||||
|
|
||||||
{ TsSpreadBIFF2Writer }
|
{ TsSpreadBIFF2Writer }
|
||||||
|
|
||||||
function TsSpreadBIFF2Writer.FEKindToExcelID(AElement: TFEKind; var AParamsNum, AFuncNum: Byte): Byte;
|
|
||||||
begin
|
|
||||||
AFuncNum := 0;
|
|
||||||
|
|
||||||
case AElement of
|
|
||||||
{ Operands }
|
|
||||||
fekCell: Result := INT_EXCEL_TOKEN_TREFV;
|
|
||||||
fekNum: Result := INT_EXCEL_TOKEN_TNUM;
|
|
||||||
{ Operators }
|
|
||||||
fekAdd: Result := INT_EXCEL_TOKEN_TADD;
|
|
||||||
fekSub: Result := INT_EXCEL_TOKEN_TSUB;
|
|
||||||
fekDiv: Result := INT_EXCEL_TOKEN_TDIV;
|
|
||||||
fekMul: Result := INT_EXCEL_TOKEN_TMUL;
|
|
||||||
{ Built-in/worksheet functions }
|
|
||||||
fekABS:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 1;
|
|
||||||
AFuncNum := INT_EXCEL_SHEET_FUNC_ABS;
|
|
||||||
end;
|
|
||||||
fekDATE:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 3;
|
|
||||||
AFuncNum := INT_EXCEL_SHEET_FUNC_DATE;
|
|
||||||
end;
|
|
||||||
fekROUND:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 2;
|
|
||||||
AFuncNum := INT_EXCEL_SHEET_FUNC_ROUND;
|
|
||||||
end;
|
|
||||||
fekTIME:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 3;
|
|
||||||
AFuncNum := INT_EXCEL_SHEET_FUNC_TIME;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell);
|
procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell);
|
||||||
var
|
var
|
||||||
BorderByte: Byte = 0;
|
BorderByte: Byte = 0;
|
||||||
@ -239,7 +197,10 @@ var
|
|||||||
i: Integer;
|
i: Integer;
|
||||||
RPNLength: Word;
|
RPNLength: Word;
|
||||||
TokenArraySizePos, RecordSizePos, FinalPos: Cardinal;
|
TokenArraySizePos, RecordSizePos, FinalPos: Cardinal;
|
||||||
FormulaKind, ParamsNum, ExtraInfo: Byte;
|
FormulaKind, ExtraInfo: Word;
|
||||||
|
r: Cardinal;
|
||||||
|
len: Integer;
|
||||||
|
s: ansistring;
|
||||||
begin
|
begin
|
||||||
RPNLength := 0;
|
RPNLength := 0;
|
||||||
FormulaResult := 0.0;
|
FormulaResult := 0.0;
|
||||||
@ -258,7 +219,7 @@ begin
|
|||||||
AStream.WriteByte($0);
|
AStream.WriteByte($0);
|
||||||
AStream.WriteByte($0);
|
AStream.WriteByte($0);
|
||||||
|
|
||||||
{ Result of the formula in IEE 754 floating-point value }
|
{ Result of the formula in IEEE 754 floating-point value }
|
||||||
AStream.WriteBuffer(FormulaResult, 8);
|
AStream.WriteBuffer(FormulaResult, 8);
|
||||||
|
|
||||||
{ 0 = Do not recalculate
|
{ 0 = Do not recalculate
|
||||||
@ -276,8 +237,9 @@ begin
|
|||||||
{ Formula data (RPN token array) }
|
{ Formula data (RPN token array) }
|
||||||
for i := 0 to Length(AFormula) - 1 do
|
for i := 0 to Length(AFormula) - 1 do
|
||||||
begin
|
begin
|
||||||
|
|
||||||
{ Token identifier }
|
{ Token identifier }
|
||||||
FormulaKind := FEKindToExcelID(AFormula[i].ElementKind, ParamsNum, ExtraInfo);
|
FormulaKind := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind, ExtraInfo);
|
||||||
AStream.WriteByte(FormulaKind);
|
AStream.WriteByte(FormulaKind);
|
||||||
Inc(RPNLength);
|
Inc(RPNLength);
|
||||||
|
|
||||||
@ -295,17 +257,60 @@ begin
|
|||||||
Inc(RPNLength, 8);
|
Inc(RPNLength, 8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TSTR:
|
||||||
|
begin
|
||||||
|
s := ansistring(AFormula[i].StringValue);
|
||||||
|
len := Length(s);
|
||||||
|
AStream.WriteByte(len);
|
||||||
|
AStream.WriteBuffer(s[1], len);
|
||||||
|
Inc(RPNLength, len + 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TBOOL:
|
||||||
|
begin
|
||||||
|
AStream.WriteByte(ord(AFormula[i].DoubleValue <> 0.0));
|
||||||
|
inc(RPNLength, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA:
|
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA:
|
||||||
begin
|
begin
|
||||||
AStream.WriteWord(AFormula[i].Row and MASK_EXCEL_ROW);
|
r := AFormula[i].Row and MASK_EXCEL_ROW;
|
||||||
|
if (rfRelRow in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(r);
|
||||||
AStream.WriteByte(AFormula[i].Col);
|
AStream.WriteByte(AFormula[i].Col);
|
||||||
Inc(RPNLength, 3);
|
Inc(RPNLength, 3);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TAREA_R: { fekCellRange }
|
||||||
|
begin
|
||||||
|
r := AFormula[i].Row and MASK_EXCEL_ROW;
|
||||||
|
if (rfRelRow in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(WordToLE(r));
|
||||||
|
|
||||||
|
r := AFormula[i].Row2 and MASK_EXCEL_ROW;
|
||||||
|
if (rfRelRow2 in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol2 in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(WordToLE(r));
|
||||||
|
|
||||||
|
AStream.WriteByte(AFormula[i].Col);
|
||||||
|
AStream.WriteByte(AFormula[i].Col2);
|
||||||
|
Inc(RPNLength, 6);
|
||||||
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
|
||||||
|
begin
|
||||||
|
AStream.WriteByte(Lo(ExtraInfo));
|
||||||
|
Inc(RPNLength, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
INT_EXCEL_TOKEN_FUNCVAR_V:
|
INT_EXCEL_TOKEN_FUNCVAR_V:
|
||||||
begin
|
begin
|
||||||
AStream.WriteByte(ParamsNum);
|
AStream.WriteByte(AFormula[i].ParamsNum);
|
||||||
AStream.WriteByte(ExtraInfo);
|
AStream.WriteByte(Lo(ExtraInfo));
|
||||||
|
// taking only the low-bytes, the high-bytes are needed for compatibility
|
||||||
|
// with other BIFF formats...
|
||||||
Inc(RPNLength, 2);
|
Inc(RPNLength, 2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -106,7 +106,6 @@ type
|
|||||||
TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter)
|
TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter)
|
||||||
private
|
private
|
||||||
WorkBookEncoding: TsEncoding;
|
WorkBookEncoding: TsEncoding;
|
||||||
function FEKindToExcelID(AElement: TFEKind; var AParamsNum: Byte; var AExtra: Word): Byte;
|
|
||||||
public
|
public
|
||||||
// constructor Create;
|
// constructor Create;
|
||||||
// destructor Destroy; override;
|
// destructor Destroy; override;
|
||||||
@ -156,8 +155,8 @@ const
|
|||||||
|
|
||||||
{ Cell Addresses constants }
|
{ Cell Addresses constants }
|
||||||
MASK_EXCEL_ROW = $3FFF;
|
MASK_EXCEL_ROW = $3FFF;
|
||||||
MASK_EXCEL_RELATIVE_ROW = $4000;
|
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||||
MASK_EXCEL_RELATIVE_COL = $8000;
|
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;
|
||||||
@ -260,48 +259,6 @@ const
|
|||||||
|
|
||||||
{ TsSpreadBIFF5Writer }
|
{ TsSpreadBIFF5Writer }
|
||||||
|
|
||||||
function TsSpreadBIFF5Writer.FEKindToExcelID(AElement: TFEKind;
|
|
||||||
var AParamsNum: Byte; var AExtra: Word): Byte;
|
|
||||||
begin
|
|
||||||
AExtra := 0;
|
|
||||||
|
|
||||||
case AElement of
|
|
||||||
{ Operands }
|
|
||||||
fekCell: Result := INT_EXCEL_TOKEN_TREFV;
|
|
||||||
fekNum: Result := INT_EXCEL_TOKEN_TNUM;
|
|
||||||
{ Operators }
|
|
||||||
fekAdd: Result := INT_EXCEL_TOKEN_TADD;
|
|
||||||
fekSub: Result := INT_EXCEL_TOKEN_TSUB;
|
|
||||||
fekDiv: Result := INT_EXCEL_TOKEN_TDIV;
|
|
||||||
fekMul: Result := INT_EXCEL_TOKEN_TMUL;
|
|
||||||
{ Built-in/Worksheet Function }
|
|
||||||
fekABS:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 1;
|
|
||||||
AExtra := INT_EXCEL_SHEET_FUNC_ABS;
|
|
||||||
end;
|
|
||||||
fekDATE:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 3;
|
|
||||||
AExtra := INT_EXCEL_SHEET_FUNC_DATE;
|
|
||||||
end;
|
|
||||||
fekROUND:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 2;
|
|
||||||
AExtra := INT_EXCEL_SHEET_FUNC_ROUND;
|
|
||||||
end;
|
|
||||||
fekTIME:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
|
||||||
AParamsNum := 3;
|
|
||||||
AExtra := INT_EXCEL_SHEET_FUNC_TIME;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{*******************************************************************
|
{*******************************************************************
|
||||||
* TsSpreadBIFF5Writer.WriteToFile ()
|
* TsSpreadBIFF5Writer.WriteToFile ()
|
||||||
*
|
*
|
||||||
@ -647,8 +604,11 @@ var
|
|||||||
i: Integer;
|
i: Integer;
|
||||||
RPNLength: Word;
|
RPNLength: Word;
|
||||||
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
|
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
|
||||||
FormulaKind, ParamsNum: Byte;
|
FormulaKind: Word;
|
||||||
ExtraInfo: Word;
|
ExtraInfo: Word;
|
||||||
|
r: Cardinal;
|
||||||
|
len: Integer;
|
||||||
|
s: ansistring;
|
||||||
begin
|
begin
|
||||||
RPNLength := 0;
|
RPNLength := 0;
|
||||||
FormulaResult := 0.0;
|
FormulaResult := 0.0;
|
||||||
@ -665,7 +625,7 @@ begin
|
|||||||
{ Index to XF Record }
|
{ Index to XF Record }
|
||||||
AStream.WriteWord($0000);
|
AStream.WriteWord($0000);
|
||||||
|
|
||||||
{ Result of the formula in IEE 754 floating-point value }
|
{ Result of the formula in IEEE 754 floating-point value }
|
||||||
AStream.WriteBuffer(FormulaResult, 8);
|
AStream.WriteBuffer(FormulaResult, 8);
|
||||||
|
|
||||||
{ Options flags }
|
{ Options flags }
|
||||||
@ -686,7 +646,7 @@ begin
|
|||||||
for i := 0 to Length(AFormula) - 1 do
|
for i := 0 to Length(AFormula) - 1 do
|
||||||
begin
|
begin
|
||||||
{ Token identifier }
|
{ Token identifier }
|
||||||
FormulaKind := FEKindToExcelID(AFormula[i].ElementKind, ParamsNum, ExtraInfo);
|
FormulaKind := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind, ExtraInfo);
|
||||||
AStream.WriteByte(FormulaKind);
|
AStream.WriteByte(FormulaKind);
|
||||||
Inc(RPNLength);
|
Inc(RPNLength);
|
||||||
|
|
||||||
@ -704,13 +664,48 @@ begin
|
|||||||
Inc(RPNLength, 8);
|
Inc(RPNLength, 8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TSTR:
|
||||||
|
begin
|
||||||
|
s := ansistring(AFormula[i].StringValue);
|
||||||
|
len := Length(s);
|
||||||
|
AStream.WriteByte(len);
|
||||||
|
AStream.WriteBuffer(s[1], len);
|
||||||
|
Inc(RPNLength, len + 1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TBOOL: { fekBool }
|
||||||
|
begin
|
||||||
|
AStream.WriteByte(ord(AFormula[i].DoubleValue <> 0.0));
|
||||||
|
inc(RPNLength, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA:
|
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA:
|
||||||
begin
|
begin
|
||||||
AStream.WriteWord(AFormula[i].Row and MASK_EXCEL_ROW);
|
r := AFormula[i].Row and MASK_EXCEL_ROW;
|
||||||
|
if (rfRelRow in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(r);
|
||||||
AStream.WriteByte(AFormula[i].Col);
|
AStream.WriteByte(AFormula[i].Col);
|
||||||
Inc(RPNLength, 3);
|
Inc(RPNLength, 3);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TAREA_R: { fekCellRange }
|
||||||
|
begin
|
||||||
|
r := AFormula[i].Row and MASK_EXCEL_ROW;
|
||||||
|
if (rfRelRow in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(WordToLE(r));
|
||||||
|
|
||||||
|
r := AFormula[i].Row2 and MASK_EXCEL_ROW;
|
||||||
|
if (rfRelRow2 in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol2 in AFormula[i].RelFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(WordToLE(r));
|
||||||
|
|
||||||
|
AStream.WriteByte(AFormula[i].Col);
|
||||||
|
AStream.WriteByte(AFormula[i].Col2);
|
||||||
|
Inc(RPNLength, 6);
|
||||||
|
end;
|
||||||
|
|
||||||
{
|
{
|
||||||
sOffset Size Contents
|
sOffset Size Contents
|
||||||
0 1 22H (tFuncVarR), 42H (tFuncVarV), 62H (tFuncVarA)
|
0 1 22H (tFuncVarR), 42H (tFuncVarV), 62H (tFuncVarA)
|
||||||
@ -724,9 +719,16 @@ begin
|
|||||||
14-0 7FFFH Index to a built-in sheet function (➜3.11) or a macro command
|
14-0 7FFFH Index to a built-in sheet function (➜3.11) or a macro command
|
||||||
15 8000H 0 = Built-in function; 1 = Macro command
|
15 8000H 0 = Built-in function; 1 = Macro command
|
||||||
}
|
}
|
||||||
|
// Functions
|
||||||
|
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
|
||||||
|
begin
|
||||||
|
AStream.WriteWord(WordToLE(ExtraInfo));
|
||||||
|
Inc(RPNLength, 2);
|
||||||
|
end;
|
||||||
|
|
||||||
INT_EXCEL_TOKEN_FUNCVAR_V:
|
INT_EXCEL_TOKEN_FUNCVAR_V:
|
||||||
begin
|
begin
|
||||||
AStream.WriteByte(ParamsNum);
|
AStream.WriteByte(AFormula[i].ParamsNum);
|
||||||
AStream.WriteWord(WordToLE(ExtraInfo));
|
AStream.WriteWord(WordToLE(ExtraInfo));
|
||||||
Inc(RPNLength, 3);
|
Inc(RPNLength, 3);
|
||||||
end;
|
end;
|
||||||
|
@ -176,6 +176,7 @@ const
|
|||||||
{ Excel record IDs }
|
{ Excel record IDs }
|
||||||
INT_EXCEL_ID_BOF = $0809;
|
INT_EXCEL_ID_BOF = $0809;
|
||||||
INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs
|
INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs
|
||||||
|
INT_EXCEL_ID_COUNTRY = $008C;
|
||||||
INT_EXCEL_ID_EOF = $000A;
|
INT_EXCEL_ID_EOF = $000A;
|
||||||
INT_EXCEL_ID_DIMENSIONS = $0200;
|
INT_EXCEL_ID_DIMENSIONS = $0200;
|
||||||
INT_EXCEL_ID_FONT = $0031;
|
INT_EXCEL_ID_FONT = $0031;
|
||||||
@ -196,12 +197,13 @@ const
|
|||||||
INT_EXCEL_ID_PALETTE = $0092;
|
INT_EXCEL_ID_PALETTE = $0092;
|
||||||
INT_EXCEL_ID_CODEPAGE = $0042;
|
INT_EXCEL_ID_CODEPAGE = $0042;
|
||||||
INT_EXCEL_ID_FORMAT = $041E;
|
INT_EXCEL_ID_FORMAT = $041E;
|
||||||
|
INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3;
|
||||||
|
|
||||||
{ Cell Addresses constants }
|
{ Cell Addresses constants }
|
||||||
MASK_EXCEL_ROW = $3FFF;
|
MASK_EXCEL_ROW = $3FFF;
|
||||||
MASK_EXCEL_COL_BITS_BIFF8=$00FF;
|
MASK_EXCEL_COL_BITS_BIFF8=$00FF;
|
||||||
MASK_EXCEL_RELATIVE_ROW = $4000;
|
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||||
MASK_EXCEL_RELATIVE_COL = $8000;
|
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
||||||
|
|
||||||
{ BOF record constants }
|
{ BOF record constants }
|
||||||
INT_BOF_BIFF8_VER = $0600;
|
INT_BOF_BIFF8_VER = $0600;
|
||||||
@ -872,6 +874,7 @@ begin
|
|||||||
AStream.WriteBuffer(WideStringToLE(WideFontName)[1], Len * Sizeof(WideChar));
|
AStream.WriteBuffer(WideStringToLE(WideFontName)[1], Len * Sizeof(WideChar));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{*******************************************************************
|
{*******************************************************************
|
||||||
* TsSpreadBIFF8Writer.WriteFormula ()
|
* TsSpreadBIFF8Writer.WriteFormula ()
|
||||||
*
|
*
|
||||||
@ -967,10 +970,13 @@ procedure TsSpreadBIFF8Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
|||||||
var
|
var
|
||||||
FormulaResult: double;
|
FormulaResult: double;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
len: Integer;
|
||||||
RPNLength: Word;
|
RPNLength: Word;
|
||||||
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
|
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
|
||||||
TokenID: Byte;
|
TokenID: Word;
|
||||||
lSecondaryID: Word;
|
lSecondaryID: Word;
|
||||||
|
c: Cardinal;
|
||||||
|
wideStr: WideString;
|
||||||
begin
|
begin
|
||||||
RPNLength := 0;
|
RPNLength := 0;
|
||||||
FormulaResult := 0.0;
|
FormulaResult := 0.0;
|
||||||
@ -988,7 +994,7 @@ begin
|
|||||||
//AStream.WriteWord(0);
|
//AStream.WriteWord(0);
|
||||||
WriteXFIndex(AStream, ACell);
|
WriteXFIndex(AStream, ACell);
|
||||||
|
|
||||||
{ Result of the formula in IEE 754 floating-point value }
|
{ Result of the formula in IEEE 754 floating-point value }
|
||||||
AStream.WriteBuffer(FormulaResult, 8);
|
AStream.WriteBuffer(FormulaResult, 8);
|
||||||
|
|
||||||
{ Options flags }
|
{ Options flags }
|
||||||
@ -1019,7 +1025,10 @@ begin
|
|||||||
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell }
|
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell }
|
||||||
begin
|
begin
|
||||||
AStream.WriteWord(AFormula[i].Row);
|
AStream.WriteWord(AFormula[i].Row);
|
||||||
AStream.WriteWord(AFormula[i].Col and MASK_EXCEL_COL_BITS_BIFF8);
|
c := AFormula[i].Col and MASK_EXCEL_COL_BITS_BIFF8;
|
||||||
|
if (rfRelRow in AFormula[i].RelFlags) then c := c or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
if (rfRelCol in AFormula[i].RelFlags) then c := c or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
AStream.WriteWord(c);
|
||||||
Inc(RPNLength, 4);
|
Inc(RPNLength, 4);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1035,8 +1044,14 @@ begin
|
|||||||
}
|
}
|
||||||
AStream.WriteWord(WordToLE(AFormula[i].Row));
|
AStream.WriteWord(WordToLE(AFormula[i].Row));
|
||||||
AStream.WriteWord(WordToLE(AFormula[i].Row2));
|
AStream.WriteWord(WordToLE(AFormula[i].Row2));
|
||||||
AStream.WriteWord(WordToLE(AFormula[i].Col));
|
c := AFormula[i].Col;
|
||||||
AStream.WriteWord(WordToLE(AFormula[i].Col2));
|
if (rfRelCol in AFormula[i].RelFlags) then c := c or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
if (rfRelRow in AFormula[i].RelFlags) then c := c or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
AStream.WriteWord(WordToLE(c));
|
||||||
|
c := AFormula[i].Col2;
|
||||||
|
if (rfRelCol2 in AFormula[i].RelFlags) then c := c or MASK_EXCEL_RELATIVE_COL;
|
||||||
|
if (rfRelRow2 in AFormula[i].RelFlags) then c := c or MASK_EXCEL_RELATIVE_ROW;
|
||||||
|
AStream.WriteWord(WordToLE(c));
|
||||||
Inc(RPNLength, 8);
|
Inc(RPNLength, 8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1046,6 +1061,23 @@ begin
|
|||||||
Inc(RPNLength, 8);
|
Inc(RPNLength, 8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TSTR: { fekString }
|
||||||
|
begin
|
||||||
|
// string constant is stored as widestring in BIFF8
|
||||||
|
wideStr := AFormula[i].StringValue;
|
||||||
|
len := Length(wideStr);
|
||||||
|
AStream.WriteByte(len); // char count in 1 byte
|
||||||
|
AStream.WriteByte(1); // Widestring flags, 1=regular unicode LE string
|
||||||
|
AStream.WriteBuffer(WideStringToLE(wideStr)[1], len * Sizeof(WideChar));
|
||||||
|
Inc(RPNLength, 1 + 1 + len*SizeOf(WideChar));
|
||||||
|
end;
|
||||||
|
|
||||||
|
INT_EXCEL_TOKEN_TBOOL: { fekBool }
|
||||||
|
begin
|
||||||
|
AStream.WriteByte(ord(AFormula[i].DoubleValue <> 0.0));
|
||||||
|
inc(RPNLength, 1);
|
||||||
|
end;
|
||||||
|
|
||||||
{ binary operation tokens }
|
{ binary operation tokens }
|
||||||
INT_EXCEL_TOKEN_TADD, INT_EXCEL_TOKEN_TSUB, INT_EXCEL_TOKEN_TMUL,
|
INT_EXCEL_TOKEN_TADD, INT_EXCEL_TOKEN_TSUB, INT_EXCEL_TOKEN_TMUL,
|
||||||
INT_EXCEL_TOKEN_TDIV, INT_EXCEL_TOKEN_TPOWER: begin end;
|
INT_EXCEL_TOKEN_TDIV, INT_EXCEL_TOKEN_TPOWER: begin end;
|
||||||
@ -1060,13 +1092,21 @@ begin
|
|||||||
Inc(RPNLength, 3);
|
Inc(RPNLength, 3);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Functions
|
// Functions with fixed parameter count
|
||||||
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
|
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
|
||||||
begin
|
begin
|
||||||
AStream.WriteWord(lSecondaryID);
|
AStream.WriteWord(WordToLE(lSecondaryID));
|
||||||
Inc(RPNLength, 2);
|
Inc(RPNLength, 2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Functions with variable parameter count
|
||||||
|
INT_EXCEL_TOKEN_FUNCVAR_V:
|
||||||
|
begin
|
||||||
|
AStream.WriteByte(AFormula[i].ParamsNum);
|
||||||
|
AStream.WriteWord(WordToLE(lSecondaryID));
|
||||||
|
Inc(RPNLength, 3);
|
||||||
|
end;
|
||||||
|
|
||||||
else
|
else
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
@ -26,19 +26,23 @@ const
|
|||||||
INT_EXCEL_TOKEN_TSUB = $04;
|
INT_EXCEL_TOKEN_TSUB = $04;
|
||||||
INT_EXCEL_TOKEN_TMUL = $05;
|
INT_EXCEL_TOKEN_TMUL = $05;
|
||||||
INT_EXCEL_TOKEN_TDIV = $06;
|
INT_EXCEL_TOKEN_TDIV = $06;
|
||||||
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation
|
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation ^
|
||||||
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation
|
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation &
|
||||||
INT_EXCEL_TOKEN_TLT = $09; // Less than
|
INT_EXCEL_TOKEN_TLT = $09; // Less than <
|
||||||
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal
|
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal <=
|
||||||
INT_EXCEL_TOKEN_TEQ = $0B; // Equal
|
INT_EXCEL_TOKEN_TEQ = $0B; // Equal =
|
||||||
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal
|
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal >=
|
||||||
INT_EXCEL_TOKEN_TGT = $0D; // Greater than
|
INT_EXCEL_TOKEN_TGT = $0D; // Greater than >
|
||||||
INT_EXCEL_TOKEN_TNE = $0E; // Not equal
|
INT_EXCEL_TOKEN_TNE = $0E; // Not equal <>
|
||||||
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
|
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
|
||||||
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
|
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
|
||||||
INT_EXCEL_TOKEN_TRANGE = $11; // Cell range
|
INT_EXCEL_TOKEN_TRANGE = $11; // Cell range
|
||||||
|
INT_EXCEL_TOKEN_TUPLUS = $12; // Unary plus +
|
||||||
|
INT_EXCEL_TOKEN_TUMINUS = $13; // Unary minus +
|
||||||
|
INT_EXCEL_TOKEN_TPERCENT= $14; // Percent (%, divides operand by 100)
|
||||||
|
|
||||||
{ Constant Operand Tokens, 3.8}
|
{ Constant Operand Tokens, 3.8}
|
||||||
|
INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand
|
||||||
INT_EXCEL_TOKEN_TSTR = $17; //string
|
INT_EXCEL_TOKEN_TSTR = $17; //string
|
||||||
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
|
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
|
||||||
INT_EXCEL_TOKEN_TINT = $1E; //integer
|
INT_EXCEL_TOKEN_TINT = $1E; //integer
|
||||||
@ -49,6 +53,9 @@ const
|
|||||||
INT_EXCEL_TOKEN_TREFR = $24;
|
INT_EXCEL_TOKEN_TREFR = $24;
|
||||||
INT_EXCEL_TOKEN_TREFV = $44;
|
INT_EXCEL_TOKEN_TREFV = $44;
|
||||||
INT_EXCEL_TOKEN_TREFA = $64;
|
INT_EXCEL_TOKEN_TREFA = $64;
|
||||||
|
INT_EXCEL_TOKEN_TAREA_R = $25;
|
||||||
|
INT_EXCEL_TOKEN_TAREA_V = $45;
|
||||||
|
INT_EXCEL_TOKEN_TAREA_A = $65;
|
||||||
|
|
||||||
{ Function Tokens }
|
{ Function Tokens }
|
||||||
// _R: reference; _V: value; _A: array
|
// _R: reference; _V: value; _A: array
|
||||||
@ -61,13 +68,109 @@ const
|
|||||||
INT_EXCEL_TOKEN_FUNCVAR_R = $22;
|
INT_EXCEL_TOKEN_FUNCVAR_R = $22;
|
||||||
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
||||||
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
||||||
INT_EXCEL_TOKEN_TAREA_R = $25;
|
|
||||||
|
|
||||||
{ Built-in/worksheet functions }
|
{ Built-in/worksheet functions }
|
||||||
|
INT_EXCEL_SHEET_FUNC_COUNT = 0;
|
||||||
|
INT_EXCEL_SHEET_FUNC_IF = 1;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISNA = 2;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISERROR = 3;
|
||||||
|
INT_EXCEL_SHEET_FUNC_SUM = 4;
|
||||||
|
INT_EXCEL_SHEET_FUNC_AVERAGE = 5;
|
||||||
|
INT_EXCEL_SHEET_FUNC_MIN = 6;
|
||||||
|
INT_EXCEL_SHEET_FUNC_MAX = 7;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ROW = 8;
|
||||||
|
INT_EXCEL_SHEET_FUNC_COLUMN = 9;
|
||||||
|
INT_EXCEL_SHEET_FUNC_STDEV = 12;
|
||||||
|
INT_EXCEL_SHEET_FUNC_SIN = 15;
|
||||||
|
INT_EXCEL_SHEET_FUNC_COS = 16;
|
||||||
|
INT_EXCEL_SHEET_FUNC_TAN = 17;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ATAN = 18;
|
||||||
|
INT_EXCEL_SHEET_FUNC_PI = 19;
|
||||||
|
INT_EXCEL_SHEET_FUNC_SQRT = 20;
|
||||||
|
INT_EXCEL_SHEET_FUNC_EXP = 21;
|
||||||
|
INT_EXCEL_SHEET_FUNC_LN = 22;
|
||||||
|
INT_EXCEL_SHEET_FUNC_LOG10 = 23;
|
||||||
INT_EXCEL_SHEET_FUNC_ABS = 24; // $18
|
INT_EXCEL_SHEET_FUNC_ABS = 24; // $18
|
||||||
|
INT_EXCEL_SHEET_FUNC_INT = 25;
|
||||||
|
INT_EXCEL_SHEET_FUNC_SIGN = 26;
|
||||||
INT_EXCEL_SHEET_FUNC_ROUND = 27; // $1B
|
INT_EXCEL_SHEET_FUNC_ROUND = 27; // $1B
|
||||||
|
INT_EXCEL_SHEET_FUNC_MID = 31;
|
||||||
|
INT_EXCEL_SHEET_FUNC_VALUE = 33;
|
||||||
|
INT_EXCEL_SHEET_FUNC_TRUE = 34;
|
||||||
|
INT_EXCEL_SHEET_FUNC_FALSE = 35;
|
||||||
|
INT_EXCEL_SHEET_FUNC_AND = 36;
|
||||||
|
INT_EXCEL_SHEET_FUNC_OR = 37;
|
||||||
|
INT_EXCEL_SHEET_FUNC_NOT = 38;
|
||||||
|
INT_EXCEL_SHEET_FUNC_VAR = 46;
|
||||||
|
INT_EXCEL_SHEET_FUNC_PV = 56;
|
||||||
|
INT_EXCEL_SHEET_FUNC_FV = 57;
|
||||||
|
INT_EXCEL_SHEET_FUNC_NPER = 58;
|
||||||
|
INT_EXCEL_SHEET_FUNC_PMT = 59;
|
||||||
|
INT_EXCEL_SHEET_FUNC_RATE = 60;
|
||||||
|
INT_EXCEL_SHEET_FUNC_RAND = 63;
|
||||||
INT_EXCEL_SHEET_FUNC_DATE = 65; // $41
|
INT_EXCEL_SHEET_FUNC_DATE = 65; // $41
|
||||||
INT_EXCEL_SHEET_FUNC_TIME = 66; // $42
|
INT_EXCEL_SHEET_FUNC_TIME = 66; // $42
|
||||||
|
INT_EXCEL_SHEET_FUNC_DAY = 67;
|
||||||
|
INT_EXCEL_SHEET_FUNC_MONTH = 68;
|
||||||
|
INT_EXCEL_SHEET_FUNC_YEAR = 69;
|
||||||
|
INT_EXCEL_SHEET_FUNC_WEEKDAY = 70;
|
||||||
|
INT_EXCEL_SHEET_FUNC_HOUR = 71;
|
||||||
|
INT_EXCEL_SHEET_FUNC_MINUTE = 72;
|
||||||
|
INT_EXCEL_SHEET_FUNC_SECOND = 73;
|
||||||
|
INT_EXCEL_SHEET_FUNC_NOW = 74;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ROWS = 76;
|
||||||
|
INT_EXCEL_SHEET_FUNC_COLUMNS = 77;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ASIN = 98;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ACOS = 99;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISREF = 105;
|
||||||
|
INT_EXCEL_SHEET_FUNC_LOG = 109;
|
||||||
|
INT_EXCEL_SHEET_FUNC_CHAR = 111;
|
||||||
|
INT_EXCEL_SHEET_FUNC_LOWER = 112;
|
||||||
|
INT_EXCEL_SHEET_FUNC_UPPER = 113;
|
||||||
|
INT_EXCEL_SHEET_FUNC_PROPER = 114;
|
||||||
|
INT_EXCEL_SHEET_FUNC_LEFT = 115;
|
||||||
|
INT_EXCEL_SHEET_FUNC_RIGHT = 116;
|
||||||
|
INT_EXCEL_SHEET_FUNC_TRIM = 118;
|
||||||
|
INT_EXCEL_SHEET_FUNC_REPLACE = 119;
|
||||||
|
INT_EXCEL_SHEET_FUNC_SUBSTITUTE = 120;
|
||||||
|
INT_EXCEL_SHEET_FUNC_CODE = 121;
|
||||||
|
INT_EXCEL_SHEET_FUNC_CELL = 125;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISERR = 126;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISTEXT = 127;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISNUMBER = 128;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISBLANK = 129;
|
||||||
|
INT_EXCEL_SHEET_FUNC_DATEVALUE = 140;
|
||||||
|
INT_EXCEL_SHEET_FUNC_TIMEVALUE = 141;
|
||||||
|
INT_EXCEL_SHEET_FUNC_COUNTA = 169;
|
||||||
|
INT_EXCEL_SHEET_FUNC_PRODUCT = 183;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISNONTEXT = 190;
|
||||||
|
INT_EXCEL_SHEET_FUNC_STDEVP = 193;
|
||||||
|
INT_EXCEL_SHEET_FUNC_VARP = 194;
|
||||||
|
INT_EXCEL_SHEET_FUNC_ISLOGICAL = 198;
|
||||||
|
INT_EXCEL_SHEET_FUNC_TODAY = 221; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_MEDIAN = 227; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_SINH = 229; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_COSH = 230; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_TANH = 231; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_ASINH = 232; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_ACOSH = 233; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_ATANH = 234; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_INFO = 244; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_AVEDEV = 269; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_BETADIST = 270; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_BETAINV = 272; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_BINOMDIST = 273; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_CHIDIST = 274; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_CHIINV = 275; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_PERMUT = 299; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_POISSON = 300; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_SUMSQ = 321; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_RADIANS = 342; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_DEGREES = 343; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_SUMIF = 345; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_COUNTIF = 346; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_COUNTBLANK = 347; // not available in BIFF2
|
||||||
|
INT_EXCEL_SHEET_FUNC_DATEDIF = 351; // not available in BIFF2
|
||||||
|
|
||||||
{ Control Tokens, Special Tokens }
|
{ Control Tokens, Special Tokens }
|
||||||
// 01H tExp Matrix formula or shared formula
|
// 01H tExp Matrix formula or shared formula
|
||||||
@ -200,7 +303,7 @@ type
|
|||||||
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
|
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
|
||||||
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
|
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
|
||||||
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
|
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
|
||||||
function FormulaElementKindToExcelTokenID(AElementKind: TFEKind; out ASecondaryID: Word): Byte;
|
function FormulaElementKindToExcelTokenID(AElementKind: TFEKind; out ASecondaryID: Word): Word;
|
||||||
// Other records which didn't change
|
// Other records which didn't change
|
||||||
// Workbook Globals records
|
// Workbook Globals records
|
||||||
// Write out used codepage for character encoding
|
// Write out used codepage for character encoding
|
||||||
@ -408,45 +511,174 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
|
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
|
||||||
AElementKind: TFEKind; out ASecondaryID: Word): Byte;
|
AElementKind: TFEKind; out ASecondaryID: Word): Word;
|
||||||
begin
|
const
|
||||||
ASecondaryID := 0;
|
{ 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}
|
||||||
|
|
||||||
case AElementKind of
|
// Basic operations
|
||||||
{ Operand Tokens }
|
(0, INT_EXCEL_TOKEN_TADD), {fekAdd, +}
|
||||||
fekCell: Result := INT_EXCEL_TOKEN_TREFR;
|
(0, INT_EXCEL_TOKEN_TSUB), {fekSub, -}
|
||||||
fekCellRange: Result := INT_EXCEL_TOKEN_TAREA_R;
|
(0, INT_EXCEL_TOKEN_TDIV), {fekDiv, /}
|
||||||
fekNum: Result := INT_EXCEL_TOKEN_TNUM;
|
(0, INT_EXCEL_TOKEN_TMUL), {fekMul, *}
|
||||||
{ Basic operations }
|
(0, INT_EXCEL_TOKEN_TPERCENT), {fekPercent, %}
|
||||||
fekAdd: Result := INT_EXCEL_TOKEN_TADD;
|
(0, INT_EXCEL_TOKEN_TPOWER), {fekPower, ^}
|
||||||
fekSub: Result := INT_EXCEL_TOKEN_TSUB;
|
(0, INT_EXCEL_TOKEN_TUMINUS), {fekUMinus, -}
|
||||||
fekDiv: Result := INT_EXCEL_TOKEN_TDIV;
|
(0, INT_EXCEL_TOKEN_TUPLUS), {fekUPlus, +}
|
||||||
fekMul: Result := INT_EXCEL_TOKEN_TMUL;
|
(0, INT_EXCEL_TOKEN_TCONCAT), {fekConcat, &, for strings}
|
||||||
{ Built-in Functions}
|
(0, INT_EXCEL_TOKEN_TEQ), {fekEqual, =}
|
||||||
fekABS:
|
(0, INT_EXCEL_TOKEN_TGT), {fekGreater, >}
|
||||||
begin
|
(0, INT_EXCEL_TOKEN_TGE), {fekGreaterEqual, >=}
|
||||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
(0, INT_EXCEL_TOKEN_TLT), {fekLess <}
|
||||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_ABS;
|
(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
|
||||||
|
case TokenIDs[AElementKind, 0] of
|
||||||
|
0: begin
|
||||||
|
Result := TokenIDs[AElementKind, 1];
|
||||||
|
ASecondaryID := 0;
|
||||||
end;
|
end;
|
||||||
fekDATE:
|
1: begin
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
Result := INT_EXCEL_TOKEN_FUNC_V;
|
||||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_DATE;
|
ASecondaryID := TokenIDs[AElementKind, 1]
|
||||||
end;
|
end;
|
||||||
fekROUND:
|
2: begin
|
||||||
begin
|
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
||||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
ASecondaryID := TokenIDs[AElementKind, 1]
|
||||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_ROUND;
|
|
||||||
end;
|
end;
|
||||||
fekTIME:
|
|
||||||
begin
|
|
||||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
|
||||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_TIME;
|
|
||||||
end;
|
|
||||||
{ Other operations }
|
|
||||||
fekOpSUM: Result := INT_EXCEL_TOKEN_TATTR;
|
|
||||||
else
|
|
||||||
Result := 0;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user