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
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpimage, AVL_Tree, avglvltree, lconvencoding;
|
||||
Classes, SysUtils, fpimage, AVL_Tree, avglvltree, lconvencoding, fpsutils;
|
||||
|
||||
type
|
||||
TsSpreadsheetFormat = (sfExcel2, sfExcel3, sfExcel4, sfExcel5, sfExcel8,
|
||||
@ -56,15 +56,51 @@ type
|
||||
DoubleValue: double;
|
||||
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 = (
|
||||
{ Basic operands }
|
||||
fekCell, fekCellRange, fekNum,
|
||||
fekCell, fekCellRef, fekCellRange, fekNum, fekString, fekBool, fekMissingArg,
|
||||
{ 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}
|
||||
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 }
|
||||
fekOpSUM
|
||||
);
|
||||
@ -72,9 +108,13 @@ type
|
||||
TsFormulaElement = record
|
||||
ElementKind: TFEKind;
|
||||
Row, Row2: Word; // zero-based
|
||||
Col, Col2: Byte; // zero-based
|
||||
Col, Col2: Word; // zero-based
|
||||
Param1, Param2: Word; // Extra parameters
|
||||
DoubleValue: double;
|
||||
IntValue: Word;
|
||||
StringValue: String;
|
||||
RelFlags: TsRelFlags; // store info on relative/absolute addresses
|
||||
ParamsNum: Byte;
|
||||
end;
|
||||
|
||||
TsExpandedFormula = array of TsFormulaElement;
|
||||
@ -378,6 +418,54 @@ type
|
||||
Format: TsSpreadsheetFormat;
|
||||
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
|
||||
GsSpreadFormats: array of TsSpreadFormatData;
|
||||
|
||||
@ -1874,6 +1962,220 @@ begin
|
||||
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
|
||||
|
||||
SetLength(GsSpreadFormats, 0);
|
||||
|
@ -14,6 +14,9 @@ uses
|
||||
type
|
||||
TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection);
|
||||
|
||||
TsRelFlag = (rfRelRow, rfRelCol, rfRelRow2, rfRelCol2);
|
||||
TsRelFlags = set of TsRelFlag;
|
||||
|
||||
const
|
||||
// Date formatting string for unambiguous date/time display as strings
|
||||
// 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;
|
||||
var AFirstCellRow, AFirstCellCol, ACount: Integer;
|
||||
var ADirection: TsSelectionDirection): Boolean;
|
||||
function ParseCellRangeString(const AStr: string;
|
||||
var AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer;
|
||||
var AFlags: TsRelFlags): Boolean;
|
||||
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;
|
||||
var AResult: Integer): Boolean;
|
||||
function ParseCellColString(const AStr: string;
|
||||
@ -148,11 +156,17 @@ function ParseIntervalString(const AStr: string;
|
||||
var AFirstCellRow, AFirstCellCol, ACount: Integer;
|
||||
var ADirection: TsSelectionDirection): Boolean;
|
||||
var
|
||||
Cells: TStringList;
|
||||
//Cells: TStringList;
|
||||
LastCellRow, LastCellCol: Integer;
|
||||
p: Integer;
|
||||
s1, s2: String;
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
{ Simpler:
|
||||
use "pos" instead of the TStringList overhead.
|
||||
And: the StringList is not free'ed here
|
||||
|
||||
// First get the cells
|
||||
Cells := TStringList.Create;
|
||||
ExtractStrings([':'],[], PChar(AStr), Cells);
|
||||
@ -162,6 +176,19 @@ begin
|
||||
if not Result then Exit;
|
||||
Result := ParseCellString(Cells[1], LastCellRow, LastCellCol);
|
||||
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
|
||||
begin
|
||||
@ -176,6 +203,42 @@ begin
|
||||
else Exit(False);
|
||||
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
|
||||
|
||||
@ -184,13 +247,17 @@ end;
|
||||
0 - Reading Column part 1 (necesserely needs a letter)
|
||||
1 - Reading Column part 2, but could be the first number as well
|
||||
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
|
||||
i: Integer;
|
||||
state: Integer;
|
||||
Col, Row: string;
|
||||
lChar: Char;
|
||||
isAbs: Boolean;
|
||||
const
|
||||
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'];
|
||||
@ -201,11 +268,20 @@ begin
|
||||
state := 0;
|
||||
Col := '';
|
||||
Row := '';
|
||||
AFlags := [rfRelCol, rfRelRow];
|
||||
isAbs := false;
|
||||
|
||||
// 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
|
||||
lChar := AStr[i + 1];
|
||||
lChar := AStr[i];
|
||||
|
||||
if lChar = '$' then begin
|
||||
if isAbs then
|
||||
exit(false);
|
||||
isAbs := true;
|
||||
continue;
|
||||
end;
|
||||
|
||||
case state of
|
||||
|
||||
@ -214,6 +290,9 @@ begin
|
||||
if lChar in cLetters then
|
||||
begin
|
||||
Col := lChar;
|
||||
if isAbs then
|
||||
Exclude(AFlags, rfRelCol);
|
||||
isAbs := false;
|
||||
state := 1;
|
||||
end
|
||||
else Exit(False);
|
||||
@ -221,10 +300,14 @@ begin
|
||||
|
||||
1:
|
||||
begin
|
||||
if lChar in cLetters then Col := Col + lChar
|
||||
if lChar in cLetters then
|
||||
Col := Col + lChar
|
||||
else if lChar in cDigits then
|
||||
begin
|
||||
Row := lChar;
|
||||
if isAbs then
|
||||
Exclude(AFlags, rfRelRow);
|
||||
isAbs := false;
|
||||
state := 2;
|
||||
end
|
||||
else Exit(False);
|
||||
@ -244,6 +327,16 @@ begin
|
||||
ParseCellColString(Col, ACellCol);
|
||||
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;
|
||||
begin
|
||||
try
|
||||
@ -267,6 +360,12 @@ begin
|
||||
AResult := (Ord(AStr[1]) - Ord('A') + 1) * INT_NUM_LETTERS
|
||||
+ Ord(AStr[2]) - Ord('A');
|
||||
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);
|
||||
|
||||
Result := True;
|
||||
|
@ -50,7 +50,7 @@ type
|
||||
procedure TearDown; override;
|
||||
published
|
||||
// Current fpspreadsheet does not yet have support for new RPN formulas
|
||||
{.$DEFINE FPSPREAD_HAS_NEWRPNSUPPORT}
|
||||
{$DEFINE FPSPREAD_HAS_NEWRPNSUPPORT}
|
||||
{$IFDEF FPSPREAD_HAS_NEWRPNSUPPORT}
|
||||
// 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.
|
||||
|
Binary file not shown.
@ -57,9 +57,8 @@ type
|
||||
|
||||
{ TsSpreadBIFF2Writer }
|
||||
|
||||
TsSpreadBIFF2Writer = class(TsCustomSpreadWriter)
|
||||
TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter)
|
||||
private
|
||||
function FEKindToExcelID(AElement: TFEKind; var AParamsNum, AFuncNum: Byte): Byte;
|
||||
procedure WriteCellFormatting(AStream: TStream; ACell: PCell);
|
||||
public
|
||||
{ General writing methods }
|
||||
@ -86,8 +85,8 @@ const
|
||||
|
||||
{ Cell Addresses constants }
|
||||
MASK_EXCEL_ROW = $3FFF;
|
||||
MASK_EXCEL_RELATIVE_ROW = $4000;
|
||||
MASK_EXCEL_RELATIVE_COL = $8000;
|
||||
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
||||
|
||||
{ BOF record constants }
|
||||
INT_EXCEL_SHEET = $0010;
|
||||
@ -96,47 +95,6 @@ const
|
||||
|
||||
{ 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);
|
||||
var
|
||||
BorderByte: Byte = 0;
|
||||
@ -239,7 +197,10 @@ var
|
||||
i: Integer;
|
||||
RPNLength: Word;
|
||||
TokenArraySizePos, RecordSizePos, FinalPos: Cardinal;
|
||||
FormulaKind, ParamsNum, ExtraInfo: Byte;
|
||||
FormulaKind, ExtraInfo: Word;
|
||||
r: Cardinal;
|
||||
len: Integer;
|
||||
s: ansistring;
|
||||
begin
|
||||
RPNLength := 0;
|
||||
FormulaResult := 0.0;
|
||||
@ -258,7 +219,7 @@ begin
|
||||
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);
|
||||
|
||||
{ 0 = Do not recalculate
|
||||
@ -276,8 +237,9 @@ begin
|
||||
{ Formula data (RPN token array) }
|
||||
for i := 0 to Length(AFormula) - 1 do
|
||||
begin
|
||||
|
||||
{ Token identifier }
|
||||
FormulaKind := FEKindToExcelID(AFormula[i].ElementKind, ParamsNum, ExtraInfo);
|
||||
FormulaKind := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind, ExtraInfo);
|
||||
AStream.WriteByte(FormulaKind);
|
||||
Inc(RPNLength);
|
||||
|
||||
@ -295,17 +257,60 @@ begin
|
||||
Inc(RPNLength, 8);
|
||||
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:
|
||||
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);
|
||||
Inc(RPNLength, 3);
|
||||
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:
|
||||
begin
|
||||
AStream.WriteByte(ParamsNum);
|
||||
AStream.WriteByte(ExtraInfo);
|
||||
AStream.WriteByte(AFormula[i].ParamsNum);
|
||||
AStream.WriteByte(Lo(ExtraInfo));
|
||||
// taking only the low-bytes, the high-bytes are needed for compatibility
|
||||
// with other BIFF formats...
|
||||
Inc(RPNLength, 2);
|
||||
end;
|
||||
|
||||
|
@ -106,7 +106,6 @@ type
|
||||
TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter)
|
||||
private
|
||||
WorkBookEncoding: TsEncoding;
|
||||
function FEKindToExcelID(AElement: TFEKind; var AParamsNum: Byte; var AExtra: Word): Byte;
|
||||
public
|
||||
// constructor Create;
|
||||
// destructor Destroy; override;
|
||||
@ -156,8 +155,8 @@ const
|
||||
|
||||
{ Cell Addresses constants }
|
||||
MASK_EXCEL_ROW = $3FFF;
|
||||
MASK_EXCEL_RELATIVE_ROW = $4000;
|
||||
MASK_EXCEL_RELATIVE_COL = $8000;
|
||||
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
||||
|
||||
{ BOF record constants }
|
||||
INT_BOF_BIFF5_VER = $0500;
|
||||
@ -260,48 +259,6 @@ const
|
||||
|
||||
{ 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 ()
|
||||
*
|
||||
@ -647,8 +604,11 @@ var
|
||||
i: Integer;
|
||||
RPNLength: Word;
|
||||
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
|
||||
FormulaKind, ParamsNum: Byte;
|
||||
FormulaKind: Word;
|
||||
ExtraInfo: Word;
|
||||
r: Cardinal;
|
||||
len: Integer;
|
||||
s: ansistring;
|
||||
begin
|
||||
RPNLength := 0;
|
||||
FormulaResult := 0.0;
|
||||
@ -665,7 +625,7 @@ begin
|
||||
{ Index to XF Record }
|
||||
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);
|
||||
|
||||
{ Options flags }
|
||||
@ -686,7 +646,7 @@ begin
|
||||
for i := 0 to Length(AFormula) - 1 do
|
||||
begin
|
||||
{ Token identifier }
|
||||
FormulaKind := FEKindToExcelID(AFormula[i].ElementKind, ParamsNum, ExtraInfo);
|
||||
FormulaKind := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind, ExtraInfo);
|
||||
AStream.WriteByte(FormulaKind);
|
||||
Inc(RPNLength);
|
||||
|
||||
@ -704,13 +664,48 @@ begin
|
||||
Inc(RPNLength, 8);
|
||||
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:
|
||||
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);
|
||||
Inc(RPNLength, 3);
|
||||
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
|
||||
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
|
||||
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:
|
||||
begin
|
||||
AStream.WriteByte(ParamsNum);
|
||||
AStream.WriteByte(AFormula[i].ParamsNum);
|
||||
AStream.WriteWord(WordToLE(ExtraInfo));
|
||||
Inc(RPNLength, 3);
|
||||
end;
|
||||
|
@ -176,6 +176,7 @@ const
|
||||
{ Excel record IDs }
|
||||
INT_EXCEL_ID_BOF = $0809;
|
||||
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_DIMENSIONS = $0200;
|
||||
INT_EXCEL_ID_FONT = $0031;
|
||||
@ -196,12 +197,13 @@ const
|
||||
INT_EXCEL_ID_PALETTE = $0092;
|
||||
INT_EXCEL_ID_CODEPAGE = $0042;
|
||||
INT_EXCEL_ID_FORMAT = $041E;
|
||||
INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3;
|
||||
|
||||
{ Cell Addresses constants }
|
||||
MASK_EXCEL_ROW = $3FFF;
|
||||
MASK_EXCEL_COL_BITS_BIFF8=$00FF;
|
||||
MASK_EXCEL_RELATIVE_ROW = $4000;
|
||||
MASK_EXCEL_RELATIVE_COL = $8000;
|
||||
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
||||
|
||||
{ BOF record constants }
|
||||
INT_BOF_BIFF8_VER = $0600;
|
||||
@ -872,6 +874,7 @@ begin
|
||||
AStream.WriteBuffer(WideStringToLE(WideFontName)[1], Len * Sizeof(WideChar));
|
||||
end;
|
||||
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF8Writer.WriteFormula ()
|
||||
*
|
||||
@ -967,10 +970,13 @@ procedure TsSpreadBIFF8Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
||||
var
|
||||
FormulaResult: double;
|
||||
i: Integer;
|
||||
len: Integer;
|
||||
RPNLength: Word;
|
||||
TokenArraySizePos, RecordSizePos, FinalPos: Int64;
|
||||
TokenID: Byte;
|
||||
TokenID: Word;
|
||||
lSecondaryID: Word;
|
||||
c: Cardinal;
|
||||
wideStr: WideString;
|
||||
begin
|
||||
RPNLength := 0;
|
||||
FormulaResult := 0.0;
|
||||
@ -988,7 +994,7 @@ begin
|
||||
//AStream.WriteWord(0);
|
||||
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);
|
||||
|
||||
{ Options flags }
|
||||
@ -1019,7 +1025,10 @@ begin
|
||||
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell }
|
||||
begin
|
||||
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);
|
||||
end;
|
||||
|
||||
@ -1035,8 +1044,14 @@ begin
|
||||
}
|
||||
AStream.WriteWord(WordToLE(AFormula[i].Row));
|
||||
AStream.WriteWord(WordToLE(AFormula[i].Row2));
|
||||
AStream.WriteWord(WordToLE(AFormula[i].Col));
|
||||
AStream.WriteWord(WordToLE(AFormula[i].Col2));
|
||||
c := AFormula[i].Col;
|
||||
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);
|
||||
end;
|
||||
|
||||
@ -1046,6 +1061,23 @@ begin
|
||||
Inc(RPNLength, 8);
|
||||
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 }
|
||||
INT_EXCEL_TOKEN_TADD, INT_EXCEL_TOKEN_TSUB, INT_EXCEL_TOKEN_TMUL,
|
||||
INT_EXCEL_TOKEN_TDIV, INT_EXCEL_TOKEN_TPOWER: begin end;
|
||||
@ -1060,13 +1092,21 @@ begin
|
||||
Inc(RPNLength, 3);
|
||||
end;
|
||||
|
||||
// Functions
|
||||
// Functions with fixed parameter count
|
||||
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
|
||||
begin
|
||||
AStream.WriteWord(lSecondaryID);
|
||||
AStream.WriteWord(WordToLE(lSecondaryID));
|
||||
Inc(RPNLength, 2);
|
||||
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
|
||||
end;
|
||||
end;
|
||||
|
@ -26,19 +26,23 @@ const
|
||||
INT_EXCEL_TOKEN_TSUB = $04;
|
||||
INT_EXCEL_TOKEN_TMUL = $05;
|
||||
INT_EXCEL_TOKEN_TDIV = $06;
|
||||
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation
|
||||
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation
|
||||
INT_EXCEL_TOKEN_TLT = $09; // Less than
|
||||
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal
|
||||
INT_EXCEL_TOKEN_TEQ = $0B; // Equal
|
||||
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal
|
||||
INT_EXCEL_TOKEN_TGT = $0D; // Greater than
|
||||
INT_EXCEL_TOKEN_TNE = $0E; // Not equal
|
||||
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation ^
|
||||
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation &
|
||||
INT_EXCEL_TOKEN_TLT = $09; // Less than <
|
||||
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal <=
|
||||
INT_EXCEL_TOKEN_TEQ = $0B; // Equal =
|
||||
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal >=
|
||||
INT_EXCEL_TOKEN_TGT = $0D; // Greater than >
|
||||
INT_EXCEL_TOKEN_TNE = $0E; // Not equal <>
|
||||
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
|
||||
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
|
||||
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}
|
||||
INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand
|
||||
INT_EXCEL_TOKEN_TSTR = $17; //string
|
||||
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
|
||||
INT_EXCEL_TOKEN_TINT = $1E; //integer
|
||||
@ -49,6 +53,9 @@ const
|
||||
INT_EXCEL_TOKEN_TREFR = $24;
|
||||
INT_EXCEL_TOKEN_TREFV = $44;
|
||||
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 }
|
||||
// _R: reference; _V: value; _A: array
|
||||
@ -61,13 +68,109 @@ const
|
||||
INT_EXCEL_TOKEN_FUNCVAR_R = $22;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
||||
INT_EXCEL_TOKEN_TAREA_R = $25;
|
||||
|
||||
{ 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_INT = 25;
|
||||
INT_EXCEL_SHEET_FUNC_SIGN = 26;
|
||||
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_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 }
|
||||
// 01H tExp Matrix formula or shared formula
|
||||
@ -200,7 +303,7 @@ type
|
||||
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
|
||||
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
|
||||
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
|
||||
// Workbook Globals records
|
||||
// Write out used codepage for character encoding
|
||||
@ -408,45 +511,174 @@ begin
|
||||
end;
|
||||
|
||||
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
|
||||
AElementKind: TFEKind; out ASecondaryID: Word): Byte;
|
||||
begin
|
||||
ASecondaryID := 0;
|
||||
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}
|
||||
|
||||
case AElementKind of
|
||||
{ Operand Tokens }
|
||||
fekCell: Result := INT_EXCEL_TOKEN_TREFR;
|
||||
fekCellRange: Result := INT_EXCEL_TOKEN_TAREA_R;
|
||||
fekNum: Result := INT_EXCEL_TOKEN_TNUM;
|
||||
{ Basic operations }
|
||||
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 Functions}
|
||||
fekABS:
|
||||
begin
|
||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_ABS;
|
||||
// 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
|
||||
case TokenIDs[AElementKind, 0] of
|
||||
0: begin
|
||||
Result := TokenIDs[AElementKind, 1];
|
||||
ASecondaryID := 0;
|
||||
end;
|
||||
fekDATE:
|
||||
begin
|
||||
1: begin
|
||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_DATE;
|
||||
ASecondaryID := TokenIDs[AElementKind, 1]
|
||||
end;
|
||||
fekROUND:
|
||||
begin
|
||||
Result := INT_EXCEL_TOKEN_FUNC_V;
|
||||
ASecondaryID := INT_EXCEL_SHEET_FUNC_ROUND;
|
||||
2: begin
|
||||
Result := INT_EXCEL_TOKEN_FUNCVAR_V;
|
||||
ASecondaryID := TokenIDs[AElementKind, 1]
|
||||
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;
|
||||
|
||||
|
Reference in New Issue
Block a user