diff --git a/components/fpspreadsheet/examples/db_import_export/db_export_import.lpi b/components/fpspreadsheet/examples/db_import_export/db_export_import.lpi
index a00db4c28..f364ff07f 100644
--- a/components/fpspreadsheet/examples/db_import_export/db_export_import.lpi
+++ b/components/fpspreadsheet/examples/db_import_export/db_export_import.lpi
@@ -27,10 +27,13 @@
-
+
-
+
+
+
+
@@ -43,6 +46,7 @@
+
diff --git a/components/fpspreadsheet/examples/excel2demo/excel2read.lpi b/components/fpspreadsheet/examples/excel2demo/excel2read.lpi
index 044140276..7e6fa76f2 100644
--- a/components/fpspreadsheet/examples/excel2demo/excel2read.lpi
+++ b/components/fpspreadsheet/examples/excel2demo/excel2read.lpi
@@ -32,7 +32,7 @@
-
+
diff --git a/components/fpspreadsheet/examples/excel2demo/excel2read.lpr b/components/fpspreadsheet/examples/excel2demo/excel2read.lpr
index 9bfa7b7ac..f3be0df21 100644
--- a/components/fpspreadsheet/examples/excel2demo/excel2read.lpr
+++ b/components/fpspreadsheet/examples/excel2demo/excel2read.lpr
@@ -32,6 +32,8 @@ begin
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
+
+ MyWorkbook.Options := MyWorkbook.Options + [boReadFormulas, boAutoCalc];
MyWorkbook.ReadFromFile(InputFilename, sfExcel2);
MyWorksheet := MyWorkbook.GetFirstWorksheet;
@@ -44,9 +46,12 @@ begin
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
begin
- WriteLn('Row: ', CurCell^.Row, ' Col: ', CurCell^.Col, ' Value: ',
- UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
- );
+ Write('Row: ', CurCell^.Row, ' Col: ', CurCell^.Col, ' Value: ',
+ UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
+ );
+ if HasFormula(CurCell) then
+ Write(' (Formula ', CurCell^.FormulaValue, ')');
+ WriteLn;
CurCell := MyWorkSheet.GetNextCell();
end;
diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpi b/components/fpspreadsheet/examples/excel2demo/excel2write.lpi
index ec66770e0..85d2183eb 100644
--- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpi
+++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpi
@@ -32,7 +32,7 @@
-
+
diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr
index d30c9df58..89fbc88fe 100644
--- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr
+++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr
@@ -51,30 +51,31 @@ begin
MyWorksheet.WriteNumber(0, 2, 3.0);
MyWorksheet.WriteNumber(0, 3, 4.0);
- // Write the formula E1 = ABS(A1)
+ // Write the formula E1 = ABS(A1) as rpn token array
SetLength(MyRPNFormula, 2);
MyRPNFormula[0].ElementKind := fekCell;
MyRPNFormula[0].Col := 0;
MyRPNFormula[0].Row := 0;
- MyRPNFormula[1].ElementKind := fekABS;
+ MyRPNFormula[1].ElementKind := fekFUNC;
+ MyRPNFormula[1].FuncName := 'ABS';
MyWorksheet.WriteRPNFormula(0, 4, MyRPNFormula);
- // Write the formula F1 = ROUND(A1, 0)
+ // Write the formula F1 = ROUND(A1, 0) as rpn token array
SetLength(MyRPNFormula, 3);
MyRPNFormula[0].ElementKind := fekCell;
MyRPNFormula[0].Col := 0;
MyRPNFormula[0].Row := 0;
MyRPNFormula[1].ElementKind := fekNum;
MyRPNFormula[1].DoubleValue := 0.0;
- MyRPNFormula[2].ElementKind := fekROUND;
+ MyRPNFormula[2].ElementKind := fekFUNC;
+ MyRPNFormula[2].FuncName := 'ROUND';
MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula);
// Write a string formula to G1 = "A" & "B"
- MyWorksheet.WriteRPNFormula(0, 6, CreateRPNFormula(
- RPNString('A',
- RPNSTring('B',
- RPNFunc(fekConcat,
- nil)))));
+ MyWorksheet.WriteFormula(0, 6, '="A"&"B"');
+
+ // Write string formula to H1 = sin(A1+B1)
+ MyWorksheet.WriteFormula(0, 7, '=SIN(A1+B1)');
// Write some string cells
MyWorksheet.WriteUTF8Text(1, 0, 'First');
diff --git a/components/fpspreadsheet/examples/excel5demo/excel5read.lpr b/components/fpspreadsheet/examples/excel5demo/excel5read.lpr
index 4c8880725..43103b1fb 100644
--- a/components/fpspreadsheet/examples/excel5demo/excel5read.lpr
+++ b/components/fpspreadsheet/examples/excel5demo/excel5read.lpr
@@ -31,6 +31,7 @@ begin
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
+ MyWorkbook.Options := MyWorkbook.Options + [boReadFormulas];
MyWorkbook.ReadFromFile(InputFilename, sfExcel5);
MyWorksheet := MyWorkbook.GetFirstWorksheet;
@@ -43,11 +44,12 @@ begin
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
begin
- WriteLn('Row: ', CurCell^.Row,
+ Write('Row: ', CurCell^.Row,
' Col: ', CurCell^.Col, ' Value: ',
- UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row,
- CurCell^.Col))
- );
+ UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col)));
+ if HasFormula(CurCell) then
+ Write(' - Formula: ', CurCell^.FormulaValue);
+ WriteLn;
CurCell := MyWorkSheet.GetNextCell();
end;
diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpi b/components/fpspreadsheet/examples/excel5demo/excel5write.lpi
index f7ff59409..a98d85c43 100644
--- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpi
+++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpi
@@ -32,7 +32,7 @@
-
+
diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr
index 4dc09f2e6..58179cfb3 100644
--- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr
+++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr
@@ -35,7 +35,9 @@ begin
MyWorkbook := TsWorkbook.Create;
MyWorksheet := MyWorkbook.AddWorksheet(UTF8ToAnsi(Str_Worksheet1));
+ MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving];
MyWorksheet.Options := MyWorksheet.Options + [soHasFrozenPanes];
+
MyWorksheet.LeftPaneWidth := 1;
MyWorksheet.TopPaneHeight := 2;
@@ -139,7 +141,7 @@ begin
end;
}
- // Write the formula E1 = A1 + B1
+ // Write the formula E1 = A1 + B1 as rpn roken array
SetLength(MyRPNFormula, 3);
MyRPNFormula[0].ElementKind := fekCell;
MyRPNFormula[0].Col := 0;
@@ -150,15 +152,22 @@ begin
MyRPNFormula[2].ElementKind := fekAdd;
MyWorksheet.WriteRPNFormula(0, 4, MyRPNFormula);
- // Write the formula F1 = ABS(A1)
+ // Write the formula F1 = ABS(A1) as rpn token array
SetLength(MyRPNFormula, 2);
MyRPNFormula[0].ElementKind := fekCell;
MyRPNFormula[0].Col := 0;
MyRPNFormula[0].Row := 0;
- MyRPNFormula[1].ElementKind := fekABS;
+ MyRPNFormula[1].ElementKind := fekFunc;
+ MyRPNFormula[1].FuncName := 'ABS';
MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula);
- r:= 10;
+ // Write formula G1 = "A"&"B" as string formula
+ MyWorksheet.WriteFormula(0, 6, '="A"&"B"');
+
+ // Write formula H1 = sin(A1+B1) as string formula
+ Myworksheet.WriteFormula(0, 7, '=SIN(A1+B1)');
+
+ r := 10;
// Write current date/time to cells B11:B16
MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDate');
MyWorksheet.WriteDateTime(r, 1, now, nfShortDate);
diff --git a/components/fpspreadsheet/examples/excel8demo/excel8read.lpi b/components/fpspreadsheet/examples/excel8demo/excel8read.lpi
index 9525d913e..3dd5974f3 100644
--- a/components/fpspreadsheet/examples/excel8demo/excel8read.lpi
+++ b/components/fpspreadsheet/examples/excel8demo/excel8read.lpi
@@ -31,7 +31,7 @@
-
+
diff --git a/components/fpspreadsheet/examples/excel8demo/excel8read.lpr b/components/fpspreadsheet/examples/excel8demo/excel8read.lpr
index a2065c1dd..41ec0f596 100644
--- a/components/fpspreadsheet/examples/excel8demo/excel8read.lpr
+++ b/components/fpspreadsheet/examples/excel8demo/excel8read.lpr
@@ -36,7 +36,7 @@ begin
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
- MyWorkbook.Options := MyWorkbook.Options + [boReadFormulas, boAutoCalc];
+ MyWorkbook.ReadFormulas := true;
MyWorkbook.ReadFromFile(InputFilename, sfExcel8);
@@ -55,8 +55,8 @@ begin
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row,
CurCell^.Col))
);
- if Length(CurCell^.RPNFormulaValue) > 0 then
- WriteLn(' Formula: ', MyWorkSheet.ReadRPNFormulaAsString(CurCell))
+ if HasFormula(CurCell) then
+ WriteLn(' Formula: ', MyWorkSheet.ReadFormulaAsString(CurCell))
else
WriteLn;
CurCell := MyWorkSheet.GetNextCell();
diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpi b/components/fpspreadsheet/examples/excel8demo/excel8write.lpi
index b7f0924fa..8ebe4184e 100644
--- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpi
+++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpi
@@ -15,64 +15,8 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -88,7 +32,7 @@
-
+
@@ -101,8 +45,12 @@
+
+
+
-
+
+
@@ -111,7 +59,7 @@
-
+
diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr
index e0836b025..9e9daf9db 100644
--- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr
+++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr
@@ -10,8 +10,7 @@ program excel8write;
{$mode delphi}{$H+}
uses
- Classes, SysUtils, fpspreadsheet, xlsbiff8,
- laz_fpspreadsheet;
+ Classes, SysUtils, fpspreadsheet, xlsbiff8;
const
Str_First = 'First';
@@ -41,6 +40,7 @@ begin
MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8));
MyWorkbook.FormatSettings.CurrencyFormat := 2;
MyWorkbook.FormatSettings.NegCurrFormat := 14;
+ MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving];
MyWorksheet := MyWorkbook.AddWorksheet(Str_Worksheet1);
MyWorksheet.Options := MyWorksheet.Options - [soShowGridLines];
@@ -147,28 +147,27 @@ begin
end;
}
- // Write the formula E1 = A1 + B1
- SetLength(MyRPNFormula, 3);
- MyRPNFormula[0].ElementKind := fekCell;
- MyRPNFormula[0].Col := 0;
- MyRPNFormula[0].Row := 0;
- MyRPNFormula[1].ElementKind := fekCell;
- MyRPNFormula[1].Col := 1;
- MyRPNFormula[1].Row := 0;
- MyRPNFormula[2].ElementKind := fekAdd;
- MyWorksheet.WriteRPNFormula(0, 4, MyRPNFormula);
- MyWorksheet.WriteFont(0, 4, 'Arial', 10, [fssUnderline], scBlack);
+ // Write the string formula E1 = A1 + B1 ...
+ MyWorksheet.WriteFormula(0, 4, 'A1+B1');
+ // ... and the rpn formula E2 = A1 + B1
+ MyWorksheet.WriteRPNFormula(1, 4, CreateRPNFormula(
+ RPNCellValue('A1',
+ RPNCellValue('B1',
+ RPNFunc(fekAdd,
+ nil)))));
- // Write the formula F1 = ABS(A1)
- SetLength(MyRPNFormula, 2);
- MyRPNFormula[0].ElementKind := fekCell;
- MyRPNFormula[0].Col := 0;
- MyRPNFormula[0].Row := 0;
- MyRPNFormula[1].ElementKind := fekABS;
- MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula);
+ // Write the formula F1 = ABS(A1) as string formula ...
+ MyWorksheet.WriteFormula(0, 5, 'ABS(A1)');
+ // ... and F2 = ABS(A1) as rpn formula
+ MyWorksheet.WriteRPNFormula(1, 5, CreateRPNFormula(
+ RPNCellValue('A1',
+ RPNFunc('ABS',
+ nil))));
- // Write a string formula to G1 = "A" & "B"
- MyWorksheet.WriteRPNFormula(0, 6, CreateRPNFormula(
+ // Write a string formula to G1 = "A" & "B" ...
+ MyWorksheet.WriteFormula(0, 6, '"A"&"B"');
+ // ... and again as rpn formula
+ MyWorksheet.WriteRPNFormula(1, 6, CreateRPNFormula(
RPNString('A',
RPNSTring('B',
RPNFunc(fekConcat,
diff --git a/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpi b/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpi
index 22e3446e0..d970e3ad0 100644
--- a/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpi
+++ b/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpi
@@ -56,7 +56,6 @@
-
diff --git a/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpr b/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpr
index 0fd7e448a..ee79836f5 100644
--- a/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpr
+++ b/components/fpspreadsheet/examples/ooxmldemo/ooxmlread.lpr
@@ -35,6 +35,7 @@ begin
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
+ MyWorkbook.Options := MyWorkbook.Options + [boReadFormulas];
MyWorkbook.ReadFromFile(InputFilename, sfOOXML);
MyWorksheet := MyWorkbook.GetFirstWorksheet;
diff --git a/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpi b/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpi
index eff9a9a72..676406870 100644
--- a/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpi
+++ b/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpi
@@ -57,10 +57,5 @@
-
-
-
-
-
diff --git a/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpr b/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpr
index dec327388..b11a7c3b1 100644
--- a/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpr
+++ b/components/fpspreadsheet/examples/ooxmldemo/ooxmlwrite.lpr
@@ -40,6 +40,11 @@ begin
MyWorksheet.WriteColWidth(0, 20);
MyWorksheet.WriteRowHeight(0, 4);
+ // Write some formulas
+ Myworksheet.WriteFormula(0, 5, '=A1-B1');
+ Myworksheet.WriteFormula(0, 6, '=SUM(A1:D1)');
+ MyWorksheet.WriteFormula(0, 7, '=SIN(A1+B1)');
+
// Uncomment this to test large XLS files
for i := 2 to 2{20} do
begin
diff --git a/components/fpspreadsheet/examples/opendocdemo/opendocread.lpi b/components/fpspreadsheet/examples/opendocdemo/opendocread.lpi
index d161ba808..2b383bdaf 100644
--- a/components/fpspreadsheet/examples/opendocdemo/opendocread.lpi
+++ b/components/fpspreadsheet/examples/opendocdemo/opendocread.lpi
@@ -32,7 +32,7 @@
-
+
@@ -51,16 +51,12 @@
+
-
-
-
-
-
diff --git a/components/fpspreadsheet/examples/opendocdemo/opendocread.lpr b/components/fpspreadsheet/examples/opendocdemo/opendocread.lpr
index 89b673173..eb4af43fd 100644
--- a/components/fpspreadsheet/examples/opendocdemo/opendocread.lpr
+++ b/components/fpspreadsheet/examples/opendocdemo/opendocread.lpr
@@ -11,7 +11,8 @@ program opendocread;
{$mode delphi}{$H+}
uses
- Classes, SysUtils, fpspreadsheet, fpsallformats;
+ Classes, SysUtils, fpspreadsheet, fpsallformats,
+ laz_fpspreadsheet;
var
MyWorkbook: TsWorkbook;
diff --git a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpi b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpi
index ce2de4812..49ad37280 100644
--- a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpi
+++ b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpi
@@ -32,7 +32,7 @@
-
+
@@ -45,22 +45,14 @@
-
-
-
-
-
+
+
-
-
-
-
-
diff --git a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr
index 1fa50146e..cd5b83481 100644
--- a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr
+++ b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr
@@ -10,7 +10,8 @@ program opendocwrite;
{$mode delphi}{$H+}
uses
- Classes, SysUtils, fpspreadsheet, fpsallformats;
+ Classes, SysUtils, fpspreadsheet, fpsallformats,
+ laz_fpspreadsheet;
var
MyWorkbook: TsWorkbook;
diff --git a/components/fpspreadsheet/examples/other/demo_expression_parser.pas b/components/fpspreadsheet/examples/other/demo_expression_parser.pas
index 9730fe7fd..2f0c64d60 100644
--- a/components/fpspreadsheet/examples/other/demo_expression_parser.pas
+++ b/components/fpspreadsheet/examples/other/demo_expression_parser.pas
@@ -10,14 +10,6 @@ uses
{ you can add units after this },
TypInfo, fpSpreadsheet, fpsUtils, fpsExprParser;
-function Prepare(AFormula: String): String;
-begin
- if (AFormula <> '') and (AFormula[1] = '=') then
- Result := Copy(AFormula, 2, Length(AFormula)-1)
- else
- Result := AFormula;
-end;
-
var
workbook: TsWorkbook;
worksheet: TsWorksheet;
@@ -26,55 +18,89 @@ var
res: TsExpressionResult;
formula: TsRPNFormula;
i: Integer;
+ s: String;
begin
workbook := TsWorkbook.Create;
try
worksheet := workbook.AddWorksheet('Test');
- {
- worksheet.WriteNumber(0, 0, 2); // A1
+
+ worksheet.WriteNumber(0, 0, 1); // A1
worksheet.WriteNumber(0, 1, 2.5); // B1
- }
+
+ {
worksheet.WriteUTF8Text(0, 0, 'Hallo'); // A1
worksheet.WriteUTF8Text(0, 1, 'World'); // B1
+ }
+ //cell := worksheet.WriteFormula(1, 0, '=4+5'); // A2
+ //cell := worksheet.WriteFormula(1, 0, 'AND(TRUE(), TRUE(), TRUE())');
+ //cell := worksheet.WriteFormula(1, 0, 'SIN(A1+B1)');
+ //cell := worksheet.WriteFormula(1, 0, '=TRUE()');
+ //cell := worksheet.WriteFormula(1, 0, '=1-(4/2)^2*2-1'); // A2
+ //cell := Worksheet.WriteFormula(1, 0, 'datedif(today(),Date(2014,1,1),"D")');
+ //cell := Worksheet.WriteFormula(1, 0, 'Day(Date(2014, 1, 12))');
+ //cell := Worksheet.WriteFormula(1, 0, 'SUM(1,2,3)');
+ //cell := Worksheet.WriteFormula(1, 0, 'CELL("address",A1)');
+ cell := Worksheet.WriteFormula(1, 0, 'ISBLANK(A1)');
- //cell := worksheet.WriteFormula(1, 0, '=(A1+2)*3'); // A2
- cell := worksheet.WriteFormula(1, 0, 'A1&" "&B1');
+ WriteLn('A1: ', worksheet.ReadAsUTF8Text(0, 0));
+ WriteLn('B1: ', worksheet.ReadAsUTF8Text(0, 1));
- WriteLn('A1 = ', worksheet.ReadAsUTF8Text(0, 0));
- WriteLn('B1 = ', worksheet.ReadAsUTF8Text(0, 1));
-
- parser := TsExpressionParser.Create(worksheet);
+ parser := TsSpreadsheetParser.Create(worksheet);
try
- parser.Builtins := [bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion, bcData,
- bcVaria, bcUser];
- parser.Expression := Prepare(cell^.FormulaValue.FormulaStr);
- res := parser.Evaluate;
+ try
+ parser.Expression := cell^.FormulaValue;
+ res := parser.Evaluate;
- Write('A2 = ', Prepare(cell^.FormulaValue.FormulaStr), ' = ');
- case res.ResultType of
- rtBoolean : WriteLn(BoolToStr(res.ResBoolean));
- rtFloat : WriteLn(FloatToStr(res.ResFloat));
- rtInteger : WriteLn(IntToStr(res.ResInteger));
- rtDateTime : WriteLn(FormatDateTime('c', res.ResDateTime));
- rtString : WriteLn(res.ResString);
- end;
-
- WriteLn('Reconstructed string formula: ', parser.BuildFormula);
-
- WriteLn('RPN formula:');
- formula := parser.BuildRPNFormula;
- for i:=0 to Length(formula)-1 do begin
- Write(' Item ', i, ': token ', GetEnumName(TypeInfo(TFEKind), ord(formula[i].ElementKind)));
- case formula[i].ElementKind of
- fekCell : Write(' / cell: ' +GetCellString(formula[i].Row, formula[i].Col, formula[i].RelFlags));
- fekNum : Write(' / number value: ', FloatToStr(formula[i].DoubleValue));
- fekString : Write(' / string value: "', formula[i].StringValue, '"');
- fekBool : Write(' / boolean value: ', BoolToStr(formula[i].DoubleValue <> 0));
+ WriteLn('A2: ', parser.Expression);
+ Write('Result: ');
+ case res.ResultType of
+ rtEmpty : WriteLn('--- empty ---');
+ rtBoolean : WriteLn(BoolToStr(res.ResBoolean, true));
+ rtFloat : WriteLn(FloatToStr(res.ResFloat));
+ rtInteger : WriteLn(IntToStr(res.ResInteger));
+ rtDateTime : WriteLn(FormatDateTime('c', res.ResDateTime));
+ rtString : WriteLn(res.ResString);
+ rtError : WriteLn(GetErrorValueStr(res.ResError));
end;
- WriteLn;
+
+ WriteLn('Reconstructed string formula: ', parser.BuildStringFormula);
+ formula := parser.RPNFormula;
+
+ for i:=0 to Length(formula)-1 do begin
+ Write(' Item ', i, ': token ', GetEnumName(TypeInfo(TFEKind), ord(formula[i].ElementKind)), ' ', formula[i].FuncName);
+ case formula[i].ElementKind of
+ fekCell : Write(' / cell: ' +GetCellString(formula[i].Row, formula[i].Col, formula[i].RelFlags));
+ fekNum : Write(' / float value: ', FloatToStr(formula[i].DoubleValue));
+ fekInteger : Write(' / integer value: ', IntToStr(formula[i].IntValue));
+ fekString : Write(' / string value: "', formula[i].StringValue, '"');
+ fekBool : Write(' / boolean value: ', BoolToStr(formula[i].DoubleValue <> 0, true));
+ end;
+ WriteLn;
+ end;
+ finally
+ parser.Free;
end;
+ except on E:Exception do
+ begin
+ WriteLn('Parser/calculation error: ', E.Message);
+ raise;
+ end;
+ end;
+
+ parser := TsSpreadsheetParser.Create(worksheet);
+ try
+ try
+ parser.RPNFormula := formula;
+ s := parser.BuildStringFormula;
+ WriteLn('String formula, reconstructed from RPN formula: ', s);
+ except on E:Exception do
+ begin
+ WriteLn('RPN/string formula conversion error: ', E.Message);
+ raise;
+ end;
+ end;
finally
parser.Free;
end;
@@ -82,5 +108,6 @@ begin
finally
workbook.Free;
end;
+
end.
diff --git a/components/fpspreadsheet/examples/other/demo_formula_func.lpi b/components/fpspreadsheet/examples/other/demo_formula_func.lpi
index 87ec5202c..613b1a53d 100644
--- a/components/fpspreadsheet/examples/other/demo_formula_func.lpi
+++ b/components/fpspreadsheet/examples/other/demo_formula_func.lpi
@@ -46,6 +46,7 @@
+
diff --git a/components/fpspreadsheet/examples/other/demo_formula_func.pas b/components/fpspreadsheet/examples/other/demo_formula_func.pas
index de09e0dad..156be53a2 100644
--- a/components/fpspreadsheet/examples/other/demo_formula_func.pas
+++ b/components/fpspreadsheet/examples/other/demo_formula_func.pas
@@ -8,7 +8,7 @@
- PMT() (payment)
- NPER() (number of payment periods)
- The demo writes an xls file which uses these formulas and then displays
+ The demo writes a spreadsheet file which uses these formulas and then displays
the result in a console window. (Open the generated file in Excel or
Open/LibreOffice and compare).
}
@@ -24,117 +24,118 @@ uses
{$ENDIF}
{$ENDIF}
Classes, SysUtils,
- math, fpspreadsheet, xlsbiff8, fpsfunc, financemath;
+ math, fpspreadsheet, fpsallformats, fpsexprparser, financemath;
+{ Base data used in this demonstration }
+const
+ INTEREST_RATE = 0.03; // interest rate per period
+ NUMBER_PAYMENTS = 10; // number of payment periods
+ REG_PAYMENT = 1000; // regular payment per period
+ PRESENT_VALUE = 10000.0; // present value of investment
+ PAYMENT_WHEN: TPaymentTime = ptEndOfPeriod; // when is the payment made
{------------------------------------------------------------------------------}
{ Adaption of financial functions to usage by fpspreadsheet }
{ The functions are implemented in the unit "financemath.pas". }
{------------------------------------------------------------------------------}
-function fpsFV(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
-var
- data: TsArgNumberArray;
+procedure fpsFV(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- // Pop the argument from the stack. This can be done by means of PopNumberValues
- // which brings the values back in the right order and reports an error
- // in case of non-numerical values.
- if Args.PopNumberValues(NumArgs, false, data, Result) then
- // Call the FutureValue function with the NumberValues of the arguments.
- Result := CreateNumberArg(FutureValue(
- data[0], // interest rate
- round(data[1]), // number of payments
- data[2], // payment
- data[3], // present value
- TPaymentTime(round(data[4])) // payment type
- ));
+ Result.ResFloat := FutureValue(
+ ArgToFloat(Args[0]), // interest rate
+ ArgToInt(Args[1]), // number of payments
+ ArgToFloat(Args[2]), // payment
+ ArgToFloat(Args[3]), // present value
+ TPaymentTime(ArgToInt(Args[4])) // payment type
+ );
end;
-function fpsPMT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
-var
- data: TsArgNumberArray;
+procedure fpsPMT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- if Args.PopNumberValues(NumArgs, false, data, Result) then
- Result := CreateNumberArg(Payment(
- data[0], // interest rate
- round(data[1]), // number of payments
- data[2], // present value
- data[3], // future value
- TPaymentTime(round(data[4])) // payment type
- ));
+ Result.ResFloat := Payment(
+ ArgToFloat(Args[0]), // interest rate
+ ArgToInt(Args[1]), // number of payments
+ ArgToFloat(Args[2]), // present value
+ ArgToFloat(Args[3]), // future value
+ TPaymentTime(ArgToInt(Args[4])) // payment type
+ );
end;
-function fpsPV(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
-// Present value
-var
- data: TsArgNumberArray;
+procedure fpsPV(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- if Args.PopNumberValues(NumArgs, false, data, Result) then
- Result := CreateNumberArg(PresentValue(
- data[0], // interest rate
- round(data[1]), // number of payments
- data[2], // payment
- data[3], // future value
- TPaymentTime(round(data[4])) // payment type
- ));
+ Result.ResFloat := PresentValue(
+ ArgToFloat(Args[0]), // interest rate
+ ArgToInt(Args[1]), // number of payments
+ ArgToFloat(Args[2]), // payment
+ ArgToFloat(Args[3]), // future value
+ TPaymentTime(ArgToInt(Args[4])) // payment type
+ );
end;
-function fpsNPER(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
-var
- data: TsArgNumberArray;
+procedure fpsNPER(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- if Args.PopNumberValues(NumArgs, false, data, Result) then
- Result := CreateNumberArg(NumberOfPeriods(
- data[0], // interest rate
- data[1], // payment
- data[2], // present value
- data[3], // future value
- TPaymentTime(round(data[4])) // payment type
- ));
+ Result.ResFloat := NumberOfPeriods(
+ ArgToFloat(Args[0]), // interest rate
+ ArgToFloat(Args[1]), // payment
+ ArgToFloat(Args[2]), // present value
+ ArgToFloat(Args[3]), // future value
+ TPaymentTime(ArgToInt(Args[4])) // payment type
+ );
end;
-function fpsRATE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
-var
- data: TsArgNumberArray;
+procedure fpsRATE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- if Args.PopNumberValues(NumArgs, false, data, Result) then
- Result := CreateNumberArg(InterestRate(
- round(data[0]), // number of payment periods
- data[1], // payment
- data[2], // present value
- data[3], // future value
- TPaymentTime(round(data[4])) // payment type
- ));
+ Result.ResFloat := InterestRate(
+ ArgToInt(Args[0]), // number of payments
+ ArgToFloat(Args[1]), // payment
+ ArgToFloat(Args[2]), // present value
+ ArgToFloat(Args[3]), // future value
+ TPaymentTime(ArgToInt(Args[4])) // payment type
+ );
end;
+
{------------------------------------------------------------------------------}
{ Write xls file comparing our own calculations with Excel result }
{------------------------------------------------------------------------------}
procedure WriteFile(AFileName: String);
const
- INTEREST_RATE = 0.03; // interest rate per period
- NUMBER_PAYMENTS = 10; // number of payment periods
- REG_PAYMENT = 1000; // regular payment per period
- PRESENT_VALUE = 10000; // present value of investment
- PAYMENT_WHEN: TPaymentTime = ptEndOfPeriod; // when is the payment made
+ 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;
var
workbook: TsWorkbook;
worksheet: TsWorksheet;
fval, pval, pmtval, nperval, rateval: Double;
+ formula: String;
+ fs: TFormatSettings;
+
begin
{ We have to register our financial functions in fpspreadsheet. Otherwise an
error code would be displayed in the reading part of this demo for these
- formula cells. }
- RegisterFormulaFunc(fekFV, @fpsFV);
- RegisterFormulaFunc(fekPMT, @fpsPMT);
- RegisterFormulaFunc(fekPV, @fpsPV);
- RegisterFormulaFunc(fekNPER, @fpsNPER);
- RegisterFormulaFunc(fekRATE, @fpsRATE);
+ formula cells.
+ The 1st parameter is the data type of the function result ('F'=float)
+ The 2nd parameter shows the data types of the arguments ('F=float, 'I'=integer)
+ The 3rd parameter is the Excel ID needed when writing to xls files. (see
+ "OpenOffice Documentation of Microsoft Excel File Format", section 3.11)
+ The 4th parameter is the address of the function to be used for calculation. }
+
+ RegisterFunction('FV', 'F', 'FIFFI', INT_EXCEL_SHEET_FUNC_FV, @fpsFV);
+ RegisterFunction('PMT', 'F', 'FIFFI', INT_EXCEL_SHEET_FUNC_PMT, @fpsPMT);
+ RegisterFunction('PV', 'F', 'FIFFI', INT_EXCEL_SHEET_FUNC_PV, @fpsPV);
+ RegisterFunction('NPER', 'F', 'FFFFI', INT_EXCEL_SHEET_FUNC_NPER, @fpsNPER);
+ RegisterFunction('RATE', 'F', 'IFFFI', INT_EXCEL_SHEET_FUNC_RATE, @fpsRATE);
+
+ // The formula parser requires a point as decimals separator.
+ fs := DefaultFormatSettings;
+ fs.DecimalSeparator := '.';
workbook := TsWorkbook.Create;
try
- workbook.Options := workbook.Options + [boCalcBeforeSaving];
+ //workbook.Options := workbook.Options + [boCalcBeforeSaving];
worksheet := workbook.AddWorksheet('Financial');
worksheet.WriteColWidth(0, 40);
@@ -167,24 +168,14 @@ begin
worksheet.WriteUTF8Text(9, 0, 'Worksheet calculation using constants');
worksheet.WriteNumberFormat(9, 1, nfCurrency, 2, '$');
- worksheet.WriteRPNFormula(9, 1, CreateRPNFormula(
- RPNNumber(INTEREST_RATE,
- RPNNumber(NUMBER_PAYMENTS,
- RPNNumber(REG_PAYMENT,
- RPNNumber(PRESENT_VALUE,
- RPNNumber(ord(PAYMENT_WHEN),
- RPNFunc(fekFV, 5,
- nil))))))));
+ worksheet.WriteNumberFormat(9, 1, nfCurrency, 2, '$');
+ formula := Format('FV(%f,%d,%f,%f,%d)',
+ [1.0*INTEREST_RATE, NUMBER_PAYMENTS, 1.0*REG_PAYMENT, 1.0*PRESENT_VALUE, ord(PAYMENT_WHEN)], fs
+ );
+ worksheet.WriteFormula(9, 1, formula);
worksheet.WriteUTF8Text(10, 0, 'Worksheet calculation using cell values');
worksheet.WriteNumberFormat(10, 1, nfCurrency, 2, '$');
- worksheet.WriteRPNFormula(10, 1, CreateRPNFormula(
- RPNCellValue('B2', // interest rate
- RPNCellValue('B3', // number of periods
- RPNCellValue('B4', // payment
- RPNCellValue('B5', // present value
- RPNCellValue('B6', // payment at end or at start
- RPNFunc(fekFV, 5, // Call Excel's FV formula
- nil))))))));
+ worksheet.WriteFormula(10, 1, 'FV(B2,B3,B4,B5,B6)');
// present value calculation
pval := PresentValue(INTEREST_RATE, NUMBER_PAYMENTS, REG_PAYMENT, fval, PAYMENT_WHEN);
@@ -194,25 +185,14 @@ begin
worksheet.WriteCurrency(13, 1, pval, nfCurrency, 2, '$');
worksheet.WriteUTF8Text(14, 0, 'Worksheet calculation using constants');
+ formula := Format('PV(%f,%d,%f,%f,%d)',
+ [1.0*INTEREST_RATE, NUMBER_PAYMENTS, 1.0*REG_PAYMENT, fval, ord(PAYMENT_WHEN)], fs
+ );
worksheet.WriteNumberFormat(14, 1, nfCurrency, 2, '$');
- worksheet.WriteRPNFormula(14, 1, CreateRPNFormula(
- RPNNumber(INTEREST_RATE,
- RPNNumber(NUMBER_PAYMENTS,
- RPNNumber(REG_PAYMENT,
- RPNNumber(fval,
- RPNNumber(ord(PAYMENT_WHEN),
- RPNFunc(fekPV, 5,
- nil))))))));
+ worksheet.WriteFormula(14, 1, formula);
Worksheet.WriteUTF8Text(15, 0, 'Worksheet calculation using cell values');
worksheet.WriteNumberFormat(15, 1, nfCurrency, 2, '$');
- worksheet.WriteRPNFormula(15, 1, CreateRPNFormula(
- RPNCellValue('B2', // interest rate
- RPNCellValue('B3', // number of periods
- RPNCellValue('B4', // payment
- RPNCellValue('B11', // future value
- RPNCellValue('B6', // payment at end or at start
- RPNFunc(fekPV, 5, // Call Excel's PV formula
- nil))))))));
+ worksheet.WriteFormula(15, 1, 'PV(B2,B3,B4,B11,B6)');
// payments calculation
pmtval := Payment(INTEREST_RATE, NUMBER_PAYMENTS, PRESENT_VALUE, fval, PAYMENT_WHEN);
@@ -223,24 +203,13 @@ begin
worksheet.WriteUTF8Text(19, 0, 'Worksheet calculation using constants');
worksheet.WriteNumberFormat(19, 1, nfCurrency, 2, '$');
- worksheet.WriteRPNFormula(19, 1, CreateRPNFormula(
- RPNNumber(INTEREST_RATE,
- RPNNumber(NUMBER_PAYMENTS,
- RPNNumber(PRESENT_VALUE,
- RPNNumber(fval,
- RPNNumber(ord(PAYMENT_WHEN),
- RPNFunc(fekPMT, 5,
- nil))))))));
+ formula := Format('PMT(%g,%d,%g,%g,%d)',
+ [INTEREST_RATE, NUMBER_PAYMENTS, PRESENT_VALUE, fval, ord(PAYMENT_WHEN)], fs
+ );
+ worksheet.WriteFormula(19, 1, formula);
Worksheet.WriteUTF8Text(20, 0, 'Worksheet calculation using cell values');
worksheet.WriteNumberFormat(20, 1, nfCurrency, 2, '$');
- worksheet.WriteRPNFormula(20, 1, CreateRPNFormula(
- RPNCellValue('B2', // interest rate
- RPNCellValue('B3', // number of periods
- RPNCellValue('B5', // present value
- RPNCellValue('B11', // future value
- RPNCellValue('B6', // payment at end or at start
- RPNFunc(fekPMT, 5, // Call Excel's PMT formula
- nil))))))));
+ worksheet.WriteFormula(20, 1, 'PMT(B2,B3,B5,B11,B6)');
// number of periods calculation
nperval := NumberOfPeriods(INTEREST_RATE, REG_PAYMENT, PRESENT_VALUE, fval, PAYMENT_WHEN);
@@ -251,24 +220,13 @@ begin
worksheet.WriteUTF8Text(24, 0, 'Worksheet calculation using constants');
worksheet.WriteNumberFormat(24, 1, nfFixed, 2);
- worksheet.WriteRPNFormula(24, 1, CreateRPNFormula(
- RPNNumber(INTEREST_RATE,
- RPNNumber(REG_PAYMENT,
- RPNNumber(PRESENT_VALUE,
- RPNNumber(fval,
- RPNNumber(ord(PAYMENT_WHEN),
- RPNFunc(fekNPER, 5,
- nil))))))));
+ formula := Format('NPER(%g,%g,%g,%g,%d)',
+ [1.0*INTEREST_RATE, 1.0*REG_PAYMENT, 1.0*PRESENT_VALUE, fval, ord(PAYMENT_WHEN)], fs
+ );
+ worksheet.WriteFormula(24, 1, formula);
Worksheet.WriteUTF8Text(25, 0, 'Worksheet calculation using cell values');
worksheet.WriteNumberFormat(25, 1, nfFixed, 2);
- worksheet.WriteRPNFormula(25, 1, CreateRPNFormula(
- RPNCellValue('B2', // interest rate
- RPNCellValue('B4', // payment
- RPNCellValue('B5', // present value
- RPNCellValue('B11', // future value
- RPNCellValue('B6', // payment at end or at start
- RPNFunc(fekNPER, 5, // Call Excel's PMT formula
- nil))))))));
+ worksheet.WriteFormula(25, 1, 'NPER(B2,B4,B5,B11,B6)');
// interest rate calculation
rateval := InterestRate(NUMBER_PAYMENTS, REG_PAYMENT, PRESENT_VALUE, fval, PAYMENT_WHEN);
@@ -279,26 +237,15 @@ begin
worksheet.WriteUTF8Text(29, 0, 'Worksheet calculation using constants');
worksheet.WriteNumberFormat(29, 1, nfPercentage, 2);
- worksheet.WriteRPNFormula(29, 1, CreateRPNFormula(
- RPNNumber(NUMBER_PAYMENTS,
- RPNNumber(REG_PAYMENT,
- RPNNumber(PRESENT_VALUE,
- RPNNumber(fval,
- RPNNumber(ord(PAYMENT_WHEN),
- RPNFunc(fekRATE, 5,
- nil))))))));
+ formula := Format('RATE(%d,%g,%g,%g,%d)',
+ [NUMBER_PAYMENTS, 1.0*REG_PAYMENT, 1.0*PRESENT_VALUE, fval, ord(PAYMENT_WHEN)], fs
+ );
+ worksheet.WriteFormula(29, 1, formula);
Worksheet.WriteUTF8Text(30, 0, 'Worksheet calculation using cell values');
worksheet.WriteNumberFormat(30, 1, nfPercentage, 2);
- worksheet.WriteRPNFormula(30, 1, CreateRPNFormula(
- RPNCellValue('B3', // number of payments
- RPNCellValue('B4', // payment
- RPNCellValue('B5', // present value
- RPNCellValue('B11', // future value
- RPNCellValue('B6', // payment at end or at start
- RPNFunc(fekRATE, 5, // Call Excel's PMT formula
- nil))))))));
+ worksheet.WriteFormula(30, 1, 'RATE(B3,B4,B5,B11,B6)');
- workbook.WriteToFile(AFileName, sfExcel8, true);
+ workbook.WriteToFile(AFileName, true);
finally
workbook.Free;
@@ -317,9 +264,8 @@ var
begin
workbook := TsWorkbook.Create;
try
- workbook.Options := workbook.Options + [boReadFormulas];
- workbook.ReadFromFile(AFilename, sfExcel8);
-
+ workbook.Options := workbook.Options + [boReadFormulas, boAutoCalc];
+ workbook.ReadFromFile(AFilename);
worksheet := workbook.GetFirstWorksheet;
// Write all cells with contents to the console
@@ -340,19 +286,27 @@ begin
WriteLn(s1+': ':50, s2);
end;
- WriteLn;
- WriteLn('Press [ENTER] to close...');
- ReadLn;
finally
workbook.Free;
end;
end;
const
- TestFile='test_fv.xls';
+ TestFile='test_user_formula.xlsx'; // Format depends on extension selected
+ // !!!! ods not working yet !!!!
+
begin
+ WriteLn('This demo registers user-defined functions for financial calculations');
+ WriteLn('and writes and reads the corresponding spreadsheet file.');
+ WriteLn;
+
WriteFile(TestFile);
ReadFile(TestFile);
+
+ WriteLn;
+ WriteLn('Open the file in Excel or OpenOffice/LibreOffice.');
+ WriteLn('Press [ENTER] to close...');
+ ReadLn;
end.
diff --git a/components/fpspreadsheet/examples/other/demo_recursive_calc.pas b/components/fpspreadsheet/examples/other/demo_recursive_calc.pas
index 2ccad431e..f1035306f 100644
--- a/components/fpspreadsheet/examples/other/demo_recursive_calc.pas
+++ b/components/fpspreadsheet/examples/other/demo_recursive_calc.pas
@@ -21,7 +21,7 @@ var
workbook: TsWorkbook;
worksheet: TsWorksheet;
const
- OutputFile='test_calc.xls';
+ OutputFile='test_recursive.xls';
begin
writeln('Starting program.');
@@ -35,26 +35,33 @@ begin
// A1
worksheet.WriteUTF8Text(0, 0, '=B2+1');
// B1
+ worksheet.WriteFormula(0, 1, 'B2+1');
+ {
worksheet.WriteRPNFormula(0, 1, CreateRPNFormula(
RPNCellValue('B2',
- RPNNumber(1,
+ RPNInteger(1,
RPNFunc(fekAdd, nil)))));
-
+ }
// A2
worksheet.WriteUTF8Text(1, 0, '=B3+1');
// B2
+ worksheet.WriteFormula(1, 1, 'B3+1');
+ {
worksheet.WriteRPNFormula(1, 1, CreateRPNFormula(
RPNCellValue('B3',
- RPNNumber(1,
+ RPNInteger(1,
RPNFunc(fekAdd, nil)))));
-
+ }
// A3
worksheet.WriteUTF8Text(2, 0, '(not dependent)');
// B3
worksheet.WriteNumber(2, 1, 1);
workbook.WriteToFile(OutputFile, sfExcel8, true);
- writeln('Finished. Please open "'+OutputFile+'" in your spreadsheet program.');
+ writeln('Finished.');
+ writeln;
+ writeln('Please open "'+OutputFile+'" in "fpsgrid".');
+ writeLn('It should show calculation results in cells B1 and B2.');
finally
workbook.Free;
end;
diff --git a/components/fpspreadsheet/examples/other/demo_virtualmode_write.lpr b/components/fpspreadsheet/examples/other/demo_virtualmode_write.lpr
index 6b1f80a44..ca5e30d1e 100644
--- a/components/fpspreadsheet/examples/other/demo_virtualmode_write.lpr
+++ b/components/fpspreadsheet/examples/other/demo_virtualmode_write.lpr
@@ -97,9 +97,9 @@ begin
{ In case of a database, you would open the dataset before calling this: }
t := Now;
- workbook.WriteToFile('test_virtual.ods', sfOpenDocument, true);
+ //workbook.WriteToFile('test_virtual.ods', sfOpenDocument, true);
//workbook.WriteToFile('test_virtual.xlsx', sfOOXML, true);
- //workbook.WriteToFile('test_virtual.xls', sfExcel8, true);
+ workbook.WriteToFile('test_virtual.xls', sfExcel8, true);
//workbook.WriteToFile('test_virtual.xls', sfExcel5, true);
//workbook.WriteToFile('test_virtual.xls', sfExcel2, true);
t := Now - t;
diff --git a/components/fpspreadsheet/examples/other/demo_write_formula.pas b/components/fpspreadsheet/examples/other/demo_write_formula.pas
index 65b7dc3cf..38fa366bd 100644
--- a/components/fpspreadsheet/examples/other/demo_write_formula.pas
+++ b/components/fpspreadsheet/examples/other/demo_write_formula.pas
@@ -21,7 +21,7 @@ var
procedure WriteFirstWorksheet();
var
- MyFormula: TsFormula;
+ MyFormula: String;
MyRPNFormula: TsRPNFormula;
MyCell: PCell;
begin
@@ -37,40 +37,41 @@ begin
Myworksheet.WriteNumber(3, 4, 300); // E4
MyWorksheet.WriteNumber(4, 4, 250); // E5
- // =Sum(E2:e5)
- MyWorksheet.WriteUTF8Text(1, 0, '=Sum(E2:e5)'); // A2
- //
- MyFormula.FormulaStr := '=Sum(E2:e5)';
- MyFormula.DoubleValue := 0.0;
+ // =Sum(E2:E5)
+ MyWorksheet.WriteUTF8Text(1, 0, '=Sum(E2:E5)'); // A2
+ MyFormula := '=Sum(E2:E5)';
MyWorksheet.WriteFormula(1, 1, MyFormula); // B2
- //
MyWorksheet.WriteRPNFormula(1, 2, CreateRPNFormula( // C2
RPNCellRange('E2:E5',
- RPNFunc(fekSum, 1, nil))));
+ RPNFunc('SUM', 1,
+ nil))));
// Write the formula =ABS(E1)
MyWorksheet.WriteUTF8Text(2, 0, '=ABS(E1)'); // A3
- //
+ MyWorksheet.WriteFormula(2, 1, 'ABS(E1)'); // B3
MyWorksheet.WriteRPNFormula(2, 2, CreateRPNFormula( // C3
RPNCellValue('E1',
- RPNFunc(fekAbs, nil))));
+ RPNFunc('ABS',
+ nil))));
// Write the formula =4+5
MyWorksheet.WriteUTF8Text(3, 0, '=4+5'); // A4
- //
+ MyWorksheet.WriteFormula(3, 1, '=4+5'); // B4
MyWorksheet.WriteRPNFormula(3, 2, CreateRPNFormula( //C4
RPNNumber(4.0,
RPNNumber(5.0,
- RPNFunc(fekAdd, nil)))));
-
+ RPNFunc(fekAdd,
+ nil)))));
+ (*
// Write a shared formula "=E1+100" to the cell range F1:F5
// Please note that shared formulas are not written by sfOOXML and sfOpenDocument formats.
MyCell := MyWorksheet.WriteRPNFormula(0, 5, CreateRPNFormula(
RPNCellOffset(0, -1, [rfRelRow, rfRelCol],
RPNNumber(100,
- RPNFunc(fekAdd, nil)))));
+ RPNFunc(fekAdd,
+ nil)))));
MyWorksheet.UseSharedFormula('F1:F5', MyCell);
-
+ *)
end;
procedure WriteSecondWorksheet();
@@ -98,15 +99,17 @@ begin
// Create the spreadsheet
MyWorkbook := TsWorkbook.Create;
+ try
+ WriteFirstWorksheet();
+ WriteSecondWorksheet();
- WriteFirstWorksheet();
+ // Save the spreadsheet to a file
+ MyWorkbook.WriteToFile(MyDir + TestFile, sfExcel8, True);
- WriteSecondWorksheet();
+ finally
+ MyWorkbook.Free;
+ end;
- // Save the spreadsheet to a file
- MyWorkbook.WriteToFile(MyDir + TestFile, sfExcel8, True);
-// MyWorkbook.WriteToFile(MyDir + 'test_formula.odt', sfOpenDocument, False);
- MyWorkbook.Free;
writeln('Finished. Please open "'+Testfile+'" in your spreadsheet program.');
end.
diff --git a/components/fpspreadsheet/examples/spready/mainform.pas b/components/fpspreadsheet/examples/spready/mainform.pas
index f874f04a0..b349be2f3 100644
--- a/components/fpspreadsheet/examples/spready/mainform.pas
+++ b/components/fpspreadsheet/examples/spready/mainform.pas
@@ -727,10 +727,15 @@ end;
procedure TForm1.EdFormulaEditingDone(Sender: TObject);
var
r, c: Cardinal;
+ s: String;
begin
r := WorksheetGrid.GetWorksheetRow(WorksheetGrid.Row);
c := WorksheetGrid.GetWorksheetCol(WorksheetGrid.Col);
- WorksheetGrid.Worksheet.WriteCellValueAsString(r, c, EdFormula.Text);
+ s := EdFormula.Text;
+ if (s <> '') and (s[1] = '=') then
+ WorksheetGrid.Worksheet.WriteFormula(r, c, Copy(s, 2, Length(s)))
+ else
+ WorksheetGrid.Worksheet.WriteCellValueAsString(r, c, EdFormula.Text);
end;
procedure TForm1.EdFrozenColsChange(Sender: TObject);
@@ -895,8 +900,10 @@ begin
cell := WorksheetGrid.Worksheet.FindCell(r, c);
if cell <> nil then begin
s := WorksheetGrid.Worksheet.ReadFormulaAsString(cell);
- if s <> '' then
- EdFormula.Text := s
+ if s <> '' then begin
+ if s[1] <> '=' then s := '=' + s;
+ EdFormula.Text := s;
+ end
else
case cell^.ContentType of
cctNumber:
@@ -993,12 +1000,9 @@ begin
then Strings.Add('ErrorValue=')
else Strings.Add(Format('ErrorValue=%s', [
GetEnumName(TypeInfo(TsErrorValue), ord(ACell^.ErrorValue)) ]));
- if (ACell=nil) or (Length(ACell^.RPNFormulaValue) = 0)
- then Strings.Add('RPNFormulaValue=')
- else Strings.Add(Format('RPNFormulaValue=(%d tokens)', [Length(ACell^.RPNFormulaValue)]));
- if (ACell=nil) or (Length(ACell^.FormulaValue.FormulaStr)=0)
- then Strings.Add('FormulaValue.FormulaStr=')
- else Strings.Add(Format('FormulaValue.FormulaStr="%s"', [ACell^.FormulaValue.FormulaStr]));
+ if (ACell=nil) or (Length(ACell^.FormulaValue)=0)
+ then Strings.Add('FormulaValue=')
+ else Strings.Add(Format('FormulaValue="%s"', [ACell^.FormulaValue]));
if (ACell=nil) or (ACell^.SharedFormulaBase=nil)
then Strings.Add('SharedFormulaBase=')
else Strings.Add(Format('SharedFormulaBase=%s', [GetCellString(
diff --git a/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpi b/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpi
index 7bf90ebc9..f129d363c 100644
--- a/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpi
+++ b/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpi
@@ -31,7 +31,7 @@
-
+
@@ -44,23 +44,13 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpr b/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpr
index 5a85fbad4..b05cd02d0 100644
--- a/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpr
+++ b/components/fpspreadsheet/examples/wikitabledemo/wikitableread.lpr
@@ -11,7 +11,7 @@ program wikitableread;
uses
Classes, SysUtils, fpspreadsheet, wikitable,
- fpsutils;
+ laz_fpspreadsheet, fpsutils;
var
MyWorkbook: TsWorkbook;
diff --git a/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpi b/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpi
index a02f90e05..815f73380 100644
--- a/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpi
+++ b/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpi
@@ -32,7 +32,7 @@
-
+
@@ -45,22 +45,14 @@
-
-
-
-
-
+
+
-
-
-
-
-
diff --git a/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpr b/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpr
index e4eb7aac0..567a6d7fc 100644
--- a/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpr
+++ b/components/fpspreadsheet/examples/wikitabledemo/wikitablewrite.lpr
@@ -10,7 +10,8 @@ program wikitablewrite;
{$mode delphi}{$H+}
uses
- Classes, SysUtils, fpspreadsheet, wikitable;
+ Classes, SysUtils, fpspreadsheet, wikitable,
+ laz_fpspreadsheet;
const
Str_First = 'First';
diff --git a/components/fpspreadsheet/fpsexprparser.pas b/components/fpspreadsheet/fpsexprparser.pas
index 825d39670..8d31582c4 100644
--- a/components/fpspreadsheet/fpsexprparser.pas
+++ b/components/fpspreadsheet/fpsexprparser.pas
@@ -12,6 +12,8 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+--------------------------------------------------------------------------------
+
Modified for integration into fpspreadsheet by Werner Pamler:
- Original file name: fpexprpars.pp
- Rename identifiers to avoid naming conflicts with the original
@@ -24,11 +26,22 @@
- TsPercentExprNode and token "%" to handle Excel's percent operation
- TsParenthesisExprNode to handle the parenthesis token in RPN formulas
- TsConcatExprNode and token "&" to handle string concatenation
+ - TsUPlusExprNode for unary plus symbol
- remove and modifiy built-in function such that the parser is compatible
- with Excel syntax (and OpenOffice - which is the same).
+ with Excel syntax (and Open/LibreOffice - which is the same).
- use double quotes for strings (instead of single quotes)
+ - add boolean constants "TRUE" and "FALSE".
+ - add property RPNFormula to interface the parser to RPN formulas of xls files.
+ - accept funtions with zero parameters
******************************************************************************}
+
+// To do:
+// Remove exceptions, use error message strings instead
+// Cell reference not working (--> formula CELL!)
+// Missing arguments
+// Keep spaces in formula
+
{$mode objfpc}
{$h+}
unit fpsExprParser;
@@ -50,15 +63,15 @@ type
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
fekParen,
*)
- TTokenType = (
+ TsTokenType = (
+ ttCell, ttCellRange, ttNumber, ttString, ttIdentifier,
ttPlus, ttMinus, ttMul, ttDiv, ttConcat, ttPercent, ttPower, ttLeft, ttRight,
ttLessThan, ttLargerThan, ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual,
- ttNumber, ttString, ttIdentifier, ttCell, ttCellRange,
- ttComma, ttAnd, ttOr, ttXor, ttTrue, ttFalse, ttNot, ttIf,
- ttEOF
+ ttComma, ttTrue, ttFalse, ttEOF
);
- TExprFloat = Double;
+ TsExprFloat = Double;
+ TsExprFloatArray = array of TsExprFloat;
const
ttDelimiters = [
@@ -74,55 +87,26 @@ type
TsExpressionParser = class;
TsBuiltInExpressionManager = class;
- { TsExpressionScanner }
- TsExpressionScanner = class(TObject)
- FSource : String;
- LSource,
- FPos: Integer;
- FChar: PChar;
- FToken: String;
- FTokenType: TTokenType;
- private
- function GetCurrentChar: Char;
- procedure ScanError(Msg: String);
- protected
- procedure SetSource(const AValue: String); virtual;
- function DoIdentifier: TTokenType;
- function DoNumber: TTokenType;
- function DoDelimiter: TTokenType;
- function DoString: TTokenType;
- function NextPos: Char; // inline;
- procedure SkipWhiteSpace; // inline;
- function IsWordDelim(C: Char): Boolean; // inline;
- function IsDelim(C: Char): Boolean; // inline;
- function IsDigit(C: Char): Boolean; // inline;
- function IsAlpha(C: Char): Boolean; // inline;
- public
- constructor Create;
- function GetToken: TTokenType;
- property Token: String read FToken;
- property TokenType: TTokenType read FTokenType;
- property Source: String read FSource write SetSource;
- property Pos: Integer read FPos;
- property CurrentChar: Char read GetCurrentChar;
- end;
-
- EExprScanner = class(Exception);
-
- TsResultType = (rtBoolean, rtInteger, rtFloat, rtDateTime, rtString);
+ TsResultType = (rtEmpty, rtBoolean, rtInteger, rtFloat, rtDateTime, rtString,
+ rtCell, rtCellRange, rtError, rtAny);
TsResultTypes = set of TsResultType;
TsExpressionResult = record
- ResString : String;
+ Worksheet : TsWorksheet;
+ ResString : String;
case ResultType : TsResultType of
- rtBoolean : (ResBoolean : Boolean);
- rtInteger : (ResInteger : Int64);
- rtFloat : (ResFloat : TExprFloat);
- rtDateTime : (ResDateTime : TDatetime);
- rtString : ();
+ rtEmpty : ();
+ rtError : (ResError : TsErrorValue);
+ rtBoolean : (ResBoolean : Boolean);
+ rtInteger : (ResInteger : Int64);
+ rtFloat : (ResFloat : TsExprFloat);
+ rtDateTime : (ResDateTime : TDatetime);
+ rtCell : (ResRow, ResCol : Cardinal);
+ rtCellRange : (ResCellRange : TsCellRange);
+ rtString : ();
end;
PsExpressionResult = ^TsExpressionResult;
- TExprParameterArray = array of TsExpressionResult;
+ TsExprParameterArray = array of TsExpressionResult;
{ TsExprNode }
TsExprNode = class(TObject)
@@ -139,7 +123,7 @@ type
function NodeValue: TsExpressionResult;
end;
- TExprArgumentArray = array of TsExprNode;
+ TsExprArgumentArray = array of TsExprNode;
{ TsBinaryOperationExprNode }
TsBinaryOperationExprNode = class(TsExprNode)
@@ -147,7 +131,7 @@ type
FLeft: TsExprNode;
FRight: TsExprNode;
protected
- procedure CheckSameNodeTypes;
+ procedure CheckSameNodeTypes; virtual;
public
constructor Create(ALeft, ARight: TsExprNode);
destructor Destroy; override;
@@ -164,35 +148,10 @@ type
function NodeType: TsResultType; override;
end;
- { TsBinaryAndExprNode }
- TsBinaryAndExprNode = class(TsBooleanOperationExprNode)
- protected
- procedure GetNodeValue(var Result: TsExpressionResult); override;
- public
- function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
- function AsString: string; override;
- end;
-
- { TsBinaryOrExprNode }
- TsBinaryOrExprNode = class(TsBooleanOperationExprNode)
- protected
- procedure GetNodeValue(var Result: TsExpressionResult); override;
- public
- function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
- function AsString: string; override;
- end;
-
- { TsBinaryXorExprNode }
- TsBinaryXorExprNode = class(TsBooleanOperationExprNode)
- protected
- procedure GetNodeValue(var Result: TsExpressionResult); override;
- public
- function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
- function AsString: string; override;
- end;
-
{ TsBooleanResultExprNode }
TsBooleanResultExprNode = class(TsBinaryOperationExprNode)
+ protected
+ procedure CheckSameNodeTypes; override;
public
procedure Check; override;
function NodeType: TsResultType; override;
@@ -219,11 +178,14 @@ type
{ TsOrderingExprNode }
TsOrderingExprNode = class(TsBooleanResultExprNode)
+ protected
+ procedure CheckSameNodeTypes; override;
+ public
procedure Check; override;
end;
- { TsLessThanExprNode }
- TsLessThanExprNode = class(TsOrderingExprNode)
+ { TsLessExprNode }
+ TsLessExprNode = class(TsOrderingExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
@@ -231,8 +193,8 @@ type
function AsString: string; override;
end;
- { TsGreaterThanExprNode }
- TsGreaterThanExprNode = class(TsOrderingExprNode)
+ { TsGreaterExprNode }
+ TsGreaterExprNode = class(TsOrderingExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
@@ -241,7 +203,7 @@ type
end;
{ TsLessEqualExprNode }
- TsLessEqualExprNode = class(TsGreaterThanExprNode)
+ TsLessEqualExprNode = class(TsGreaterExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
@@ -250,7 +212,7 @@ type
end;
{ TsGreaterEqualExprNode }
- TsGreaterEqualExprNode = class(TsLessThanExprNode)
+ TsGreaterEqualExprNode = class(TsLessExprNode)
protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
@@ -258,36 +220,23 @@ type
function AsString: string; override;
end;
- { TIfExprNode }
- TIfExprNode = class(TsBinaryOperationExprNode)
- private
- FCondition: TsExprNode;
- protected
- procedure GetNodeValue(var Result: TsExpressionResult); override;
- procedure Check; override;
- function NodeType: TsResultType; override;
- public
- constructor Create(ACondition, ALeft, ARight: TsExprNode);
- destructor Destroy; override;
- function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
- function AsString: string; override;
- property Condition: TsExprNode read FCondition;
- end;
-
{ TsConcatExprNode }
TsConcatExprNode = class(TsBinaryOperationExprNode)
protected
- procedure Check; override;
+ procedure CheckSameNodeTypes; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
- function NodeType: TsResultType; override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
+ procedure Check; override;
+ function NodeType: TsResultType; override;
end;
{ TsMathOperationExprNode }
TsMathOperationExprNode = class(TsBinaryOperationExprNode)
protected
+ procedure CheckSameNodeTypes; override;
+ public
procedure Check; override;
function NodeType: TsResultType; override;
end;
@@ -304,7 +253,6 @@ type
{ TsSubtractExprNode }
TsSubtractExprNode = class(TsMathOperationExprNode)
protected
- procedure Check; override;
procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
@@ -314,21 +262,29 @@ type
{ TsMultiplyExprNode }
TsMultiplyExprNode = class(TsMathOperationExprNode)
protected
- procedure check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsDivideExprNode }
TsDivideExprNode = class(TsMathOperationExprNode)
protected
- Procedure Check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string ; override;
+ function NodeType: TsResultType; override;
+ end;
+
+ { TsPowerExprNode }
+ TsPowerExprNode = class(TsMathOperationExprNode)
+ protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
+ public
+ function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
+ function AsString: string ; override;
function NodeType: TsResultType; override;
end;
@@ -336,10 +292,11 @@ type
TsUnaryOperationExprNode = class(TsExprNode)
private
FOperand: TsExprNode;
+ protected
+ procedure Check; override;
public
constructor Create(AOperand: TsExprNode);
destructor Destroy; override;
- procedure Check; override;
property Operand: TsExprNode read FOperand;
end;
@@ -353,85 +310,103 @@ type
TsNotExprNode = class(TsUnaryOperationExprNode)
protected
procedure Check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsConvertToIntExprNode }
TsConvertToIntExprNode = class(TsConvertExprNode)
- protected
+ public
procedure Check; override;
end;
{ TsIntToFloatExprNode }
TsIntToFloatExprNode = class(TsConvertToIntExprNode)
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsIntToDateTimeExprNode }
TsIntToDateTimeExprNode = class(TsConvertToIntExprNode)
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsFloatToDateTimeExprNode }
TsFloatToDateTimeExprNode = class(TsConvertExprNode)
protected
procedure Check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
- { TsNegateExprNode }
- TsNegateExprNode = class(TsUnaryOperationExprNode)
+ { TsUPlusExprNode }
+ TsUPlusExprNode = class(TsUnaryOperationExprNode)
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
procedure Check; override;
function NodeType: TsResultType; override;
+ end;
+
+ { TsUMinusExprNode }
+ TsUMinusExprNode = class(TsUnaryOperationExprNode)
+ protected
procedure GetNodeValue(var Result: TsExpressionResult); override;
+ public
+ function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
+ function AsString: String; override;
+ procedure Check; override;
+ function NodeType: TsResultType; override;
end;
{ TsPercentExprNode }
TsPercentExprNode = class(TsUnaryOperationExprNode)
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
procedure Check; override;
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsParenthesisExprNode }
TsParenthesisExprNode = class(TsUnaryOperationExprNode)
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
end;
{ TsConstExprNode }
- TsConstExprNode = Class(TsExprNode)
+ TsConstExprNode = class(TsExprNode)
private
FValue: TsExpressionResult;
+ protected
+ procedure Check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
constructor CreateString(AValue: String);
constructor CreateInteger(AValue: Int64);
constructor CreateDateTime(AValue: TDateTime);
- constructor CreateFloat(AValue: TExprFloat);
+ constructor CreateFloat(AValue: TsExprFloat);
constructor CreateBoolean(AValue: Boolean);
+ constructor CreateError(AValue: TsErrorValue);
function AsString: string; override;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
- procedure Check; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
function NodeType : TsResultType; override;
// For inspection
property ConstValue: TsExpressionResult read FValue;
@@ -440,10 +415,10 @@ type
TsExprIdentifierType = (itVariable, itFunctionCallBack, itFunctionHandler);
TsExprFunctionCallBack = procedure (var Result: TsExpressionResult;
- const Args: TExprParameterArray);
+ const Args: TsExprParameterArray);
TsExprFunctionEvent = procedure (var Result: TsExpressionResult;
- const Args: TExprParameterArray) of object;
+ const Args: TsExprParameterArray) of object;
{ TsExprIdentifierDef }
TsExprIdentifierDef = class(TCollectionItem)
@@ -453,11 +428,13 @@ type
FArgumentTypes: String;
FIDType: TsExprIdentifierType;
FName: ShortString;
+ FExcelCode: Integer;
+ FVariableArgumentCount: Boolean;
FOnGetValue: TsExprFunctionEvent;
FOnGetValueCB: TsExprFunctionCallBack;
function GetAsBoolean: Boolean;
function GetAsDateTime: TDateTime;
- function GetAsFloat: TExprFloat;
+ function GetAsFloat: TsExprFloat;
function GetAsInteger: Int64;
function GetAsString: String;
function GetResultType: TsResultType;
@@ -465,7 +442,7 @@ type
procedure SetArgumentTypes(const AValue: String);
procedure SetAsBoolean(const AValue: Boolean);
procedure SetAsDateTime(const AValue: TDateTime);
- procedure SetAsFloat(const AValue: TExprFloat);
+ procedure SetAsFloat(const AValue: TsExprFloat);
procedure SetAsInteger(const AValue: Int64);
procedure SetAsString(const AValue: String);
procedure SetName(const AValue: ShortString);
@@ -477,11 +454,13 @@ type
public
function ArgumentCount: Integer;
procedure Assign(Source: TPersistent); override;
- property AsFloat: TExprFloat Read GetAsFloat Write SetAsFloat;
+ property AsFloat: TsExprFloat Read GetAsFloat Write SetAsFloat;
property AsInteger: Int64 Read GetAsInteger Write SetAsInteger;
property AsString: String Read GetAsString Write SetAsString;
property AsBoolean: Boolean Read GetAsBoolean Write SetAsBoolean;
property AsDateTime: TDateTime Read GetAsDateTime Write SetAsDateTime;
+ function HasFixedArgumentCount: Boolean;
+ function IsOptionalArgument(AIndex: Integer): Boolean;
property OnGetFunctionValueCallBack: TsExprFunctionCallBack read FOnGetValueCB write FOnGetValueCB;
published
property IdentifierType: TsExprIdentifierType read FIDType write FIDType;
@@ -489,11 +468,13 @@ type
property Value: String read GetValue write SetValue;
property ParameterTypes: String read FArgumentTypes write SetArgumentTypes;
property ResultType: TsResultType read GetResultType write SetResultType;
+ property ExcelCode: Integer read FExcelCode write FExcelCode;
+ property VariableArgumentCount: Boolean read FVariableArgumentCount write FVariableArgumentCount;
property OnGetFunctionValue: TsExprFunctionEvent read FOnGetValue write FOnGetValue;
end;
- TsBuiltInExprCategory = (bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion,
- bcData, bcVaria, bcUser);
+ TsBuiltInExprCategory = (bcMath, bcStatistics, bcStrings, bcLogical, bcDateTime,
+ bcLookup, bcInfo, bcUser);
TsBuiltInExprCategories = set of TsBuiltInExprCategory;
@@ -517,9 +498,11 @@ type
procedure Update(Item: TCollectionItem); override;
property Parser: TsExpressionParser read FParser;
public
- function IndexOfIdentifier(const AName: ShortString): Integer;
function FindIdentifier(const AName: ShortString): TsExprIdentifierDef;
+ function IdentifierByExcelCode(const AExcelCode: Integer): TsExprIdentifierDef;
function IdentifierByName(const AName: ShortString): TsExprIdentifierDef;
+ function IndexOfIdentifier(const AName: ShortString): Integer; overload;
+ function IndexOfIdentifier(const AExcelCode: Integer): Integer; overload;
function AddVariable(const AName: ShortString; AResultType: TsResultType;
AValue: String): TsExprIdentifierDef;
function AddBooleanVariable(const AName: ShortString;
@@ -527,15 +510,17 @@ type
function AddIntegerVariable(const AName: ShortString;
AValue: Integer): TsExprIdentifierDef;
function AddFloatVariable(const AName: ShortString;
- AValue: TExprFloat): TsExprIdentifierDef;
+ AValue: TsExprFloat): TsExprIdentifierDef;
function AddStringVariable(const AName: ShortString;
AValue: String): TsExprIdentifierDef;
function AddDateTimeVariable(const AName: ShortString;
AValue: TDateTime): TsExprIdentifierDef;
function AddFunction(const AName: ShortString; const AResultType: Char;
- const AParamTypes: String; ACallBack: TsExprFunctionCallBack): TsExprIdentifierDef;
+ const AParamTypes: String; const AExcelCode: Integer;
+ ACallBack: TsExprFunctionCallBack): TsExprIdentifierDef;
function AddFunction(const AName: ShortString; const AResultType: Char;
- const AParamTypes: String; ACallBack: TsExprFunctionEvent): TsExprIdentifierDef;
+ const AParamTypes: String; const AExcelCode: Integer;
+ ACallBack: TsExprFunctionEvent): TsExprIdentifierDef;
property Identifiers[AIndex: Integer]: TsExprIdentifierDef read GetI write SetI; default;
end;
@@ -545,57 +530,60 @@ type
FID: TsExprIdentifierDef;
PResult: PsExpressionResult;
FResultType: TsResultType;
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
constructor CreateIdentifier(AID: TsExprIdentifierDef);
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
property Identifier: TsExprIdentifierDef read FID;
end;
- { TFPExprVariable }
- TFPExprVariable = class(TsIdentifierExprNode)
+ { TsVariableExprNode }
+ TsVariableExprNode = class(TsIdentifierExprNode)
procedure Check; override;
function AsString: string; override;
Function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
end;
- { TFPExprFunction }
- TFPExprFunction = class(TsIdentifierExprNode)
+ { TsFunctionExprNode }
+ TsFunctionExprNode = class(TsIdentifierExprNode)
private
- FArgumentNodes: TExprArgumentArray;
- FargumentParams: TExprParameterArray;
+ FArgumentNodes: TsExprArgumentArray;
+ FargumentParams: TsExprParameterArray;
protected
procedure CalcParams;
- procedure Check; override;
public
constructor CreateFunction(AID: TsExprIdentifierDef;
- const Args: TExprArgumentArray); virtual;
+ const Args: TsExprArgumentArray); virtual;
destructor Destroy; override;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
- property ArgumentNodes: TExprArgumentArray read FArgumentNodes;
- property ArgumentParams: TExprParameterArray read FArgumentParams;
+ procedure Check; override;
+ property ArgumentNodes: TsExprArgumentArray read FArgumentNodes;
+ property ArgumentParams: TsExprParameterArray read FArgumentParams;
end;
- { TFPFunctionCallBack }
- TFPFunctionCallBack = class(TFPExprFunction)
+ { TsFunctionCallBackExprNode }
+ TsFunctionCallBackExprNode = class(TsFunctionExprNode)
private
FCallBack: TsExprFunctionCallBack;
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
constructor CreateFunction(AID: TsExprIdentifierDef;
- const Args: TExprArgumentArray); override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
+ const Args: TsExprArgumentArray); override;
property CallBack: TsExprFunctionCallBack read FCallBack;
end;
- { TFPFunctionEventHandler }
- TFPFunctionEventHandler = class(TFPExprFunction)
+ { TFPFunctionEventHandlerExprNode }
+ TFPFunctionEventHandlerExprNode = class(TsFunctionExprNode)
private
FCallBack: TsExprFunctionEvent;
+ protected
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
constructor CreateFunction(AID: TsExprIdentifierDef;
- const Args: TExprArgumentArray); override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
+ const Args: TsExprArgumentArray); override;
property CallBack: TsExprFunctionEvent read FCallBack;
end;
@@ -603,18 +591,78 @@ type
TsCellExprNode = class(TsExprNode)
private
FWorksheet: TsWorksheet;
- FCell: PCell;
+ FRow, FCol: Cardinal;
FFlags: TsRelFlags;
+ FCell: PCell;
+ FIsRef: Boolean;
+ protected
+ procedure Check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
public
constructor Create(AWorksheet: TsWorksheet; ACellString: String); overload;
- constructor Create(AWorksheet: TsWorksheet; ACell: PCell; AFlags: TsRelFlags); overload;
+ constructor Create(AWorksheet: TsWorksheet; ARow, ACol: Cardinal;
+ AFlags: TsRelFlags); overload;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
- procedure Check; override;
function NodeType: TsResultType; override;
- procedure GetNodeValue(var Result: TsExpressionResult); override;
+ property Worksheet: TsWorksheet read FWorksheet;
end;
+ { TsCellRangeExprNode }
+ TsCellRangeExprNode = class(TsExprNode)
+ private
+ FWorksheet: TsWorksheet;
+ FRow1, FRow2: Cardinal;
+ FCol1, FCol2: Cardinal;
+ FFlags: TsRelFlags;
+ protected
+ procedure Check; override;
+ procedure GetNodeValue(var Result: TsExpressionResult); override;
+ public
+ constructor Create(AWorksheet: TsWorksheet; ACellRangeString: String); overload;
+ constructor Create(AWorksheet: TsWorksheet; ARow1,ACol1, ARow2,ACol2: Cardinal;
+ AFlags: TsRelFlags); overload;
+ function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
+ function AsString: String; override;
+ function NodeType: TsResultType; override;
+ property Worksheet: TsWorksheet read FWorksheet;
+ end;
+
+ { TsExpressionScanner }
+ TsExpressionScanner = class(TObject)
+ FSource : String;
+ LSource,
+ FPos: Integer;
+ FChar: PChar;
+ FToken: String;
+ FTokenType: TsTokenType;
+ private
+ function GetCurrentChar: Char;
+ procedure ScanError(Msg: String);
+ protected
+ procedure SetSource(const AValue: String); virtual;
+ function DoIdentifier: TsTokenType;
+ function DoNumber: TsTokenType;
+ function DoDelimiter: TsTokenType;
+ function DoString: TsTokenType;
+ function NextPos: Char; // inline;
+ procedure SkipWhiteSpace; // inline;
+ function IsWordDelim(C: Char): Boolean; // inline;
+ function IsDelim(C: Char): Boolean; // inline;
+ function IsDigit(C: Char): Boolean; // inline;
+ function IsAlpha(C: Char): Boolean; // inline;
+ public
+ constructor Create;
+ function GetToken: TsTokenType;
+ property Token: String read FToken;
+ property TokenType: TsTokenType read FTokenType;
+ property Source: String read FSource write SetSource;
+ property Pos: Integer read FPos;
+ property CurrentChar: Char read GetCurrentChar;
+ end;
+
+ EExprScanner = class(Exception);
+
{ TsExpressionParser }
TsExpressionParser = class
private
@@ -627,22 +675,27 @@ type
FDirty: Boolean;
FWorksheet: TsWorksheet;
procedure CheckEOF;
+ procedure CheckNodes(var ALeft, ARight: TsExprNode);
function ConvertNode(Todo: TsExprNode; ToType: TsResultType): TsExprNode;
function GetAsBoolean: Boolean;
function GetAsDateTime: TDateTime;
- function GetAsFloat: TExprFloat;
+ function GetAsFloat: TsExprFloat;
function GetAsInteger: Int64;
function GetAsString: String;
+ function GetRPNFormula: TsRPNFormula;
function MatchNodes(Todo, Match: TsExprNode): TsExprNode;
- procedure CheckNodes(var ALeft, ARight: TsExprNode);
procedure SetBuiltIns(const AValue: TsBuiltInExprCategories);
procedure SetIdentifiers(const AValue: TsExprIdentifierDefs);
+ procedure SetRPNFormula(const AFormula: TsRPNFormula);
+
protected
+ class function BuiltinExpressionManager: TsBuiltInExpressionManager;
procedure ParserError(Msg: String);
procedure SetExpression(const AValue: String); virtual;
procedure CheckResultType(const Res: TsExpressionResult;
AType: TsResultType); inline;
- class function BuiltinExpressionManager: TsBuiltInExpressionManager;
+ function CurrentToken: String;
+ function GetToken: TsTokenType;
function Level1: TsExprNode;
function Level2: TsExprNode;
function Level3: TsExprNode;
@@ -650,35 +703,40 @@ type
function Level5: TsExprNode;
function Level6: TsExprNode;
function Primitive: TsExprNode;
- function GetToken: TTokenType;
- function TokenType: TTokenType;
- function CurrentToken: String;
+ function TokenType: TsTokenType;
procedure CreateHashList;
property Scanner: TsExpressionScanner read FScanner;
property ExprNode: TsExprNode read FExprNode;
property Dirty: Boolean read FDirty;
+
public
- constructor Create(AWorksheet: TsWorksheet);
+ constructor Create(AWorksheet: TsWorksheet); virtual;
destructor Destroy; override;
function IdentifierByName(AName: ShortString): TsExprIdentifierDef; virtual;
procedure Clear;
- function BuildRPNFormula: TsRPNFormula;
- function BuildFormula: String;
- procedure EvaluateExpression(var Result: TsExpressionResult);
+ function BuildStringFormula: String;
function Evaluate: TsExpressionResult;
+ procedure EvaluateExpression(var Result: TsExpressionResult);
function ResultType: TsResultType;
- property AsFloat: TExprFloat read GetAsFloat;
+ property AsFloat: TsExprFloat read GetAsFloat;
property AsInteger: Int64 read GetAsInteger;
property AsString: String read GetAsString;
property AsBoolean: Boolean read GetAsBoolean;
property AsDateTime: TDateTime read GetAsDateTime;
- property Worksheet: TsWorksheet read FWorksheet;
// The expression to parse
property Expression: String read FExpression write SetExpression;
+ property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula;
property Identifiers: TsExprIdentifierDefs read FIdentifiers write SetIdentifiers;
property BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns;
+ property Worksheet: TsWorksheet read FWorksheet;
end;
+ TsSpreadsheetParser = class(TsExpressionParser)
+ public
+ constructor Create(AWorksheet: TsWorksheet); override;
+ end;
+
+
{ TsBuiltInExpressionManager }
TsBuiltInExpressionManager = class(TComponent)
private
@@ -692,6 +750,7 @@ type
destructor Destroy; override;
function IndexOfIdentifier(const AName: ShortString): Integer;
function FindIdentifier(const AName: ShortString): TsBuiltInExprIdentifierDef;
+ function IdentifierByExcelCode(const AExcelCode: Integer): TsBuiltInExprIdentifierDef;
function IdentifierByName(const AName: ShortString): TsBuiltInExprIdentifierDef;
function AddVariable(const ACategory: TsBuiltInExprCategory; const AName: ShortString;
AResultType: TsResultType; AValue: String): TsBuiltInExprIdentifierDef;
@@ -700,39 +759,57 @@ type
function AddIntegerVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: Integer): TsBuiltInExprIdentifierDef;
function AddFloatVariable(const ACategory: TsBuiltInExprCategory;
- const AName: ShortString; AValue: TExprFloat): TsBuiltInExprIdentifierDef;
+ const AName: ShortString; AValue: TsExprFloat): TsBuiltInExprIdentifierDef;
function AddStringVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: String): TsBuiltInExprIdentifierDef;
function AddDateTimeVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AValue: TDateTime): TsBuiltInExprIdentifierDef;
function AddFunction(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; const AResultType: Char; const AParamTypes: String;
- ACallBack: TsExprFunctionCallBack): TsBuiltInExprIdentifierDef;
+ const AExcelCode: Integer; ACallBack: TsExprFunctionCallBack): TsBuiltInExprIdentifierDef;
function AddFunction(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; const AResultType: Char; const AParamTypes: String;
- ACallBack: TsExprFunctionEvent): TsBuiltInExprIdentifierDef;
+ const AExcelCode: Integer; ACallBack: TsExprFunctionEvent): TsBuiltInExprIdentifierDef;
property IdentifierCount: Integer read GetCount;
property Identifiers[AIndex: Integer]: TsBuiltInExprIdentifierDef read GetI;
end;
EExprParser = class(Exception);
-function TokenName(AToken: TTokenType): String;
+function TokenName(AToken: TsTokenType): String;
function ResultTypeName(AResult: TsResultType): String;
function CharToResultType(C: Char): TsResultType;
function BuiltinIdentifiers: TsBuiltInExpressionManager;
procedure RegisterStdBuiltins(AManager: TsBuiltInExpressionManager);
-function ArgToFloat(Arg: TsExpressionResult): TExprFloat;
+function ArgToBoolean(Arg: TsExpressionResult): Boolean;
+function ArgToCell(Arg: TsExpressionResult): PCell;
+function ArgToDateTime(Arg: TsExpressionResult): TDateTime;
+function ArgToInt(Arg: TsExpressionResult): Integer;
+function ArgToFloat(Arg: TsExpressionResult): TsExprFloat;
+function ArgToString(Arg: TsExpressionResult): String;
+procedure ArgsToFloatArray(const Args: TsExprParameterArray; out AData: TsExprFloatArray);
+function BooleanResult(AValue: Boolean): TsExpressionResult;
+function DateTimeResult(AValue: TDateTime): TsExpressionResult;
+function EmptyResult: TsExpressionResult;
+function ErrorResult(const AValue: TsErrorValue): TsExpressionResult;
+function FloatResult(const AValue: TsExprFloat): TsExpressionResult;
+function IntegerResult(const AValue: Integer): TsExpressionResult;
+function StringResult(const AValue: String): TsExpressionResult;
+
+procedure RegisterFunction(const AName: ShortString; const AResultType: Char;
+ const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionCallBack);
const
- AllBuiltIns = [bcStrings, bcDateTime, bcMath, bcBoolean, bcConversion,
- bcData, bcVaria, bcUser];
+ AllBuiltIns = [bcMath, bcStatistics, bcStrings, bcLogical, bcDateTime, bcLookup,
+ bcInfo, bcUser];
+var
+ ExprFormatSettings: TFormatSettings;
implementation
uses
- typinfo, fpsutils;
+ typinfo, math, lazutf8, dateutils, xlsconst, fpsutils;
const
cNull = #0;
@@ -740,9 +817,9 @@ const
Digits = ['0'..'9', '.'];
WhiteSpace = [' ', #13, #10, #9];
- Operators = ['+', '-', '<', '>', '=', '/', '*', '&', '%'];
+ Operators = ['+', '-', '<', '>', '=', '/', '*', '&', '%', '^'];
Delimiters = Operators + [',', '(', ')'];
- Symbols = ['^'] + Delimiters;
+ Symbols = Delimiters;
WordDelimiters = WhiteSpace + Symbols;
resourcestring
@@ -766,9 +843,9 @@ resourcestring
SErrNoLeftOperand = 'No left operand for binary operation %s';
SErrNoRightOperand = 'No left operand for binary operation %s';
SErrNoNegation = 'Cannot negate expression of type %s: %s';
+ SErrNoUPlus = 'Cannot perform unary plus operation on type %s: %s';
SErrNoNOTOperation = 'Cannot perform NOT operation on expression of type %s: %s';
SErrNoPercentOperation = 'Cannot perform percent operation on expression of type %s: %s';
- SErrNoXOROperationRPN = 'Cannot create RPN item for "xor" expression';
SErrTypesDoNotMatch = 'Type mismatch: %s<>%s for expressions "%s" and "%s".';
SErrTypesIncompatible = 'Incompatible types: %s<>%s for expressions "%s" and "%s".';
SErrNoNodeToCheck = 'Internal error: No node to check !';
@@ -781,12 +858,7 @@ resourcestring
SErrInvalidResultType = 'Invalid result type: %s';
SErrNotVariable = 'Identifier %s is not a variable';
SErrInactive = 'Operation not allowed while an expression is active';
- SErrIFNeedsBoolean = 'First argument to IF must be of type boolean: %s';
- SErrCaseLabelNotAConst = 'Case label %d "%s" is not a constant expression';
- SErrCaseLabelType = 'Case label %d "%s" needs type %s, but has type %s';
- SErrCaseValueType = 'Case value %d "%s" needs type %s, but has type %s';
- SErrNoCellOperand = 'Cell operand is NIL.';
- SErrCellError = 'Cell %s contains an error.';
+ SErrCircularReference = 'Circular reference found when calculating worksheet formulas';
{ ---------------------------------------------------------------------
Auxiliary functions
@@ -802,9 +874,9 @@ begin
raise EExprParser.CreateFmt(Fmt, Args);
end;
-function TokenName (AToken: TTokenType): String;
+function TokenName(AToken: TsTokenType): String;
begin
- Result := GetEnumName(TypeInfo(TTokenType), ord(AToken));
+ Result := GetEnumName(TypeInfo(TsTokenType), ord(AToken));
end;
function ResultTypeName(AResult: TsResultType): String;
@@ -820,6 +892,9 @@ begin
'B' : Result := rtBoolean;
'I' : Result := rtInteger;
'F' : Result := rtFloat;
+ 'R' : Result := rtCellRange;
+ 'C' : Result := rtCell;
+ '?' : Result := rtAny;
else
RaiseParserError(SErrInvalidResultCharacter, [C]);
end;
@@ -850,53 +925,7 @@ begin
Source := '';
end;
-function TsExpressionScanner.IsAlpha(C: Char): Boolean;
-begin
- Result := C in ['A'..'Z', 'a'..'z'];
-end;
-
-procedure TsExpressionScanner.SetSource(const AValue: String);
-begin
- FSource := AValue;
- LSource := Length(FSource);
- FTokenType := ttEOF;
- if LSource = 0 then
- FPos := 0
- else
- FPos := 1;
- FChar := PChar(FSource);
- FToken := '';
-end;
-
-function TsExpressionScanner.NextPos: Char;
-begin
- Inc(FPos);
- Inc(FChar);
- Result := FChar^;
-end;
-
-function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
-begin
- Result := C in WordDelimiters;
-end;
-
-function TsExpressionScanner.IsDelim(C: Char): Boolean;
-begin
- Result := C in Delimiters;
-end;
-
-function TsExpressionScanner.IsDigit(C: Char): Boolean;
-begin
- Result := C in Digits;
-end;
-
-procedure TsExpressionScanner.SkipWhiteSpace;
-begin
- while (FChar^ in WhiteSpace) and (FPos <= LSource) do
- NextPos;
-end;
-
-function TsExpressionScanner.DoDelimiter: TTokenType;
+function TsExpressionScanner.DoDelimiter: TsTokenType;
var
B : Boolean;
C, D : Char;
@@ -938,12 +967,59 @@ begin
end;
end;
-procedure TsExpressionScanner.ScanError(Msg: String);
+function TsExpressionScanner.DoIdentifier: TsTokenType;
+var
+ C: Char;
+ S: String;
+ row, row2: Cardinal;
+ col, col2: Cardinal;
+ flags: TsRelFlags;
begin
- raise EExprScanner.Create(Msg)
+ C := CurrentChar;
+ while (not IsWordDelim(C)) and (C <> cNull) do
+ begin
+ FToken := FToken + C;
+ C := NextPos;
+ end;
+ S := LowerCase(Token);
+ if ParseCellString(S, row, col, flags) and (C <> '(') then
+ Result := ttCell
+ else if ParseCellRangeString(S, row, col, row2, col2, flags) and (C <> '(') then
+ Result := ttCellRange
+ else if (S = 'true') and (C <> '(') then
+ Result := ttTrue
+ else if (S = 'false') and (C <> '(') then
+ Result := ttFalse
+ else
+ Result := ttIdentifier;
end;
-function TsExpressionScanner.DoString: TTokenType;
+function TsExpressionScanner.DoNumber: TsTokenType;
+var
+ C: Char;
+ X: TsExprFloat;
+ prevC: Char;
+begin
+ C := CurrentChar;
+ prevC := #0;
+ while (not IsWordDelim(C) or (prevC = 'E')) and (C <> cNull) do
+ begin
+ if not ( IsDigit(C)
+ or ((FToken <> '') and (Upcase(C) = 'E'))
+ or ((FToken <> '') and (C in ['+', '-']) and (prevC = 'E'))
+ )
+ then
+ ScanError(Format(SErrInvalidNumberChar, [C]));
+ FToken := FToken+C;
+ prevC := Upcase(C);
+ C := NextPos;
+ end;
+ if not TryStrToFloat(FToken, X, ExprFormatSettings) then
+ ScanError(Format(SErrInvalidNumber, [FToken]));
+ Result := ttNumber;
+end;
+
+function TsExpressionScanner.DoString: TsTokenType;
function TerminatingChar(C: Char): boolean;
begin
@@ -979,71 +1055,7 @@ begin
Result := #0;
end;
-function TsExpressionScanner.DoNumber: TTokenType;
-var
- C: Char;
- X: TExprFloat;
- I: Integer;
- prevC: Char;
-begin
- C := CurrentChar;
- prevC := #0;
- while (not IsWordDelim(C) or (prevC = 'E')) and (C <> cNull) do
- begin
- if not ( IsDigit(C)
- or ((FToken <> '') and (Upcase(C) = 'E'))
- or ((FToken <> '') and (C in ['+', '-']) and (prevC = 'E'))
- )
- then
- ScanError(Format(SErrInvalidNumberChar, [C]));
- FToken := FToken+C;
- prevC := Upcase(C);
- C := NextPos;
- end;
- val(FToken, X, I);
- if (I <> 0) then
- ScanError(Format(SErrInvalidNumber, [FToken]));
- Result := ttNumber;
-end;
-
-function TsExpressionScanner.DoIdentifier: TTokenType;
-var
- C: Char;
- S: String;
- row, row2: Cardinal;
- col, col2: Cardinal;
- flags: TsRelFlags;
-begin
- C := CurrentChar;
- while (not IsWordDelim(C)) and (C <> cNull) do
- begin
- FToken := FToken + C;
- C := NextPos;
- end;
- S := LowerCase(Token);
- if (S = 'or') then
- Result := ttOr
- else if (S = 'xor') then
- Result := ttXOr
- else if (S = 'and') then
- Result := ttAnd
- else if (S = 'true') then
- Result := ttTrue
- else if (S = 'false') then
- Result := ttFalse
- else if (S = 'not') then
- Result := ttNot
- else if (S = 'if') then
- Result := ttIF
- else if ParseCellString(S, row, col, flags) then
- Result := ttCell
- else if ParseCellRangeString(S, row, col, row2, col2, flags) then
- Result := ttCellRange
- else
- Result := ttIdentifier;
-end;
-
-function TsExpressionScanner.GetToken: TTokenType;
+function TsExpressionScanner.GetToken: TsTokenType;
var
C: Char;
begin
@@ -1058,13 +1070,64 @@ begin
Result := DoString
else if IsDigit(C) then
Result := DoNumber
- else if IsAlpha(C) then
+ else if IsAlpha(C) or (C = '$') then
Result := DoIdentifier
else
ScanError(Format(SErrUnknownCharacter, [FPos, C]));
FTokenType := Result;
end;
+function TsExpressionScanner.IsAlpha(C: Char): Boolean;
+begin
+ Result := C in ['A'..'Z', 'a'..'z'];
+end;
+
+function TsExpressionScanner.IsDelim(C: Char): Boolean;
+begin
+ Result := C in Delimiters;
+end;
+
+function TsExpressionScanner.IsDigit(C: Char): Boolean;
+begin
+ Result := C in Digits;
+end;
+
+function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
+begin
+ Result := C in WordDelimiters;
+end;
+
+function TsExpressionScanner.NextPos: Char;
+begin
+ Inc(FPos);
+ Inc(FChar);
+ Result := FChar^;
+end;
+
+procedure TsExpressionScanner.ScanError(Msg: String);
+begin
+ raise EExprScanner.Create(Msg)
+end;
+
+procedure TsExpressionScanner.SetSource(const AValue: String);
+begin
+ FSource := AValue;
+ LSource := Length(FSource);
+ FTokenType := ttEOF;
+ if LSource = 0 then
+ FPos := 0
+ else
+ FPos := 1;
+ FChar := PChar(FSource);
+ FToken := '';
+end;
+
+procedure TsExpressionScanner.SkipWhiteSpace;
+begin
+ while (FChar^ in WhiteSpace) and (FPos <= LSource) do
+ NextPos;
+end;
+
{------------------------------------------------------------------------------}
{ TsExpressionParser }
@@ -1089,69 +1152,17 @@ begin
inherited Destroy;
end;
-function TsExpressionParser.BuildRPNFormula: TsRPNFormula;
+function TsExpressionParser.BuildStringFormula: String;
begin
- Result := CreateRPNFormula(FExprNode.AsRPNItem(nil), true);
+ if FExprNode = nil then
+ Result := ''
+ else
+ Result := FExprNode.AsString;
end;
-function TsExpressionParser.BuildFormula: String;
+class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager;
begin
- Result := FExprNode.AsString;
-end;
-
-procedure TsExpressionParser.Clear;
-begin
- FExpression := '';
- FHashList.Clear;
- FExprNode.Free;
-end;
-
-procedure TsExpressionParser.CreateHashList;
-var
- ID: TsExprIdentifierDef;
- BID: TsBuiltInExprIdentifierDef;
- i: Integer;
- M: TsBuiltInExpressionManager;
-begin
- FHashList.Clear;
- // Builtins
- M := BuiltinExpressionManager;
- If (FBuiltins <> []) and Assigned(M) then
- for i:=0 to M.IdentifierCount-1 do
- begin
- BID := M.Identifiers[i];
- If BID.Category in FBuiltins then
- FHashList.Add(LowerCase(BID.Name), BID);
- end;
- // User
- for i:=0 to FIdentifiers.Count-1 do
- begin
- ID := FIdentifiers[i];
- FHashList.Add(LowerCase(ID.Name), ID);
- end;
- FDirty := False;
-end;
-
-function TsExpressionParser.CurrentToken: String;
-begin
- Result := FScanner.Token;
-end;
-
-function TsExpressionParser.TokenType: TTokenType;
-begin
- Result := FScanner.TokenType;
-end;
-
-function TsExpressionParser.IdentifierByName(AName: ShortString): TsExprIdentifierDef;
-begin
- If FDirty then
- CreateHashList;
- Result := TsExprIdentifierDef(FHashList.Find(LowerCase(AName)));
-end;
-
-function TsExpressionParser.GetToken: TTokenType;
-begin
- Result := FScanner.GetToken;
+ Result := BuiltinIdentifiers;
end;
procedure TsExpressionParser.CheckEOF;
@@ -1160,23 +1171,25 @@ begin
ParserError(SErrUnexpectedEndOfExpression);
end;
-procedure TsExpressionParser.SetIdentifiers(const AValue: TsExprIdentifierDefs);
+{ If the result types differ, they are converted to a common type if possible. }
+procedure TsExpressionParser.CheckNodes(var ALeft, ARight: TsExprNode);
begin
- FIdentifiers.Assign(AValue)
+ ALeft := MatchNodes(ALeft, ARight);
+ ARight := MatchNodes(ARight, ALeft);
end;
-procedure TsExpressionParser.EvaluateExpression(var Result: TsExpressionResult);
+procedure TsExpressionParser.CheckResultType(const Res: TsExpressionResult;
+ AType: TsResultType); inline;
begin
- if (FExpression = '') then
- ParserError(SErrInExpressionEmpty);
- if not Assigned(FExprNode) then
- ParserError(SErrInExpression);
- FExprNode.GetNodeValue(Result);
+ if (Res.ResultType <> AType) then
+ RaiseParserError(SErrInvalidResultType, [ResultTypeName(Res.ResultType)]);
end;
-procedure TsExpressionParser.ParserError(Msg: String);
+procedure TsExpressionParser.Clear;
begin
- raise EExprParser.Create(Msg);
+ FExpression := '';
+ FHashList.Clear;
+ FreeAndNil(FExprNode);
end;
function TsExpressionParser.ConvertNode(ToDo: TsExprNode;
@@ -1196,6 +1209,51 @@ begin
end;
end;
+procedure TsExpressionParser.CreateHashList;
+var
+ ID: TsExprIdentifierDef;
+ BID: TsBuiltInExprIdentifierDef;
+ i: Integer;
+ M: TsBuiltInExpressionManager;
+begin
+ FHashList.Clear;
+ // Builtins
+ M := BuiltinExpressionManager;
+ If (FBuiltins <> []) and Assigned(M) then
+ for i:=0 to M.IdentifierCount-1 do
+ begin
+ BID := M.Identifiers[i];
+ If BID.Category in FBuiltins then
+ FHashList.Add(UpperCase(BID.Name), BID);
+ end;
+ // User
+ for i:=0 to FIdentifiers.Count-1 do
+ begin
+ ID := FIdentifiers[i];
+ FHashList.Add(UpperCase(ID.Name), ID);
+ end;
+ FDirty := False;
+end;
+
+function TsExpressionParser.CurrentToken: String;
+begin
+ Result := FScanner.Token;
+end;
+
+function TsExpressionParser.Evaluate: TsExpressionResult;
+begin
+ EvaluateExpression(Result);
+end;
+
+procedure TsExpressionParser.EvaluateExpression(var Result: TsExpressionResult);
+begin
+ if (FExpression = '') then
+ ParserError(SErrInExpressionEmpty);
+ if not Assigned(FExprNode) then
+ ParserError(SErrInExpression);
+ FExprNode.GetNodeValue(Result);
+end;
+
function TsExpressionParser.GetAsBoolean: Boolean;
var
Res: TsExpressionResult;
@@ -1214,7 +1272,7 @@ begin
Result := Res.ResDatetime;
end;
-function TsExpressionParser.GetAsFloat: TExprFloat;
+function TsExpressionParser.GetAsFloat: TsExprFloat;
var
Res: TsExpressionResult;
begin
@@ -1241,56 +1299,35 @@ begin
Result := Res.ResString;
end;
-{
- Checks types of todo and match. If ToDO can be converted to it matches
- the type of match, then a node is inserted.
- For binary operations, this function is called for both operands.
-}
-function TsExpressionParser.MatchNodes(ToDo, Match: TsExprNode): TsExprNode;
-Var
- TT, MT : TsResultType;
+function TsExpressionParser.GetRPNFormula: TsRPNFormula;
begin
- Result := ToDo;
- TT := ToDo.NodeType;
- MT := Match.NodeType;
- If TT <> MT then
- begin
- if TT = rtInteger then
- begin
- if (MT in [rtFloat, rtDateTime]) then
- Result := ConvertNode(ToDo, MT);
- end
- else if (TT = rtFloat) then
- begin
- if (MT = rtDateTime) then
- Result := ConvertNode(ToDo, rtDateTime);
- end;
- end;
+ Result := CreateRPNFormula(FExprNode.AsRPNItem(nil), true);
end;
-{
- If the result types differ, they are converted to a common type if possible.
-}
-procedure TsExpressionParser.CheckNodes(var ALeft, ARight: TsExprNode);
+function TsExpressionParser.GetToken: TsTokenType;
begin
- ALeft := MatchNodes(ALeft, ARight);
- ARight := MatchNodes(ARight, ALeft);
+ Result := FScanner.GetToken;
end;
-procedure TsExpressionParser.SetBuiltIns(const AValue: TsBuiltInExprCategories);
+function TsExpressionParser.IdentifierByName(AName: ShortString): TsExprIdentifierDef;
+var
+ s: String;
begin
- if FBuiltIns = AValue then
- exit;
- FBuiltIns := AValue;
- FDirty := true;
+ if FDirty then
+ CreateHashList;
+ s := FHashList.NameOfIndex(0);
+ s := FHashList.NameOfIndex(25);
+ s := FHashList.NameOfIndex(36);
+ Result := TsExprIdentifierDef(FHashList.Find(UpperCase(AName)));
end;
function TsExpressionParser.Level1: TsExprNode;
var
- tt: TTokenType;
+ tt: TsTokenType;
Right: TsExprNode;
begin
{$ifdef debugexpr}Writeln('Level 1 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{
if TokenType = ttNot then
begin
GetToken;
@@ -1299,9 +1336,26 @@ begin
Result := TsNotExprNode.Create(Right);
end
else
- Result := Level2;
-
+ }
+ Result := Level2;
+ {
try
+ if TokenType = ttPower then
+ begin
+ tt := Tokentype;
+ GetToken;
+ CheckEOF;
+ Right := Level2;
+ Result := TsPowerExprNode.Create(Result, Right);
+ end;
+ except
+ Result.Free;
+ raise;
+ end;
+ }
+{
+ try
+
while (TokenType in [ttAnd, ttOr, ttXor]) do
begin
tt := TokenType;
@@ -1320,15 +1374,16 @@ begin
Result.Free;
raise;
end;
+}
end;
function TsExpressionParser.Level2: TsExprNode;
var
right: TsExprNode;
- tt: TTokenType;
+ tt: TsTokenType;
C: TsBinaryOperationExprNodeClass;
begin
-{$ifdef debugexpr} Writeln('Level 2 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{$ifdef debugexpr} Writeln('Level 2 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
Result := Level3;
try
if (TokenType in ttComparisons) then
@@ -1339,9 +1394,9 @@ begin
Right := Level3;
CheckNodes(Result, right);
case tt of
- ttLessthan : C := TsLessThanExprNode;
+ ttLessthan : C := TsLessExprNode;
ttLessthanEqual : C := TsLessEqualExprNode;
- ttLargerThan : C := TsGreaterThanExprNode;
+ ttLargerThan : C := TsGreaterExprNode;
ttLargerThanEqual : C := TsGreaterEqualExprNode;
ttEqual : C := TsEqualExprNode;
ttNotEqual : C := TsNotEqualExprNode;
@@ -1358,10 +1413,10 @@ end;
function TsExpressionParser.Level3: TsExprNode;
var
- tt: TTokenType;
+ tt: TsTokenType;
right: TsExprNode;
begin
-{$ifdef debugexpr} Writeln('Level 3 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{$ifdef debugexpr} Writeln('Level 3 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
Result := Level4;
try
while TokenType in [ttPlus, ttMinus, ttConcat] do begin
@@ -1384,10 +1439,10 @@ end;
function TsExpressionParser.Level4: TsExprNode;
var
- tt: TTokenType;
+ tt: TsTokenType;
right: TsExprNode;
begin
-{$ifdef debugexpr} Writeln('Level 4 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{$ifdef debugexpr} Writeln('Level 4 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
Result := Level5;
try
while (TokenType in [ttMul, ttDiv]) do
@@ -1409,23 +1464,31 @@ end;
function TsExpressionParser.Level5: TsExprNode;
var
- B: Boolean;
+ isPlus, isMinus: Boolean;
+ tt: TsTokenType;
begin
-{$ifdef debugexpr} Writeln('Level 5 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
- B := false;
+{$ifdef debugexpr} Writeln('Level 5 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+ isPlus := false;
+ isMinus := false;
if (TokenType in [ttPlus, ttMinus]) then
begin
- B := (TokenType = ttMinus);
+ isPlus := (TokenType = ttPlus);
+ isMinus := (TokenType = ttMinus);
GetToken;
end;
Result := Level6;
- if B then
- Result := TsNegateExprNode.Create(Result);
+ if isPlus then
+ Result := TsUPlusExprNode.Create(Result);
+ if isMinus then
+ Result := TsUMinusExprNode.Create(Result);
end;
function TsExpressionParser.Level6: TsExprNode;
+var
+ tt: TsTokenType;
+ Right: TsExprNode;
begin
-{$ifdef debugexpr} Writeln('Level 6 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{$ifdef debugexpr} Writeln('Level 6 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
if (TokenType = ttLeft) then
begin
GetToken;
@@ -1441,21 +1504,67 @@ begin
end
else
Result := Primitive;
+
+ if TokenType = ttPower then
+ begin
+ try
+ CheckEOF;
+ tt := Tokentype;
+ GetToken;
+ Right := Primitive;
+ CheckNodes(Result, right);
+ Result := TsPowerExprNode.Create(Result, Right);
+ //GetToken;
+ except
+ Result.Free;
+ raise;
+ end;
+ end;
+end;
+
+{ Checks types of todo and match. If ToDO can be converted to it matches
+ the type of match, then a node is inserted.
+ For binary operations, this function is called for both operands. }
+function TsExpressionParser.MatchNodes(ToDo, Match: TsExprNode): TsExprNode;
+var
+ TT, MT : TsResultType;
+begin
+ Result := ToDo;
+ TT := ToDo.NodeType;
+ MT := Match.NodeType;
+ if TT <> MT then
+ begin
+ if TT = rtInteger then
+ begin
+ if (MT in [rtFloat, rtDateTime]) then
+ Result := ConvertNode(ToDo, MT);
+ end
+ else if (TT = rtFloat) then
+ begin
+ if (MT = rtDateTime) then
+ Result := ConvertNode(ToDo, rtDateTime);
+ end;
+ end;
+end;
+
+procedure TsExpressionParser.ParserError(Msg: String);
+begin
+ raise EExprParser.Create(Msg);
end;
function TsExpressionParser.Primitive: TsExprNode;
var
I: Int64;
- C: Integer;
- X: TExprFloat;
- ACount: Integer;
- isIF: Boolean;
+ X: TsExprFloat;
+ lCount: Integer;
ID: TsExprIdentifierDef;
- Args: TExprArgumentArray;
+ Args: TsExprArgumentArray;
AI: Integer;
cell: PCell;
+ optional: Boolean;
+ token: String;
begin
-{$ifdef debugexpr} Writeln('Primitive : ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{$ifdef debugexpr} Writeln('Primitive : ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
SetLength(Args, 0);
if (TokenType = ttNumber) then
begin
@@ -1463,65 +1572,79 @@ begin
Result := TsConstExprNode.CreateInteger(I)
else
begin
- val(CurrentToken, X, C);
- if (I = 0) then
+ if TryStrToFloat(CurrentToken, X, ExprFormatSettings) then
Result := TsConstExprNode.CreateFloat(X)
else
ParserError(Format(SErrInvalidFloat, [CurrentToken]));
end;
end
+ else if (TokenType = ttTrue) then
+ Result := TsConstExprNode.CreateBoolean(true)
+ else if (TokenType = ttFalse) then
+ Result := TsConstExprNode.CreateBoolean(false)
else if (TokenType = ttString) then
Result := TsConstExprNode.CreateString(CurrentToken)
- else if (TokenType in [ttTrue, ttFalse]) then
- Result := TsConstExprNode.CreateBoolean(TokenType = ttTrue)
else if (TokenType = ttCell) then
Result := TsCellExprNode.Create(FWorksheet, CurrentToken)
else if (TokenType = ttCellRange) then
- raise Exception.Create('Cell range missing')
- else if not (TokenType in [ttIdentifier, ttIf]) then
+ Result := TsCellRangeExprNode.Create(FWorksheet, CurrentToken)
+ else if not (TokenType in [ttIdentifier{, ttIf}]) then
ParserError(Format(SerrUnknownTokenAtPos, [Scanner.Pos, CurrentToken]))
else
begin
- isIF := (TokenType = ttIf);
- if not isIF then
+ token := Uppercase(CurrentToken);
+ ID := self.IdentifierByName(token);
+ if (ID = nil) then
+ ParserError(Format(SErrUnknownIdentifier, [token]));
+ if (ID.IdentifierType in [itFunctionCallBack, itFunctionHandler]) then
begin
- ID := self.IdentifierByName(CurrentToken);
- If (ID = nil) then
- ParserError(Format(SErrUnknownIdentifier,[CurrentToken]))
- end;
- // Determine number of arguments
- if isIF then
- ACount := 3
- else if (ID.IdentifierType in [itFunctionCallBack, itFunctionHandler]) then
- ACount := ID.ArgumentCount
+ lCount := ID.ArgumentCount;
+ if lCount = 0 then // we have to handle the () here, it will be skipped below.
+ begin
+ GetToken;
+ if (TokenType <> ttLeft) then
+ ParserError(Format(SErrLeftBracketExpected, [Scanner.Pos, CurrentToken]));
+ GetToken;
+ if (TokenType <> ttRight) then
+ ParserError(Format(SErrBracketExpected, [Scanner.Pos, CurrentToken]));
+ SetLength(Args, 0);
+ end;
+ end
else
- ACount := 0;
+ lCount := 0;
+
// Parse arguments.
// Negative is for variable number of arguments, where Abs(value) is the minimum number of arguments
- if (ACount <> 0) then
+ if (lCount <> 0) then
begin
GetToken;
if (TokenType <> ttLeft) then
- ParserError(Format(SErrLeftBracketExpected, [Scanner.Pos, CurrentToken]));
- SetLength(Args, abs(ACount));
+ ParserError(Format(SErrLeftBracketExpected, [Scanner.Pos, CurrentToken]));
+ SetLength(Args, abs(lCount));
AI := 0;
try
repeat
GetToken;
// Check if we must enlarge the argument array
- if (ACount < 0) and (AI = Length(Args)) then
+ if (lCount < 0) and (AI = Length(Args)) then
begin
SetLength(Args, AI+1);
Args[AI] := nil;
end;
Args[AI] := Level1;
inc(AI);
- if (TokenType <> ttComma) then
- if (AI < abs(ACount)) then
- ParserError(Format(SErrCommaExpected, [Scanner.Pos, CurrentToken]))
- until (AI = ACount) or ((ACount < 0) and (TokenType = ttRight));
+ optional := ID.IsOptionalArgument(AI+1);
+ if not optional then
+ begin
+ if (TokenType <> ttComma) then
+ if (AI < abs(lCount)) then
+ ParserError(Format(SErrCommaExpected, [Scanner.Pos, CurrentToken]))
+ end;
+ until (AI = lCount) or (((lCount < 0) or optional) and (TokenType = ttRight));
if TokenType <> ttRight then
ParserError(Format(SErrBracketExpected, [Scanner.Pos, CurrentToken]));
+ if AI < abs(lCount) then
+ SetLength(Args, AI);
except
on E: Exception do
begin
@@ -1535,14 +1658,11 @@ begin
end;
end;
end;
- if isIF then
- Result := TIfExprNode.Create(Args[0], Args[1], Args[2])
- else
- case ID.IdentifierType of
- itVariable : Result := TFPExprVariable.CreateIdentifier(ID);
- itFunctionCallBack : Result := TFPFunctionCallback.CreateFunction(ID, Args);
- itFunctionHandler : Result := TFPFunctionEventHandler.CreateFunction(ID, Args);
- end;
+ case ID.IdentifierType of
+ itVariable : Result := TsVariableExprNode.CreateIdentifier(ID);
+ itFunctionCallBack : Result := TsFunctionCallBackExprNode.CreateFunction(ID, Args);
+ itFunctionHandler : Result := TFPFunctionEventHandlerExprNode.CreateFunction(ID, Args);
+ end;
end;
GetToken;
if TokenType = ttPercent then begin
@@ -1551,43 +1671,6 @@ begin
end;
end;
-procedure TsExpressionParser.SetExpression(const AValue: String);
-begin
- if FExpression = AValue then
- exit;
- FExpression := AValue;
- FScanner.Source := AValue;
- if Assigned(FExprNode) then
- FreeAndNil(FExprNode);
- if (FExpression <> '') then
- begin
- GetToken;
- FExprNode := Level1;
- if (TokenType <> ttEOF) then
- ParserError(Format(SErrUnterminatedExpression, [Scanner.Pos, CurrentToken]));
- FExprNode.Check;
- end
- else
- FExprNode := nil;
-end;
-
-procedure TsExpressionParser.CheckResultType(const Res: TsExpressionResult;
- AType: TsResultType); inline;
-begin
- if (Res.ResultType <> AType) then
- RaiseParserError(SErrInvalidResultType, [ResultTypeName(Res.ResultType)]);
-end;
-
-class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager;
-begin
- Result := BuiltinIdentifiers;
-end;
-
-function TsExpressionParser.Evaluate: TsExpressionResult;
-begin
- EvaluateExpression(Result);
-end;
-
function TsExpressionParser.ResultType: TsResultType;
begin
if not Assigned(FExprNode) then
@@ -1595,33 +1678,299 @@ begin
Result := FExprNode.NodeType;;
end;
-
-{ ---------------------------------------------------------------------
- TsExprIdentifierDefs
- ---------------------------------------------------------------------}
-
-function TsExprIdentifierDefs.GetI(AIndex : Integer): TsExprIdentifierDef;
+procedure TsExpressionParser.SetBuiltIns(const AValue: TsBuiltInExprCategories);
begin
- Result := TsExprIdentifierDef(Items[AIndex]);
+ if FBuiltIns = AValue then
+ exit;
+ FBuiltIns := AValue;
+ FDirty := true;
end;
-procedure TsExprIdentifierDefs.SetI(AIndex: Integer;
- const AValue: TsExprIdentifierDef);
+procedure TsExpressionParser.SetExpression(const AValue: String);
begin
- Items[AIndex] := AValue;
+ if FExpression = AValue then
+ exit;
+ FExpression := AValue;
+ if (AValue <> '') and (AValue[1] = '=') then
+ FScanner.Source := Copy(AValue, 2, Length(AValue))
+ else
+ FScanner.Source := AValue;
+ FreeAndNil(FExprNode);
+ if (FExpression <> '') then
+ begin
+ GetToken;
+ FExprNode := Level1;
+ if (TokenType <> ttEOF) then
+ ParserError(Format(SErrUnterminatedExpression, [Scanner.Pos, CurrentToken]));
+ FExprNode.Check;
+ end;
end;
-procedure TsExprIdentifierDefs.Update(Item: TCollectionItem);
+procedure TsExpressionParser.SetIdentifiers(const AValue: TsExprIdentifierDefs);
begin
- if Assigned(FParser) then
- FParser.FDirty := true;
+ FIdentifiers.Assign(AValue)
end;
-function TsExprIdentifierDefs.IndexOfIdentifier(const AName: ShortString): Integer;
+procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula);
+
+ procedure CreateNodeFromRPN(var ANode: TsExprNode; var AIndex: Integer);
+ var
+ node: TsExprNode;
+ left: TsExprNode;
+ right: TsExprNode;
+ operand: TsExprNode;
+ fek: TFEKind;
+ r,c, r2,c2: Cardinal;
+ flags: TsRelFlags;
+ ID: TsExprIdentifierDef;
+ i, n: Integer;
+ args: TsExprArgumentArray;
+ begin
+ if AIndex < 0 then
+ exit;
+
+ fek := AFormula[AIndex].ElementKind;
+
+ case fek of
+ fekCell, fekCellRef:
+ begin
+ r := AFormula[AIndex].Row;
+ c := AFormula[AIndex].Col;
+ flags := AFormula[AIndex].RelFlags;
+ ANode := TsCellExprNode.Create(FWorksheet, r, c, flags);
+ dec(AIndex);
+ end;
+ fekCellRange:
+ begin
+ r := AFormula[AIndex].Row;
+ c := AFormula[AIndex].Col;
+ r2 := AFormula[AIndex].Row2;
+ c2 := AFormula[AIndex].Col2;
+ flags := AFormula[AIndex].RelFlags;
+ ANode := TsCellRangeExprNode.Create(FWorksheet, r, c, r2, c2, flags);
+ dec(AIndex);
+ end;
+ fekNum:
+ begin
+ ANode := TsConstExprNode.CreateFloat(AFormula[AIndex].DoubleValue);
+ dec(AIndex);
+ end;
+ fekInteger:
+ begin
+ ANode := TsConstExprNode.CreateInteger(AFormula[AIndex].IntValue);
+ dec(AIndex);
+ end;
+ fekString:
+ begin
+ ANode := TsConstExprNode.CreateString(AFormula[AIndex].StringValue);
+ dec(AIndex);
+ end;
+ fekBool:
+ begin
+ ANode := TsConstExprNode.CreateBoolean(AFormula[AIndex].DoubleValue <> 0.0);
+ dec(AIndex);
+ end;
+ fekErr:
+ begin
+ ANode := TsConstExprNode.CreateError(TsErrorValue(AFormula[AIndex].IntValue));
+ dec(AIndex);
+ end;
+
+ // unary operations
+ fekPercent, fekUMinus, fekUPlus, fekParen:
+ begin
+ dec(AIndex);
+ CreateNodeFromRPN(operand, AIndex);
+ case fek of
+ fekPercent : ANode := TsPercentExprNode.Create(operand);
+ fekUMinus : ANode := TsUMinusExprNode.Create(operand);
+ fekUPlus : ANode := TsUPlusExprNode.Create(operand);
+ fekParen : ANode := TsParenthesisExprNode.Create(operand);
+ end;
+ end;
+
+ // binary operations
+ fekAdd, fekSub, fekMul, fekDiv,
+ fekPower, fekConcat,
+ fekEqual, fekNotEqual,
+ fekGreater, fekGreaterEqual,
+ fekLess, fekLessEqual:
+ begin
+ dec(AIndex);
+ CreateNodeFromRPN(right, AIndex);
+ CreateNodeFromRPN(left, AIndex);
+ CheckNodes(left, right);
+ case fek of
+ fekAdd : ANode := TsAddExprNode.Create(left, right);
+ fekSub : ANode := TsSubtractExprNode.Create(left, right);
+ fekMul : ANode := TsMultiplyExprNode.Create(left, right);
+ fekDiv : ANode := TsDivideExprNode.Create(left, right);
+ fekPower : ANode := TsPowerExprNode.Create(left, right);
+ fekConcat : ANode := tsConcatExprNode.Create(left, right);
+ fekEqual : ANode := TsEqualExprNode.Create(left, right);
+ fekNotEqual : ANode := TsNotEqualExprNode.Create(left, right);
+ fekGreater : ANode := TsGreaterExprNode.Create(left, right);
+ fekGreaterEqual: ANode := TsGreaterEqualExprNode.Create(left, right);
+ fekLess : ANode := TsLessExprNode.Create(left, right);
+ fekLessEqual : ANode := tsLessEqualExprNode.Create(left, right);
+ end;
+ end;
+
+ // functions
+ fekFunc:
+ begin
+ ID := self.IdentifierByName(AFormula[AIndex].FuncName);
+ if ID = nil then
+ begin
+ ParserError(Format(SErrUnknownIdentifier,[AFormula[AIndex].FuncName]));
+ dec(AIndex);
+ end else
+ begin
+ if ID.HasFixedArgumentCount then
+ n := ID.ArgumentCount
+ else
+ n := AFormula[AIndex].ParamsNum;
+ dec(AIndex);
+ SetLength(args, n);
+ for i:=n-1 downto 0 do
+ CreateNodeFromRPN(args[i], AIndex);
+ case ID.IdentifierType of
+ itVariable : ANode := TsVariableExprNode.CreateIdentifier(ID);
+ itFunctionCallBack : ANode := TsFunctionCallBackExprNode.CreateFunction(ID, args);
+ itFunctionHandler : ANode := TFPFunctionEventHandlerExprNode.CreateFunction(ID, args);
+ end;
+ end;
+ end;
+
+ end; //case
+ end; //begin
+
+var
+ index: Integer;
+ node: TsExprNode;
begin
- Result := Count-1;
- while (Result >= 0) and (CompareText(GetI(Result).Name, AName) <> 0) do
- dec(Result);
+ FExpression := '';
+ FreeAndNil(FExprNode);
+ index := Length(AFormula)-1;
+ CreateNodeFromRPN(FExprNode, index);
+ if Assigned(FExprNode) then FExprNode.Check;
+end;
+
+function TsExpressionParser.TokenType: TsTokenType;
+begin
+ Result := FScanner.TokenType;
+end;
+
+
+{------------------------------------------------------------------------------}
+{ TsSpreadsheetParser }
+{------------------------------------------------------------------------------}
+
+constructor TsSpreadsheetParser.Create(AWorksheet: TsWorksheet);
+begin
+ inherited Create(AWorksheet);
+ BuiltIns := AllBuiltIns;
+end;
+
+
+{------------------------------------------------------------------------------}
+{ TsExprIdentifierDefs }
+{------------------------------------------------------------------------------}
+
+function TsExprIdentifierDefs.AddBooleanVariable(const AName: ShortString;
+ AValue: Boolean): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.IdentifierType := itVariable;
+ Result.Name := AName;
+ Result.ResultType := rtBoolean;
+ Result.FValue.ResBoolean := AValue;
+end;
+
+function TsExprIdentifierDefs.AddDateTimeVariable(const AName: ShortString;
+ AValue: TDateTime): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.IdentifierType := itVariable;
+ Result.Name := AName;
+ Result.ResultType := rtDateTime;
+ Result.FValue.ResDateTime := AValue;
+end;
+
+function TsExprIdentifierDefs.AddFloatVariable(const AName: ShortString;
+ AValue: TsExprFloat): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.IdentifierType := itVariable;
+ Result.Name := AName;
+ Result.ResultType := rtFloat;
+ Result.FValue.ResFloat := AValue;
+end;
+
+function TsExprIdentifierDefs.AddFunction(const AName: ShortString;
+ const AResultType: Char; const AParamTypes: String; const AExcelCode: Integer;
+ ACallBack: TsExprFunctionCallBack): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.Name := AName;
+ Result.IdentifierType := itFunctionCallBack;
+ Result.ResultType := CharToResultType(AResultType);
+ Result.ExcelCode := AExcelCode;
+ Result.FOnGetValueCB := ACallBack;
+ if (Length(AParamTypes) > 0) and (AParamTypes[Length(AParamTypes)]='+') then
+ begin
+ Result.ParameterTypes := Copy(AParamTypes, 1, Length(AParamTypes)-1);
+ Result.VariableArgumentCount := true;
+ end else
+ Result.ParameterTypes := AParamTypes;
+end;
+
+function TsExprIdentifierDefs.AddFunction(const AName: ShortString;
+ const AResultType: Char; const AParamTypes: String; const AExcelCode: Integer;
+ ACallBack: TsExprFunctionEvent): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.Name := AName;
+ Result.IdentifierType := itFunctionHandler;
+ Result.ResultType := CharToResultType(AResultType);
+ Result.ExcelCode := AExcelCode;
+ Result.FOnGetValue := ACallBack;
+ if (Length(AParamTypes) > 0) and (AParamTypes[Length(AParamTypes)]='+') then
+ begin
+ Result.ParameterTypes := Copy(AParamTypes, 1, Length(AParamTypes)-1);
+ Result.VariableArgumentCount := true;
+ end else
+ Result.ParameterTypes := AParamTypes;
+end;
+
+function TsExprIdentifierDefs.AddIntegerVariable(const AName: ShortString;
+ AValue: Integer): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.IdentifierType := itVariable;
+ Result.Name := AName;
+ Result.ResultType := rtInteger;
+ Result.FValue.ResInteger := AValue;
+end;
+
+function TsExprIdentifierDefs.AddStringVariable(const AName: ShortString;
+ AValue: String): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.IdentifierType := itVariable;
+ Result.Name := AName;
+ Result.ResultType := rtString;
+ Result.FValue.ResString := AValue;
+end;
+
+function TsExprIdentifierDefs.AddVariable(const AName: ShortString;
+ AResultType: TsResultType; AValue: String): TsExprIdentifierDef;
+begin
+ Result := Add as TsExprIdentifierDef;
+ Result.IdentifierType := itVariable;
+ Result.Name := AName;
+ Result.ResultType := AResultType;
+ Result.Value := AValue;
end;
function TsExprIdentifierDefs.FindIdentifier(const AName: ShortString
@@ -1636,6 +1985,23 @@ begin
Result := GetI(I);
end;
+function TsExprIdentifierDefs.GetI(AIndex : Integer): TsExprIdentifierDef;
+begin
+ Result := TsExprIdentifierDef(Items[AIndex]);
+end;
+
+function TsExprIdentifierDefs.IdentifierByExcelCode(const AExcelCode: Integer
+ ): TsExprIdentifierDef;
+var
+ I: Integer;
+begin
+ I := IndexOfIdentifier(AExcelCode);
+ if I = -1 then
+ Result := nil
+ else
+ Result := GetI(I);
+end;
+
function TsExprIdentifierDefs.IdentifierByName(const AName: ShortString
): TsExprIdentifierDef;
begin
@@ -1644,88 +2010,39 @@ begin
RaiseParserError(SErrUnknownIdentifier, [AName]);
end;
-function TsExprIdentifierDefs.AddVariable(const AName: ShortString;
- AResultType: TsResultType; AValue: String): TsExprIdentifierDef;
+function TsExprIdentifierDefs.IndexOfIdentifier(const AName: ShortString): Integer;
begin
- Result := Add as TsExprIdentifierDef;
- Result.IdentifierType := itVariable;
- Result.Name := AName;
- Result.ResultType := AResultType;
- Result.Value := AValue;
+ Result := Count-1;
+ while (Result >= 0) and (CompareText(GetI(Result).Name, AName) <> 0) do
+ dec(Result);
end;
-function TsExprIdentifierDefs.AddBooleanVariable(const AName: ShortString;
- AValue: Boolean): TsExprIdentifierDef;
+function TsExprIdentifierDefs.IndexOfIdentifier(const AExcelCode: Integer): Integer;
+var
+ ID: TsExprIdentifierDef;
begin
- Result := Add as TsExprIdentifierDef;
- Result.IdentifierType := itVariable;
- Result.Name := AName;
- Result.ResultType := rtBoolean;
- Result.FValue.ResBoolean := AValue;
+ Result := Count-1;
+ while (Result >= 0) do begin
+ ID := GetI(Result);
+ if ID.ExcelCode = AExcelCode then exit;
+ dec(Result);
+ end;
+ {
+ while (Result >= 0) and (GetI(Result).ExcelCode = AExcelCode) do
+ dec(Result);
+ }
end;
-function TsExprIdentifierDefs.AddIntegerVariable(const AName: ShortString;
- AValue: Integer): TsExprIdentifierDef;
+procedure TsExprIdentifierDefs.SetI(AIndex: Integer;
+ const AValue: TsExprIdentifierDef);
begin
- Result := Add as TsExprIdentifierDef;
- Result.IdentifierType := itVariable;
- Result.Name := AName;
- Result.ResultType := rtInteger;
- Result.FValue.ResInteger := AValue;
+ Items[AIndex] := AValue;
end;
-function TsExprIdentifierDefs.AddFloatVariable(const AName: ShortString;
- AValue: TExprFloat): TsExprIdentifierDef;
+procedure TsExprIdentifierDefs.Update(Item: TCollectionItem);
begin
- Result := Add as TsExprIdentifierDef;
- Result.IdentifierType := itVariable;
- Result.Name := AName;
- Result.ResultType := rtFloat;
- Result.FValue.ResFloat := AValue;
-end;
-
-function TsExprIdentifierDefs.AddStringVariable(const AName: ShortString;
- AValue: String): TsExprIdentifierDef;
-begin
- Result := Add as TsExprIdentifierDef;
- Result.IdentifierType := itVariable;
- Result.Name := AName;
- Result.ResultType := rtString;
- Result.FValue.ResString := AValue;
-end;
-
-function TsExprIdentifierDefs.AddDateTimeVariable(const AName: ShortString;
- AValue: TDateTime): TsExprIdentifierDef;
-begin
- Result := Add as TsExprIdentifierDef;
- Result.IdentifierType := itVariable;
- Result.Name := AName;
- Result.ResultType := rtDateTime;
- Result.FValue.ResDateTime := AValue;
-end;
-
-function TsExprIdentifierDefs.AddFunction(const AName: ShortString;
- const AResultType: Char; const AParamTypes: String;
- ACallBack: TsExprFunctionCallBack): TsExprIdentifierDef;
-begin
- Result := Add as TsExprIdentifierDef;
- Result.Name := AName;
- Result.IdentifierType := itFunctionCallBack;
- Result.ParameterTypes := AParamTypes;
- Result.ResultType := CharToResultType(AResultType);
- Result.FOnGetValueCB := ACallBack;
-end;
-
-function TsExprIdentifierDefs.AddFunction(const AName: ShortString;
- const AResultType: Char; const AParamTypes: String;
- ACallBack: TsExprFunctionEvent): TsExprIdentifierDef;
-begin
- Result := Add as TsExprIdentifierDef;
- Result.Name := AName;
- Result.IdentifierType := itFunctionHandler;
- Result.ParameterTypes := AParamTypes;
- Result.ResultType := CharToResultType(AResultType);
- Result.FOnGetValue := ACallBack;
+ if Assigned(FParser) then
+ FParser.FDirty := true;
end;
@@ -1733,6 +2050,174 @@ end;
{ TsExprIdentifierDef }
{------------------------------------------------------------------------------}
+function TsExprIdentifierDef.ArgumentCount: Integer;
+begin
+ if FVariableArgumentCount then
+ Result := -Length(FArgumentTypes)
+ else
+ Result := Length(FArgumentTypes);
+end;
+
+procedure TsExprIdentifierDef.Assign(Source: TPersistent);
+var
+ EID: TsExprIdentifierDef;
+begin
+ if (Source is TsExprIdentifierDef) then
+ begin
+ EID := Source as TsExprIdentifierDef;
+ FStringValue := EID.FStringValue;
+ FValue := EID.FValue;
+ FArgumentTypes := EID.FArgumentTypes;
+ FVariableArgumentCount := EID.FVariableArgumentCount;
+ FExcelCode := EID.ExcelCode;
+ FIDType := EID.FIDType;
+ FName := EID.FName;
+ FOnGetValue := EID.FOnGetValue;
+ FOnGetValueCB := EID.FOnGetValueCB;
+ end
+ else
+ inherited Assign(Source);
+end;
+
+procedure TsExprIdentifierDef.CheckResultType(const AType: TsResultType);
+begin
+ if (FValue.ResultType <> AType) then
+ RaiseParserError(SErrInvalidResultType, [ResultTypeName(AType)])
+end;
+
+procedure TsExprIdentifierDef.CheckVariable;
+begin
+ if Identifiertype <> itVariable then
+ RaiseParserError(SErrNotVariable, [Name]);
+end;
+
+function TsExprIdentifierDef.GetAsBoolean: Boolean;
+begin
+ CheckResultType(rtBoolean);
+ CheckVariable;
+ Result := FValue.ResBoolean;
+end;
+
+function TsExprIdentifierDef.GetAsDateTime: TDateTime;
+begin
+ CheckResultType(rtDateTime);
+ CheckVariable;
+ Result := FValue.ResDateTime;
+end;
+
+function TsExprIdentifierDef.GetAsFloat: TsExprFloat;
+begin
+ CheckResultType(rtFloat);
+ CheckVariable;
+ Result := FValue.ResFloat;
+end;
+
+function TsExprIdentifierDef.GetAsInteger: Int64;
+begin
+ CheckResultType(rtInteger);
+ CheckVariable;
+ Result := FValue.ResInteger;
+end;
+
+function TsExprIdentifierDef.GetAsString: String;
+begin
+ CheckResultType(rtString);
+ CheckVariable;
+ Result := FValue.ResString;
+end;
+
+function TsExprIdentifierDef.GetResultType: TsResultType;
+begin
+ Result := FValue.ResultType;
+end;
+
+function TsExprIdentifierDef.GetValue: String;
+begin
+ case FValue.ResultType of
+ rtBoolean : if FValue.ResBoolean then
+ Result := 'True'
+ else
+ Result := 'False';
+ rtInteger : Result := IntToStr(FValue.ResInteger);
+ rtFloat : Result := FloatToStr(FValue.ResFloat, ExprFormatSettings);
+ rtDateTime : Result := FormatDateTime('cccc', FValue.ResDateTime);
+ rtString : Result := FValue.ResString;
+ end;
+end;
+
+{ Returns true if the epxression has a fixed number of arguments. }
+function TsExprIdentifierDef.HasFixedArgumentCount: Boolean;
+var
+ i: Integer;
+begin
+ if FVariableArgumentCount then
+ Result := false
+ else
+ begin
+ for i:= 1 to Length(FArgumentTypes) do
+ if IsOptionalArgument(i) then
+ begin
+ Result := false;
+ exit;
+ end;
+ Result := true;
+ end;
+end;
+
+{ Checks whether an argument is optional. Index number starts at 1.
+ Optional arguments are lower-case characters in the argument list. }
+function TsExprIdentifierDef.IsOptionalArgument(AIndex: Integer): Boolean;
+begin
+ Result := (AIndex <= Length(FArgumentTypes))
+ and (UpCase(FArgumentTypes[AIndex]) <> FArgumentTypes[AIndex]);
+end;
+
+procedure TsExprIdentifierDef.SetArgumentTypes(const AValue: String);
+var
+ i: integer;
+begin
+ if FArgumentTypes = AValue then
+ exit;
+ for i:=1 to Length(AValue) do
+ CharToResultType(AValue[i]);
+ FArgumentTypes := AValue;
+end;
+
+procedure TsExprIdentifierDef.SetAsBoolean(const AValue: Boolean);
+begin
+ CheckVariable;
+ CheckResultType(rtBoolean);
+ FValue.ResBoolean := AValue;
+end;
+
+procedure TsExprIdentifierDef.SetAsDateTime(const AValue: TDateTime);
+begin
+ CheckVariable;
+ CheckResultType(rtDateTime);
+ FValue.ResDateTime := AValue;
+end;
+
+procedure TsExprIdentifierDef.SetAsFloat(const AValue: TsExprFloat);
+begin
+ CheckVariable;
+ CheckResultType(rtFloat);
+ FValue.ResFloat := AValue;
+end;
+
+procedure TsExprIdentifierDef.SetAsInteger(const AValue: Int64);
+begin
+ CheckVariable;
+ CheckResultType(rtInteger);
+ FValue.ResInteger := AValue;
+end;
+
+procedure TsExprIdentifierDef.SetAsString(const AValue: String);
+begin
+ CheckVariable;
+ CheckResultType(rtString);
+ FValue.resString := AValue;
+end;
+
procedure TsExprIdentifierDef.SetName(const AValue: ShortString);
begin
if FName = AValue then
@@ -1773,142 +2258,6 @@ begin
end
end;
-procedure TsExprIdentifierDef.CheckResultType(const AType: TsResultType);
-begin
- if FValue.ResultType <> AType then
- RaiseParserError(SErrInvalidResultType, [ResultTypeName(AType)])
-end;
-
-procedure TsExprIdentifierDef.CheckVariable;
-begin
- if Identifiertype <> itVariable then
- RaiseParserError(SErrNotVariable, [Name]);
-end;
-
-function TsExprIdentifierDef.ArgumentCount: Integer;
-begin
- Result := Length(FArgumentTypes);
-end;
-
-procedure TsExprIdentifierDef.Assign(Source: TPersistent);
-var
- EID: TsExprIdentifierDef;
-begin
- if (Source is TsExprIdentifierDef) then
- begin
- EID := Source as TsExprIdentifierDef;
- FStringValue := EID.FStringValue;
- FValue := EID.FValue;
- FArgumentTypes := EID.FArgumentTypes;
- FIDType := EID.FIDType;
- FName := EID.FName;
- FOnGetValue := EID.FOnGetValue;
- FOnGetValueCB := EID.FOnGetValueCB;
- end
- else
- inherited Assign(Source);
-end;
-
-procedure TsExprIdentifierDef.SetArgumentTypes(const AValue: String);
-var
- i: integer;
-begin
- if FArgumentTypes = AValue then
- exit;
- for i:=1 to Length(AValue) do
- CharToResultType(AValue[i]);
- FArgumentTypes := AValue;
-end;
-
-procedure TsExprIdentifierDef.SetAsBoolean(const AValue: Boolean);
-begin
- CheckVariable;
- CheckResultType(rtBoolean);
- FValue.ResBoolean := AValue;
-end;
-
-procedure TsExprIdentifierDef.SetAsDateTime(const AValue: TDateTime);
-begin
- CheckVariable;
- CheckResultType(rtDateTime);
- FValue.ResDateTime := AValue;
-end;
-
-procedure TsExprIdentifierDef.SetAsFloat(const AValue: TExprFloat);
-begin
- CheckVariable;
- CheckResultType(rtFloat);
- FValue.ResFloat := AValue;
-end;
-
-procedure TsExprIdentifierDef.SetAsInteger(const AValue: Int64);
-begin
- CheckVariable;
- CheckResultType(rtInteger);
- FValue.ResInteger := AValue;
-end;
-
-procedure TsExprIdentifierDef.SetAsString(const AValue: String);
-begin
- CheckVariable;
- CheckResultType(rtString);
- FValue.resString := AValue;
-end;
-
-function TsExprIdentifierDef.GetValue: String;
-begin
- case FValue.ResultType of
- rtBoolean : if FValue.ResBoolean then
- Result := 'True'
- else
- Result := 'False';
- rtInteger : Result := IntToStr(FValue.ResInteger);
- rtFloat : Result := FloatToStr(FValue.ResFloat);
- rtDateTime : Result := FormatDateTime('cccc', FValue.ResDateTime);
- rtString : Result := FValue.ResString;
- end;
-end;
-
-function TsExprIdentifierDef.GetResultType: TsResultType;
-begin
- Result := FValue.ResultType;
-end;
-
-function TsExprIdentifierDef.GetAsFloat: TExprFloat;
-begin
- CheckResultType(rtFloat);
- CheckVariable;
- Result := FValue.ResFloat;
-end;
-
-function TsExprIdentifierDef.GetAsBoolean: Boolean;
-begin
- CheckResultType(rtBoolean);
- CheckVariable;
- Result := FValue.ResBoolean;
-end;
-
-function TsExprIdentifierDef.GetAsDateTime: TDateTime;
-begin
- CheckResultType(rtDateTime);
- CheckVariable;
- Result := FValue.ResDateTime;
-end;
-
-function TsExprIdentifierDef.GetAsInteger: Int64;
-begin
- CheckResultType(rtInteger);
- CheckVariable;
- Result := FValue.ResInteger;
-end;
-
-function TsExprIdentifierDef.GetAsString: String;
-begin
- CheckResultType(rtString);
- CheckVariable;
- Result := FValue.ResString;
-end;
-
{------------------------------------------------------------------------------}
{ TsBuiltInExpressionManager }
@@ -1926,33 +2275,6 @@ begin
inherited Destroy;
end;
-function TsBuiltInExpressionManager.GetCount: Integer;
-begin
- Result := FDefs.Count;
-end;
-
-function TsBuiltInExpressionManager.GetI(AIndex: Integer): TsBuiltInExprIdentifierDef;
-begin
- Result := TsBuiltInExprIdentifierDef(FDefs[Aindex])
-end;
-
-function TsBuiltInExpressionManager.IndexOfIdentifier(const AName: ShortString): Integer;
-begin
- Result := FDefs.IndexOfIdentifier(AName);
-end;
-
-function TsBuiltInExpressionManager.FindIdentifier(const AName: ShortString
- ): TsBuiltInExprIdentifierDef;
-begin
- Result := TsBuiltInExprIdentifierDef(FDefs.FindIdentifier(AName));
-end;
-
-function TsBuiltInExpressionManager.IdentifierByName(const AName: ShortString
- ): TsBuiltInExprIdentifierDef;
-begin
- Result := TsBuiltInExprIdentifierDef(FDefs.IdentifierByName(AName));
-end;
-
function TsBuiltInExpressionManager.AddVariable(const ACategory: TsBuiltInExprCategory;
const AName: ShortString; AResultType: TsResultType; AValue: String
): TsBuiltInExprIdentifierDef;
@@ -1969,22 +2291,48 @@ begin
Result.Category := ACategory;
end;
-function TsBuiltInExpressionManager.AddIntegerVariable(
- const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: Integer
+function TsBuiltInExpressionManager.AddDateTimeVariable(
+ const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: TDateTime
): TsBuiltInExprIdentifierDef;
begin
- Result := TsBuiltInExprIdentifierDef(FDefs.AddIntegerVariable(AName, AValue));
+ Result := TsBuiltInExprIdentifierDef(FDefs.AddDateTimeVariable(AName, AValue));
Result.Category := ACategory;
end;
function TsBuiltInExpressionManager.AddFloatVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString;
- AValue: TExprFloat): TsBuiltInExprIdentifierDef;
+ AValue: TsExprFloat): TsBuiltInExprIdentifierDef;
begin
Result := TsBuiltInExprIdentifierDef(FDefs.AddFloatVariable(AName, AValue));
Result.Category := ACategory;
end;
+function TsBuiltInExpressionManager.AddFunction(const ACategory: TsBuiltInExprCategory;
+ const AName: ShortString; const AResultType: Char; const AParamTypes: String;
+ const AExcelCode: Integer; ACallBack: TsExprFunctionCallBack): TsBuiltInExprIdentifierDef;
+begin
+ Result := TsBuiltInExprIdentifierDef(FDefs.AddFunction(AName, AResultType,
+ AParamTypes, AExcelCode, ACallBack));
+ Result.Category := ACategory;
+end;
+
+function TsBuiltInExpressionManager.AddFunction(const ACategory: TsBuiltInExprCategory;
+ const AName: ShortString; const AResultType: Char; const AParamTypes: String;
+ const AExcelCode: Integer; ACallBack: TsExprFunctionEvent): TsBuiltInExprIdentifierDef;
+begin
+ Result := TsBuiltInExprIdentifierDef(FDefs.AddFunction(AName, AResultType,
+ AParamTypes, AExcelCode, ACallBack));
+ Result.Category := ACategory;
+end;
+
+function TsBuiltInExpressionManager.AddIntegerVariable(
+ const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: Integer
+ ): TsBuiltInExprIdentifierDef;
+begin
+ Result := TsBuiltInExprIdentifierDef(FDefs.AddIntegerVariable(AName, AValue));
+ Result.Category := ACategory;
+end;
+
function TsBuiltInExpressionManager.AddStringVariable(
const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: String
): TsBuiltInExprIdentifierDef;
@@ -1993,28 +2341,37 @@ begin
Result.Category := ACategory;
end;
-function TsBuiltInExpressionManager.AddDateTimeVariable(
- const ACategory: TsBuiltInExprCategory; const AName: ShortString; AValue: TDateTime
+function TsBuiltInExpressionManager.FindIdentifier(const AName: ShortString
): TsBuiltInExprIdentifierDef;
begin
- Result := TsBuiltInExprIdentifierDef(FDefs.AddDateTimeVariable(AName, AValue));
- Result.Category := ACategory;
+ Result := TsBuiltInExprIdentifierDef(FDefs.FindIdentifier(AName));
end;
-function TsBuiltInExpressionManager.AddFunction(const ACategory: TsBuiltInExprCategory;
- const AName: ShortString; const AResultType: Char; const AParamTypes: String;
- ACallBack: TsExprFunctionCallBack): TsBuiltInExprIdentifierDef;
+function TsBuiltInExpressionManager.GetCount: Integer;
begin
- Result := TsBuiltInExprIdentifierDef(FDefs.AddFunction(AName, AResultType, AParamTypes, ACallBack));
- Result.Category := ACategory;
+ Result := FDefs.Count;
end;
-function TsBuiltInExpressionManager.AddFunction(const ACategory: TsBuiltInExprCategory;
- const AName: ShortString; const AResultType: Char; const AParamTypes: String;
- ACallBack: TsExprFunctionEvent): TsBuiltInExprIdentifierDef;
+function TsBuiltInExpressionManager.GetI(AIndex: Integer): TsBuiltInExprIdentifierDef;
begin
- Result := TsBuiltInExprIdentifierDef(FDefs.AddFunction(AName, AResultType, AParamTypes, ACallBack));
- Result.Category := ACategory;
+ Result := TsBuiltInExprIdentifierDef(FDefs[Aindex])
+end;
+
+function TsBuiltInExpressionManager.IdentifierByExcelCode(const AExcelCode: Integer
+ ): TsBuiltInExprIdentifierDef;
+begin
+ Result := TsBuiltInExprIdentifierDef(FDefs.IdentifierByExcelCode(AExcelCode));
+end;
+
+function TsBuiltInExpressionManager.IdentifierByName(const AName: ShortString
+ ): TsBuiltInExprIdentifierDef;
+begin
+ Result := TsBuiltInExprIdentifierDef(FDefs.IdentifierByName(AName));
+end;
+
+function TsBuiltInExpressionManager.IndexOfIdentifier(const AName: ShortString): Integer;
+begin
+ Result := FDefs.IndexOfIdentifier(AName);
end;
@@ -2022,6 +2379,55 @@ end;
{ Various Nodes }
{------------------------------------------------------------------------------}
+{ TsExprNode }
+
+procedure TsExprNode.CheckNodeType(ANode: TsExprNode; Allowed: TsResultTypes);
+var
+ S: String;
+ A: TsResultType;
+begin
+ if (ANode = nil) then
+ RaiseParserError(SErrNoNodeToCheck);
+ if not (ANode.NodeType in Allowed) then
+ begin
+ S := '';
+ for A := Low(TsResultType) to High(TsResultType) do
+ if A in Allowed then
+ begin
+ if S <> '' then
+ S := S + ',';
+ S := S + ResultTypeName(A);
+ end;
+ RaiseParserError(SInvalidNodeType, [ResultTypeName(ANode.NodeType), S, ANode.AsString]);
+ end;
+end;
+
+function TsExprNode.NodeValue: TsExpressionResult;
+begin
+ GetNodeValue(Result);
+end;
+
+
+{ TsUnaryOperationExprNode }
+
+constructor TsUnaryOperationExprNode.Create(AOperand: TsExprNode);
+begin
+ FOperand := AOperand;
+end;
+
+destructor TsUnaryOperationExprNode.Destroy;
+begin
+ FreeAndNil(FOperand);
+ inherited Destroy;
+end;
+
+procedure TsUnaryOperationExprNode.Check;
+begin
+ if not Assigned(Operand) then
+ RaiseParserError(SErrNoOperand, [Self.ClassName]);
+end;
+
+
{ TsBinaryOperationExprNode }
constructor TsBinaryOperationExprNode.Create(ALeft, ARight: TsExprNode);
@@ -2056,23 +2462,19 @@ begin
end;
-{ TsUnaryOperationExprNode }
+{ TsBooleanOperationExprNode }
-constructor TsUnaryOperationExprNode.Create(AOperand: TsExprNode);
+procedure TsBooleanOperationExprNode.Check;
begin
- FOperand := AOperand;
+ inherited Check;
+ CheckNodeType(Left, [rtBoolean, rtCell, rtError, rtEmpty]);
+ CheckNodeType(Right, [rtBoolean, rtCell, rtError, rtEmpty]);
+ CheckSameNodeTypes;
end;
-destructor TsUnaryOperationExprNode.Destroy;
+function TsBooleanOperationExprNode.NodeType: TsResultType;
begin
- FreeAndNil(FOperand);
- inherited Destroy;
-end;
-
-procedure TsUnaryOperationExprNode.Check;
-begin
- if not Assigned(Operand) then
- RaiseParserError(SErrNoOperand, [Self.ClassName]);
+ Result := Left.NodeType;
end;
@@ -2096,7 +2498,7 @@ begin
FValue.ResDateTime := AValue;
end;
-constructor TsConstExprNode.CreateFloat(AValue: TExprFloat);
+constructor TsConstExprNode.CreateFloat(AValue: TsExprFloat);
begin
Inherited Create;
FValue.ResultType := rtFloat;
@@ -2109,6 +2511,12 @@ begin
FValue.ResBoolean := AValue;
end;
+constructor TsConstExprNode.CreateError(AValue: TsErrorValue);
+begin
+ FValue.ResultType := rtError;
+ FValue.ResError := AValue;
+end;
+
procedure TsConstExprNode.Check;
begin
// Nothing to check;
@@ -2131,7 +2539,7 @@ begin
rtInteger : Result := IntToStr(FValue.ResInteger);
rtDateTime : Result := '''' + FormatDateTime('cccc', FValue.ResDateTime) + ''''; // Probably wrong !!!
rtBoolean : if FValue.ResBoolean then Result := 'TRUE' else Result := 'FALSE';
- rtFloat : Str(FValue.ResFloat, Result);
+ rtFloat : Result := FloatToStr(FValue.ResFloat, ExprFormatSettings);
end;
end;
@@ -2139,7 +2547,7 @@ function TsConstExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
case NodeType of
rtString : Result := RPNString(FValue.ResString, ANext);
- rtInteger : Result := RPNNumber(FValue.ResInteger, ANext);
+ rtInteger : Result := RPNInteger(FValue.ResInteger, ANext);
rtDateTime : Result := RPNNumber(FValue.ResDateTime, ANext);
rtBoolean : Result := RPNBool(FValue.ResBoolean, ANext);
rtFloat : Result := RPNNumber(FValue.ResFloat, ANext);
@@ -2147,9 +2555,69 @@ begin
end;
-{ TsNegateExprNode }
+{ TsUPlusExprNode }
-function TsNegateExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+function TsUPlusExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+begin
+ Result := RPNFunc(fekUPlus,
+ Operand.AsRPNItem(
+ ANext
+ ));
+end;
+
+function TsUPlusExprNode.AsString: String;
+begin
+ Result := '+' + TrimLeft(Operand.AsString);
+end;
+
+procedure TsUPlusExprNode.Check;
+const
+ AllowedTokens = [rtInteger, rtFloat, rtCell, rtEmpty, rtError];
+begin
+ inherited;
+ if not (Operand.NodeType in AllowedTokens) then
+ RaiseParserError(SErrNoUPlus, [ResultTypeName(Operand.NodeType), Operand.AsString])
+end;
+
+procedure TsUPlusExprNode.GetNodeValue(var Result: TsExpressionResult);
+var
+ res: TsExpressionresult;
+ cell: PCell;
+begin
+ Operand.GetNodeValue(Result);
+ case Result.ResultType of
+ rtInteger, rtFloat, rtError:
+ exit;
+ rtCell:
+ begin
+ cell := ArgToCell(Result);
+ if cell = nil then
+ Result := FloatResult(0.0)
+ else
+ if cell^.ContentType = cctNumber then
+ begin
+ if frac(cell^.NumberValue) = 0.0 then
+ Result := IntegerResult(trunc(cell^.NumberValue))
+ else
+ Result := FloatResult(cell^.NumberValue);
+ end;
+ end;
+ rtEmpty:
+ Result := FloatResult(0.0);
+ else
+ Result := ErrorResult(errWrongType);
+ end;
+end;
+
+function TsUPlusExprNode.NodeType: TsResultType;
+begin
+ Result := Operand.NodeType;
+end;
+
+
+{ TsUMinusExprNode }
+
+function TsUMinusExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekUMinus,
Operand.AsRPNItem(
@@ -2157,28 +2625,52 @@ begin
));
end;
-function TsNegateExprNode.AsString: String;
+function TsUMinusExprNode.AsString: String;
begin
Result := '-' + TrimLeft(Operand.AsString);
end;
-procedure TsNegateExprNode.Check;
+procedure TsUMinusExprNode.Check;
+const
+ AllowedTokens = [rtInteger, rtFloat, rtCell, rtEmpty, rtError];
begin
inherited;
- if not (Operand.NodeType in [rtInteger, rtFloat]) then
+ if not (Operand.NodeType in AllowedTokens) then
RaiseParserError(SErrNoNegation, [ResultTypeName(Operand.NodeType), Operand.AsString])
end;
-procedure TsNegateExprNode.GetNodeValue(var Result: TsExpressionResult);
+procedure TsUMinusExprNode.GetNodeValue(var Result: TsExpressionResult);
+var
+ cell: PCell;
begin
Operand.GetNodeValue(Result);
case Result.ResultType of
- rtInteger : Result.ResInteger := -Result.ResInteger;
- rtFloat : Result.ResFloat := -Result.ResFloat;
+ rtError:
+ exit;
+ rtFloat:
+ Result := FloatResult(-Result.ResFloat);
+ rtInteger:
+ Result := IntegerResult(-Result.ResInteger);
+ rtCell:
+ begin
+ cell := ArgToCell(Result);
+ if (cell <> nil) and (cell^.ContentType = cctNumber) then
+ begin
+ if frac(cell^.NumberValue) = 0.0 then
+ Result := IntegerResult(-trunc(cell^.NumberValue))
+ else
+ Result := FloatResult(cell^.NumberValue);
+ end else
+ Result := FloatResult(0.0);
+ end;
+ rtEmpty:
+ Result := FloatResult(0.0);
+ else
+ Result := ErrorResult(errWrongType);
end;
end;
-function TsNegateExprNode.NodeType: TsResultType;
+function TsUMinusExprNode.NodeType: TsResultType;
begin
Result := Operand.NodeType;
end;
@@ -2200,9 +2692,11 @@ begin
end;
procedure TsPercentExprNode.Check;
+const
+ AllowedTokens = [rtInteger, rtFloat, rtCell, rtEmpty, rtError];
begin
inherited;
- if not (Operand.NodeType in [rtInteger, rtFloat]) then
+ if not (Operand.NodeType in AllowedTokens) then
RaiseParserError(SErrNoPercentOperation, [ResultTypeName(Operand.NodeType), Operand.AsString])
end;
@@ -2210,10 +2704,13 @@ procedure TsPercentExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
case Result.ResultType of
- rtInteger : Result.ResFloat := 0.01 * Result.ResInteger;
- rtFloat : Result.ResFloat := 0.01 * Result.ResFloat;
+ rtError:
+ exit;
+ rtFloat, rtInteger, rtCell:
+ Result := FloatResult(ArgToFloat(Result)*0.01);
+ else
+ Result := ErrorResult(errWrongType);
end;
- Result.ResultType := Nodetype;
end;
function TsPercentExprNode.NodeType: TsResultType;
@@ -2248,136 +2745,11 @@ begin
end;
-{ TsBinaryAndExprNode }
-
-function TsBinaryAndExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
-begin
- Result := RPNFunc(fekAND,
- Right.AsRPNItem(
- Left.AsRPNItem(
- ANext
- )));
-end;
-
-function TsBinaryAndExprNode.AsString: string;
-begin
- Result := Left.AsString + ' and ' + Right.AsString;
-end;
-
-procedure TsBooleanOperationExprNode.Check;
-begin
- inherited Check;
- CheckNodeType(Left, [rtInteger, rtBoolean]);
- CheckNodeType(Right, [rtInteger, rtBoolean]);
- CheckSameNodeTypes;
-end;
-
-function TsBooleanOperationExprNode.NodeType: TsResultType;
-begin
- Result := Left.NodeType;
-end;
-
-procedure TsBinaryAndExprNode.GetNodeValue(var Result: TsExpressionResult);
-var
- RRes: TsExpressionResult;
-begin
- Left.GetNodeValue(Result);
- Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtBoolean : Result.resBoolean := Result.ResBoolean and RRes.ResBoolean;
- rtInteger : Result.resInteger := Result.ResInteger and RRes.ResInteger;
- end;
-end;
-
-
-{ TsExprNode }
-
-procedure TsExprNode.CheckNodeType(Anode: TsExprNode; Allowed: TsResultTypes);
-var
- S: String;
- A: TsResultType;
-begin
- if (Anode = nil) then
- RaiseParserError(SErrNoNodeToCheck);
- if not (ANode.NodeType in Allowed) then
- begin
- S := '';
- for A := Low(TsResultType) to High(TsResultType) do
- if A in Allowed then
- begin
- if S <> '' then
- S := S + ',';
- S := S + ResultTypeName(A);
- end;
- RaiseParserError(SInvalidNodeType, [ResultTypeName(ANode.NodeType), S, ANode.AsString]);
- end;
-end;
-
-function TsExprNode.NodeValue: TsExpressionResult;
-begin
- GetNodeValue(Result);
-end;
-
-
-{ TsBinaryOrExprNode }
-
-function TsBinaryOrExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
-begin
- Result := RPNFunc(fekOR,
- Right.AsRPNItem(
- Left.AsRPNItem(
- ANext
- )));
-end;
-
-function TsBinaryOrExprNode.AsString: string;
-begin
- Result := Left.AsString + ' or ' + Right.AsString;
-end;
-
-procedure TsBinaryOrExprNode.GetNodeValue(var Result: TsExpressionResult);
-var
- RRes: TsExpressionResult;
-begin
- Left.GetNodeValue(Result);
- Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtBoolean : Result.resBoolean := Result.ResBoolean or RRes.ResBoolean;
- rtInteger : Result.resInteger := Result.ResInteger or RRes.ResInteger;
- end;
-end;
-
-
-{ TsBinaryXorExprNode }
-
-function TsBinaryXorExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
-begin
- RaiseParserError(SErrNoXOROperationRPN);
-end;
-
-function TsBinaryXorExprNode.AsString: string;
-begin
- Result := Left.AsString + ' xor ' + Right.AsString;
-end;
-
-procedure TsBinaryXorExprNode.GetNodeValue(var Result: TsExpressionResult);
-var
- RRes: TsExpressionResult;
-begin
- Left.GetNodeValue(Result);
- Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtBoolean : Result.resBoolean := Result.ResBoolean xor RRes.ResBoolean;
- rtInteger : Result.resInteger := Result.ResInteger xor RRes.ResInteger;
- end;
-end;
-
-
{ TsNotExprNode }
function TsNotExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
- Result := RPNFunc(fekNOT,
+ Result := RPNFunc('NOT',
Operand.AsRPNItem(
ANext
));
@@ -2389,8 +2761,10 @@ begin
end;
procedure TsNotExprNode.Check;
+const
+ AllowedTokens = [rtBoolean, rtEmpty, rtError];
begin
- if not (Operand.NodeType in [rtInteger, rtBoolean]) then
+ if not (Operand.NodeType in AllowedTokens) then
RaiseParserError(SErrNoNotOperation, [ResultTypeName(Operand.NodeType), Operand.AsString])
end;
@@ -2398,8 +2772,8 @@ procedure TsNotExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
case Result.ResultType of
- rtInteger : Result.ResInteger := not Result.ResInteger;
rtBoolean : Result.ResBoolean := not Result.ResBoolean;
+ rtEmpty : Result := BooleanResult(true);
end
end;
@@ -2409,66 +2783,6 @@ begin
end;
-{ TIfExprNode }
-
-constructor TIfExprNode.Create(ACondition, ALeft, ARight: TsExprNode);
-begin
- inherited Create(ALeft,ARight);
- FCondition := ACondition;
-end;
-
-destructor TIfExprNode.Destroy;
-begin
- FreeAndNil(FCondition);
- inherited Destroy;
-end;
-
-function TIfExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
-begin
- if Left = nil then
- Result := RPNFunc(fekIF,
- Right.AsRPNItem(
- ANext
- ))
- else
- Result := RPNFunc(fekIF,
- Right.AsRPNItem(
- Left.AsRPNItem(
- ANext
- )));
-end;
-
-function TIfExprNode.AsString: string;
-begin
- if Right = nil then
- Result := Format('IF(%s, %s)', [Condition.AsString, Left.AsString])
- else
- Result := Format('IF(%s, %s, %s)',[Condition.AsString, Left.AsString, Right.AsString]);
-end;
-
-procedure TIfExprNode.Check;
-begin
- inherited Check;
- if (Condition.NodeType <> rtBoolean) then
- RaiseParserError(SErrIFNeedsBoolean, [Condition.AsString]);
- CheckSameNodeTypes;
-end;
-
-procedure TIfExprNode.GetNodeValue(var Result: TsExpressionResult);
-begin
- FCondition.GetNodeValue(Result);
- if Result.ResBoolean then
- Left.GetNodeValue(Result)
- else
- Right.GetNodeValue(Result)
-end;
-
-function TIfExprNode.NodeType: TsResultType;
-begin
- Result := Left.NodeType;
-end;
-
-
{ TsBooleanResultExprNode }
procedure TsBooleanResultExprNode.Check;
@@ -2477,6 +2791,11 @@ begin
CheckSameNodeTypes;
end;
+procedure TsBooleanResultExprNode.CheckSameNodeTypes;
+begin
+ // Same node types are checked in GetNodevalue
+end;
+
function TsBooleanResultExprNode.NodeType: TsResultType;
begin
Result := rtBoolean;
@@ -2496,7 +2815,7 @@ end;
function TsEqualExprNode.AsString: string;
begin
- Result := Left.AsString + ' = ' + Right.AsString;
+ Result := Left.AsString + '=' + Right.AsString;
end;
procedure TsEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
@@ -2505,14 +2824,34 @@ var
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtBoolean : Result.resBoolean := Result.ResBoolean = RRes.ResBoolean;
- rtInteger : Result.resBoolean := Result.ResInteger = RRes.ResInteger;
- rtFloat : Result.resBoolean := Result.ResFloat = RRes.ResFLoat;
- rtDateTime : Result.resBoolean := Result.ResDateTime = RRes.ResDateTime;
- rtString : Result.resBoolean := Result.ResString = RRes.ResString;
- end;
- Result.ResultType := rtBoolean;
+
+ if (Result.ResultType in [rtInteger, rtFloat, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtInteger, rtFloat, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToFloat(Result) = ArgToFloat(RRes))
+ else
+ if (Result.ResultType in [rtString, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtString, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToString(Result) = ArgToString(RRes))
+ else
+ if (Result.ResultType in [rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtDateTime, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToDateTime(Result) = ArgToDateTime(RRes))
+ else
+ if (Result.ResultType in [rtBoolean, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtBoolean, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToBoolean(Result) = ArgToBoolean(RRes))
+ else
+ if (Result.ResultType = rtError)
+ then Result := ErrorResult(Result.ResError)
+ else
+ if (RRes.ResultType = rtError)
+ then Result := ErrorResult(RRes.ResError)
+ else
+ Result := BooleanResult(false);
end;
@@ -2529,7 +2868,7 @@ end;
function TsNotEqualExprNode.AsString: string;
begin
- Result := Left.AsString + ' <> ' + Right.AsString;
+ Result := Left.AsString + '<>' + Right.AsString;
end;
procedure TsNotEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
@@ -2539,9 +2878,38 @@ begin
end;
-{ TsLessThanExprNode }
+{ TsOrderingExprNode }
-function TsLessThanExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+procedure TsOrderingExprNode.Check;
+const
+ AllowedTypes = [rtBoolean, rtInteger, rtFloat, rtDateTime, rtString, rtEmpty, rtError, rtCell];
+begin
+ CheckNodeType(Left, AllowedTypes);
+ CheckNodeType(Right, AllowedTypes);
+ inherited Check;
+end;
+
+procedure TsOrderingExprNode.CheckSameNodeTypes;
+var
+ LT, RT: TsResultType;
+begin
+ {
+ LT := Left.NodeType;
+ RT := Right.NodeType;
+ case LT of
+ rtFloat, rtInteger:
+ if (RT in [rtFloat, rtInteger]) or
+ ((Rt = rtCell) and (Right.Res
+ if (RT <> LT) then
+ RaiseParserError(SErrTypesDoNotMatch, [ResultTypeName(LT), ResultTypeName(RT), Left.AsString, Right.AsString])
+ }
+end;
+
+
+
+{ TsLessExprNode }
+
+function TsLessExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekLess,
Right.AsRPNItem(
@@ -2550,30 +2918,45 @@ begin
)));
end;
-function TsLessThanExprNode.AsString: string;
+function TsLessExprNode.AsString: string;
begin
- Result := Left.AsString + ' < ' + Right.AsString;
+ Result := Left.AsString + '<' + Right.AsString;
end;
-procedure TsLessThanExprNode.GetNodeValue(var Result: TsExpressionResult);
+procedure TsLessExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtInteger : Result.resBoolean := Result.ResInteger < RRes.ResInteger;
- rtFloat : Result.resBoolean := Result.ResFloat < RRes.ResFLoat;
- rtDateTime : Result.resBoolean := Result.ResDateTime < RRes.ResDateTime;
- rtString : Result.resBoolean := Result.ResString < RRes.ResString;
- end;
- Result.ResultType := rtBoolean;
+ if (Result.ResultType in [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToFloat(Result) < ArgToFloat(RRes))
+ else
+ if (Result.ResultType in [rtString, rtInteger, rtFloat, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtString, rtInteger, rtFloat, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToString(Result) < ArgToString(RRes))
+ else
+ if (Result.ResultType in [rtBoolean, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtBoolean, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ord(ArgToBoolean(Result)) < ord(ArgToBoolean(RRes)))
+ else
+ if (Result.ResultType = rtError)
+ then Result := ErrorResult(Result.ResError)
+ else
+ if (RRes.ResultType = rtError)
+ then Result := ErrorResult(RRes.ResError)
+ else
+ Result := ErrorResult(errWrongType);
end;
-{ TsGreaterThanExprNode }
+{ TsGreaterExprNode }
-function TsGreaterThanExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+function TsGreaterExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekGreater,
Right.AsRPNItem(
@@ -2582,30 +2965,39 @@ begin
)));
end;
-function TsGreaterThanExprNode.AsString: string;
+function TsGreaterExprNode.AsString: string;
begin
- Result := Left.AsString + ' > ' + Right.AsString;
+ Result := Left.AsString + '>' + Right.AsString;
end;
-procedure TsGreaterThanExprNode.GetNodeValue(var Result: TsExpressionResult);
+procedure TsGreaterExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtInteger : case Right.NodeType of
- rtInteger : Result.resBoolean := Result.ResInteger > RRes.ResInteger;
- rtFloat : Result.resBoolean := Result.ResInteger > RRes.ResFloat;
- end;
- rtFloat : case Right.NodeType of
- rtInteger : Result.resBoolean := Result.ResFloat > RRes.ResInteger;
- rtFloat : Result.resBoolean := Result.ResFloat > RRes.ResFLoat;
- end;
- rtDateTime : Result.resBoolean := Result.ResDateTime > RRes.ResDateTime;
- rtString : Result.resBoolean := Result.ResString > RRes.ResString;
- end;
- Result.ResultType := rtBoolean;
+ if (Result.ResultType in [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToFloat(Result) > ArgToFloat(RRes))
+ else
+ if (Result.ResultType in [rtString, rtInteger, rtFloat, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtString, rtInteger, rtFloat, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ArgToString(Result) > ArgToString(RRes))
+ else
+ if (Result.ResultType in [rtBoolean, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtBoolean, rtCell, rtEmpty])
+ then
+ Result := BooleanResult(ord(ArgToBoolean(Result)) > ord(ArgToBoolean(RRes)))
+ else
+ if (Result.ResultType = rtError)
+ then Result := ErrorResult(Result.ResError)
+ else
+ if (RRes.ResultType = rtError)
+ then Result := ErrorResult(RRes.ResError)
+ else
+ Result := ErrorResult(errWrongType);
end;
@@ -2622,7 +3014,7 @@ end;
function TsGreaterEqualExprNode.AsString: string;
begin
- Result := Left.AsString + ' >= ' + Right.AsString;
+ Result := Left.AsString + '>=' + Right.AsString;
end;
procedure TsGreaterEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
@@ -2645,7 +3037,7 @@ end;
function TsLessEqualExprNode.AsString: string;
begin
- Result := Left.AsString + ' <= ' + Right.AsString;
+ Result := Left.AsString + '<=' + Right.AsString;
end;
procedure TsLessEqualExprNode.GetNodeValue(var Result: TsExpressionResult);
@@ -2655,42 +3047,8 @@ begin
end;
-{ TsOrderingExprNode }
-
-procedure TsOrderingExprNode.Check;
-const
- AllowedTypes =[rtInteger, rtfloat, rtDateTime, rtString];
-begin
- CheckNodeType(Left, AllowedTypes);
- CheckNodeType(Right, AllowedTypes);
- inherited Check;
-end;
-
-
{ TsConcatExprNode }
-procedure TsConcatExprNode.Check;
-begin
- inherited Check;
- CheckNodeType(Left, [rtString]);
- CheckNodeType(Right, [rtString]);
-end;
-
-procedure TsConcatExprNode.GetNodeValue(var Result: TsExpressionResult);
-var
- RRes : TsExpressionResult;
-begin
- Left.GetNodeValue(Result);
- Right.GetNodeValue(RRes);
- Result.ResString := Result.ResString + RRes.ResString;
- Result.ResultType := rtString;
-end;
-
-function TsConcatExprNode.NodeType: TsResultType;
-begin
- Result := rtString;
-end;
-
function TsConcatExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := RPNFunc(fekConcat,
@@ -2704,12 +3062,46 @@ begin
Result := Left.AsString + '&' + Right.AsString;
end;
+procedure TsConcatExprNode.Check;
+begin
+ inherited Check;
+ CheckNodeType(Left, [rtString, rtCell, rtEmpty, rtError]);
+ CheckNodeType(Right, [rtString, rtCell, rtEmpty, rtError]);
+end;
+
+procedure TsConcatExprNode.CheckSameNodeTypes;
+begin
+ // Same node types are checked in GetNodevalue
+end;
+
+procedure TsConcatExprNode.GetNodeValue(var Result: TsExpressionResult);
+var
+ RRes : TsExpressionResult;
+begin
+ Left.GetNodeValue(Result);
+ if (Result.ResultType = rtError)
+ then exit;
+ Right.GetNodeValue(RRes);
+ if (Result.ResultType in [rtString, rtCell]) and (RRes.ResultType in [rtString, rtCell])
+ then Result := StringResult(ArgToString(Result) + ArgToString(RRes))
+ else
+ if (RRes.ResultType = rtError)
+ then Result := ErrorResult(RRes.ResError)
+ else
+ Result := ErrorResult(errWrongType);
+end;
+
+function TsConcatExprNode.NodeType: TsResultType;
+begin
+ Result := rtString;
+end;
+
{ TsMathOperationExprNode }
procedure TsMathOperationExprNode.Check;
const
- AllowedTypes = [rtInteger, rtfloat, rtDateTime];
+ AllowedTypes = [rtInteger, rtFloat, rtDateTime, rtCell, rtEmpty, rtError];
begin
inherited Check;
CheckNodeType(Left, AllowedTypes);
@@ -2717,6 +3109,11 @@ begin
CheckSameNodeTypes;
end;
+procedure TsMathOperationExprNode.CheckSameNodeTypes;
+begin
+ // Same node types are checked in GetNodevalue
+end;
+
function TsMathOperationExprNode.NodeType: TsResultType;
begin
Result := Left.NodeType;
@@ -2741,17 +3138,28 @@ end;
procedure TsAddExprNode.GetNodeValue(var Result: TsExpressionResult);
var
- RRes : TsExpressionResult;
+ RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
+ if Result.ResultType = rtError then
+ exit;
+
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtInteger : Result.ResInteger := Result.ResInteger + RRes.ResInteger;
-// rtString : Result.ResString := Result.ResString + RRes.ResString;
- rtDateTime : Result.ResDateTime := Result.ResDateTime + RRes.ResDateTime;
- rtFloat : Result.ResFloat := Result.ResFloat + RRes.ResFloat;
+ if RRes.ResultType = rtError then
+ begin
+ Result := ErrorResult(RRes.ResError);
+ exit;
end;
- Result.ResultType := NodeType;
+
+ if (Result.ResultType in [rtInteger, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtInteger, rtCell, rtEmpty])
+ then
+ Result := IntegerResult(ArgToInt(Result) + ArgToInt(RRes))
+ else
+ if (Result.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty])
+ then
+ Result := FloatResult(ArgToFloat(Result) + ArgToFloat(RRes));
end;
@@ -2771,26 +3179,30 @@ begin
Result := Left.AsString + '-' + Right.asString;
end;
-procedure TsSubtractExprNode.Check;
-const
- AllowedTypes =[rtInteger, rtfloat, rtDateTime];
-begin
- CheckNodeType(Left, AllowedTypes);
- CheckNodeType(Right, AllowedTypes);
- inherited Check;
-end;
-
procedure TsSubtractExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
+ if Result.ResultType = rtError then
+ exit;
+
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtInteger : Result.ResInteger := Result.ResInteger - RRes.ResInteger;
- rtDateTime : Result.ResDateTime := Result.ResDateTime - RRes.ResDateTime;
- rtFloat : Result.ResFLoat := Result.ResFLoat - RRes.ResFLoat;
+ if RRes.ResultType = rtError then
+ begin
+ Result := ErrorResult(RRes.ResError);
+ exit;
end;
+
+ if (Result.ResultType in [rtInteger, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtInteger, rtCell, rtEmpty])
+ then
+ Result := IntegerResult(ArgToInt(Result) - ArgToInt(RRes))
+ else
+ if (Result.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty])
+ then
+ Result := FloatResult(ArgToFloat(Result) - ArgToFloat(RRes));
end;
@@ -2810,25 +3222,30 @@ begin
Result := Left.AsString + '*' + Right.AsString;
end;
-procedure TsMultiplyExprNode.Check;
-const
- AllowedTypes = [rtInteger, rtFloat];
-begin
- CheckNodeType(Left, AllowedTypes);
- CheckNodeType(Right, AllowedTypes);
- inherited;
-end;
-
procedure TsMultiplyExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
begin
Left.GetNodeValue(Result);
+ if Result.ResultType = rtError then
+ exit;
+
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtInteger : Result.ResInteger := Result.ResInteger * RRes.ResInteger;
- rtFloat : Result.ResFloat := Result.ResFloat * RRes.ResFloat;
+ if RRes.ResultType = rtError then
+ begin
+ Result := ErrorResult(RRes.ResError);
+ exit;
end;
+
+ if (Result.ResultType in [rtInteger, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtInteger, rtCell, rtEmpty])
+ then
+ Result := IntegerResult(ArgToInt(Result) * ArgToInt(RRes))
+ else
+ if (Result.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty])
+ then
+ Result := FloatResult(ArgToFloat(Result) * ArgToFloat(RRes));
end;
@@ -2848,26 +3265,31 @@ begin
Result := Left.AsString + '/' + Right.asString;
end;
-procedure TsDivideExprNode.Check;
-const
- AllowedTypes =[rtInteger, rtFloat];
-begin
- CheckNodeType(Left, AllowedTypes);
- CheckNodeType(Right, AllowedTypes);
- inherited Check;
-end;
-
procedure TsDivideExprNode.GetNodeValue(var Result: TsExpressionResult);
var
RRes: TsExpressionResult;
+ y: TsExprFloat;
begin
Left.GetNodeValue(Result);
+ if Result.ResultType = rtError then
+ exit;
+
Right.GetNodeValue(RRes);
- case Result.ResultType of
- rtInteger : Result.ResFloat := Result.ResInteger / RRes.ResInteger;
- rtFloat : Result.ResFloat := Result.ResFloat / RRes.ResFloat;
+ if RRes.ResultType = rtError then
+ begin
+ Result := ErrorResult(RRes.ResError);
+ exit;
+ end;
+
+ if (Result.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty])
+ then begin
+ y := ArgToFloat(RRes);
+ if y = 0.0 then
+ Result := ErrorResult(errDivideByZero)
+ else
+ Result := FloatResult(ArgToFloat(Result) / y);
end;
- Result.ResultType := rtFloat;
end;
function TsDivideExprNode.NodeType: TsResultType;
@@ -2876,6 +3298,54 @@ begin
end;
+{ TsPowerExprNode }
+
+function TsPowerExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+begin
+ Result := RPNFunc(fekPower,
+ Right.AsRPNItem(
+ Left.AsRPNItem(
+ ANext
+ )));
+end;
+
+function TsPowerExprNode.AsString: string;
+begin
+ Result := Left.AsString + '^' + Right.AsString;
+end;
+
+procedure TsPowerExprNode.GetNodeValue(var Result: TsExpressionResult);
+var
+ RRes: TsExpressionResult;
+ ex: TsExprFloat;
+begin
+ Left.GetNodeValue(Result);
+ if Result.ResultType = rtError then
+ exit;
+
+ Right.GetNodeValue(RRes);
+ if RRes.ResultType = rtError then
+ begin
+ Result := ErrorResult(RRes.ResError);
+ exit;
+ end;
+
+ if (Result.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty]) and
+ (RRes.ResultType in [rtFloat, rtInteger, rtDateTime, rtCell, rtEmpty])
+ then
+ try
+ Result := FloatResult(Power(ArgToFloat(Result), ArgToFloat(RRes)));
+ except
+ on E: EInvalidArgument do Result := ErrorResult(errOverflow);
+ end;
+end;
+
+function TsPowerExprNode.NodeType: TsResultType;
+begin
+ Result := rtFloat;
+end;
+
+
{ TsConvertExprNode }
function TsConvertExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
@@ -2894,14 +3364,14 @@ end;
procedure TsConvertToIntExprNode.Check;
begin
inherited Check;
- CheckNodeType(Operand, [rtInteger])
+ CheckNodeType(Operand, [rtInteger, rtCell])
end;
procedure TsIntToFloatExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
- Result.ResFloat := Result.ResInteger;
- Result.ResultType := rtFloat;
+ if Result.ResultType in [rtInteger, rtCell] then
+ Result := FloatResult(ArgToInt(Result));
end;
function TsIntToFloatExprNode.NodeType: TsResultType;
@@ -2920,16 +3390,17 @@ end;
procedure TsIntToDateTimeExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetnodeValue(Result);
- Result.ResDateTime := Result.ResInteger;
- Result.ResultType := rtDateTime;
+ if Result.ResultType in [rtInteger, rtCell] then
+ Result := DateTimeResult(ArgToInt(Result));
end;
+
{ TsFloatToDateTimeExprNode }
procedure TsFloatToDateTimeExprNode.Check;
begin
inherited Check;
- CheckNodeType(Operand, [rtFloat]);
+ CheckNodeType(Operand, [rtFloat, rtCell]);
end;
function TsFloatToDateTimeExprNode.NodeType: TsResultType;
@@ -2940,8 +3411,8 @@ end;
procedure TsFloatToDateTimeExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
Operand.GetNodeValue(Result);
- Result.ResDateTime := Result.ResFloat;
- Result.ResultType := rtDateTime;
+ if Result.ResultType in [rtFloat, rtCell] then
+ Result := DateTimeResult(ArgToFloat(Result));
end;
@@ -2967,35 +3438,35 @@ begin
end;
-{ TFPExprVariable }
+{ TsVariableExprNode }
-procedure TFPExprVariable.Check;
+procedure TsVariableExprNode.Check;
begin
// Do nothing;
end;
-function TFPExprVariable.AsRPNItem(ANext: PRPNItem): PRPNItem;
+function TsVariableExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
RaiseParserError('Cannot handle variables for RPN, so far.');
end;
-function TFPExprVariable.AsString: string;
+function TsVariableExprNode.AsString: string;
begin
Result := FID.Name;
end;
-{ TFPExprFunction }
+{ TsFunctionExprNode }
-constructor TFPExprFunction.CreateFunction(AID: TsExprIdentifierDef;
- const Args: TExprArgumentArray);
+constructor TsFunctionExprNode.CreateFunction(AID: TsExprIdentifierDef;
+ const Args: TsExprArgumentArray);
begin
inherited CreateIdentifier(AID);
FArgumentNodes := Args;
SetLength(FArgumentParams, Length(Args));
end;
-destructor TFPExprFunction.Destroy;
+destructor TsFunctionExprNode.Destroy;
var
i: Integer;
begin
@@ -3004,13 +3475,22 @@ begin
inherited Destroy;
end;
-function TFPExprFunction.AsRPNItem(ANext: PRPNItem): PRPNItem;
+function TsFunctionExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+var
+ i, n: Integer;
begin
+ if FID.HasFixedArgumentCount then
+ n := FID.ArgumentCount
+ else
+ n := Length(FArgumentNodes);
Result := ANext;
-// RaiseParserError('Cannot handle functions for RPN, so far.');
+// for i:=Length(FArgumentNodes)-1 downto 0 do
+ for i:=0 to High(FArgumentNodes) do
+ Result := FArgumentNodes[i].AsRPNItem(Result);
+ Result := RPNFunc(FID.Name, n, Result);
end;
-function TFPExprFunction.AsString: String;
+function TsFunctionExprNode.AsString: String;
var
S : String;
i : Integer;
@@ -3022,78 +3502,109 @@ begin
S := S + ',';
S := S + FArgumentNodes[i].AsString;
end;
- if (S <> '') then
- S := '(' + S + ')';
+ S := '(' + S + ')';
Result := FID.Name + S;
end;
-procedure TFPExprFunction.CalcParams;
+procedure TsFunctionExprNode.CalcParams;
var
i : Integer;
begin
for i := 0 to Length(FArgumentParams)-1 do
+ {
+ case FArgumentParams[i].ResultType of
+ rtEmpty: FID.FValue.ResultType := rtEmpty;
+ rtError: if FID.FValue.ResultType <> rtError then
+ begin
+ FID.FValue.ResultType := rtError;
+ FID.FValue.ResError := FArgumentParams[i].ResError;
+ end;
+ else FArgumentNodes[i].GetNodeValue(FArgumentParams[i]);
+ end;
+ }
FArgumentNodes[i].GetNodeValue(FArgumentParams[i]);
end;
-procedure TFPExprFunction.Check;
+procedure TsFunctionExprNode.Check;
var
i: Integer;
- rtp, rta: TsResultType;
+ rta, // parameter types passed to the function
+ rtp: TsResultType; // Parameter types expected from the parameter symbol
+ lastrtp: TsResultType;
begin
if Length(FArgumentNodes) <> FID.ArgumentCount then
- RaiseParserError(ErrInvalidArgumentCount, [FID.Name]);
+ begin
+ for i:=Length(FArgumentNodes)+1 to FID.ArgumentCount do
+ if not FID.IsOptionalArgument(i) then
+ RaiseParserError(ErrInvalidArgumentCount, [FID.Name]);
+ end;
+
for i := 0 to Length(FArgumentNodes)-1 do
begin
- rtp := CharToResultType(FID.ParameterTypes[i+1]);
rta := FArgumentNodes[i].NodeType;
- if (rtp <> rta) then
+
+ // A "cell" can return any type --> no type conversion required here.
+ if rta = rtCell then
+ Continue;
+
+ if i+1 <= Length(FID.ParameterTypes) then
begin
- // Automatically convert integers to floats in functions that return
- // a float
+ rtp := CharToResultType(FID.ParameterTypes[i+1]);
+ lastrtp := rtp;
+ end else
+ rtp := lastrtp;
+ if rtp = rtAny then
+ Continue;
+ if (rtp <> rta) and not (rta in [rtCellRange, rtError, rtEmpty]) then
+ begin
+ // Automatically convert integers to floats in functions that return a float
if (rta = rtInteger) and (rtp = rtFloat) then
begin
FArgumentNodes[i] := TsIntToFloatExprNode(FArgumentNodes[i]);
exit;
end;
- RaiseParserError(SErrInvalidArgumentType, [I+1, ResultTypeName(rtp), ResultTypeName(rta)])
+ // Floats are truncated automatically to integers - that's what Excel does.
+ if (rta = rtFloat) and (rtp = rtInteger) then
+ exit;
+ RaiseParserError(SErrInvalidArgumentType, [i+1, ResultTypeName(rtp), ResultTypeName(rta)])
end;
end;
end;
-{ TFPFunctionCallBack }
+{ TsFunctionCallBackExprNode }
-constructor TFPFunctionCallBack.CreateFunction(AID: TsExprIdentifierDef;
- const Args: TExprArgumentArray);
+constructor TsFunctionCallBackExprNode.CreateFunction(AID: TsExprIdentifierDef;
+ const Args: TsExprArgumentArray);
begin
inherited;
FCallBack := AID.OnGetFunctionValueCallBack;
end;
-procedure TFPFunctionCallBack.GetNodeValue(var Result: TsExpressionResult);
+procedure TsFunctionCallBackExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
+ Result.ResultType := NodeType; // was at end!
if Length(FArgumentParams) > 0 then
CalcParams;
FCallBack(Result, FArgumentParams);
- Result.ResultType := NodeType;
end;
-{ TFPFunctionEventHandler }
+{ TFPFunctionEventHandlerExprNode }
-constructor TFPFunctionEventHandler.CreateFunction(AID: TsExprIdentifierDef;
- const Args: TExprArgumentArray);
+constructor TFPFunctionEventHandlerExprNode.CreateFunction(AID: TsExprIdentifierDef;
+ const Args: TsExprArgumentArray);
begin
inherited;
FCallBack := AID.OnGetFunctionValue;
end;
-procedure TFPFunctionEventHandler.GetNodeValue(var Result: TsExpressionResult);
+procedure TFPFunctionEventHandlerExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
- if Length(FArgumentParams)>0 then
+ Result.ResultType := NodeType; // was at end
+ if Length(FArgumentParams) > 0 then
CalcParams;
FCallBack(Result, FArgumentParams);
- Result.ResultType := NodeType;
end;
@@ -3105,545 +3616,1873 @@ var
flags: TsRelFlags;
begin
ParseCellString(ACellString, r, c, flags);
- Create(AWorksheet, AWorksheet.FindCell(r, c), flags);
+ Create(AWorksheet, r, c, flags);
end;
-constructor TsCellExprNode.Create(AWorksheet: TsWorksheet; ACell: PCell; AFlags: TsRelFlags);
+constructor TsCellExprNode.Create(AWorksheet: TsWorksheet; ARow,ACol: Cardinal;
+ AFlags: TsRelFlags);
begin
- FCell := ACell;
+ FWorksheet := AWorksheet;
+ FRow := ARow;
+ FCol := ACol;
FFlags := AFlags;
+ FCell := AWorksheet.FindCell(FRow, FCol);
end;
function TsCellExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
- Result := RPNCellValue(FCell^.Row, FCell^.Col, FFlags, ANext);
+ if FIsRef then
+ Result := RPNCellRef(FRow, FCol, FFlags, ANext)
+ else
+ Result := RPNCellValue(FRow, FCol, FFlags, ANext);
end;
function TsCellExprNode.AsString: string;
begin
- Result := GetCellString(FCell^.Row, FCell^.Col, FFlags);
-end;
-
-procedure TsCellExprNode.Check;
-begin
- if not Assigned(FCell) then
- RaiseParserError(SErrNoCellOperand);
- if (FCell^.ContentType = cctError) and (FCell^.ErrorValue <> errOK) then
- raise EExprParser.CreateFmt(SErrCellError, [AsString]);
+ Result := GetCellString(FRow, FCol, FFlags);
end;
procedure TsCellExprNode.GetNodeValue(var Result: TsExpressionResult);
begin
- case FCell^.ContentType of
- cctNumber:
- Result.ResFloat := FCell^.NumberValue;
- cctDateTime:
- Result.ResDateTime := FCell^.DateTimeValue;
- cctUTF8String:
- Result.ResString := FCell^.UTF8StringValue;
- cctBool:
- Result.ResBoolean := FCell^.BoolValue;
- cctEmpty:
- Result.ResString := '';
- end;
- Result.ResultType := NodeType;
+ if (FCell <> nil) and HasFormula(FCell) then
+ case FCell^.CalcState of
+ csNotCalculated:
+ Worksheet.CalcFormula(FCell);
+ csCalculating:
+ raise Exception.Create(SErrCircularReference);
+ end;
+
+ Result.ResultType := rtCell;
+ Result.ResRow := FRow;
+ Result.ResCol := FCol;
+ Result.Worksheet := FWorksheet;
+end;
+
+procedure TsCellExprNode.Check;
+begin
+ // Nothing to check;
end;
function TsCellExprNode.NodeType: TsResultType;
begin
- case FCell^.ContentType of
- cctNumber:
- Result := rtFloat;
- cctDateTime:
- Result := rtDateTime;
- cctUTF8String:
- Result := rtString;
- cctBool:
- Result := rtBoolean;
- cctEmpty:
- Result := rtString;
+ Result := rtCell;
+ {
+ if FIsRef then
+ Result := rtCell
+ else
+ begin
+ Result := rtEmpty;
+ if FCell <> nil then
+ case FCell^.ContentType of
+ cctNumber:
+ if frac(FCell^.NumberValue) = 0 then
+ Result := rtInteger
+ else
+ Result := rtFloat;
+ cctDateTime:
+ Result := rtDateTime;
+ cctUTF8String:
+ Result := rtString;
+ cctBool:
+ Result := rtBoolean;
+ cctError:
+ Result := rtError;
+ end;
+ end;
+ }
+end;
+
+
+{ TsCellRangeExprNode }
+
+constructor TsCellRangeExprNode.Create(AWorksheet: TsWorksheet; ACellRangeString: String);
+var
+ r1, c1, r2, c2: Cardinal;
+ flags: TsRelFlags;
+begin
+ if pos(':', ACellRangeString) = 0 then
+ begin
+ ParseCellString(ACellRangeString, r1, c1, flags);
+ if rfRelRow in flags then Include(flags, rfRelRow2);
+ if rfRelCol in flags then Include(flags, rfRelCol2);
+ Create(AWorksheet, r1, c1, r1, c1, flags);
+ end else
+ begin
+ ParseCellRangeString(ACellRangeString, r1, c1, r2, c2, flags);
+ Create(AWorksheet, r1, c1, r2, c2, flags);
+ end;
+end;
+
+constructor TsCellRangeExprNode.Create(AWorksheet: TsWorksheet;
+ ARow1,ACol1,ARow2,ACol2: Cardinal; AFlags: TsRelFlags);
+begin
+ FWorksheet := AWorksheet;
+ FRow1 := ARow1;
+ FCol1 := ACol1;
+ FRow2 := ARow2;
+ FCol2 := ACol2;
+ FFlags := AFlags;
+end;
+
+function TsCellRangeExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
+begin
+ {
+ if (FRow1 = FRow2) and (FCol1 = FCol2) then
+ Result := RPNCellRef(FRow1, FCol1, FFlags, ANext)
+ else
+ }
+ Result := RPNCellRange(FRow1, FCol1, FRow2, FCol2, FFlags, ANext);
+end;
+
+function TsCellRangeExprNode.AsString: string;
+begin
+ if (FRow1 = FRow2) and (FCol1 = FCol2) then
+ Result := GetCellString(FRow1, FCol1, FFlags)
+ else
+ Result := GetCellRangeString(FRow1, FCol1, FRow2, FCol2, FFlags);
+end;
+
+procedure TsCellRangeExprNode.Check;
+begin
+ // Nothing to check;
+end;
+
+procedure TsCellRangeExprNode.GetNodeValue(var Result: TsExpressionResult);
+var
+ r,c: Cardinal;
+ cell: PCell;
+begin
+ for r := FRow1 to FRow2 do
+ for c := FCol1 to FCol2 do
+ begin
+ cell := FWorksheet.FindCell(r, c);
+ if HasFormula(cell) then
+ case cell^.CalcState of
+ csNotCalculated: FWorksheet.CalcFormula(cell);
+ csCalculating : raise Exception.Create(SErrCircularReference);
+ end;
+ end;
+
+ Result.ResultType := rtCellRange;
+ Result.ResCellRange.Row1 := FRow1;
+ Result.ResCellRange.Col1 := FCol1;
+ Result.ResCellRange.Row2 := FRow2;
+ Result.ResCellRange.Col2 := FCol2;
+ Result.Worksheet := FWorksheet;
+end;
+
+function TsCellRangeExprNode.NodeType: TsResultType;
+begin
+ Result := rtCellRange;
+end;
+
+
+{------------------------------------------------------------------------------}
+{ Conversion of arguments to simple data types }
+{------------------------------------------------------------------------------}
+
+function ArgToBoolean(Arg: TsExpressionResult): Boolean;
+var
+ cell: PCell;
+begin
+ Result := false;
+ if Arg.ResultType = rtBoolean then
+ Result := Arg.ResBoolean
+ else
+ if (Arg.ResultType = rtCell) then begin
+ cell := ArgToCell(Arg);
+ if (cell <> nil) and (cell^.ContentType = cctBool) then
+ Result := cell^.BoolValue;
+ end;
+end;
+
+function ArgToCell(Arg: TsExpressionResult): PCell;
+begin
+ if Arg.ResultType = rtCell then
+ Result := Arg.Worksheet.FindCell(Arg.ResRow, Arg.ResCol)
+ else
+ Result := nil;
+end;
+
+function ArgToInt(Arg: TsExpressionResult): Integer;
+var
+ cell: PCell;
+begin
+ Result := 0;
+ if Arg.ResultType = rtInteger then
+ result := Arg.ResInteger
+ else
+ if Arg.ResultType = rtFloat then
+ result := trunc(Arg.ResFloat)
+ else
+ if Arg.ResultType = rtDateTime then
+ result := trunc(Arg.ResDateTime)
+ else
+ if (Arg.ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Arg);
+ if Assigned(cell) and (cell^.ContentType = cctNumber) then
+ result := trunc(cell^.NumberValue);
+ end;
+end;
+
+function ArgToFloat(Arg: TsExpressionResult): TsExprFloat;
+// Utility function for the built-in math functions. Accepts also integers
+// in place of the floating point arguments. To be called in builtins or
+// user-defined callbacks having float results.
+var
+ cell: PCell;
+begin
+ Result := 0.0;
+ if Arg.ResultType = rtInteger then
+ result := Arg.ResInteger
+ else
+ if Arg.ResultType = rtDateTime then
+ result := Arg.ResDateTime
+ else
+ if Arg.ResultType = rtFloat then
+ result := Arg.ResFloat
+ else
+ if (Arg.ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Arg);
+ if Assigned(cell) then
+ case cell^.ContentType of
+ cctNumber : Result := cell^.NumberValue;
+ cctDateTime : Result := cell^.DateTimeValue;
+ end;
+ end;
+end;
+
+function ArgToDateTime(Arg: TsExpressionResult): TDateTime;
+var
+ cell: PCell;
+begin
+ Result := 0.0;
+ if Arg.ResultType = rtDateTime then
+ result := Arg.ResDateTime
+ else
+ if Arg.ResultType = rtInteger then
+ Result := Arg.ResInteger
+ else
+ if Arg.ResultType = rtFloat then
+ Result := Arg.ResFloat
+ else
+ if (Arg.ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Arg);
+ if Assigned(cell) and (cell^.ContentType = cctDateTime) then
+ Result := cell^.DateTimeValue;
+ end;
+end;
+
+function ArgToString(Arg: TsExpressionResult): String;
+var
+ cell: PCell;
+begin
+ Result := '';
+ case Arg.ResultType of
+ rtString : result := Arg.ResString;
+ rtInteger : Result := IntToStr(Arg.ResInteger);
+ rtFloat : Result := FloatToStr(Arg.ResFloat);
+ rtCell : begin
+ cell := ArgToCell(Arg);
+ if Assigned(cell) and (cell^.ContentType = cctUTF8String) then
+ Result := cell^.UTF8Stringvalue;
+ end;
end;
end;
-{ ---------------------------------------------------------------------
- Standard Builtins support
- ---------------------------------------------------------------------}
+{------------------------------------------------------------------------------}
+{ Conversion simple data types to ExpressionResults }
+{------------------------------------------------------------------------------}
-{ Template for builtin.
-
-Procedure MyCallback (Var Result : TsExpressionResult; Const Args : TExprParameterArray);
+function BooleanResult(AValue: Boolean): TsExpressionResult;
begin
+ Result.ResultType := rtBoolean;
+ Result.ResBoolean := AValue;
end;
-}
-function ArgToFloat(Arg: TsExpressionResult): TExprFloat;
-// Utility function for the built-in math functions. Accepts also integers
-// in place of the floating point arguments. To be called in builtins or
-// user-defined callbacks having float results.
+function DateTimeResult(AValue: TDateTime): TsExpressionResult;
begin
- if Arg.ResultType = rtInteger then
- result := Arg.resInteger
+ Result.ResultType := rtDateTime;
+ Result.ResDateTime := AValue;
+end;
+
+function EmptyResult: TsExpressionResult;
+begin
+ Result.ResultType := rtEmpty;
+end;
+
+function ErrorResult(const AValue: TsErrorValue): TsExpressionResult;
+begin
+ Result.ResultType := rtError;
+ Result.ResError := AValue;
+end;
+
+function FloatResult(const AValue: TsExprFloat): TsExpressionResult;
+begin
+ Result.ResultType := rtFloat;
+ Result.ResFloat := AValue;
+end;
+
+function IntegerResult(const AValue: Integer): TsExpressionResult;
+begin
+ Result.ResultType := rtInteger;
+ Result.ResInteger := AValue;
+end;
+
+function StringResult(const AValue: string): TsExpressionResult;
+begin
+ Result.ResultType := rtString;
+ Result.ResString := AValue;
+end;
+
+
+{------------------------------------------------------------------------------}
+{ Standard Builtins support }
+{------------------------------------------------------------------------------}
+
+// Builtin math functions
+
+procedure fpsABS(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(abs(ArgToFloat(Args[0])));
+end;
+
+procedure fpsACOS(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
+begin
+ x := ArgToFloat(Args[0]);
+ if InRange(x, -1, +1) then
+ Result := FloatResult(arccos(x))
else
- result := Arg.resFloat;
+ Result := ErrorResult(errOverflow); // #NUM!
end;
-
-// Math builtins
-
-procedure BuiltInCos(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsACOSH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
begin
- Result.ResFloat := cos(ArgToFloat(Args[0]));
+ x := ArgToFloat(Args[0]);
+ if x >= 1 then
+ Result := FloatResult(arccosh(ArgToFloat(Args[0])))
+ else
+ Result := ErrorResult(errOverflow);
end;
-procedure BuiltInSin(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsASIN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
begin
- Result.ResFloat := sin(ArgToFloat(Args[0]));
+ x := ArgToFloat(Args[0]);
+ if InRange(x, -1, +1) then
+ Result := FloatResult(arcsin(ArgToFloat(Args[0])))
+ else
+ Result := ErrorResult(errOverflow);
end;
-procedure BuiltInArcTan(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsASINH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- Result.ResFloat := arctan(ArgToFloat(Args[0]));
+ Result := FloatResult(arcsinh(ArgToFloat(Args[0])));
end;
-procedure BuiltInAbs(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsATAN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- Result.ResFloat := abs(ArgToFloat(Args[0]));
+ Result := FloatResult(arctan(ArgToFloat(Args[0])));
end;
-procedure BuiltInSqr(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsATANH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
begin
- Result.ResFloat := sqr(ArgToFloat(Args[0]));
+ x := ArgToFloat(Args[0]);
+ if (x > -1) and (x < +1) then
+ Result := FloatResult(arctanh(ArgToFloat(Args[0])))
+ else
+ Result := ErrorResult(errOverflow); // #NUM!
end;
-procedure BuiltInSqrt(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsCOS(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- Result.ResFloat := sqrt(ArgToFloat(Args[0]));
+ Result := FloatResult(cos(ArgToFloat(Args[0])));
end;
-procedure BuiltInExp(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsCOSH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- Result.ResFloat := exp(ArgToFloat(Args[0]));
+ Result := FloatResult(cosh(ArgToFloat(Args[0])));
end;
-procedure BuiltInLn(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsDEGREES(var Result: TsExpressionResult; const Args: TsExprParameterArray);
begin
- Result.ResFloat := ln(ArgToFloat(Args[0]));
+ Result := FloatResult(RadToDeg(ArgToFloat(Args[0])));
end;
+procedure fpsEXP(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(exp(ArgToFloat(Args[0])));
+end;
+
+procedure fpsINT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(floor(ArgToFloat(Args[0])));
+end;
+
+procedure fpsLN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
+begin
+ x := ArgToFloat(Args[0]);
+ if x > 0 then
+ Result := FloatResult(ln(x))
+ else
+ Result := ErrorResult(errOverflow); // #NUM!
+end;
+
+procedure fpsLOG(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// LOG( number [, base] ) - base is 10 if omitted.
+var
+ x: TsExprFloat;
+ base: TsExprFloat;
+begin
+ x := ArgToFloat(Args[0]);
+ if x <= 0 then begin
+ Result := ErrorResult(errOverflow); // #NUM!
+ exit;
+ end;
+
+ if Length(Args) = 2 then begin
+ base := ArgToFloat(Args[1]);
+ if base < 0 then begin
+ Result := ErrorResult(errOverflow); // #NUM!
+ exit;
+ end;
+ end else
+ base := 10;
+
+ Result := FloatResult(logn(base, x));
+end;
+
+procedure fpsLOG10(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
+begin
+ x := ArgToFloat(Args[0]);
+ if x > 0 then
+ Result := FloatResult(log10(x))
+ else
+ Result := ErrorResult(errOverflow); // #NUM!
+end;
+
+procedure fpsPI(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Unused(Args);
+ Result := FloatResult(pi);
+end;
+
+procedure fpsPOWER(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ try
+ Result := FloatResult(Power(ArgToFloat(Args[0]), ArgToFloat(Args[1])));
+ except
+ Result := ErrorResult(errOverflow);
+ end;
+end;
+
+procedure fpsRADIANS(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(DegToRad(ArgToFloat(Args[0])));
+end;
+
+procedure fpsRAND(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Unused(Args);
+ Result := FloatResult(random);
+end;
+
+procedure fpsROUND(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ n: Integer;
+begin
+ if Args[1].ResultType = rtInteger then
+ n := Args[1].ResInteger
+ else
+ n := round(Args[1].ResFloat);
+ Result := FloatResult(RoundTo(ArgToFloat(Args[0]), n));
+end;
+
+procedure fpsSIGN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(sign(ArgToFloat(Args[0])));
+end;
+
+procedure fpsSIN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(sin(ArgToFloat(Args[0])));
+end;
+
+procedure fpsSINH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(sinh(ArgToFloat(Args[0])));
+end;
+
+procedure fpsSQRT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
+begin
+ x := ArgToFloat(Args[0]);
+ if x >= 0 then
+ Result := FloatResult(sqrt(x))
+ else
+ Result := ErrorResult(errOverflow);
+end;
+
+procedure fpsTAN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+var
+ x: TsExprFloat;
+begin
+ x := ArgToFloat(Args[0]);
+ if frac(x / (pi*0.5)) = 0 then
+ Result := ErrorResult(errOverflow) // #NUM!
+ else
+ Result := FloatResult(tan(ArgToFloat(Args[0])));
+end;
+
+procedure fpsTANH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+begin
+ Result := FloatResult(tanh(ArgToFloat(Args[0])));
+end;
+
+
+// Builtin date/time functions
+
+procedure fpsDATE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// DATE( year, month, day )
+begin
+ Result := DateTimeResult(
+ EncodeDate(ArgToInt(Args[0]), ArgToInt(Args[1]), ArgToInt(Args[2]))
+ );
+end;
+
+procedure fpsDATEDIF(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+{ DATEDIF( start_date, end_date, interval )
+ start_date <= end_date !
+ interval = Y - The number of complete years.
+ = M - The number of complete months.
+ = D - The number of days.
+ = MD - The difference between the days (months and years are ignored).
+ = YM - The difference between the months (days and years are ignored).
+ = YD - The difference between the days (years and dates are ignored). }
+var
+ interval: String;
+ start_date, end_date: TDate;
+begin
+ start_date := ArgToDateTime(Args[0]);
+ end_date := ArgToDateTime(Args[1]);
+ interval := ArgToString(Args[2]);
+
+ if end_date > start_date then
+ Result := ErrorResult(errOverflow)
+ else if interval = 'Y' then
+ Result := FloatResult(YearsBetween(end_date, start_date))
+ else if interval = 'M' then
+ Result := FloatResult(MonthsBetween(end_date, start_date))
+ else if interval = 'D' then
+ Result := FloatResult(DaysBetween(end_date, start_date))
+ else
+ Result := ErrorResult(errFormulaNotSupported);
+end;
+
+procedure fpsDATEVALUE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the serial number of a date. Input is a string.
+// DATE( date_string )
+var
+ d: TDateTime;
+begin
+ if TryStrToDate(Args[0].ResString, d) then
+ Result := DateTimeResult(d)
+ else
+ Result := ErrorResult(errWrongType);
+end;
+
+procedure fpsDAY(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// DAY( date_value )
+// date_value can be a serial number or a string
+var
+ y,m,d: Word;
+ dt: TDateTime;
+begin
+ if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger]) then
+ DecodeDate(ArgToFloat(Args[0]), y,m,d)
+ else
+ if Args[0].ResultType in [rtString] then
+ begin
+ if TryStrToDate(Args[0].ResString, dt) then
+ DecodeDate(dt, y,m,d)
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ end;
+ Result := IntegerResult(d);
+end;
+
+procedure fpsHOUR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// HOUR( time_value )
+// time_value can be a number or a string.
+var
+ h, m, s, ms: Word;
+ t: double;
+begin
+ if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger]) then
+ DecodeTime(ArgToFloat(Args[0]), h,m,s,ms)
+ else
+ if (Args[0].ResultType in [rtString]) then
+ begin
+ if TryStrToTime(Args[0].ResString, t) then
+ DecodeTime(t, h,m,s,ms)
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ end;
+ Result := IntegerResult(h);
+end;
+
+procedure fpsMINUTE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// MINUTE( serial_number or string )
+var
+ h, m, s, ms: Word;
+ t: double;
+begin
+ if (Args[0].resultType in [rtDateTime, rtFloat, rtInteger]) then
+ DecodeTime(ArgToFloat(Args[0]), h,m,s,ms)
+ else
+ if (Args[0].ResultType in [rtString]) then
+ begin
+ if TryStrToTime(Args[0].ResString, t) then
+ DecodeTime(t, h,m,s,ms)
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ end;
+ Result := IntegerResult(m);
+end;
+
+procedure fpsMONTH(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// MONTH( date_value or string )
+var
+ y,m,d: Word;
+ dt: TDateTime;
+begin
+ if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger]) then
+ DecodeDate(ArgToFloat(Args[0]), y,m,d)
+ else
+ if (Args[0].ResultType in [rtString]) then
+ begin
+ if TryStrToDate(Args[0].ResString, dt) then
+ DecodeDate(dt, y,m,d)
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ end;
+ Result := IntegerResult(m);
+end;
+
+procedure fpsNOW(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the current system date and time. Willrefresh the date/time value
+// whenever the worksheet recalculates.
+// NOW()
+begin
+ Result := DateTimeResult(Now);
+end;
+
+procedure fpsSECOND(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// SECOND( serial_number )
+var
+ h, m, s, ms: Word;
+ t: Double;
+begin
+ if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger]) then
+ DecodeTime(ArgToFloat(Args[0]), h,m,s,ms)
+ else
+ if (Args[0].ResultType in [rtString]) then
+ begin
+ if TryStrToTime(Args[0].ResString, t) then
+ DecodeTime(t, h,m,s,ms)
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ end;
+ Result := IntegerResult(s);
+end;
+
+procedure fpsTIME(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// TIME( hour, minute, second)
+begin
+ Result := DateTimeResult(
+ EncodeTime(ArgToInt(Args[0]), ArgToInt(Args[1]), ArgToInt(Args[2]), 0)
+ );
+end;
+
+procedure fpsTIMEVALUE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the serial number of a time. Input must be a string.
+// DATE( date_string )
+var
+ t: TDateTime;
+begin
+ if TryStrToTime(Args[0].ResString, t) then
+ Result := DateTimeResult(t)
+ else
+ Result := ErrorResult(errWrongType);
+end;
+
+procedure fpsTODAY(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the current system date. This function will refresh the date
+// whenever the worksheet recalculates.
+// TODAY()
+begin
+ Result := DateTimeResult(Date);
+end;
+
+procedure fpsWEEKDAY(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+{ WEEKDAY( serial_number, [return_value] )
+ return_value = 1 - Returns a number from 1 (Sunday) to 7 (Saturday) (default)
+ = 2 - Returns a number from 1 (Monday) to 7 (Sunday).
+ = 3 - Returns a number from 0 (Monday) to 6 (Sunday). }
+var
+ n: Integer;
+ dow: Integer;
+ dt: TDateTime;
+begin
+ if Length(Args) = 2 then
+ n := ArgToInt(Args[1])
+ else
+ n := 1;
+ if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger] then
+ dt := ArgToDateTime(Args[0])
+ else
+ if Args[0].ResultType in [rtString] then
+ if not TryStrToDate(Args[0].ResString, dt) then
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ dow := DayOfWeek(dt); // Sunday = 1 ... Saturday = 7
+ case n of
+ 1: ;
+ 2: if dow > 1 then dow := dow - 1 else dow := 7;
+ 3: if dow > 1 then dow := dow - 2 else dow := 6;
+ end;
+ Result := IntegerResult(dow);
+end;
+
+procedure fpsYEAR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// YEAR( date_value )
+var
+ y,m,d: Word;
+ dt: TDateTime;
+begin
+ if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger] then
+ DecodeDate(ArgToFloat(Args[0]), y,m,d)
+ else
+ if Args[0].ResultType in [rtString] then
+ begin
+ if TryStrToDate(Args[0].ResString, dt) then
+ DecodeDate(dt, y,m,d)
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ end;
+ Result := IntegerResult(y);
+end;
+
+
+// Builtin string functions
+
+procedure fpsCHAR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// CHAR( ascii_value )
+// returns the character based on the ASCII value
+var
+ arg: Integer;
+begin
+ Result := ErrorResult(errWrongType);
+ case Args[0].ResultType of
+ rtInteger, rtFloat:
+ if Args[0].ResultType in [rtInteger, rtFloat] then
+ begin
+ arg := ArgToInt(Args[0]);
+ if (arg >= 0) and (arg < 256) then
+ Result := StringResult(AnsiToUTF8(Char(arg)));
+ end;
+ rtError:
+ Result := ErrorResult(Args[0].ResError);
+ rtEmpty:
+ Result.ResultType := rtEmpty;
+ end;
+end;
+
+procedure fpsCODE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// CODE( text )
+// returns the ASCII value of a character or the first character in a string.
+var
+ s: String;
+ ch: Char;
+begin
+ s := ArgToString(Args[0]);
+ if s = '' then
+ Result := ErrorResult(errWrongType)
+ else
+ begin
+ ch := UTF8ToAnsi(s)[1];
+ Result := IntegerResult(ord(ch));
+ end;
+end;
+
+procedure fpsCONCATENATE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// CONCATENATE( text1, text2, ... text_n )
+// Joins two or more strings together
+var
+ s: String;
+ i: Integer;
+begin
+ s := '';
+ for i:=0 to Length(Args)-1 do
+ begin
+ if Args[i].ResultType = rtError then
+ begin
+ Result := ErrorResult(Args[i].ResError);
+ exit;
+ end;
+ s := s + ArgToString(Args[i]);
+ end;
+ Result := StringResult(s);
+end;
+
+procedure fpsLEFT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// LEFT( text, [number_of_characters] )
+// extracts a substring from a string, starting from the left-most character
+var
+ s: String;
+ count: Integer;
+begin
+ s := Args[0].ResString;
+ if s = '' then
+ Result.ResultType := rtEmpty
+ else
+ begin
+ if Length(Args) = 1 then
+ count := 1
+ else
+ if Args[1].ResultType in [rtInteger, rtFloat] then
+ count := ArgToInt(Args[1])
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ Result := StringResult(UTF8LeftStr(s, count));
+ end;
+end;
+
+procedure fpsLEN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// LEN( text )
+// returns the length of the specified string.
+begin
+ Result := IntegerResult(UTF8Length(Args[0].ResString));
+end;
+
+procedure fpsLOWER(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// LOWER( text )
+// converts all letters in the specified string to lowercase. If there are
+// characters in the string that are not letters, they are not affected.
+begin
+ Result := StringResult(UTF8Lowercase(Args[0].ResString));
+end;
+
+procedure fpsMID(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// MID( text, start_position, number_of_characters )
+// extracts a substring from a string (starting at any position).
+begin
+ Result := StringResult(UTF8Copy(Args[0].ResString, ArgToInt(Args[1]), ArgToInt(Args[2])));
+end;
+
+procedure fpsREPLACE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// REPLACE( old_text, start, number_of_chars, new_text )
+// replaces a sequence of characters in a string with another set of characters
+var
+ sOld, sNew, s1, s2: String;
+ start: Integer;
+ count: Integer;
+begin
+ sOld := Args[0].ResString;
+ start := ArgToInt(Args[1]);
+ count := ArgToInt(Args[2]);
+ sNew := Args[3].ResString;
+ s1 := UTF8Copy(sOld, 1, start-1);
+ s2 := UTF8Copy(sOld, start+count, UTF8Length(sOld));
+ Result := StringResult(s1 + sNew + s2);
+end;
+
+procedure fpsRIGHT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// RIGHT( text, [number_of_characters] )
+// extracts a substring from a string, starting from the last character
+var
+ s: String;
+ count: Integer;
+begin
+ s := Args[0].ResString;
+ if s = '' then
+ Result.ResultType := rtEmpty
+ else begin
+ if Length(Args) = 1 then
+ count := 1
+ else
+ if Args[1].ResultType in [rtInteger, rtFloat] then
+ count := ArgToInt(Args[1])
+ else
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ Result := StringResult(UTF8RightStr(s, count));
+ end;
+end;
+
+procedure fpsSUBSTITUTE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// SUBSTITUTE( text, old_text, new_text, [nth_appearance] )
+// replaces a set of characters with another.
+var
+ sOld: String;
+ sNew: String;
+ s1, s2: String;
+ n: Integer;
+ s: String;
+ p: Integer;
+begin
+ s := Args[0].ResString;
+ sOld := ArgToString(Args[1]);
+ sNew := ArgToString(Args[2]);
+ if Length(Args) = 4 then
+ begin
+ n := ArgToInt(Args[3]); // THIS PART NOT YET CHECKED !!!!!!
+ if n <= 0 then
+ begin
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+ p := UTF8Pos(sOld, s);
+ while (n > 1) do begin
+ p := UTF8Pos(sOld, s, p+1);
+ dec(n);
+ end;
+ if p > 0 then begin
+ s1 := UTF8Copy(s, 1, p-1);
+ s2 := UTF8Copy(s, p+UTF8Length(sOld), UTF8Length(s));
+ s := s1 + sNew + s2;
+ end;
+ Result := StringResult(s);
+ end else
+ Result := StringResult(UTF8StringReplace(s, sOld, sNew, [rfReplaceAll]));
+end;
+
+procedure fpsTRIM(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// TRIM( text )
+// returns a text value with the leading and trailing spaces removed
+begin
+ Result := StringResult(UTF8Trim(Args[0].ResString));
+end;
+
+procedure fpsUPPER(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// UPPER( text )
+// converts all letters in the specified string to uppercase. If there are
+// characters in the string that are not letters, they are not affected.
+begin
+ Result := StringResult(UTF8Uppercase(Args[0].ResString));
+end;
+
+procedure fpsVALUE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// VALUE( text )
+// converts a text value that represents a number to a number.
+var
+ x: Double;
+ n: Integer;
+ s: String;
+begin
+ s := ArgToString(Args[0]);
+ if TryStrToInt(s, n) then
+ Result := IntegerResult(n)
+ else
+ if TryStrToFloat(s, x, ExprFormatSettings) then
+ Result := FloatResult(x)
+ else
+ Result := ErrorResult(errWrongType);
+end;
+
+
+{ Builtin logical functions }
+
+procedure fpsAND(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// AND( condition1, [condition2], ... )
+// up to 30 parameters. At least 1 parameter.
+var
+ i: Integer;
+ b: Boolean;
+begin
+ b := true;
+ for i:=0 to High(Args) do
+ b := b and Args[i].ResBoolean;
+ Result.ResBoolean := b;
+end;
+
+procedure fpsFALSE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// FALSE ()
+begin
+ Unused(Args);
+ Result.ResBoolean := false;
+end;
+
+procedure fpsIF(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// IF( condition, value_if_true, [value_if_false] )
+begin
+ if Length(Args) > 2 then
+ begin
+ if Args[0].ResBoolean then
+ Result := Args[1]
+ else
+ Result := Args[2];
+ end else
+ begin
+ if Args[0].ResBoolean then
+ Result := Args[1]
+ else
+ Result.ResBoolean := false;
+ end;
+end;
+
+procedure fpsNOT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// NOT( condition )
+begin
+ Result.ResBoolean := not Args[0].ResBoolean;
+end;
+
+procedure fpsOR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// OR( condition1, [condition2], ... )
+// up to 30 parameters. At least 1 parameter.
+var
+ i: Integer;
+ b: Boolean;
+begin
+ b := false;
+ for i:=0 to High(Args) do
+ b := b or Args[i].ResBoolean;
+ Result.ResBoolean := b;
+end;
+
+procedure fpsTRUE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// TRUE()
+begin
+ Unused(Args);
+ Result.ResBoolean := true;
+end;
+
+
+{ Builtin statistical functions }
+
+procedure ArgsToFloatArray(const Args: TsExprParameterArray; out AData: TsExprFloatArray);
const
- ln10 = ln(10);
-
-procedure BuiltInLog(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResFloat := ln(ArgToFloat(Args[0]))/ln10;
-end;
-
-procedure BuiltInRound(var Result: TsExpressionResult; const Args: TExprParameterArray);
+ BLOCKSIZE = 128;
var
- decs: Integer;
- f: TExprFloat;
-begin (*
- decs := round(ArgToFloat(Args[1]));
- f := 1.0;
- while decs > 0 do begin
- f := f * 10;
- dec(decs);
- end; *)
- Result.ResInteger := round(ArgToFloat(Args[0]));
-end;
-
-procedure BuiltInTrunc(var Result: TsExpressionResult; const Args: TExprParameterArray);
+ i, n: Integer;
+ r, c: Cardinal;
+ cell: PCell;
+ arg: TsExpressionResult;
begin
- Result.ResInteger := trunc(ArgToFloat(Args[0]));
-end;
-
-procedure BuiltInInt(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResFloat := int(ArgToFloat(Args[0]));
-end;
-
-procedure BuiltInFrac(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResFloat := frac(ArgToFloat(Args[0]));
+ SetLength(AData, BLOCKSIZE);
+ n := 0;
+ for i:=0 to High(Args) do
+ begin
+ arg := Args[i];
+ if arg.ResultType = rtCellRange then
+ for r := arg.ResCellRange.Row1 to arg.ResCellRange.Row2 do
+ for c := arg.ResCellRange.Col1 to arg.ResCellRange.Col2 do
+ begin
+ cell := arg.Worksheet.FindCell(r, c);
+ if (cell <> nil) and (cell^.ContentType in [cctNumber, cctDateTime]) then
+ begin
+ case cell^.ContentType of
+ cctNumber : AData[n] := cell^.NumberValue;
+ cctDateTime : AData[n] := cell^.DateTimeValue
+ end;
+ inc(n);
+ if n = Length(AData) then SetLength(AData, length(AData) + BLOCKSIZE);
+ end;
+ end
+ else
+ if (arg.ResultType in [rtInteger, rtFloat, rtDateTime, rtCell]) then
+ begin
+ AData[n] := ArgToFloat(arg);
+ inc(n);
+ if n = Length(AData) then SetLength(AData, Length(AData) + BLOCKSIZE);
+ end;
+ end;
+ SetLength(AData, n);
end;
-// String builtins
-
-procedure BuiltInLength(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := Length(Args[0].ResString);
-end;
-
-procedure BuiltInCopy(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := copy(Args[0].ResString, Args[1].ResInteger, Args[2].ResInteger);
-end;
-
-procedure BuiltInDelete(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := Args[0].resString;
- Delete(Result.ResString, Args[1].ResInteger, Args[2].ResInteger);
-end;
-
-procedure BuiltInPos(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := pos(Args[0].ResString, Args[1].ResString);
-end;
-
-procedure BuiltInUppercase(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := Uppercase(Args[0].ResString);
-end;
-
-procedure BuiltInLowercase(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := Lowercase(Args[0].ResString);
-end;
-
-procedure BuiltInStringReplace(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsAVEDEV(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Average value of absolute deviations of data from their mean.
+// AVEDEV( value1, [value2, ... value_n] )
var
- flags : TReplaceFlags;
+ data: TsExprFloatArray;
+ m: TsExprFloat;
+ i: Integer;
begin
- flags := [];
- if Args[3].ResBoolean then
- Include(flags, rfReplaceAll);
- if Args[4].ResBoolean then
- Include(flags, rfIgnoreCase);
- Result.ResString := StringReplace(Args[0].ResString, Args[1].ResString, Args[2].ResString, flags);
+ ArgsToFloatArray(Args, data);
+ m := Mean(data);
+ for i:=0 to High(data) do // replace data by their average deviation from the mean
+ data[i] := abs(data[i] - m);
+ Result.ResFloat := Mean(data);
end;
-procedure BuiltInCompareText(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := CompareText(Args[0].ResString, Args[1].ResString);
-end;
-
-
-// Date/Time builtins
-
-procedure BuiltInDate(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := Date;
-end;
-
-procedure BuiltInTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := Time;
-end;
-
-procedure BuiltInNow(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := Now;
-end;
-
-procedure BuiltInDayOfWeek(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := DayOfWeek(Args[0].resDateTime);
-end;
-
-procedure BuiltInExtractYear(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsAVERAGE(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// AVERAGE( value1, [value2, ... value_n] )
var
- Y, M, D: Word;
+ data: TsExprFloatArray;
begin
- DecodeDate(Args[0].ResDateTime, Y, M, D);
- Result.ResInteger := Y;
+ ArgsToFloatArray(Args, data);
+ Result.ResFloat := Mean(data);
end;
-procedure BuiltInExtractMonth(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsCOUNT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+{ counts the number of cells that contain numbers as well as the number of
+ arguments that contain numbers.
+ COUNT( value1, [value2, ... value_n] ) }
var
- Y, M, D: Word;
+ data: TsExprFloatArray;
begin
- DecodeDate(Args[0].ResDateTime, Y, M, D);
- Result.ResInteger := M;
+ ArgsToFloatArray(Args, data);
+ Result.ResInteger := Length(data);
end;
-procedure BuiltInExtractDay(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsCOUNTA(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Counts the number of cells that are not empty as well as the number of
+// arguments that contain values
+// COUNTA( value1, [value2, ... value_n] )
var
- Y, M, D: Word;
+ i, n: Integer;
+ r, c: Cardinal;
+ cell: PCell;
+ arg: TsExpressionResult;
begin
- DecodeDate(Args[0].ResDateTime, Y, M, D);
- Result.ResInteger := D;
+ n := 0;
+ for i:=0 to High(Args) do
+ begin
+ arg := Args[i];
+ case arg.ResultType of
+ rtInteger, rtFloat, rtDateTime, rtBoolean:
+ inc(n);
+ rtString:
+ if arg.ResString <> '' then inc(n);
+ rtError:
+ if arg.ResError <> errOK then inc(n);
+ rtCell:
+ begin
+ cell := ArgToCell(arg);
+ if cell <> nil then
+ case cell^.ContentType of
+ cctNumber, cctDateTime, cctBool: inc(n);
+ cctUTF8String: if cell^.UTF8StringValue <> '' then inc(n);
+ cctError: if cell^.ErrorValue <> errOK then inc(n);
+ end;
+ end;
+ rtCellRange:
+ for r := arg.ResCellRange.Row1 to arg.ResCellRange.Row2 do
+ for c := arg.ResCellRange.Col1 to arg.ResCellRange.Col2 do
+ begin
+ cell := arg.Worksheet.FindCell(r, c);
+ if (cell <> nil) then
+ case cell^.ContentType of
+ cctNumber, cctDateTime, cctBool : inc(n);
+ cctUTF8String: if cell^.UTF8StringValue <> '' then inc(n);
+ cctError: if cell^.ErrorValue <> errOK then inc(n);
+ end;
+ end;
+ end;
+ end;
+ Result.ResInteger := n;
end;
-procedure BuiltInExtractHour(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsCOUNTBLANK(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+{ Counts the number of empty cells in a range.
+ COUNTBLANK( range )
+ "range" is the range of cells to count empty cells. }
var
- H, M, S, MS: Word;
+ n: Integer;
+ r, c: Cardinal;
+ cell: PCell;
+ arg: TsExpressionResult;
begin
- DecodeTime(Args[0].ResDateTime, H, M, S, MS);
- Result.ResInteger := H;
+ n := 0;
+ case Args[0].ResultType of
+ rtEmpty:
+ inc(n);
+ rtCell:
+ begin
+ cell := ArgToCell(Args[0]);
+ if cell = nil then
+ inc(n)
+ else
+ case cell^.ContentType of
+ cctNumber, cctDateTime, cctBool: ;
+ cctUTF8String: if cell^.UTF8StringValue = '' then inc(n);
+ cctError: if cell^.ErrorValue = errOK then inc(n);
+ end;
+ end;
+ rtCellRange:
+ for r := Args[0].ResCellRange.Row1 to Args[0].ResCellRange.Row2 do
+ for c := Args[0].ResCellRange.Col1 to Args[0].ResCellRange.Col2 do begin
+ cell := Args[0].Worksheet.FindCell(r, c);
+ if cell = nil then
+ inc(n)
+ else
+ case cell^.ContentType of
+ cctNumber, cctDateTime, cctBool: ;
+ cctUTF8String: if cell^.UTF8StringValue = '' then inc(n);
+ cctError: if cell^.ErrorValue = errOK then inc(n);
+ end;
+ end;
+ end;
+ Result.ResInteger := n;
end;
-procedure BuiltInExtractMin(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsMAX(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// MAX( value1, [value2, ... value_n] )
var
- H, M, S, MS: word;
+ data: TsExprFloatArray;
begin
- DecodeTime(Args[0].ResDateTime, H, M, S, MS);
- Result.ResInteger := M;
+ ArgsToFloatArray(Args, data);
+ Result.ResFloat := MaxValue(data);
end;
-procedure BuiltInExtractSec(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsMIN(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// MIN( value1, [value2, ... value_n] )
var
- H, M, S, MS: Word;
+ data: TsExprFloatArray;
begin
- DecodeTime(Args[0].ResDateTime, H, M, S, MS);
- Result.ResInteger := S;
+ ArgsToFloatArray(Args, data);
+ Result.ResFloat := MinValue(data);
end;
-procedure BuiltInExtractMSec(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsPRODUCT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// PRODUCT( value1, [value2, ... value_n] )
var
- H, M, S, MS: Word;
+ data: TsExprFloatArray;
+ i: Integer;
+ p: TsExprFloat;
begin
- DecodeTime(Args[0].ResDateTime, H, M, S, MS);
- Result.ResInteger := MS;
+ ArgsToFloatArray(Args, data);
+ p := 1.0;
+ for i := 0 to High(data) do
+ p := p * data[i];
+ Result.ResFloat := p;
end;
-procedure BuiltInEncodeDate(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsSTDEV(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the standard deviation of a population based on a sample of numbers
+// of numbers.
+// STDEV( value1, [value2, ... value_n] )
+var
+ data: TsExprFloatArray;
begin
- Result.ResDateTime := Encodedate(Args[0].ResInteger, Args[1].ResInteger, Args[2].ResInteger);
-end;
-
-procedure BuiltInEncodeTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := EncodeTime(Args[0].ResInteger, Args[1].ResInteger, Args[2].ResInteger, Args[3].ResInteger);
-end;
-
-procedure BuiltInEncodeDateTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := EncodeDate(Args[0].ResInteger, Args[1].ResInteger, Args[2].ResInteger)
- + EncodeTime(Args[3].ResInteger, Args[4].ResInteger, Args[5].ResInteger, Args[6].ResInteger);
-end;
-
-procedure BuiltInShortDayName(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := ShortDayNames[Args[0].ResInteger];
-end;
-
-procedure BuiltInShortMonthName(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := ShortMonthNames[Args[0].ResInteger];
-end;
-
-Procedure BuiltInLongDayName(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := LongDayNames[Args[0].ResInteger];
-end;
-
-procedure BuiltInLongMonthName(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := LongMonthNames[Args[0].ResInteger];
-end;
-
-procedure BuiltInFormatDateTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := FormatDateTime(Args[0].ResString, Args[1].ResDateTime);
-end;
-
-
-// Conversion
-procedure BuiltInIntToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := IntToStr(Args[0].Resinteger);
-end;
-
-procedure BuiltInStrToInt(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := StrToInt(Args[0].ResString);
-end;
-
-procedure BuiltInStrToIntDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := StrToIntDef(Args[0].ResString, Args[1].ResInteger);
-end;
-
-procedure BuiltInFloatToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := FloatToStr(Args[0].ResFloat);
-end;
-
-procedure BuiltInStrToFloat(var Result: TsExpressionResult; Const Args: TExprParameterArray);
-begin
- Result.ResFloat := StrToFloat(Args[0].ResString);
-end;
-
-procedure BuiltInStrToFloatDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResFloat := StrToFloatDef(Args[0].ResString, Args[1].ResFloat);
-end;
-
-procedure BuiltInDateToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := DateToStr(Args[0].ResDateTime);
-end;
-
-procedure BuiltInTimeToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := TimeToStr(Args[0].ResDateTime);
-end;
-
-procedure BuiltInStrToDate(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := StrToDate(Args[0].ResString);
-end;
-
-procedure BuiltInStrToDateDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := StrToDateDef(Args[0].ResString, Args[1].ResDateTime);
-end;
-
-procedure BuiltInStrToTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := StrToTime(Args[0].ResString);
-end;
-
-procedure BuiltInStrToTimeDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := StrToTimeDef(Args[0].ResString, Args[1].ResDateTime);
-end;
-
-procedure BuiltInStrToDateTime(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := StrToDateTime(Args[0].ResString);
-end;
-
-procedure BuiltInStrToDateTimeDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResDateTime := StrToDateTimeDef(Args[0].ResString, Args[1].ResDateTime);
-end;
-
-procedure BuiltInBoolToStr(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResString := BoolToStr(Args[0].ResBoolean);
-end;
-
-procedure BuiltInStrToBool(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResBoolean := StrToBool(Args[0].ResString);
-end;
-
-procedure BuiltInStrToBoolDef(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResBoolean := StrToBoolDef(Args[0].ResString, Args[1].ResBoolean);
-end;
-
-
-// Boolean
-
-procedure BuiltInShl(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := Args[0].ResInteger shl Args[1].ResInteger
-end;
-
-procedure BuiltInShr(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- Result.ResInteger := Args[0].ResInteger shr Args[1].ResInteger
-end;
-
-procedure BuiltinIFS(var Result: TsExpressionResult; const Args: TExprParameterArray);
-begin
- if Args[0].ResBoolean then
- Result.ResString := Args[1].ResString
+ ArgsToFloatArray(Args, data);
+ if Length(data) > 1 then
+ Result.ResFloat := StdDev(data)
else
- Result.ResString := Args[2].ResString
+ begin
+ Result.ResultType := rtError;
+ Result.ResError := errDivideByZero;
+ end;
end;
-procedure BuiltinIFI(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsSTDEVP(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the standard deviation of a population based on an entire population
+// STDEVP( value1, [value2, ... value_n] )
+var
+ data: TsExprFloatArray;
begin
- if Args[0].ResBoolean then
- Result.ResInteger := Args[1].ResInteger
+ ArgsToFloatArray(Args, data);
+ if Length(data) > 0 then
+ Result.ResFloat := PopnStdDev(data)
else
- Result.ResInteger := Args[2].ResInteger
+ begin
+ Result.ResultType := rtError;
+ Result.ResError := errDivideByZero;
+ end;
end;
-procedure BuiltinIFF(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsSUM(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// SUM( value1, [value2, ... value_n] )
+var
+ data: TsExprFloatArray;
begin
- if Args[0].ResBoolean then
- Result.ResFloat := Args[1].ResFloat
- else
- Result.ResFloat := Args[2].ResFloat
+ ArgsToFloatArray(Args, data);
+ Result.ResFloat := Sum(data);
end;
-procedure BuiltinIFD(var Result: TsExpressionResult; const Args: TExprParameterArray);
+procedure fpsSUMSQ(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the sum of the squares of a series of values.
+// SUMSQ( value1, [value2, ... value_n] )
+var
+ data: TsExprFloatArray;
begin
- if Args[0].ResBoolean then
- Result.ResDateTime := Args[1].ResDateTime
- else
- Result.ResDateTime := Args[2].ResDateTime
+ ArgsToFloatArray(Args, data);
+ Result.ResFloat := SumOfSquares(data);
end;
+procedure fpsVAR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the variance of a population based on a sample of numbers.
+// VAR( value1, [value2, ... value_n] )
+var
+ data: TsExprFloatArray;
+begin
+ ArgsToFloatArray(Args, data);
+ if Length(data) > 1 then
+ Result.ResFloat := Variance(data)
+ else
+ begin
+ Result.ResultType := rtError;
+ Result.ResError := errDivideByZero;
+ end;
+end;
+
+procedure fpsVARP(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// Returns the variance of a population based on an entire population of numbers.
+// VARP( value1, [value2, ... value_n] )
+var
+ data: TsExprFloatArray;
+begin
+ ArgsToFloatArray(Args, data);
+ if Length(data) > 0 then
+ Result.ResFloat := PopnVariance(data)
+ else
+ begin
+ Result.ResultType := rtError;
+ Result.ResError := errDivideByZero;
+ end;
+end;
+
+
+{ Builtin info functions }
+
+{ !!!!!!!!!!!!!! not working !!!!!!!!!!!!!!!!!!!!!! }
+{ !!!!!!!!!!!!!! needs localized strings !!!!!!!!!!! }
+
+procedure fpsCELL(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// CELL( type, [range] )
+
+{ from http://www.techonthenet.com/excel/formulas/cell.php:
+
+ "type" is the type of information that we retrieve for the cell and can have
+ one of the following values:
+ Value Explanation
+ ------------- --------------------------------------------------------------
+ "address" Address of the cell. If the cell refers to a range, it is the
+ first cell in the range.
+ "col" Column number of the cell.
+ "color" Returns 1 if the color is a negative value; Otherwise it returns 0.
+ "contents" Contents of the upper-left cell.
+ "filename" Filename of the file that contains reference.
+ "format" Number format of the cell according to:
+ "G" General
+ "F0" 0
+ ",0" #,##0
+ "F2" 0.00
+ ",2" #,##0.00
+ "C0" $#,##0_);($#,##0)
+ "C0-" $#,##0_);[Red]($#,##0)
+ "C2" $#,##0.00_);($#,##0.00)
+ "C2-" $#,##0.00_);[Red]($#,##0.00)
+ "P0" 0%
+ "P2" 0.00%
+ "S2" 0.00E+00
+ "G" # ?/? or # ??/??
+ "D4" m/d/yy or m/d/yy h:mm or mm/dd/yy
+ "D1" d-mmm-yy or dd-mmm-yy
+ "D2" d-mmm or dd-mmm
+ "D3" mmm-yy
+ "D5" mm/dd
+ "D6" h:mm:ss AM/PM
+ "D7" h:mm AM/PM
+ "D8" h:mm:ss
+ "D9" h:mm
+ "parentheses" Returns 1 if the cell is formatted with parentheses;
+ Otherwise, it returns 0.
+ "prefix" Label prefix for the cell.
+ - Returns a single quote (') if the cell is left-aligned.
+ - Returns a double quote (") if the cell is right-aligned.
+ - Returns a caret (^) if the cell is center-aligned.
+ - Returns a back slash (\) if the cell is fill-aligned.
+ - Returns an empty text value for all others.
+ "protect" Returns 1 if the cell is locked. Returns 0 if the cell is not locked.
+ "row" Row number of the cell.
+ "type" Returns "b" if the cell is empty.
+ Returns "l" if the cell contains a text constant.
+ Returns "v" for all others.
+ "width" Column width of the cell, rounded to the nearest integer.
+
+ !!!! NOT ALL OF THEM ARE SUPPORTED HERE !!!
+
+ "range" is optional in Excel. It is the cell (or range) that you wish to retrieve
+ information for. If the range parameter is omitted, the CELL function will
+ assume that you are retrieving information for the last cell that was changed.
+
+ "range" is NOT OPTIONAL here because we don't know the last cell changed !!!
+}
+var
+ stype: String;
+ r1,r2, c1,c2: Cardinal;
+ cell: PCell;
+ res: TsExpressionResult;
+begin
+ if Length(Args)=1 then
+ begin
+ // This case is not supported by us, but it is by Excel.
+ // Therefore the error is not quite correct...
+ Result := ErrorResult(errIllegalRef);
+ exit;
+ end;
+
+ stype := lowercase(ArgToString(Args[0]));
+
+ case Args[1].ResultType of
+ rtCell:
+ begin
+ cell := ArgToCell(Args[1]);
+ r1 := Args[1].ResRow;
+ c1 := Args[1].ResCol;
+ r2 := r1;
+ c2 := c1;
+ end;
+ rtCellRange:
+ begin
+ r1 := Args[1].ResCellRange.Row1;
+ r2 := Args[1].ResCellRange.Row2;
+ c1 := Args[1].ResCellRange.Col1;
+ c2 := Args[1].ResCellRange.Col2;
+ cell := Args[1].Worksheet.FindCell(r1, c1);
+ end;
+ else
+ Result := ErrorResult(errWrongType);
+ exit;
+ end;
+
+ if stype = 'address' then
+ Result := StringResult(GetCellString(r1, c1, []))
+ else
+ if stype = 'col' then
+ Result := IntegerResult(c1+1)
+ else
+ if stype = 'color' then
+ begin
+ if (cell <> nil) and (cell^.NumberFormat = nfCurrencyRed) then
+ Result := IntegerResult(1)
+ else
+ Result := IntegerResult(0);
+ end else
+ if stype = 'contents' then
+ begin
+ if cell = nil then
+ Result := IntegerResult(0)
+ else
+ case cell^.ContentType of
+ cctNumber : if frac(cell^.NumberValue) = 0 then
+ Result := IntegerResult(trunc(cell^.NumberValue))
+ else
+ Result := FloatResult(cell^.NumberValue);
+ cctDateTime : Result := DateTimeResult(cell^.DateTimeValue);
+ cctUTF8String : Result := StringResult(cell^.UTF8StringValue);
+ cctBool : Result := BooleanResult(cell^.BoolValue);
+ cctError : Result := ErrorResult(cell^.ErrorValue);
+ end;
+ end else
+ if stype = 'filename' then
+ Result := Stringresult(
+ ExtractFilePath(Args[1].Worksheet.Workbook.FileName) + '[' +
+ ExtractFileName(Args[1].Worksheet.Workbook.FileName) + ']' +
+ Args[1].Worksheet.Name
+ )
+ else
+ if stype = 'format' then begin
+ Result := StringResult('G');
+ if cell <> nil then
+ case cell^.NumberFormat of
+ nfGeneral:
+ Result := StringResult('G');
+ nfFixed:
+ if cell^.NumberFormatStr= '0' then Result := StringResult('0') else
+ if cell^.NumberFormatStr = '0.00' then Result := StringResult('F0');
+ nfFixedTh:
+ if cell^.NumberFormatStr = '#,##0' then Result := StringResult(',0') else
+ if cell^.NumberFormatStr = '#,##0.00' then Result := StringResult(',2');
+ nfPercentage:
+ if cell^.NumberFormatStr = '0%' then Result := StringResult('P0') else
+ if cell^.NumberFormatStr = '0.00%' then Result := StringResult('P2');
+ nfExp:
+ if cell^.NumberFormatStr = '0.00E+00' then Result := StringResult('S2');
+ nfShortDate, nfLongDate, nfShortDateTime:
+ Result := StringResult('D4');
+ nfLongTimeAM:
+ Result := StringResult('D6');
+ nfShortTimeAM:
+ Result := StringResult('D7');
+ nfLongTime:
+ Result := StringResult('D8');
+ nfShortTime:
+ Result := StringResult('D9');
+ end;
+ end else
+ if stype = 'prefix' then
+ begin
+ Result := StringResult('');
+ if (cell^.ContentType = cctUTF8String) then
+ case cell^.HorAlignment of
+ haLeft : Result := StringResult('''');
+ haCenter: Result := StringResult('^');
+ haRight : Result := StringResult('"');
+ end;
+ end else
+ if stype = 'row' then
+ Result := IntegerResult(r1+1)
+ else
+ if stype = 'type' then begin
+ if (cell = nil) or (cell^.ContentType = cctEmpty) then
+ Result := StringResult('b')
+ else if cell^.ContentType = cctUTF8String then begin
+ if (cell^.UTF8StringValue = '')
+ then Result := StringResult('b')
+ else Result := StringResult('l');
+ end else
+ Result := StringResult('v');
+ end else
+ if stype = 'width' then
+ Result := FloatResult(Args[1].Worksheet.GetColWidth(c1))
+ else
+ Result := ErrorResult(errWrongType);
+end;
+
+procedure fpsISBLANK(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISBLANK( value )
+// Checks for blank or null values.
+// "value" is the value that you want to test.
+// If "value" is blank, this function will return TRUE.
+// If "value" is not blank, the function will return FALSE.
+var
+ cell: PCell;
+begin
+ case Args[0].ResultType of
+ rtEmpty : Result := BooleanResult(true);
+ rtString: Result := BooleanResult(Result.ResString = '');
+ rtCell : begin
+ cell := ArgToCell(Args[0]);
+ if (cell = nil) or (cell^.ContentType = cctEmpty) then
+ Result := BooleanResult(true)
+ else
+ Result := BooleanResult(false);
+ end;
+ end;
+end;
+
+procedure fpsISERR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISERR( value )
+// If "value" is an error value (except #N/A), this function will return TRUE.
+// Otherwise, it will return FALSE.
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell <> nil) and (cell^.ContentType = cctError) and (cell^.ErrorValue <> errArgError)
+ then Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType = rtError) and (Args[0].ResError <> errArgError) then
+ Result := BooleanResult(true);
+end;
+
+procedure fpsISERROR(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISERROR( value )
+// If "value" is an error value (#N/A, #VALUE!, #REF!, #DIV/0!, #NUM!, #NAME?
+// or #NULL), this function will return TRUE. Otherwise, it will return FALSE.
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell <> nil) and (cell^.ContentType = cctError) and (cell^.ErrorValue <= errArgError)
+ then Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType = rtError) then
+ Result := BooleanResult(true);
+end;
+
+procedure fpsISLOGICAL(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISLOGICAL( value )
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell <> nil) and (cell^.ContentType = cctBool) then
+ Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType = rtBoolean) then
+ Result := BooleanResult(true);
+end;
+
+procedure fpsISNA(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISNA( value )
+// If "value" is a #N/A error value , this function will return TRUE.
+// Otherwise, it will return FALSE.
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell <> nil) and (cell^.ContentType = cctError) and (cell^.ErrorValue = errArgError)
+ then Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType = rtError) and (Args[0].ResError = errArgError) then
+ Result := BooleanResult(true);
+end;
+
+procedure fpsISNONTEXT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISNONTEXT( value )
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell = nil) or ((cell <> nil) and (cell^.ContentType <> cctUTF8String)) then
+ Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType <> rtString) then
+ Result := BooleanResult(true);
+end;
+
+procedure fpsISNUMBER(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISNUMBER( value )
+// Tests "value" for a number (or date/time - checked with Excel).
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell <> nil) and (cell^.ContentType in [cctNumber, cctDateTime]) then
+ Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType in [rtFloat, rtInteger, rtDateTime]) then
+ Result := BooleanResult(true);
+end;
+
+procedure fpsISREF(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISREF( value )
+begin
+ Result := BooleanResult(Args[0].ResultType in [rtCell, rtCellRange]);
+end;
+
+procedure fpsISTEXT(var Result: TsExpressionResult; const Args: TsExprParameterArray);
+// ISTEXT( value )
+var
+ cell: PCell;
+begin
+ Result := BooleanResult(false);
+ if (Args[0].ResultType = rtCell) then
+ begin
+ cell := ArgToCell(Args[0]);
+ if (cell <> nil) and (cell^.ContentType = cctUTF8String) then
+ Result := BooleanResult(true);
+ end else
+ if (Args[0].ResultType = rtString) then
+ Result := BooleanResult(true);
+end;
+
+
+{------------------------------------------------------------------------------}
+{@@
+ Registers a non-built-in function:
+
+ @param AName Name of the function as used for calling it in the spreadsheet
+ @param AResultType A character classifying the data type of the function result:
+ 'I' integer
+ 'F' floating point number
+ 'D' date/time value
+ 'S' string
+ 'B' boolean value (TRUE/FALSE)
+ 'R' cell range, can also be used for functions requiring
+ a cell "reference", like "CELL(..)"
+ @param AParamTypes A string with result type symbols for each parameter of the
+ function. Symbols as used for "ResultType" with these
+ additions:
+ - Use a lower-case character if a parameter is optional.
+ (must be at the end of the string)
+ - Add "+" if the last parameter type is valid for a variable
+ parameter count (Excel does pose a limit of 30, though).
+ - Use "?" if the data type should not be checked.
+ @param AExcelCode ID of the function needed in the xls biff file. Please see
+ the "OpenOffice Documentation of Microsoft Excel File Format"
+ section 3.11.
+ @param ACallBack Address of the procedure called when the formula is
+ calculated.
+}
+{------------------------------------------------------------------------------}
+procedure RegisterFunction(const AName: ShortString; const AResultType: Char;
+ const AParamTypes: String; const AExcelCode: Integer; ACallback: TsExprFunctionCallBack);
+begin
+ with BuiltinIdentifiers do
+ AddFunction(bcUser, AName, AResultType, AParamTypes, AExcelCode, ACallBack);
+end;
+
+{@@
+ Registers the built-in functions. Called automatically.
+}
procedure RegisterStdBuiltins(AManager : TsBuiltInExpressionManager);
+var
+ cat: TsBuiltInExprCategory;
begin
with AManager do
begin
- AddFloatVariable(bcMath, 'pi', Pi);
// Math functions
- AddFunction(bcMath, 'cos', 'F', 'F', @BuiltinCos);
- AddFunction(bcMath, 'sin', 'F', 'F', @BuiltinSin);
- AddFunction(bcMath, 'arctan', 'F', 'F', @BuiltinArctan);
- AddFunction(bcMath, 'abs', 'F', 'F', @BuiltinAbs);
- AddFunction(bcMath, 'sqr', 'F', 'F', @BuiltinSqr);
- AddFunction(bcMath, 'sqrt', 'F', 'F', @BuiltinSqrt);
- AddFunction(bcMath, 'exp', 'F', 'F', @BuiltinExp);
- AddFunction(bcMath, 'ln', 'F', 'F', @BuiltinLn);
- AddFunction(bcMath, 'log', 'F', 'F', @BuiltinLog);
- AddFunction(bcMath, 'frac', 'F', 'F', @BuiltinFrac);
- AddFunction(bcMath, 'int', 'F', 'F', @BuiltinInt);
- AddFunction(bcMath, 'round', 'I', 'F', @BuiltinRound);
- AddFunction(bcMath, 'trunc', 'I', 'F', @BuiltinTrunc);
- // String
- AddFunction(bcStrings, 'length', 'I', 'S', @BuiltinLength);
- AddFunction(bcStrings, 'copy', 'S', 'SII', @BuiltinCopy);
- AddFunction(bcStrings, 'delete', 'S', 'SII', @BuiltinDelete);
- AddFunction(bcStrings, 'pos', 'I', 'SS', @BuiltinPos);
- AddFunction(bcStrings, 'lowercase', 'S', 'S', @BuiltinLowercase);
- AddFunction(bcStrings, 'uppercase', 'S', 'S', @BuiltinUppercase);
- AddFunction(bcStrings, 'stringreplace','S', 'SSSBB',@BuiltinStringReplace);
- AddFunction(bcStrings, 'comparetext', 'I', 'SS', @BuiltinCompareText);
- // Date/Time
- AddFunction(bcDateTime, 'date', 'D', '', @BuiltinDate);
- AddFunction(bcDateTime, 'time', 'D', '', @BuiltinTime);
- AddFunction(bcDateTime, 'now', 'D', '', @BuiltinNow);
- AddFunction(bcDateTime, 'dayofweek', 'I', 'D', @BuiltinDayofweek);
- AddFunction(bcDateTime, 'extractyear', 'I', 'D', @BuiltinExtractYear);
- AddFunction(bcDateTime, 'extractmonth', 'I', 'D', @BuiltinExtractMonth);
- AddFunction(bcDateTime, 'extractday', 'I', 'D', @BuiltinExtractDay);
- AddFunction(bcDateTime, 'extracthour', 'I', 'D', @BuiltinExtractHour);
- AddFunction(bcDateTime, 'extractmin', 'I', 'D', @BuiltinExtractMin);
- AddFunction(bcDateTime, 'extractsec', 'I', 'D', @BuiltinExtractSec);
- AddFunction(bcDateTime, 'extractmsec', 'I', 'D', @BuiltinExtractMSec);
- AddFunction(bcDateTime, 'encodedate', 'D', 'III', @BuiltinEncodedate);
- AddFunction(bcDateTime, 'encodetime', 'D', 'IIII',@BuiltinEncodeTime);
- AddFunction(bcDateTime, 'encodedatetime', 'D', 'IIIIIII',@BuiltinEncodeDateTime);
- AddFunction(bcDateTime, 'shortdayname', 'S', 'I', @BuiltinShortDayName);
- AddFunction(bcDateTime, 'shortmonthname', 'S', 'I', @BuiltinShortMonthName);
- AddFunction(bcDateTime, 'longdayname', 'S', 'I', @BuiltinLongDayName);
- AddFunction(bcDateTime, 'longmonthname', 'S', 'I', @BuiltinLongMonthName);
- AddFunction(bcDateTime, 'formatdatetime', 'S', 'SD', @BuiltinFormatDateTime);
- // Boolean
- AddFunction(bcBoolean, 'shl', 'I', 'II', @BuiltinShl);
- AddFunction(bcBoolean, 'shr', 'I', 'II', @BuiltinShr);
- AddFunction(bcBoolean, 'IFS', 'S', 'BSS', @BuiltinIFS);
- AddFunction(bcBoolean, 'IFF', 'F', 'BFF', @BuiltinIFF);
- AddFunction(bcBoolean, 'IFD', 'D', 'BDD', @BuiltinIFD);
- AddFunction(bcBoolean, 'IFI', 'I', 'BII', @BuiltinIFI);
- // Conversion
- AddFunction(bcConversion, 'inttostr', 'S', 'I', @BuiltInIntToStr);
- AddFunction(bcConversion, 'strtoint', 'I', 'S', @BuiltInStrToInt);
- AddFunction(bcConversion, 'strtointdef', 'I', 'SI', @BuiltInStrToIntDef);
- AddFunction(bcConversion, 'floattostr', 'S', 'F', @BuiltInFloatToStr);
- AddFunction(bcConversion, 'strtofloat', 'F', 'S', @BuiltInStrToFloat);
- AddFunction(bcConversion, 'strtofloatdef', 'F', 'SF', @BuiltInStrToFloatDef);
- AddFunction(bcConversion, 'booltostr', 'S', 'B', @BuiltInBoolToStr);
- AddFunction(bcConversion, 'strtobool', 'B', 'S', @BuiltInStrToBool);
- AddFunction(bcConversion, 'strtobooldef', 'B', 'SB', @BuiltInStrToBoolDef);
- AddFunction(bcConversion, 'datetostr', 'S', 'D', @BuiltInDateToStr);
- AddFunction(bcConversion, 'timetostr', 'S', 'D', @BuiltInTimeToStr);
- AddFunction(bcConversion, 'strtodate', 'D', 'S', @BuiltInStrToDate);
- AddFunction(bcConversion, 'strtodatedef', 'D', 'SD', @BuiltInStrToDateDef);
- AddFunction(bcConversion, 'strtotime', 'D', 'S', @BuiltInStrToTime);
- AddFunction(bcConversion, 'strtotimedef', 'D', 'SD', @BuiltInStrToTimeDef);
- AddFunction(bcConversion, 'strtodatetime', 'D', 'S', @BuiltInStrToDateTime);
- AddFunction(bcConversion, 'strtodatetimedef', 'D', 'SD', @BuiltInStrToDateTimeDef);
+ cat := bcMath;
+ AddFunction(cat, 'ABS', 'F', 'F', INT_EXCEL_SHEET_FUNC_ABS, @fpsABS);
+ AddFunction(cat, 'ACOS', 'F', 'F', INT_EXCEL_SHEET_FUNC_ACOS, @fpsACOS);
+ AddFunction(cat, 'ACOSH', 'F', 'F', INT_EXCEL_SHEET_FUNC_ACOSH, @fpsACOSH);
+ AddFunction(cat, 'ASIN', 'F', 'F', INT_EXCEL_SHEET_FUNC_ASIN, @fpsASIN);
+ AddFunction(cat, 'ASINH', 'F', 'F', INT_EXCEL_SHEET_FUNC_ASINH, @fpsASINH);
+ AddFunction(cat, 'ATAN', 'F', 'F', INT_EXCEL_SHEET_FUNC_ATAN, @fpsATAN);
+ AddFunction(cat, 'ATANH', 'F', 'F', INT_EXCEL_SHEET_FUNC_ATANH, @fpsATANH);
+ AddFunction(cat, 'COS', 'F', 'F', INT_EXCEL_SHEET_FUNC_COS, @fpsCOS);
+ AddFunction(cat, 'COSH', 'F', 'F', INT_EXCEL_SHEET_FUNC_COSH, @fpsCOSH);
+ AddFunction(cat, 'DEGREES', 'F', 'F', INT_EXCEL_SHEET_FUNC_DEGREES, @fpsDEGREES);
+ AddFunction(cat, 'EXP', 'F', 'F', INT_EXCEL_SHEET_FUNC_EXP, @fpsEXP);
+ AddFunction(cat, 'INT', 'I', 'F', INT_EXCEL_SHEET_FUNC_INT, @fpsINT);
+ AddFunction(cat, 'LN', 'F', 'F', INT_EXCEL_SHEET_FUNC_LN, @fpsLN);
+ AddFunction(cat, 'LOG', 'F', 'Ff', INT_EXCEL_SHEET_FUNC_LOG, @fpsLOG);
+ AddFunction(cat, 'LOG10', 'F', 'F', INT_EXCEL_SHEET_FUNC_LOG10, @fpsLOG10);
+ AddFunction(cat, 'PI', 'F', '', INT_EXCEL_SHEET_FUNC_PI, @fpsPI);
+ AddFunction(cat, 'POWER', 'F', 'FF', INT_EXCEL_SHEET_FUNC_POWER, @fpsPOWER);
+ AddFunction(cat, 'RADIANS', 'F', 'F', INT_EXCEL_SHEET_FUNC_RADIANS, @fpsRADIANS);
+ AddFunction(cat, 'RAND', 'F', '', INT_EXCEL_SHEET_FUNC_RAND, @fpsRAND);
+ AddFunction(cat, 'ROUND', 'F', 'FF', INT_EXCEL_SHEET_FUNC_ROUND, @fpsROUND);
+ AddFunction(cat, 'SIGN', 'F', 'F', INT_EXCEL_SHEET_FUNC_SIGN, @fpsSIGN);
+ AddFunction(cat, 'SIN', 'F', 'F', INT_EXCEL_SHEET_FUNC_SIN, @fpsSIN);
+ AddFunction(cat, 'SINH', 'F', 'F', INT_EXCEL_SHEET_FUNC_SINH, @fpsSINH);
+ AddFunction(cat, 'SQRT', 'F', 'F', INT_EXCEL_SHEET_FUNC_SQRT, @fpsSQRT);
+ AddFunction(cat, 'TAN', 'F', 'F', INT_EXCEL_SHEET_FUNC_TAN, @fpsTAN);
+ AddFunction(cat, 'TANH', 'F', 'F', INT_EXCEL_SHEET_FUNC_TANH, @fpsTANH);
+
+ // Date/time
+ cat := bcDateTime;
+ AddFunction(cat, 'DATE', 'D', 'III', INT_EXCEL_SHEET_FUNC_DATE, @fpsDATE);
+ AddFunction(cat, 'DATEDIF', 'F', 'DDS', INT_EXCEL_SHEET_FUNC_DATEDIF, @fpsDATEDIF);
+ AddFunction(cat, 'DATEVALUE', 'D', 'S', INT_EXCEL_SHEET_FUNC_DATEVALUE, @fpsDATEVALUE);
+ AddFunction(cat, 'DAY', 'I', '?', INT_EXCEL_SHEET_FUNC_DAY, @fpsDAY);
+ AddFunction(cat, 'HOUR', 'I', '?', INT_EXCEL_SHEET_FUNC_HOUR, @fpsHOUR);
+ AddFunction(cat, 'MINUTE', 'I', '?', INT_EXCEL_SHEET_FUNC_MINUTE, @fpsMINUTE);
+ AddFunction(cat, 'MONTH', 'I', '?', INT_EXCEL_SHEET_FUNC_MONTH, @fpsMONTH);
+ AddFunction(cat, 'NOW', 'D', '', INT_EXCEL_SHEET_FUNC_NOW, @fpsNOW);
+ AddFunction(cat, 'SECOND', 'I', '?', INT_EXCEL_SHEET_FUNC_SECOND, @fpsSECOND);
+ AddFunction(cat, 'TIME' , 'D', 'III', INT_EXCEL_SHEET_FUNC_TIME, @fpsTIME);
+ AddFunction(cat, 'TIMEVALUE', 'D', 'S', INT_EXCEL_SHEET_FUNC_TIMEVALUE, @fpsTIMEVALUE);
+ AddFunction(cat, 'TODAY', 'D', '', INT_EXCEL_SHEET_FUNC_TODAY, @fpsTODAY);
+ AddFunction(cat, 'WEEKDAY', 'I', '?i', INT_EXCEL_SHEET_FUNC_WEEKDAY, @fpsWEEKDAY);
+ AddFunction(cat, 'YEAR', 'I', '?', INT_EXCEL_SHEET_FUNC_YEAR, @fpsYEAR);
+
+ // Strings
+ cat := bcStrings;
+ AddFunction(cat, 'CHAR', 'S', 'I', INT_EXCEL_SHEET_FUNC_CHAR, @fpsCHAR);
+ AddFunction(cat, 'CODE', 'I', 'S', INT_EXCEL_SHEET_FUNC_CODE, @fpsCODE);
+ AddFunction(cat, 'CONCATENATE','S','S+', INT_EXCEL_SHEET_FUNC_CONCATENATE,@fpsCONCATENATE);
+ AddFunction(cat, 'LEFT', 'S', 'Si', INT_EXCEL_SHEET_FUNC_LEFT, @fpsLEFT);
+ AddFunction(cat, 'LEN', 'I', 'S', INT_EXCEL_SHEET_FUNC_LEN, @fpsLEN);
+ AddFunction(cat, 'LOWER', 'S', 'S', INT_EXCEL_SHEET_FUNC_LOWER, @fpsLOWER);
+ AddFunction(cat, 'MID', 'S', 'SII', INT_EXCEL_SHEET_FUNC_MID, @fpsMID);
+ AddFunction(cat, 'REPLACE', 'S', 'SIIS', INT_EXCEL_SHEET_FUNC_REPLACE, @fpsREPLACE);
+ AddFunction(cat, 'RIGHT', 'S', 'Si', INT_EXCEL_SHEET_FUNC_RIGHT, @fpsRIGHT);
+ AddFunction(cat, 'SUBSTITUTE','S', 'SSSi', INT_EXCEL_SHEET_FUNC_SUBSTITUTE, @fpsSUBSTITUTE);
+ AddFunction(cat, 'TRIM', 'S', 'S', INT_EXCEL_SHEET_FUNC_TRIM, @fpsTRIM);
+ AddFunction(cat, 'UPPER', 'S', 'S', INT_EXCEL_SHEET_FUNC_UPPER, @fpsUPPER);
+ AddFunction(cat, 'VALUE', 'F', 'S', INT_EXCEL_SHEET_FUNC_VALUE, @fpsVALUE);
+
+ // Logical
+ cat := bcLogical;
+ AddFunction(cat, 'AND', 'B', 'B+', INT_EXCEL_SHEET_FUNC_AND, @fpsAND);
+ AddFunction(cat, 'FALSE', 'B', '', INT_EXCEL_SHEET_FUNC_FALSE, @fpsFALSE);
+ AddFunction(cat, 'IF', 'B', 'B?+', INT_EXCEL_SHEET_FUNC_IF, @fpsIF);
+ AddFunction(cat, 'NOT', 'B', 'B', INT_EXCEL_SHEET_FUNC_NOT, @fpsNOT);
+ AddFunction(cat, 'OR', 'B', 'B+', INT_EXCEL_SHEET_FUNC_OR, @fpsOR);
+ AddFunction(cat, 'TRUE', 'B', '', INT_EXCEL_SHEET_FUNC_TRUE , @fpsTRUE);
+
+ // Statistical
+ cat := bcStatistics;
+ AddFunction(cat, 'AVEDEV', 'F', '?+', INT_EXCEL_SHEET_FUNC_AVEDEV, @fpsAVEDEV);
+ AddFunction(cat, 'AVERAGE', 'F', '?+', INT_EXCEL_SHEET_FUNC_AVERAGE, @fpsAVERAGE);
+ AddFunction(cat, 'COUNT', 'I', '?+', INT_EXCEL_SHEET_FUNC_COUNT, @fpsCOUNT);
+ AddFunction(cat, 'COUNTA', 'I', '?+', INT_EXCEL_SHEET_FUNC_COUNTA, @fpsCOUNTA);
+ AddFunction(cat, 'COUNTBLANK','I', 'R', INT_EXCEL_SHEET_FUNC_COUNTBLANK, @fpsCOUNTBLANK);
+ AddFunction(cat, 'MAX', 'F', '?+', INT_EXCEL_SHEET_FUNC_MAX, @fpsMAX);
+ AddFunction(cat, 'MIN', 'F', '?+', INT_EXCEL_SHEET_FUNC_MIN, @fpsMIN);
+ AddFunction(cat, 'PRODUCT', 'F', '?+', INT_EXCEL_SHEET_FUNC_PRODUCT, @fpsPRODUCT);
+ AddFunction(cat, 'STDEV', 'F', '?+', INT_EXCEL_SHEET_FUNC_STDEV, @fpsSTDEV);
+ AddFunction(cat, 'STDEVP', 'F', '?+', INT_EXCEL_SHEET_FUNC_STDEVP, @fpsSTDEVP);
+ AddFunction(cat, 'SUM', 'F', '?+', INT_EXCEL_SHEET_FUNC_SUM, @fpsSUM);
+ AddFunction(cat, 'SUMSQ', 'F', '?+', INT_EXCEL_SHEET_FUNC_SUMSQ, @fpsSUMSQ);
+ AddFunction(cat, 'VAR', 'F', '?+', INT_EXCEL_SHEET_FUNC_VAR, @fpsVAR);
+ AddFunction(cat, 'VARP', 'F', '?+', INT_EXCEL_SHEET_FUNC_VARP, @fpsVARP);
+ // to do: CountIF, SUMIF
+
+ // Info functions
+ cat := bcInfo;
+ AddFunction(cat, 'CELL', '?', 'Sr', INT_EXCEL_SHEET_FUNC_CELL, @fpsCELL);
+ AddFunction(cat, 'ISBLANK', 'B', '?', INT_EXCEL_SHEET_FUNC_ISBLANK, @fpsISBLANK);
+ AddFunction(cat, 'ISERR', 'B', '?', INT_EXCEL_SHEET_FUNC_ISERR, @fpsISERR);
+ AddFunction(cat, 'ISERROR', 'B', '?', INT_EXCEL_SHEET_FUNC_ISERROR, @fpsISERROR);
+ AddFunction(cat, 'ISLOGICAL', 'B', '?', INT_EXCEL_SHEET_FUNC_ISLOGICAL, @fpsISLOGICAL);
+ AddFunction(cat, 'ISNA', 'B', '?', INT_EXCEL_SHEET_FUNC_ISNA, @fpsISNA);
+ AddFunction(cat, 'ISNONTEXT', 'B', '?', INT_EXCEL_SHEET_FUNC_ISNONTEXT, @fpsISNONTEXT);
+ AddFunction(cat, 'ISNUMBER', 'B', '?', INT_EXCEL_SHEET_FUNC_ISNUMBER, @fpsISNUMBER);
+ AddFunction(cat, 'ISREF', 'B', '?', INT_EXCEL_SHEET_FUNC_ISREF, @fpsISREF);
+ AddFunction(cat, 'ISTEXT', 'B', '?', INT_EXCEL_SHEET_FUNC_ISTEXT, @fpsISTEXT);
+
+ (*
+ // Lookup / reference functions
+ cat := bcLookup;
+ AddFunction(cat, 'COLUMN', 'I', 'R', INT_EXCEL_SHEET_FUNC_COLUMN, @fpsCOLUMN);
+ *)
+
end;
end;
@@ -3653,10 +5492,14 @@ procedure TsBuiltInExprIdentifierDef.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TsBuiltInExprIdentifierDef then
- FCategory:=(Source as TsBuiltInExprIdentifierDef).Category;
+ FCategory := (Source as TsBuiltInExprIdentifierDef).Category;
end;
initialization
+ ExprFormatSettings := DefaultFormatSettings;
+ ExprFormatSettings.DecimalSeparator := '.';
+ ExprFormatSettings.ListSeparator := ',';
+
RegisterStdBuiltins(BuiltinIdentifiers);
finalization
diff --git a/components/fpspreadsheet/fpsfunc.pas b/components/fpspreadsheet/fpsfunc.pas
index 6c13300e2..85520af8d 100644
--- a/components/fpspreadsheet/fpsfunc.pas
+++ b/components/fpspreadsheet/fpsfunc.pas
@@ -2182,7 +2182,7 @@ begin
end;
-{ Lookup / refernence functions }
+{ Lookup / reference functions }
function fpsCOLUMN(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
{ COLUMN( [reference] )
diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas
index e7d4d1c19..a01541409 100755
--- a/components/fpspreadsheet/fpsopendocument.pas
+++ b/components/fpspreadsheet/fpsopendocument.pas
@@ -1272,7 +1272,7 @@ begin
// Read formula, store in the cell's FormulaValue.FormulaStr
formula := GetAttrValue(ACellNode, 'table:formula');
if formula <> '' then Delete(formula, 1, 3); // delete "of:"
- cell^.FormulaValue.FormulaStr := formula;
+ cell^.FormulaValue := formula;
// Read formula results
// ... number value
@@ -3590,7 +3590,7 @@ begin
AppendToStream(AStream, Format(
'' +
'', [
- ACell^.FormulaValue.FormulaStr, lStyle
+ ACell^.FormulaValue, lStyle
]));
end;
diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas
index 51f4545f3..eec901751 100755
--- a/components/fpspreadsheet/fpspreadsheet.pas
+++ b/components/fpspreadsheet/fpspreadsheet.pas
@@ -40,6 +40,8 @@ const
STR_WIKITABLE_PIPES = '.wikitable_pipes';
STR_WIKITABLE_WIKIMEDIA = '.wikitable_wikimedia';
+ MAX_COL_COUNT = 65535;
+
type
{@@ Possible encodings for a non-unicode encoded text }
@@ -52,7 +54,7 @@ type
seHebrew,
seArabic
);
-
+ (*
{@@ Describes a formula
Supported syntax:
@@ -65,7 +67,7 @@ type
FormulaStr: string;
DoubleValue: double;
end;
-
+ *)
{@@ Tokens to identify the elements in an expanded formula.
See http://www.techonthenet.com/excel/formulas/ for an explanation of
@@ -79,6 +81,19 @@ type
in TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID, unit xlscommon,
in sync
}
+ TFEKind = (
+ { Basic operands }
+ fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
+ fekString, fekBool, fekErr, fekMissingArg,
+ { Basic operations }
+ fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
+ fekConcat, // string concatenation
+ fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
+ fekParen, // show parenthesis around expression node
+ { Functions - they are identified by their name }
+ fekFunc
+ );
+ (*
TFEKind = (
{ Basic operands }
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
@@ -119,15 +134,17 @@ type
{ Other operations }
fekOpSUM {Unary sum operation. Note: CANNOT be used for summing sell contents; use fekSUM}
);
-
+ *)
{@@ These tokens identify operands in RPN formulas. }
TOperandTokens = fekCell..fekMissingArg;
{@@ These tokens identify basic operations in RPN formulas. }
TBasicOperationTokens = fekAdd..fekParen;
+ (*
{@@ These tokens identify spreadsheet functions in RPN formulas. }
TFuncTokens = fekAbs..fekOpSum;
+ *)
{@@ Flags to mark the address or a cell or a range of cells to be absolute
or relative. They are used in the set TsRelFlags. }
@@ -149,20 +166,17 @@ type
IntValue: Word;
StringValue: String;
RelFlags: TsRelFlags; // store info on relative/absolute addresses
+ FuncName: String;
ParamsNum: Byte;
end;
- {@@ Expanded formula. Used by backend modules. Provides more information than the text only.
- Is an array of TsFormulaElement items. }
- TsExpandedFormula = array of TsFormulaElement;
-
{@@ RPN formula. Similar to the expanded formula, but in RPN notation.
Simplifies the task of format writers which need RPN }
TsRPNFormula = array of TsFormulaElement;
{@@ Describes the type of content in a cell of a TsWorksheet }
- TCellContentType = (cctEmpty, cctFormula, cctRPNFormula, cctNumber,
- cctUTF8String, cctDateTime, cctBool, cctError);
+ TCellContentType = (cctEmpty, cctFormula, cctNumber, cctUTF8String,
+ cctDateTime, cctBool, cctError);
{@@ Error code values }
TsErrorValue = (
@@ -383,6 +397,16 @@ type
{@@ State flags while calculating formulas }
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
+ {@@ Record combining a cell's row and column indexes }
+ TsCellCoord = record
+ Row, Col: Cardinal;
+ end;
+
+ {@@ Record combining row and column cornder indexes of a range of cells }
+ TsCellRange = record
+ Row1, Col1, Row2, Col2: Cardinal;
+ end;
+
{@@ Pointer to a TCell record }
PCell = ^TCell;
@@ -401,8 +425,8 @@ type
Row: Cardinal; // zero-based
ContentType: TCellContentType;
{ Possible values for the cells }
- FormulaValue: TsFormula;
- RPNFormulaValue: TsRPNFormula;
+ FormulaValue: string;
+// RPNFormulaValue: TsRPNFormula;
NumberValue: double;
UTF8StringValue: ansistring;
DateTimeValue: TDateTime;
@@ -475,7 +499,6 @@ type
TsCustomSpreadWriter = class;
TsWorkbook = class;
-
{ TsWorksheet }
{@@ This event fires whenever a cell value or cell formatting changes. It is
@@ -511,7 +534,6 @@ type
procedure RemoveCallback(data, arg: pointer);
protected
- procedure CalcRPNFormula(ACell: PCell);
function CellUsedInFormula(ARow, ACol: Cardinal): Boolean;
procedure ChangedCell(ARow, ACol: Cardinal);
@@ -582,8 +604,6 @@ type
function WriteErrorValue(ARow, ACol: Cardinal; AValue: TsErrorValue): PCell; overload;
procedure WriteErrorValue(ACell: PCell; AValue: TsErrorValue); overload;
- function WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula): PCell; overload;
- procedure WriteFormula(ACell: PCell; AFormula: TsFormula); overload;
function WriteFormula(ARow, ACol: Cardinal; AFormula: String): PCell; overload;
procedure WriteFormula(ACell: PCell; AFormula: String); overload;
@@ -676,8 +696,10 @@ type
procedure WriteWordwrap(ACell: PCell; AValue: boolean); overload;
{ Formulas }
+ function BuildRPNFormula(ACell: PCell): TsRPNFormula;
+ procedure CalcFormula(ACell: PCell);
procedure CalcFormulas;
- function ReadRPNFormulaAsString(ACell: PCell): String;
+ function ConvertRPNFormulaToStringFormula(const AFormula: TsRPNFormula): String;
function UseSharedFormula(ARow, ACol: Cardinal; ASharedFormulaBase: PCell): PCell; overload;
procedure UseSharedFormula(ACellRangeStr: String; ASharedFormulaBase: PCell); overload;
@@ -765,12 +787,12 @@ type
@param boCalcBeforeSaving Calculates formulas before saving the file.
Otherwise there are no results when the file is
loaded back by fpspreadsheet.
- @param boReadFormulas Allows to turn on/off reading of formulas; this
- is a precaution since formulas not or not fully
+ @param boReadFormulas Allows to turn off reading of rpn formulas; this is
+ a precaution since formulas not correctly
implemented by fpspreadsheet could crash the
reading operation. }
- TsWorkbookOption = (boVirtualMode, boBufStream,
- boAutoCalc, boCalcBeforeSaving, boReadFormulas);
+ TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc, boCalcBeforeSaving,
+ boReadFormulas);
{@@
Set of options flags for the workbook }
@@ -1069,7 +1091,6 @@ type
{ Helper routines }
procedure AddDefaultFormats(); virtual;
procedure CheckLimitations;
- function ExpandFormula(AFormula: TsFormula): TsExpandedFormula;
function FindFormattingInList(AFormat: PCell): Integer;
procedure FixCellColors(ACell: PCell);
function FixColor(AColor: TsColor): TsColor; virtual;
@@ -1159,14 +1180,15 @@ type
function RPNParenthesis(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;
+ function RPNFunc(AFuncName: String; ANext: PRPNItem): PRPNItem; overload;
+ function RPNFunc(AFuncName: String; ANumParams: Byte; ANext: PRPNItem): PRPNItem; overload;
- function FixedParamCount(AElementKind: TFEKind): Boolean;
+// function FixedParamCount(AElementKind: TFEKind): Boolean;
var
GsSpreadFormats: array of TsSpreadFormatData;
-procedure RegisterFormulaFunc(AFormulaKind: TFEKind; AFunc: pointer);
+//procedure RegisterFormulaFunc(AFormulaKind: TFEKind; AFunc: pointer);
procedure RegisterSpreadFormat( AReaderClass: TsSpreadReaderClass;
AWriterClass: TsSpreadWriterClass; AFormat: TsSpreadsheetFormat);
@@ -1185,7 +1207,8 @@ function HasFormula(ACell: PCell): Boolean;
implementation
uses
- Math, StrUtils, TypInfo, fpsStreams, fpsUtils, fpsNumFormatParser, fpsFunc;
+ Math, StrUtils, TypInfo,
+ fpsStreams, fpsUtils, fpsNumFormatParser, fpsExprParser, fpsFunc;
{ Translatable strings }
resourcestring
@@ -1312,7 +1335,7 @@ var
'wheat' // $16
);
-
+(*
{ Properties of formula elements }
type
@@ -1328,7 +1351,7 @@ type
end;
var
- FEProps: array[TFEKind] of TFEProp = ( // functions marked by (*)
+ FEProps: array[TFEKind] of TFEProp = ( // functions marked by ( * )
{ Operands } // are only partially supported
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCell
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRef
@@ -1385,7 +1408,7 @@ var
(Symbol:'TANH'; MinParams:1; MaxParams:1; Func:fpsTANH), // fekTANH,
{ date/time }
(Symbol:'DATE'; MinParams:3; MaxParams:3; Func:fpsDATE), // fekDATE
- (Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:fpsDATEDIF), // fekDATEDIF (*)
+ (Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:fpsDATEDIF), // fekDATEDIF ( * )
(Symbol:'DATEVALUE'; MinParams:1; MaxParams:1; Func:fpsDATEVALUE), // fekDATEVALUE
(Symbol:'DAY'; MinParams:1; MaxParams:1; Func:fpsDAY), // fekDAY
(Symbol:'HOUR'; MinParams:1; MaxParams:1; Func:fpsHOUR), // fekHOUR
@@ -1445,7 +1468,7 @@ var
(Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER
(Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:fpsREPLACE), // fekREPLACE
(Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:fpsRIGHT), // fekRIGHT
- (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:fpsSUBSTITUTE), // fekSUBSTITUTE (*)
+ (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:fpsSUBSTITUTE), // fekSUBSTITUTE ( * )
(Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM
(Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER
{ lookup/reference }
@@ -1454,8 +1477,8 @@ var
(Symbol:'ROW'; MinParams:0; MaxParams:1; Func:fpsROW), // fekROW
(Symbol:'ROWS'; MinParams:1; MaxParams:1; Func:fpsROWS), // fekROWS
{ info }
- (Symbol:'CELL'; MinParams:1; MaxParams:2; Func:fpsCELLINFO), // fekCELLINFO (*)
- (Symbol:'INFO'; MinParams:1; MaxParams:1; Func:fpsINFO), // fekINFO (*)
+ (Symbol:'CELL'; MinParams:1; MaxParams:2; Func:fpsCELLINFO), // fekCELLINFO ( * )
+ (Symbol:'INFO'; MinParams:1; MaxParams:1; Func:fpsINFO), // fekINFO ( * )
(Symbol:'ISBLANK'; MinParams:1; MaxParams:1; Func:fpsISBLANK), // fekIsBLANK
(Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:fpsISERR), // fekIsERR
(Symbol:'ISERROR'; MinParams:1; MaxParams:1; Func:fpsISERROR), // fekIsERROR
@@ -1484,7 +1507,7 @@ procedure RegisterFormulaFunc(AFormulaKind: TFEKind; AFunc: Pointer);
begin
FEProps[AFormulaKind].Func := TsFormulaFunc(AFunc);
end;
-
+ *)
{@@
Registers a new reader/writer pair for a given spreadsheet file format
@@ -1615,8 +1638,7 @@ end;
}
procedure InitCell(out ACell: TCell);
begin
- ACell.RPNFormulaValue := nil;
- ACell.FormulaValue.FormulaStr := '';
+ ACell.FormulaValue := '';
ACell.UTF8StringValue := '';
ACell.NumberFormatStr := '';
FillChar(ACell, SizeOf(ACell), 0);
@@ -1643,9 +1665,7 @@ end;
function HasFormula(ACell: PCell): Boolean;
begin
Result := Assigned(ACell) and (
- (ACell^.SharedFormulaBase <> nil) or
- (Length(ACell^.RPNFormulaValue) > 0) or
- (Length(ACell^.FormulaValue.FormulaStr) > 0)
+ (ACell^.SharedFormulaBase <> nil) or (Length(ACell^.FormulaValue) > 0)
);
end;
@@ -1723,7 +1743,29 @@ begin
end;
{@@
- Helper method for clearing the records in a spreadsheet.
+ Helper function which constructs an rpn formula from the cell's string
+ formula. This is needed, for example, when writing a formula to xls biff
+ file format.
+}
+function TsWorksheet.BuildRPNFormula(ACell: PCell): TsRPNFormula;
+var
+ parser: TsSpreadsheetParser;
+begin
+ if not HasFormula(ACell) then begin
+ SetLength(Result, 0);
+ exit;
+ end;
+ parser := TsSpreadsheetParser.Create(self);
+ try
+ parser.Expression := ACell^.FormulaValue;
+ Result := parser.RPNFormula;
+ finally
+ parser.Free;
+ end;
+end;
+
+{@@
+ Helper method for calculation of the formulas in a spreadsheet.
}
procedure TsWorksheet.CalcFormulaCallback(data, arg: pointer);
var
@@ -1736,35 +1778,103 @@ begin
if (cell = nil) or (cell^.ContentType = cctError) then
exit;
- // Cell contains an RPN formula --> calculate the formula
- if (Length(cell^.RPNFormulaValue) > 0) or
- ((cell^.SharedFormulaBase <> nil) and (Length(cell^.SharedFormulaBase^.RPNFormulaValue) > 0))
- then
- CalcRPNFormula(cell);
-
- // Here should be other case of string formula:
- {
- else
- if (Length(cell^.FormulaValue.FormulaStr) > 0) or
- ((cell^.SharedFormulaBase <> nil) and (Length(cell^.SharedFormulaBase^.FomrulaValue.FormulaStr) > 0))
- then
- CalcStringFormula(cell);
- }
+ if HasFormula(cell) or HasFormula(cell^.SharedFormulaBase) then
+ CalcFormula(cell);
end;
{@@
- Helper method marking all cells with formulas as "not calculated". This flag
- is needed for recursive calculation of the entire worksheet.
-}
-procedure TsWorksheet.CalcStateCallback(data, arg: Pointer);
-var
- cell: PCell;
-begin
- Unused(arg);
- cell := PCell(data);
+ Calculates the formula in a cell
+ Should not be called by itself because the result may depend on other cells
+ which may have not yet been calculated. It is better to call CalcFormulas
+ instead.
- if Length(cell^.RPNFormulaValue) > 0 then
- cell^.CalcState := csNotCalculated;
+ @param ACell Cell containing the formula.
+}
+procedure TsWorksheet.CalcFormula(ACell: PCell);
+var
+ parser: TsSpreadsheetParser;
+ res: TsExpressionResult;
+ rpnFormula: TsRPNFormula;
+ cell: PCell;
+ i: Integer;
+ r, c: Cardinal;
+ fe: TsFormulaElement;
+begin
+ ACell^.CalcState := csCalculating;
+
+ parser := TsSpreadsheetParser.Create(self);
+ try
+ parser.Expression := ACell^.FormulaValue;
+ (*
+ // Check whether all used cells are already calculated.
+ rpnFormula := parser.RPNFormula;
+ for i:=0 to High(rpnFormula) do begin
+ fe := rpnFormula[i];
+ case fe.ElementKind of
+ fekCell, fekCellRef:
+ begin
+ cell := FindCell(fe.Row, fe.Col);
+ if cell <> nil then
+ case cell^.CalcState of
+ csNotCalculated: CalcFormula(cell);
+ csCalculating : raise Exception.Create(lpCircularReference);
+ end;
+ end;
+ fekCellRange:
+ begin
+ for r := fe.Row to fe.Row2 do
+ for c := fe.Col to fe.Col2 do begin
+ cell := FindCell(r, c);
+ if cell <> nil then
+ case cell^.CalcState of
+ csNotCalculated: CalcFormula(cell);
+ csCalculating : raise Exception.Create(lpCircularReference);
+ end;
+ end;
+ end;
+ {
+ fekCellOffset:
+ begin
+ if ACell^.SharedFormulaBase = nil then begin
+ ACell^.WriteErrorValue(ACell, errIllegalRef);
+ exit;
+ end;
+ if (rfRelRow in fe.RelFlags)
+ then r := ACell^.Row + SmallInt(fe.Row)
+ else r := ACell^.SharedFormulaBase^.Row;
+ if (rfRelCol in fe.RelFlags)
+ then c := ACell^.Col + SmallInt(fe.Col)
+ else c := ACell^.SharedFormulaBase^.Col;
+ cell := FindCell(r, c);
+ if cell <> nil then begin
+ case cell^.CalcState of
+ csNotCalculated: CalcFormula(cell);
+ csCalculating : raise Exception.Create(lpCircularReference);
+ end;
+ end else begin
+ WriteErrorValue(ACell, errIllegalRef);
+ exit;
+ end;
+ end;
+ }
+ end;
+ end;
+ *)
+ parser.EvaluateExpression(res);
+ case res.ResultType of
+ rtEmpty : WriteBlank(ACell);
+ rtError : WriteErrorValue(ACell, res.ResError);
+ rtInteger : WriteNumber(ACell, res.ResInteger);
+ rtFloat : WriteNumber(ACell, res.ResFloat);
+ rtDateTime : WriteDateTime(ACell, res.ResDateTime);
+ rtString : WriteUTF8Text(ACell, res.ResString);
+ rtBoolean : WriteBoolValue(ACell, res.ResBoolean);
+ end;
+ finally
+ parser.Free;
+ end;
+
+ ACell^.CalcState := csCalculated;
end;
{@@
@@ -1798,142 +1908,18 @@ begin
end;
{@@
- Calculates the rpn formula assigned to a cell.
- Should not be called by itself because the result may depend on other cells
- which may have not yet been calculated. It is better to call CalcFormulas
- instead.
-
- @param ACell Cell containing the rpn formula.
+ Helper method marking all cells with formulas as "not calculated". This flag
+ is needed for recursive calculation of the entire worksheet.
}
-procedure TsWorksheet.CalcRPNFormula(ACell: PCell);
+procedure TsWorksheet.CalcStateCallback(data, arg: Pointer);
var
- i: Integer;
- args: TsArgumentStack;
- func: TsFormulaFunc;
- val: TsArgument;
- fe: TsFormulaElement;
cell: PCell;
- r,c: Cardinal;
- formula: TsRPNFormula;
begin
- if (ACell^.ContentType = cctError) then
- exit;
+ Unused(arg);
+ cell := PCell(data);
- if (ACell^.SharedFormulaBase = nil) and (Length(ACell^.RPNFormulaValue) = 0) then
- exit;
-
- if (ACell^.SharedFormulaBase <> nil) and (Length(ACell^.SharedFormulaBase^.RPNFormulaValue) = 0)
- then exit;
-
- ACell^.CalcState := csCalculating;
-
- args := TsArgumentStack.Create;
- try
- // Take care of shared formulas
- if ACell^.SharedFormulaBase = nil then
- formula := ACell^.RPNFormulaValue
- else
- formula := ACell^.SharedFormulaBase^.RPNFormulaValue;
-
- for i := 0 to Length(formula) - 1 do begin
- fe := formula[i]; // "fe" means "formula element"
- case fe.ElementKind of
- fekCell, fekCellRef:
- begin
- cell := FindCell(fe.Row, fe.Col);
- if cell <> nil then
- case cell^.CalcState of
- csNotCalculated: CalcRPNFormula(cell);
- csCalculating : raise Exception.Create(lpCircularReference);
- end;
- args.PushCell(cell, self);
- end;
- fekCellRange:
- begin
- for r := fe.Row to fe.Row2 do
- for c := fe.Col to fe.Col2 do begin
- cell := FindCell(r, c);
- if cell <> nil then
- case cell^.CalcState of
- csNotCalculated: CalcRPNFormula(cell);
- csCalculating : raise Exception.Create(lpCircularReference);
- end;
- end;
- args.PushCellRange(fe.Row, fe.Col, fe.Row2, fe.Col2, self);
- end;
- fekCellOffset:
- begin
- if ACell^.SharedFormulaBase = nil then begin
- WriteErrorValue(ACell, errIllegalRef);
- exit;
- end;
- if (rfRelRow in fe.RelFlags)
- then r := ACell^.Row + SmallInt(fe.Row)
- else r := ACell^.SharedFormulaBase^.Row;
- if (rfRelCol in fe.RelFlags)
- then c := ACell^.Col + SmallInt(fe.Col)
- else c := ACell^.SharedFormulaBase^.Col;
- cell := FindCell(r, c);
- if cell <> nil then begin
- case cell^.CalcState of
- csNotCalculated: CalcRPNFormula(cell);
- csCalculating : raise Exception.Create(lpCircularReference);
- end;
- args.PushCell(cell, self);
- end else begin
- WriteErrorValue(ACell, errIllegalRef);
- exit;
- end;
- end;
- fekNum:
- args.PushNumber(fe.DoubleValue, self);
- fekInteger:
- args.PushNumber(1.0*fe.IntValue, self);
- fekString:
- args.PushString(fe.StringValue, self);
- fekBool:
- args.PushBool(fe.DoubleValue <> 0.0, self);
- fekMissingArg:
- args.PushMissing(self);
- fekParen: ; // visual effect only
- fekErr:
- exit;
- else
- func := FEProps[fe.ElementKind].Func;
- if not Assigned(func) then begin
- // calculation of function not implemented
- WriteErrorValue(ACell, errFormulaNotSupported);
- exit;
- end;
- if args.Count < fe.ParamsNum then begin
- // not enough parameters
- WriteErrorValue(ACell, errArgError);
- exit;
- end;
- // Result of function
- val := func(args, fe.ParamsNum);
- // Push result on stack for usage by next function or as final result
- args.Push(val, self);
- end; // case
- end; // for
-
- { When all formula elements have been processed the stack contains the
- final result. }
- if args.Count = 1 then begin
- val := args.Pop;
- case val.ArgumentType of
- atNumber: WriteNumber(ACell, val.NumberValue);
- atBool : WriteBoolValue(ACell, val.BoolValue);
- atString: WriteUTF8Text(ACell, val.StringValue);
- atError : WriteErrorValue(ACell, val.ErrorValue);
- atEmpty : WriteBlank(ACell);
- end;
- end else
- WriteErrorValue(ACell, errArgError);
- finally
- ACell^.CalcState := csCalculated;
- args.Free;
- end;
+ if HasFormula(cell) then
+ cell^.CalcState := csNotCalculated;
end;
{@@
@@ -1958,14 +1944,16 @@ var
cellNode: TAVLTreeNode;
fe: TsFormulaElement;
i: Integer;
+ rpnFormula: TsRPNFormula;
begin
cellNode := FCells.FindLowest;
while Assigned(cellNode) do begin
cell := PCell(cellNode.Data);
- if Length(cell^.RPNFormulaValue) > 0 then
- for i := 0 to Length(cell^.RPNFormulaValue)-1 do
+ if HasFormula(cell) then begin
+ rpnFormula := BuildRPNFormula(cell);
+ for i := 0 to Length(rpnFormula)-1 do
begin
- fe := cell^.RPNFormulaValue[i];
+ fe := rpnFormula[i];
case fe.ElementKind of
fekCell, fekCellRef:
if (fe.Row = ARow) and (fe.Col = ACol) then
@@ -1982,8 +1970,10 @@ begin
end;
end;
end;
+ end;
cellNode := FCells.FindSuccessor(cellNode);
end;
+ SetLength(rpnFormula, 0);
end;
{@@
@@ -2689,18 +2679,15 @@ end;
is returned as a string in Excel syntax.
@param ACell Pointer to the cell considered
- @return Formula string in Excel syntax.
+ @return Formula string in Excel syntax (does not contain a leading "=")
}
function TsWorksheet.ReadFormulaAsString(ACell: PCell): String;
begin
Result := '';
if ACell = nil then
exit;
- if HasFormula(ACell) then begin
- Result := ReadRPNFormulaAsString(ACell);
- if Result = '' then
- Result := ACell^.FormulaValue.FormulaStr;
- end;
+ if HasFormula(ACell) then
+ Result := ACell^.FormulaValue;
end;
{@@
@@ -2737,152 +2724,24 @@ end;
{@@
- If a cell contains an RPN formula an Excel-like formula string is constructed
- and returned.
+ Converts an RPN formula (as read from an xls biff file, for example) to a
+ string formula.
- @param ACell Pointer to the cell considered
- @return Formula string in Excel syntax.
+ @param AFormula Array of rpn formula tokens
+ @return Formula string in Excel syntax (without leading "=")
}
-function TsWorksheet.ReadRPNFormulaAsString(ACell: PCell): String;
+function TsWorksheet.ConvertRPNFormulaToStringFormula(const AFormula: TsRPNFormula): String;
var
- fs: TFormatSettings;
- elem: TsFormulaElement;
- i, j: Integer;
- L: TStringList;
- s: String;
- ptr: Pointer;
- fek: TFEKind;
- formula: TsRPNFormula;
- r,c: Cardinal;
+ parser: TsSpreadsheetParser;
begin
Result := '';
- if ACell = nil then
- exit;
- fs := Workbook.FormatSettings;
- L := TStringList.Create;
+ parser := TsSpreadsheetParser.Create(self);
try
- // Take care of shared formulas
- if ACell^.SharedFormulaBase = nil then
- formula := ACell^.RPNFormulaValue
- else
- formula := ACell^.SharedFormulaBase^.RPNFormulaValue;
-
- if Length(formula) = 0 then
- exit;
-
- // We store the cell values and operation codes in a stringlist which serves
- // as kind of stack. Therefore, we do not destroy the original formula array.
- // We reverse the order of the items because in the next step stringlist
- // items will subsequently be deleted, and this is much easier when going
- // in reverse direction.
- for i := Length(formula)-1 downto 0 do begin
- elem := formula[i];
- ptr := Pointer(elem.ElementKind);
- case elem.ElementKind of
- fekNum:
- L.AddObject(Format('%g', [elem.DoubleValue], fs), ptr);
- fekInteger:
- L.AddObject(IntToStr(elem.IntValue), ptr);
- fekString:
- L.AddObject('"' + elem.StringValue + '"', ptr);
- fekBool:
- L.AddObject(IfThen(boolean(elem.IntValue), 'FALSE', 'TRUE'), ptr);
- fekCell,
- fekCellRef:
- L.AddObject(GetCellString(elem.Row, elem.Col, elem.RelFlags), ptr);
- fekCellRange:
- L.AddObject(GetCellRangeString(elem.Row, elem.Col, elem.Row2, elem.Col2, elem.RelFlags), ptr);
- fekCellOffset:
- begin
- if rfRelRow in elem.RelFlags
- then r := ACell^.Row + SmallInt(elem.Row)
- else r := elem.Row;
- if rfRelCol in elem.RelFlags
- then c := ACell^.Col + SmallInt(elem.Col)
- else c := elem.Col;
- L.AddObject(GetCellString(r, c, elem.RelFlags), ptr);
- end
- // Operations:
- else
- L.AddObject(FEProps[elem.ElementKind].Symbol, ptr);
- end;
- end;
-
- // Now we construct the string from the parts stored in the stringlist.
- // Every item processed is deleted from the list for error detection.
- // In order not to confuse indexes we start at the end of the list and
- // work forward.
- i := L.Count-1;
- while (L.Count > 0) and (i >= 0) do begin
- fek := TFEKind(PtrInt(L.Objects[i]));
- case fek of
- fekAdd, fekSub, fekMul, fekDiv, fekPower, fekConcat,
- fekEqual, fekNotEqual, fekLess, fekLessEqual, fekGreater, fekGreaterEqual:
- if i+2 < L.Count then begin
- L.Strings[i] := Format('%s%s%s', [L[i+2], L[i], L[i+1]]);
- L.Delete(i+2);
- L.Delete(i+1);
- L.Objects[i] := pointer(fekString);
- end else begin
- Result := '=' + lpErrArgError;
- exit;
- end;
- fekUPlus, fekUMinus:
- if i+1 < L.Count then begin
- L.Strings[i] := L[i]+L[i+1];
- L.Delete(i+1);
- L.Objects[i] := Pointer(fekString);
- end else begin
- Result := '=' + lpErrArgError;
- exit;
- end;
- fekPercent:
- if i+1 < L.Count then begin
- L.Strings[i] := L[i+1]+L[i];
- L.Delete(i+1);
- L.Objects[i] := Pointer(fekString);
- end else begin
- Result := '=' + lpErrArgError;
- exit;
- end;
- fekParen:
- if i+1 < L.Count then begin
- L.Strings[i] := Format('(%s)', [L[i+1]]);
- L.Delete(i+1);
- L.Objects[i] := pointer(fekString);
- end else begin
- Result := '=' + lpErrArgError;
- exit;
- end;
- else
- if fek >= fekAdd then begin
- elem := formula[Length(formula) - 1 - i];
- s := '';
- for j:= i+elem.ParamsNum downto i+1 do begin
- if j < L.Count then begin
- s := s + fs.ListSeparator + ' ' + L[j];
- L.Delete(j);
- end else begin
- Result := '=' + lpErrArgError;
- exit;
- end;
- end;
- Delete(s, 1, 2);
- L.Strings[i] := Format('%s(%s)', [L[i], s]);
- L.Objects[i] := pointer(fekString);
- end;
- end;
- dec(i);
- end;
-
- if L.Count > 1 then
- Result := '=' + lpErrArgError // too many arguments
- else
- Result := '=' + L[0];
-
+ parser.RPNFormula := AFormula;
+ Result := parser.BuildStringFormula;
finally
- L.Free;
+ parser.Free;
end;
end;
@@ -2979,8 +2838,8 @@ begin
end;
Result := GetCell(ARow, ACol);
Result.SharedFormulaBase := ASharedFormulaBase;
- if ((Length(Result^.RPNFormulaValue) > 0) or (Length(Result^.FormulaValue.FormulaStr) > 0))
- and ((ASharedFormulaBase.Row <> ARow) or (ASharedFormulaBase.Col <> ACol))
+ if HasFormula(Result) and
+ ((ASharedFormulaBase.Row <> ARow) or (ASharedFormulaBase.Col <> ACol))
then
raise Exception.CreateFmt('Cell %s uses a shared formula, but contains an own formula.',
[GetCellString(ARow, ACol)]);
@@ -3702,36 +3561,7 @@ end;
@param ARow The row of the cell
@param ACol The column of the cell
- @param AFormula The formula to be written
- @return Pointer to the cell
-}
-function TsWorksheet.WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula): PCell;
-begin
- Result := GetCell(ARow, ACol);
- WriteFormula(Result, AFormula);
-end;
-
-{@@
- Writes a formula to a given cell
-
- @param ACell Pointer to the cell
- @param AFormula Formula to be written
-}
-procedure TsWorksheet.WriteFormula(ACell: PCell; AFormula: TsFormula);
-begin
- if ACell = nil then
- exit;
- ACell^.ContentType := cctFormula;
- ACell^.FormulaValue := AFormula;
- ChangedCell(ACell^.Row, ACell^.Col);
-end;
-
-{@@
- Writes a formula to a given cell
-
- @param ARow The row of the cell
- @param ACol The column of the cell
- @param AFormula The formula string to be written
+ @param AFormula The formula string to be written. A leading "=" will be removed.
@return Pointer to the cell
}
function TsWorksheet.WriteFormula(ARow, ACol: Cardinal; AFormula: string): PCell;
@@ -3744,14 +3574,17 @@ end;
Writes a formula to a given cell
@param ACell Pointer to the cell
- @param AFormula Formula string to be written
+ @param AFormula Formula string to be written. A leading '=' will be removed.
}
-procedure TsWorksheet.WriteFormula(ACell: PCell; AFormula: String);
+procedure TsWorksheet.WriteFormula(ACell: PCell; AFormula: string);
begin
if ACell = nil then
exit;
ACell^.ContentType := cctFormula;
- ACell^.FormulaValue.FormulaStr := AFormula;
+ if (AFormula <> '') and (AFormula[1] = '=') then
+ ACell^.FormulaValue := Copy(AFormula, 2, Length(AFormula))
+ else
+ ACell^.FormulaValue := AFormula;
ChangedCell(ACell^.Row, ACell^.Col);
end;
@@ -3887,7 +3720,8 @@ end;
{@@
Writes an RPN formula to a cell. An RPN formula is an array of tokens
- describing the calculation to be performed.
+ describing the calculation to be performed. In addition,the RPN formula is
+ converted to a string formula.
@param ACell Pointer to the cell
@param AFormula Array of TsFormulaElements. The array can be created by
@@ -3902,8 +3736,9 @@ begin
if ACell = nil then
exit;
- ACell^.ContentType := cctRPNFormula;
- ACell^.RPNFormulaValue := AFormula;
+ ACell^.ContentType := cctFormula;
+ ACell^.FormulaValue := ConvertRPNFormulaToStringFormula(AFormula);
+
ChangedCell(ACell^.Row, ACell^.Col);
end;
@@ -4762,6 +4597,7 @@ var
cell: PCell;
col: PtrInt;
fe: TsFormulaElement;
+ formula: TsRPNFormula;
i: Integer;
begin
col := PtrInt(arg);
@@ -4771,11 +4607,15 @@ begin
if cell^.Col >= col then
inc(cell^.Col);
- // Update rpn formulas
- for i:=0 to Length(cell^.RPNFormulaValue)-1 do begin
- fe := cell^.RPNFormulaValue[i]; // "fe" means "formula element"
+ // Update formulas
+ // (1) create an rpn formula
+ formula := BuildRPNFormula(cell);
+ // (2) update cell addresses affected by the insertion of a column
+ for i:=0 to Length(formula)-1 do begin
+ fe := Formula[i]; // "fe" means "formula element"
case fe.ElementKind of
- fekCell, fekCellRef: if fe.Col >= col then inc(fe.Col);
+ fekCell, fekCellRef:
+ if fe.Col >= col then inc(fe.Col);
fekCellRange:
begin
if fe.Col >= col then inc(fe.Col);
@@ -4783,6 +4623,8 @@ begin
end;
end;
end;
+ // (3) convert rpn formula back to string formula
+ cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
end;
{@@
@@ -4824,6 +4666,7 @@ var
row: PtrInt;
i: Integer;
fe: TsFormulaElement;
+ formula: TsRPNFormula;
begin
row := PtrInt(arg);
cell := PCell(data);
@@ -4833,10 +4676,14 @@ begin
inc(cell^.Row);
// Update rpn formulas
- for i:=0 to Length(cell^.RPNFormulaValue)-1 do begin
- fe := cell^.RPNFormulaValue[i]; // "fe" means "formula element"
+ // (1) create an rpn formula
+ formula := BuildRPNFormula(cell);
+ // (2) update cell addresses affected by the insertion of a column
+ for i:=0 to Length(formula)-1 do begin
+ fe := formula[i]; // "fe" means "formula element"
case fe.ElementKind of
- fekCell, fekCellRef: if fe.Row >= row then inc(fe.Row);
+ fekCell, fekCellRef:
+ if fe.Row >= row then inc(fe.Row);
fekCellRange:
begin
if fe.Row >= row then inc(fe.Row);
@@ -4844,6 +4691,8 @@ begin
end;
end;
end;
+ // (3) convert rpn formula back to string formula
+ cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
end;
{@@
@@ -6965,50 +6814,6 @@ begin
NumFormatList.Sort;
end;
-{@@
- Expands a formula, separating it in it's constituent parts,
- so that it is already partially parsed and it is easier to
- convert it into the format supported by the writer module
-}
-function TsCustomSpreadWriter.ExpandFormula(AFormula: TsFormula): TsExpandedFormula;
-var
- StrPos: Integer;
- ResPos: Integer;
-begin
- ResPos := -1;
- SetLength(Result, 0);
-
- // The formula needs to start with a "=" character.
- if AFormula.FormulaStr[1] <> '=' then raise Exception.Create('Formula doesn''t start with =');
-
- StrPos := 2;
-
- while Length(AFormula.FormulaStr) <= StrPos do
- begin
- // Checks for cell with the format [Letter][Number]
-{ if (AFormula.FormulaStr[StrPos] in [a..zA..Z]) and
- (AFormula.FormulaStr[StrPos + 1] in [0..9]) then
- begin
- Inc(ResPos);
- SetLength(Result, ResPos + 1);
- Result[ResPos].ElementKind := fekCell;
-// Result[ResPos].Col1 := fekCell;
- Result[ResPos].Row1 := AFormula.FormulaStr[StrPos + 1];
-
- Inc(StrPos);
- end
- // Checks for arithmetical operations
- else} if AFormula.FormulaStr[StrPos] = '+' then
- begin
- Inc(ResPos);
- SetLength(Result, ResPos + 1);
- Result[ResPos].ElementKind := fekAdd;
- end;
-
- Inc(StrPos);
- end;
-end;
-
{@@
Helper function for the spreadsheet writers. Writes the cell value to the
stream. Calls the WriteNumber method of the worksheet for writing a number,
@@ -7152,7 +6957,8 @@ end;
procedure TsCustomSpreadWriter.WriteFormula(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell);
begin
- Unused(AStream, ARow, ACol);
+ Unused(AStream);
+ Unused(ARow, ACol, ACell);
end;
(*
{@@
@@ -7405,8 +7211,7 @@ begin
end;
{@@
- Creates an entry in the RPN array for a number. Integers and floating-point
- values can be used likewise.
+ Creates an entry in the RPN array for a floating point number.
@param AValue Number value to be inserted into the formula
@param ANext Pointer to the next RPN item in the list
@@ -7459,10 +7264,35 @@ end;
}
function RPNFunc(AToken: TFEKind; ANext: PRPNItem): PRPNItem;
begin
+ {
if FEProps[AToken].MinParams <> FEProps[AToken].MaxParams then
raise Exception.CreateFmt(lpSpecifyNumberOfParams, [FEProps[AToken].Symbol]);
+ }
+ Result := NewRPNItem;
+ Result^.FE.ElementKind := AToken;
+ Result^.Fe.FuncName := '';
+ Result^.Next := ANext;
+end;
- Result := RPNFunc(AToken, FEProps[AToken].MinParams, ANext);
+
+{@@
+ 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.
+
+ @param AToken Formula element indicating the function to be executed,
+ see the TFEKind enumeration for possible values.
+ @param ANext Pointer to the next RPN item in the list
+
+ @see TFEKind
+}
+function RPNFunc(AFuncName: String; ANext: PRPNItem): PRPNItem;
+begin
+ {
+ if FEProps[AToken].MinParams <> FEProps[AToken].MaxParams then
+ raise Exception.CreateFmt(lpSpecifyNumberOfParams, [FEProps[AToken].Symbol]);
+ }
+ Result := RPNFunc(AFuncName, 255, ANext); //FEProps[AToken].MinParams, ANext);
end;
{@@
@@ -7472,27 +7302,29 @@ end;
@param AToken Formula element indicating the function to be executed,
see the TFEKind enumeration for possible values.
- @param ANumParams Number of arguments used in the formula
+ @param ANumParams Number of arguments used in the formula. If -1 then the
+ fixed number of arguments known from the function definiton
+ is used.
@param ANext Pointer to the next RPN item in the list
@see TFEKind
}
-function RPNFunc(AToken: TFEKind; ANumParams: Byte; ANext: PRPNItem): PRPNItem;
+function RPNFunc(AFuncName: String; ANumParams: Byte; ANext: PRPNItem): PRPNItem;
begin
- if ord(AToken) < ord(fekAdd) then
- raise Exception.Create('No basic tokens allowed here.');
-
- if (ANumParams < FEProps[AToken].MinParams) or (ANumParams > FEProps[AToken].MaxParams) then
- raise Exception.CreateFmt(lpIncorrectParamCount, [
- FEProps[AToken].Symbol, FEProps[AToken].MinParams, FEProps[AToken].MaxParams
- ]);
-
+ {
+ if (ANumParams > -1) then
+ if (ANumParams < FEProps[AToken].MinParams) or (ANumParams > FEProps[AToken].MaxParams) then
+ raise Exception.CreateFmt(lpIncorrectParamCount, [
+ FEProps[AToken].Symbol, FEProps[AToken].MinParams, FEProps[AToken].MaxParams
+ ]);
+ }
Result := NewRPNItem;
- Result^.FE.ElementKind := AToken;
+ Result^.FE.ElementKind := fekFunc;
+ Result^.Fe.FuncName := AFuncName;
Result^.FE.ParamsNum := ANumParams;
Result^.Next := ANext;
end;
-
+ (*
{@@
Returns if the function defined by the token requires a fixed number of parameter.
@@ -7503,7 +7335,7 @@ begin
Result := (FEProps[AElementKind].MinParams = FEProps[AElementKind].MaxParams)
and (FEProps[AElementKind].MinParams >= 0);
end;
-
+ *)
{@@
Creates an RPN formula by a single call using nested RPN items.
diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas
index 21d5d8265..f33debdfa 100644
--- a/components/fpspreadsheet/fpspreadsheetgrid.pas
+++ b/components/fpspreadsheet/fpspreadsheetgrid.pas
@@ -2975,10 +2975,6 @@ end;
initial column widths and row heights.
}
procedure TsCustomWorksheetGrid.Setup;
-var
- i: Integer;
- lCol: PCol;
- lRow: PRow;
begin
if (FWorksheet = nil) or (FWorksheet.GetCellCount = 0) then begin
if ShowHeaders then begin
diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas
index 7b593f122..5613b2ba8 100644
--- a/components/fpspreadsheet/fpsutils.pas
+++ b/components/fpspreadsheet/fpsutils.pas
@@ -483,6 +483,8 @@ function ParseCellString(const AStr: String; out ACellRow, ACellCol: Cardinal;
while (i <= Length(AStr)) do begin
if (UpCase(AStr[i]) in LETTERS) then begin
ACellCol := Cardinal(ord(UpCase(AStr[i])) - ord('A')) + 1 + ACellCol * 26;
+ if ACellCol >= MAX_COL_COUNT then // too many columns (dropping this limitation could cause overflow if a too long string is passed
+ exit;
inc(i);
end
else
diff --git a/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi b/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi
index d6f438136..ae8bd0e7a 100644
--- a/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi
+++ b/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi
@@ -155,6 +155,7 @@
+
diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas
index 2ba663ed5..388c04cae 100644
--- a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas
+++ b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas
@@ -12,18 +12,6 @@ type
TBIFFDetailsEvent = procedure(Sender: TObject; ADetails: TStrings) of object;
- TBIFF2RichTextRun = packed record // valid up to BIFF5
- IndexToFirstChar: Byte;
- FontIndex: Byte;
- end;
-
- TBIFF8RichTextRun = packed record
- IndexToFirstChar: Word;
- FontIndex: Word;
- end;
-
- TRichTextRuns = array of TBiff8RichTextRun;
-
TBIFFGrid = class(TStringGrid)
private
FRecType: Word;
@@ -126,11 +114,7 @@ type
procedure Click; override;
procedure DoExtractDetails;
procedure ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
- out AString: String; out ANumBytes: Integer; out AOffsetToAsianPhoneticBlock: Integer;
- out AsianPhoneticBlockSize: DWord; out ARichTextRuns: TRichTextRuns;
- AIgnoreCompressedFlag: Boolean = false); overload;
- procedure ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
- out AString: String; out ANumBytes: Integer; AIgnoreCompressedFlag: Boolean = false); overload;
+ out AString: String; out ANumBytes: Integer; IgnoreCompressedFlag: Boolean = false);
procedure PopulateGrid;
procedure ShowInRow(var ARow: Integer; var AOffs: LongWord; ASize: Word; AValue,ADescr: String);
procedure ShowRowColData(var ABufIndex: LongWord);
@@ -147,7 +131,7 @@ type
implementation
uses
- StrUtils, Math, lazutf8,
+ StrUtils, Math,
fpsutils,
beBIFFUtils;
@@ -198,32 +182,19 @@ end;
procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
- out AString: String; out ANumBytes: Integer; out AOffsetToAsianPhoneticBlock: Integer;
- out AsianPhoneticBlockSize: DWord; out ARichTextRuns: TRichTextRuns;
- AIgnoreCompressedFlag: Boolean = false);
+ out AString: String; out ANumBytes: Integer; IgnoreCompressedFlag: Boolean = false);
var
- i: Integer;
ls: Integer;
sa: ansiString;
sw: WideString;
w: Word;
optn: Byte;
- bytesPerChar: Byte;
- containsAsianPhonetics: Boolean;
- containsRichText: Boolean;
- richTextCount: Word = 0;
- savedBufIndex: Integer;
begin
- AString := '';
- ANumBytes := 0;
- AOffsetToAsianPhoneticBlock := -1;
- AsianPhoneticBlockSize := 0;
- SetLength(ARichTextRuns, 0);
-
- if Length(FBuffer) = 0 then
+ if Length(FBuffer) = 0 then begin
+ AString := '';
+ ANumBytes := 0;
exit;
-
- savedBufIndex := ABufIndex;
+ end;
if ALenBytes = 1 then
ls := FBuffer[ABufIndex]
else begin
@@ -232,48 +203,18 @@ begin
end;
if AUnicode then begin
optn := FBuffer[ABufIndex + ALenBytes];
- if (optn and $01 = 0) and (not AIgnoreCompressedFlag) then
- bytesPerChar := 1
- else
- bytesPerChar := 2;
- containsAsianPhonetics := (optn and $04 <> 0);
- containsRichText := (optn and $08 <> 0);
- ABufIndex := ABufIndex + ALenBytes + 1;
- if containsRichText then begin
- Move(FBuffer[ABufIndex], richTextCount, 2);
- richTextCount := WordLEToN(richTextCount);
- inc(ABufIndex, 2);
- end;
- if containsAsianPhonetics then begin
- Move(FBuffer[ABufIndex], AsianPhoneticBlockSize, 4);
- AsianPhoneticBlockSize := DWordLEToN(AsianPhoneticBlockSize);
- inc(ABufIndex, 4);
- end;
- if bytesPerChar = 1 then begin
+ if (optn and $01 = 0) and (not IgnoreCompressedFlag)
+ then begin // compressed --> 1 byte per character
SetLength(sa, ls);
- Move(FBuffer[ABufIndex], sa[1], ls*SizeOf(AnsiChar));
- inc(ABufIndex, ls*SizeOf(AnsiChar));
- AString := AnsiToUTF8(sa);
+ ANumbytes := ls*SizeOf(AnsiChar) + ALenBytes + 1;
+ Move(FBuffer[ABufIndex + ALenBytes + 1], sa[1], ls*SizeOf(AnsiChar));
+ AString := sa;
end else begin
SetLength(sw, ls);
- Move(FBuffer[ABufIndex], sw[1], ls*SizeOf(WideChar));
- inc(ABufIndex, ls*SizeOf(WideChar));
+ ANumBytes := ls*SizeOf(WideChar) + ALenBytes + 1;
+ Move(FBuffer[ABufIndex + ALenBytes + 1], sw[1], ls*SizeOf(WideChar));
AString := UTF8Encode(WideStringLEToN(sw));
end;
- if containsRichText then begin
- SetLength(ARichTextRuns, richTextCount);
- Move(FBuffer[ABufIndex], ARichTextRuns[0], richTextCount*SizeOf(TBiff8RichTextRun));
- for i:=0 to richTextCount-1 do begin
- ARichTextRuns[i].IndexToFirstchar := WordLEToN(ARichTextRuns[i].IndexToFirstChar);
- ARichTextRuns[i].FontIndex := WordLEToN(ARichTextRuns[i].FontIndex);
- end;
- inc(ABufIndex, richTextCount*SizeOf(word));
- end;
- if containsAsianPhonetics then begin
- AOffsetToAsianPhoneticBlock := ABufIndex;
- inc(ABufIndex, AsianPhoneticBlockSize);
- end;
- ANumBytes := ABufIndex - savedBufIndex;
end else begin
SetLength(sa, ls);
ANumBytes := ls*SizeOf(AnsiChar) + ALenBytes;
@@ -282,17 +223,6 @@ begin
end;
end;
-procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
- out AString: String; out ANumBytes: Integer; AIgnoreCompressedFlag: Boolean = false);
-var
- asianPhoneticBlockOffset: Integer;
- asianPhoneticBlockSize: DWord;
- richTextRuns: TRichTextRuns;
-begin
- ExtractString(ABufIndex, ALenBytes, AUnicode, AString, ANumBytes,
- asianPhoneticBlockOffset, asianPhoneticBlockSize, richTextRuns,
- AIgnoreCompressedFlag);
-end;
function TBIFFGrid.GetStringType: String;
begin
@@ -1575,7 +1505,6 @@ begin
'(relict of BIFF5)');
end else begin
ExtractString(FBufferIndex, 2, true, s, numBytes);
-
if Row = FCurrRow then begin
FDetails.Add('Encoded URL without sheet name:'#13);
case s[1] of
@@ -4225,12 +4154,7 @@ var
numBytes: Integer;
s: String;
total1, total2: DWord;
- i, j: Integer;
- asianPhoneticBlockOffset: Integer;
- asianPhoneticBlockSize: DWord;
- richTextRuns: TRichTextRuns;
- dw: DWord;
- b: Byte;
+ i: Integer;
begin
numBytes := 4;
Move(FBuffer[FBufferIndex], total1, numBytes);
@@ -4246,47 +4170,9 @@ begin
'Number of following strings');
for i:=1 to total2 do begin
- ExtractString(FBufferIndex, 2, true, s, numBytes, asianPhoneticBlockOffset,
- asianPhoneticBlockSize, richTextRuns);
- if FFormat = sfExcel8 then begin
- if Row = FCurrRow then begin
- FDetails.Add('Wide string info:'#13);
- FDetails.Add('2 length bytes: ' + IntToStr(UTF8Length(s)));
- b := FBuffer[FBufferIndex+2];
- FDetails.Add('Options byte: ' + IntToStr(b));
- if b and $01 = 0
- then FDetails.Add(' Bit 1 = 0: compressed characters (8-bit characters)')
- else FDetails.Add(' Bit 1 = 1: uncompressed characters (16-bit characters)');
- if b and $04 = 0
- then FDetails.Add(' Bit 4 = 0: Does not contain Asian phonetic settings')
- else FDetails.Add(' Bit 4 = 1: Contains Asian phonetic settings');
- if b and $08 = 0
- then FDetails.Add(' Bit 8 = 0: Does not contain Rich-Text settings')
- else FDetails.Add(' Bit 8 = 1: Contains Rich-Text settings');
- if Length(richTextRuns) > 0 then begin
- FDetails.Add('Rich-Text information (2 bytes):');
- FDetails.Add(' ' +IntToStr(Length(richTextRuns)) + ' Rich-Text runs');
- end;
- if asianPhoneticBlockSize > 0 then begin
- FDetails.Add('Asian phonetic block size information (4 bytes): ');
- FDetails.Add(' Block size: ' + IntToStr(AsianPhoneticBlockSize) + ' bytes');
- end;
- FDetails.Add('String text: ' + s);
- if Length(richTextRuns)>0 then begin
- FDetails.Add('Rich text runs:');
- for j:=0 to High(richTextRuns) do
- FDetails.Add(Format(' Rich text run #%d: binary data $%.4x --> index of first formatted character %d, font index %d',
- [j, DWord(richTextRuns[j]), richTextRuns[j].IndexToFirstChar, richTextRuns[j].FontIndex]));
- end;
- if asianPhoneticBlockSize>0 then begin
- FDetails.Add('Asian phonetic block:');
- FDetails.Add(' Size: ' + IntToStr(asianPhoneticBlockSize));
- FDetails.Add(' (not decoded)');
- end;
- end;
+ ExtractString(FBufferIndex, 2, true, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i]));
end;
- end;
end;
diff --git a/components/fpspreadsheet/tests/errortests.pas b/components/fpspreadsheet/tests/errortests.pas
index d6cc4d630..7cbb6c90c 100644
--- a/components/fpspreadsheet/tests/errortests.pas
+++ b/components/fpspreadsheet/tests/errortests.pas
@@ -61,15 +61,14 @@ var
row, col: Cardinal;
row1, row2: Cardinal;
col1, col2: Cardinal;
- formula: TsFormula;
+ formula: string;
s: String;
TempFile: String;
ErrList: TStringList;
newColor: TsColor;
expected: integer;
begin
- formula.FormulaStr := '=A1';
- formula.DoubleValue := 0.0;
+ formula := '=A1';
ErrList := TStringList.Create;
try
diff --git a/components/fpspreadsheet/tests/formulatests.pas b/components/fpspreadsheet/tests/formulatests.pas
index 60bad8ec4..0e13ca03e 100644
--- a/components/fpspreadsheet/tests/formulatests.pas
+++ b/components/fpspreadsheet/tests/formulatests.pas
@@ -17,7 +17,7 @@ uses
// Not using Lazarus package as the user may be working with multiple versions
// Instead, add .. to unit search path
Classes, SysUtils, fpcunit, testutils, testregistry,
- fpsallformats, fpspreadsheet, fpsfunc,
+ fpsallformats, fpspreadsheet, fpsexprparser,
xlsbiff8 {and a project requirement for lclbase for utf8 handling},
testsutility;
@@ -31,27 +31,42 @@ type
procedure SetUp; override;
procedure TearDown; override;
// Test formula strings
- procedure TestWriteReadFormulaStrings(AFormat: TsSpreadsheetFormat);
+ procedure TestWriteReadFormulaStrings(AFormat: TsSpreadsheetFormat;
+ UseRPNFormula: Boolean);
// Test calculation of rpn formulas
- procedure TestCalcRPNFormulas(AFormat: TsSpreadsheetformat);
+ procedure TestCalcFormulas(AFormat: TsSpreadsheetformat; UseRPNFormula: Boolean);
published
// Writes out numbers & reads back.
// If previous read tests are ok, this effectively tests writing.
{ BIFF2 Tests }
- procedure TestWriteRead_BIFF2_FormulaStrings;
+ procedure Test_Write_Read_FormulaStrings_BIFF2;
{ BIFF5 Tests }
- procedure TestWriteRead_BIFF5_FormulaStrings;
+ procedure Test_Write_Read_FormulaStrings_BIFF5;
{ BIFF8 Tests }
- procedure TestWriteRead_BIFF8_FormulaStrings;
+ procedure Test_Write_Read_FormulaStrings_BIFF8;
+ { OOXML Tests }
+ procedure Test_Write_Read_FormulaStrings_OOXML;
- // Writes out and calculates formulas, read back
+ // Writes out and calculates rpn formulas, read back
{ BIFF2 Tests }
- procedure TestWriteRead_BIFF2_CalcRPNFormula;
+ procedure Test_Write_Read_CalcRPNFormula_BIFF2;
{ BIFF5 Tests }
- procedure TestWriteRead_BIFF5_CalcRPNFormula;
+ procedure Test_Write_Read_CalcRPNFormula_BIFF5;
{ BIFF8 Tests }
- procedure TestWriteRead_BIFF8_CalcRPNFormula;
+ procedure Test_Write_Read_CalcRPNFormula_BIFF8;
+ { OOXML Tests }
+ procedure Test_Write_Read_CalcRPNFormula_OOXML;
+
+ // Writes out and calculates string formulas, read back
+ { BIFF2 Tests }
+ procedure Test_Write_Read_CalcStringFormula_BIFF2;
+ { BIFF5 Tests }
+ procedure Test_Write_Read_CalcStringFormula_BIFF5;
+ { BIFF8 Tests }
+ procedure Test_Write_Read_CalcStringFormula_BIFF8;
+ { OOXML Tests }
+ procedure Test_Write_Read_CalcStringFormula_OOXML;
end;
implementation
@@ -59,6 +74,18 @@ implementation
uses
math, typinfo, lazUTF8, fpsUtils, rpnFormulaUnit;
+var
+ // Array containing the "true" results of the formulas, for comparison
+ SollValues: array of TsExpressionResult;
+
+// Helper for statistics tests
+const
+ STATS_NUMBERS: Array[0..4] of Double = (1.0, 1.1, 1.2, 0.9, 0.8);
+var
+ numberArray: array[0..4] of Double;
+
+
+
{ TSpreadWriteReadFormatTests }
procedure TSpreadWriteReadFormulaTests.SetUp;
@@ -71,7 +98,10 @@ begin
inherited TearDown;
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteReadFormulaStrings(AFormat: TsSpreadsheetFormat);
+procedure TSpreadWriteReadFormulaTests.TestWriteReadFormulaStrings(
+ AFormat: TsSpreadsheetFormat; UseRPNFormula: Boolean);
+{ If UseRPNFormula is true the test formulas are generated from RPN formulas.
+ Otherwise they are generated from string formulas. }
const
SHEET = 'Sheet1';
var
@@ -79,20 +109,29 @@ var
MyWorkbook: TsWorkbook;
Row: Integer;
TempFile: string; //write xls/xml to this file and read back from it
+ formula: String;
expected: String;
actual: String;
cell: PCell;
+ cellB1: Double;
+ cellB2: Double;
+ number: Double;
+ s: String;
+ hr, min, sec, msec: Word;
+ k: Integer;
begin
TempFile := GetTempFileName;
// Create test workbook
MyWorkbook := TsWorkbook.Create;
try
+ MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving];
MyWorkSheet:= MyWorkBook.AddWorksheet(SHEET);
// Write out all test formulas
// All formulas are in column B
- WriteRPNFormulaSamples(MyWorksheet, AFormat, true);
+ {$I testcases_calcrpnformula.inc}
+// WriteRPNFormulaSamples(MyWorksheet, AFormat, true, UseRPNFormula);
MyWorkBook.WriteToFile(TempFile, AFormat, true);
finally
MyWorkbook.Free;
@@ -113,8 +152,8 @@ begin
for Row := 0 to MyWorksheet.GetLastRowIndex do
begin
cell := MyWorksheet.FindCell(Row, 1);
- if (cell <> nil) and (Length(cell^.RPNFormulaValue) > 0) then begin
- actual := MyWorksheet.ReadRPNFormulaAsString(cell);
+ if HasFormula(cell) then begin
+ actual := MyWorksheet.ReadFormulaAsString(cell);
expected := MyWorksheet.ReadAsUTF8Text(Row, 0);
CheckEquals(expected, actual, 'Test read formula mismatch, cell '+CellNotation(MyWorkSheet,Row,1));
end;
@@ -126,40 +165,46 @@ begin
end;
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF2_FormulaStrings;
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_FormulaStrings_BIFF2;
begin
- TestWriteReadFormulaStrings(sfExcel2);
+ TestWriteReadFormulaStrings(sfExcel2, true);
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF5_FormulaStrings;
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_FormulaStrings_BIFF5;
begin
- TestWriteReadFormulaStrings(sfExcel5);
+ TestWriteReadFormulaStrings(sfExcel5, true);
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF8_FormulaStrings;
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_FormulaStrings_BIFF8;
begin
- TestWriteReadFormulaStrings(sfExcel8);
+ TestWriteReadFormulaStrings(sfExcel8, true);
+end;
+
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_FormulaStrings_OOXML;
+begin
+ TestWriteReadFormulaStrings(sfOOXML, true);
end;
-{ Test calculation of rpn formulas }
+{ Test calculation of formulas }
-procedure TSpreadWriteReadFormulaTests.TestCalcRPNFormulas(AFormat: TsSpreadsheetFormat);
+procedure TSpreadWriteReadFormulaTests.TestCalcFormulas(AFormat: TsSpreadsheetFormat;
+ UseRPNFormula: Boolean);
+{ If UseRPNFormula is TRUE, the test formulas are generated from RPN syntax,
+ otherwise string formulas are used. }
const
SHEET = 'Sheet1';
- STATS_NUMBERS: Array[0..4] of Double = (1.0, 1.1, 1.2, 0.9, 0.8);
var
MyWorksheet: TsWorksheet;
MyWorkbook: TsWorkbook;
Row: Integer;
TempFile: string; //write xls/xml to this file and read back from it
- actual: TsArgument;
- expected: TsArgument;
+ actual: TsExpressionResult;
+ expected: TsExpressionResult;
cell: PCell;
- sollValues: array of TsArgument;
+ sollValues: array of TsExpressionResult;
formula: String;
s: String;
- t: TTime;
hr,min,sec,msec: Word;
ErrorMargin: double;
k: Integer;
@@ -168,7 +213,8 @@ var
the formula calculation as well. The next variables, along with STATS_NUMBERS
above, hold the arguments for the direction function calls. }
number: Double;
- numberArray: array[0..4] of Double;
+ cellB1: Double;
+ cellB2: Double;
begin
ErrorMargin:=0; //1.44E-7;
//1.44E-7 for SUMSQ formula
@@ -200,6 +246,7 @@ begin
// Open the workbook
MyWorkbook := TsWorkbook.Create;
try
+ MyWorkbook.Options := Myworkbook.Options + [boReadFormulas];
MyWorkbook.ReadFromFile(TempFile, AFormat);
if AFormat = sfExcel2 then
MyWorksheet := MyWorkbook.GetFirstWorksheet
@@ -214,15 +261,24 @@ begin
cell := MyWorksheet.FindCell(Row, 1);
if (cell = nil) then
fail('Error in test code: Failed to get cell ' + CellNotation(MyWorksheet, Row, 1));
+
case cell^.ContentType of
- cctBool : actual := CreateBoolArg(cell^.BoolValue);
- cctNumber : actual := CreateNumberArg(cell^.NumberValue);
- cctError : actual := CreateErrorArg(cell^.ErrorValue);
- cctUTF8String : actual := CreateStringArg(cell^.UTF8StringValue);
+ cctBool : actual := BooleanResult(cell^.BoolValue);
+ cctNumber : actual := FloatResult(cell^.NumberValue);
+ cctDateTime : actual := DateTimeResult(cell^.DateTimeValue);
+ cctUTF8String : actual := StringResult(cell^.UTF8StringValue);
+ cctError : actual := ErrorResult(cell^.ErrorValue);
+ cctEmpty : actual := EmptyResult;
else fail('ContentType not supported');
end;
+
expected := SollValues[row];
- CheckEquals(ord(expected.ArgumentType), ord(actual.ArgumentType),
+ // Cell does not store integers!
+ if expected.ResultType = rtInteger then expected := FloatResult(expected.ResInteger);
+
+ CheckEquals(
+ GetEnumName(TypeInfo(TsExpressionResult), ord(expected.ResultType)),
+ GetEnumName(TypeInfo(TsExpressionResult), ord(actual.ResultType)),
'Test read calculated formula data type mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
@@ -231,22 +287,22 @@ begin
// the file value in the same second. Therefore we neglect the milliseconds.
if formula = '=NOW()' then begin
// Round soll value to seconds
- DecodeTime(expected.NumberValue, hr,min,sec,msec);
- expected.NumberValue := EncodeTime(hr, min, sec, 0);
+ DecodeTime(expected.ResDateTime, hr,min,sec,msec);
+ expected.ResDateTime := EncodeTime(hr, min, sec, 0);
// Round formula value to seconds
- DecodeTime(actual.NumberValue, hr,min,sec,msec);
- actual.NumberValue := EncodeTime(hr,min,sec,0);
+ DecodeTime(actual.ResDateTime, hr,min,sec,msec);
+ actual.ResDateTime := EncodeTime(hr,min,sec,0);
end;
- case actual.ArgumentType of
- atBool:
- CheckEquals(BoolToStr(expected.BoolValue), BoolToStr(actual.BoolValue),
+ case actual.ResultType of
+ rtBoolean:
+ CheckEquals(BoolToStr(expected.ResBoolean), BoolToStr(actual.ResBoolean),
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
- atNumber:
+ rtFloat:
{$if (defined(mswindows)) or (FPC_FULLVERSION>=20701)}
// FPC 2.6.x and trunk on Windows need this, also FPC trunk on Linux x64
- CheckEquals(expected.NumberValue, actual.NumberValue, ErrorMargin,
+ CheckEquals(expected.ResFloat, actual.ResFloat, ErrorMargin,
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
{$else}
@@ -255,14 +311,14 @@ begin
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
{$endif}
- atString:
- CheckEquals(expected.StringValue, actual.StringValue,
+ rtString:
+ CheckEquals(expected.ResString, actual.ResString,
'Test read calculated formula result mismatch, formula "' + formula +
'", cell '+CellNotation(MyWorkSheet,Row,1));
- atError:
+ rtError:
CheckEquals(
- GetEnumName(TypeInfo(TsErrorValue), ord(expected.ErrorValue)),
- GetEnumname(TypeInfo(TsErrorValue), ord(actual.ErrorValue)),
+ GetEnumName(TypeInfo(TsErrorValue), ord(expected.ResError)),
+ GetEnumname(TypeInfo(TsErrorValue), ord(actual.ResError)),
'Test read calculated formula error value mismatch, formula ' + formula +
', cell '+CellNotation(MyWorkSheet,Row,1));
end;
@@ -274,19 +330,44 @@ begin
end;
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF2_CalcRPNFormula;
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcRPNFormula_BIFF2;
begin
- TestCalcRPNFormulas(sfExcel2);
+ TestCalcFormulas(sfExcel2, true);
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF5_CalcRPNFormula;
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcRPNFormula_BIFF5;
begin
- TestCalcRPNFormulas(sfExcel5);
+ TestCalcFormulas(sfExcel5, true);
end;
-procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF8_CalcRPNFormula;
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcRPNFormula_BIFF8;
begin
- TestCalcRPNFormulas(sfExcel8);
+ TestCalcFormulas(sfExcel8, true);
+end;
+
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcRPNFormula_OOXML;
+begin
+ TestCalcFormulas(sfOOXML, true);
+end;
+
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcStringFormula_BIFF2;
+begin
+ TestCalcFormulas(sfExcel2, false);
+end;
+
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcStringFormula_BIFF5;
+begin
+ TestCalcFormulas(sfExcel5, false);
+end;
+
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcStringFormula_BIFF8;
+begin
+ TestCalcFormulas(sfExcel8, false);
+end;
+
+procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcStringFormula_OOXML;
+begin
+ TestCalcFormulas(sfOOXML, false);
end;
diff --git a/components/fpspreadsheet/tests/internaltests.pas b/components/fpspreadsheet/tests/internaltests.pas
index ad55d1e35..509824630 100644
--- a/components/fpspreadsheet/tests/internaltests.pas
+++ b/components/fpspreadsheet/tests/internaltests.pas
@@ -350,7 +350,8 @@ begin
CheckEquals('$XFE$1',GetCellString(0,16384,[])); // the first column beyond xlsx
// Something VERY big, beyond xlsx
- s := 'ZZZZ1';
+// s := 'ZZZZ1'; // this is case is no longer possible because max column count has been cut down to 65536
+ s := 'CRAA1';
ParseCellString(s, r, c, flags);
CheckEquals(s, GetCellString(r, c, flags));
end;
diff --git a/components/fpspreadsheet/tests/manualtests.pas b/components/fpspreadsheet/tests/manualtests.pas
index bbc513e49..730737ba7 100644
--- a/components/fpspreadsheet/tests/manualtests.pas
+++ b/components/fpspreadsheet/tests/manualtests.pas
@@ -58,6 +58,8 @@ type
// 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.
procedure TestRPNFormula;
+ // Dto, but writes string formulas.
+// procedure TestStringFormula;
{$ENDIF}
// For BIFF8 format, writes all background colors in A1..A16
procedure TestBiff8CellBackgroundColor;
@@ -69,8 +71,9 @@ uses
fpsUtils, rpnFormulaUnit;
const
- COLORSHEETNAME='colorsheet'; //for background color tests
- RPNSHEETNAME='formula_sheet'; //for rpn formula tests
+ COLORSHEETNAME='color_sheet'; //for background color tests
+ RPNSHEETNAME='rpn_formula_sheet'; //for rpn formula tests
+ FORMULASHEETNAME='formula_sheet'; // for string formula tests
OUTPUT_FORMAT = sfExcel8; //change manually if you want to test different formats. To do: automatically output all formats
var
@@ -195,7 +198,7 @@ begin
Worksheet := Workbook.AddWorksheet(COLORSHEETNAME);
WorkSheet.WriteUTF8Text(0,1,'TSpreadManualTests.TestBiff8CellBackgroundColor');
- RowOffset:=1;
+ RowOffset := 1;
for i:=0 to Workbook.GetPaletteSize-1 do begin
WorkSheet.WriteUTF8Text(i+RowOffset,0,'BACKGROUND COLOR TEST');
Cell := Worksheet.GetCell(i+RowOffset, 0);
@@ -218,6 +221,18 @@ begin
Worksheet := Workbook.AddWorksheet(RPNSHEETNAME);
WriteRPNFormulaSamples(Worksheet, OUTPUT_FORMAT, false);
end;
+ (*
+procedure TSpreadManualTests.TestStringFormula;
+var
+ Worksheet: TsWorksheet;
+begin
+ if Workbook = nil then
+ Workbook := TsWorkbook.Create;
+
+ Worksheet := Workbook.AddWorksheet(FORMULASHEETNAME);
+ WriteRPNFormulaSamples(Worksheet, OUTPUT_FORMAT, false, false);
+end;
+*)
{$ENDIF}
initialization
diff --git a/components/fpspreadsheet/tests/rpnformulaunit.pas b/components/fpspreadsheet/tests/rpnformulaunit.pas
index e5f380297..2323fab84 100644
--- a/components/fpspreadsheet/tests/rpnformulaunit.pas
+++ b/components/fpspreadsheet/tests/rpnformulaunit.pas
@@ -150,7 +150,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=COUNT(%s)', [cellAddr]));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange(cellAddr,
- RPNFunc(fekCOUNT, 1, // 1 parameter used in COUNT
+ RPNFunc('COUNT', 1, // 1 parameter used in COUNT
nil
))));
Worksheet.WriteNumber(Row, 2, 6); // 7 cells, but 1 is alpha-numerical!
@@ -161,7 +161,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=COUNT(%s)', [cellAddr]));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange(cellAddr,
- RPNFunc(fekCOUNT, 1,
+ RPNFunc('COUNT', 1,
nil
))));
Worksheet.WriteNumber(Row, 2, 6); // 7 cells, but 1 is alph-numerical!
@@ -172,7 +172,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=COUNT(%s)', [cellAddr]));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange(cellAddr,
- RPNFunc(fekCOUNT, 1,
+ RPNFunc('COUNT', 1,
nil
))));
Worksheet.WriteNumber(Row, 2, 6); // 7 cells, but 1 is alpha-numerical!
@@ -486,7 +486,7 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=TRUE()');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekTRUE,
+ RPNFunc('TRUE',
nil)));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[true]);
@@ -494,7 +494,7 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=FALSE()');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekFALSE,
+ RPNFunc('FALSE',
nil)));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[false]);
@@ -505,8 +505,8 @@ begin
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('C1',
RPNCellValue('C1',
- RPNFunc(fekEQUAL,
- RPNFunc(fekNOT,
+ RPNFunc(fekEqual,
+ RPNFunc('NOT',
nil))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[not (cellC1=cellC1)]);
@@ -520,7 +520,7 @@ begin
RPNNumber(1,
RPNNumber(2,
RPNFunc(fekEQUAL,
- RPNFunc(fekAND, 2,
+ RPNFunc('AND', 2,
nil)))))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[(1=0) and (1=2)]);
@@ -534,7 +534,7 @@ begin
RPNNumber(2,
RPNNumber(2,
RPNFunc(fekEQUAL,
- RPNFunc(fekAND, 2,
+ RPNFunc('AND', 2,
nil)))))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[(1=0) and (2=2)]);
@@ -548,7 +548,7 @@ begin
RPNNumber(2,
RPNNumber(2,
RPNFunc(fekEQUAL,
- RPNFunc(fekAND, 2,
+ RPNFunc('AND', 2,
nil)))))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[(1=1) and (2=2)]);
@@ -562,7 +562,7 @@ begin
RPNNumber(1,
RPNNumber(2,
RPNFunc(fekEQUAL,
- RPNFunc(fekOR, 2,
+ RPNFunc('OR', 2,
nil)))))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[(1=0) or (1=2)]);
@@ -576,7 +576,7 @@ begin
RPNNumber(2,
RPNNumber(2,
RPNFunc(fekEQUAL,
- RPNFunc(fekOR, 2,
+ RPNFunc('OR', 2,
nil)))))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[(1=0) or (2=2)]);
@@ -590,7 +590,7 @@ begin
RPNNumber(2,
RPNNumber(2,
RPNFunc(fekEQUAL,
- RPNFunc(fekOR, 2,
+ RPNFunc('OR', 2,
nil)))))))));
Worksheet.WriteUTF8Text(Row, 2, FALSE_TRUE[(1=1) or (2=2)]);
@@ -603,7 +603,7 @@ begin
RPNFunc(fekEQUAL,
RPNString('correct',
RPNString('wrong',
- RPNFunc(fekIF, 3,
+ RPNFunc('IF', 3,
nil))))))));
Worksheet.WriteUTF8Text(Row, 2, IfThen(cellB1=1.0, 'correct', 'wrong'));
@@ -616,7 +616,7 @@ begin
RPNFunc(fekNotEQUAL,
RPNString('correct',
RPNString('wrong',
- RPNFunc(fekIF, 3,
+ RPNFunc('IF', 3,
nil))))))));
Worksheet.WriteUTF8Text(Row, 2, IfThen(cellB1<>1.0, 'correct', 'wrong'));
@@ -628,7 +628,7 @@ begin
RPNNumber(1,
RPNFunc(fekEQUAL,
RPNString('correct',
- RPNFunc(fekIF, 2,
+ RPNFunc('IF', 2,
nil)))))));
Worksheet.WriteUTF8Text(Row, 2, IfThen(cellB1=1.0, 'correct', 'FALSE'));
@@ -640,7 +640,7 @@ begin
RPNNumber(1,
RPNFunc(fekNotEQUAL,
RPNString('correct',
- RPNFunc(fekIF, 2,
+ RPNFunc('IF', 2,
nil)))))));
Worksheet.WriteUTF8Text(Row, 2, IfThen(cellB1<>1.0, 'correct', 'FALSE'));
@@ -656,7 +656,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=ABS($B1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('$B1',
- RPNFunc(fekABS,
+ RPNFunc('ABS',
nil))));
Worksheet.WriteNumber(Row, 2, abs(cellB1));
@@ -665,7 +665,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=ABS(E$1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('E$1',
- RPNFunc(fekABS,
+ RPNFunc('ABS',
nil))));
Worksheet.WriteNumber(Row, 2, abs(cellE1));
@@ -674,7 +674,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=SIGN(F1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('F1',
- RPNFunc(fekSIGN,
+ RPNFunc('SIGN',
nil))));
Worksheet.WriteNumber(Row, 2, sign(cellF1));
@@ -683,7 +683,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=SIGN(0)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(0,
- RPNFunc(fekSIGN,
+ RPNFunc('SIGN',
nil))));
Worksheet.WriteNumber(Row, 2, sign(0));
@@ -692,7 +692,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=SIGN(G1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('G1',
- RPNFunc(fekSIGN,
+ RPNFunc('SIGN',
nil))));
Worksheet.WriteNumber(Row, 2, sign(cellG1));
@@ -700,7 +700,7 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=RAND()');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekRAND,
+ RPNFunc('RAND',
nil)));
Worksheet.WriteUTF8Text(Row, 2, '(random number - cannot compare)');
@@ -708,7 +708,7 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=PI()');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI,
+ RPNFunc('PI',
nil)));
Worksheet.WriteNumber(Row, 2, pi);
@@ -718,10 +718,10 @@ begin
value := pi/2;
Worksheet.WriteUTF8Text(Row, 0, '=DEGREES(PI()/2)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI,
+ RPNFunc('PI',
RPNNumber(2,
RPNFunc(fekDIV,
- RPNFunc(fekDEGREES,
+ RPNFunc('DEGREES',
nil))))));
Worksheet.WriteNumber(Row, 2, value/pi*180);
@@ -731,7 +731,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=RADIANS(90)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekRADIANS,
+ RPNFunc('RADIANS',
nil))));
Worksheet.WriteNumber(Row, 2, value/180*pi);
end;
@@ -740,10 +740,10 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=SIN(PI()/2)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI,
+ RPNFunc('PI',
RPNNumber(2,
RPNFunc(fekDIV,
- RPNFunc(fekSIN,
+ RPNFunc('SIN',
nil))))));
Worksheet.WriteNumber(Row, 2, sin(pi/2));
@@ -753,7 +753,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=ASIN(%.1f)', [value], fs));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekASIN,
+ RPNFunc('ASIN',
nil))));
Worksheet.WriteNumber(Row, 2, arcsin(value));
@@ -761,8 +761,8 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=COS(PI())');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI,
- RPNFunc(fekCOS,
+ RPNFunc('PI',
+ RPNFunc('COS',
nil))));
Worksheet.WriteNumber(Row, 2, cos(pi));
@@ -772,7 +772,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=ACOS(%.1f)', [value], fs));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekACOS,
+ RPNFunc('ACOS',
nil))));
Worksheet.WriteNumber(Row, 2, arccos(value));
@@ -780,10 +780,10 @@ begin
inc(Row);
Worksheet.WriteUTF8Text(Row, 0, '=TAN(PI()/4)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI,
+ RPNFunc('PI',
RPNNumber(4,
RPNFunc(fekDiv,
- RPNFunc(fekTAN,
+ RPNFunc('TAN',
nil))))));
Worksheet.WriteNumber(Row, 2, tan(pi/4));
@@ -793,7 +793,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=ATAN(1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekATAN,
+ RPNFunc('ATAN',
nil))));
Worksheet.WriteNumber(Row, 2, arctan(1.0));
@@ -806,7 +806,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=SINH(3)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekSINH,
+ RPNFunc('SINH',
nil))));
Worksheet.WriteNumber(Row, 2, sinh(value));
@@ -816,7 +816,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=ASINH(%.1f)', [value], fs));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekASINH,
+ RPNFunc('ASINH',
nil))));
Worksheet.WriteNumber(Row, 2, arcsinh(value));
@@ -826,7 +826,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=COSH(3)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekCOSH,
+ RPNFunc('COSH',
nil))));
Worksheet.WriteNumber(Row, 2, cosh(value));
@@ -836,7 +836,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=ACOSH(10)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekACOSH,
+ RPNFunc('ACOSH',
nil))));
Worksheet.WriteNumber(Row, 2, arccosh(value));
@@ -846,7 +846,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=TANH(3)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekTANH,
+ RPNFunc('TANH',
nil))));
Worksheet.WriteNumber(Row, 2, tanh(value));
@@ -856,7 +856,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=ATANH(%.1f)', [value], fs));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekATANH,
+ RPNFunc('ATANH',
nil))));
Worksheet.WriteNumber(Row, 2, arctanh(value));
end;
@@ -867,7 +867,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=SQRT(2)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekSQRT,
+ RPNFunc('SQRT',
nil))));
Worksheet.WriteNumber(Row, 2, sqrt(value));
@@ -877,7 +877,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=EXP(2)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekEXP,
+ RPNFunc('EXP',
nil))));
Worksheet.WriteNumber(Row, 2, exp(value));
@@ -887,7 +887,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=LN(2)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekLN,
+ RPNFunc('LN',
nil))));
Worksheet.WriteNumber(Row, 2, ln(value));
@@ -898,7 +898,7 @@ begin
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
RPNNumber(2,
- RPNFunc(fekLOG, 2,
+ RPNFunc('LOG', 2,
nil)))));
Worksheet.WriteNumber(Row, 2, logn(2.0, value));
@@ -908,7 +908,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=LOG10(100)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekLOG10,
+ RPNFunc('LOG10',
nil))));
Worksheet.WriteNumber(Row, 2, log10(value));
@@ -918,7 +918,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, Format('=LOG10(%.2f)', [value], fs));
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(value,
- RPNFunc(fekLOG10,
+ RPNFunc('LOG10',
nil))));
Worksheet.WriteNumber(Row, 2, log10(value));
@@ -935,7 +935,7 @@ begin
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('$F$1',
RPNNumber(1,
- RPNFunc(fekROUND,
+ RPNFunc('ROUND',
nil)))));
Worksheet.WriteNumber(Row, 2, Round(cellF1*10)/10); //RoundTo(cellF1, 1));
@@ -945,7 +945,7 @@ begin
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('G1',
RPNNumber(1,
- RPNFunc(fekROUND,
+ RPNFunc('ROUND',
nil)))));
Worksheet.WriteNumber(Row, 2, Round(cellG1*10)/10); //RoundTo(cellG1, 1));
@@ -954,7 +954,7 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=INT(F1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('F1',
- RPNFunc(fekINT,
+ RPNFunc('INT',
nil))));
Worksheet.WriteNumber(Row, 2, trunc(cellF1));
@@ -963,10 +963,10 @@ begin
Worksheet.WriteUTF8Text(Row, 0, '=INT(G1)');
Worksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellValue('G1',
- RPNFunc(fekINT,
+ RPNFunc('INT',
nil))));
Worksheet.WriteNumber(Row, 2, floor(cellG1)); // is this true?
-
+ (*
{ ---------- }
inc(Row);
@@ -2012,6 +2012,7 @@ begin
Worksheet.WriteUTF8Text(Row, 2, 'Error #N/A!');
Worksheet.WriteUTF8Text(Row, 3, 'Should be "=1/2", but there are too many operands...');
end;
+ *)
end;
end.
diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi
index 873fd93e2..251664efc 100644
--- a/components/fpspreadsheet/tests/spreadtestgui.lpi
+++ b/components/fpspreadsheet/tests/spreadtestgui.lpi
@@ -56,14 +56,17 @@
+
+
+
@@ -72,11 +75,11 @@
+
-
diff --git a/components/fpspreadsheet/tests/testbiff8_1899.xls b/components/fpspreadsheet/tests/testbiff8_1899.xls
index c73994677..c4af4f2e4 100644
Binary files a/components/fpspreadsheet/tests/testbiff8_1899.xls and b/components/fpspreadsheet/tests/testbiff8_1899.xls differ
diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc
index a21c0aa9e..6b7531337 100644
--- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc
+++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc
@@ -15,13 +15,19 @@
// Addition
Row := 0;
- MyWorksheet.WriteUTF8Text(Row, 0, '=1+1');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.0,
- RPNFunc(fekAdd, nil)))));
+ formula := '1+1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.0,
+ RPNFunc(fekAdd, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1.0 + 1.0); // B1 = 2
+ sollValues[Row] := FloatResult(1 + 1); // B1 = 2
+ cellB1 := 1 + 1;
// don't refer to the cell contents here because they have not yet been calculated!
{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT CHANGE THIS FORMULA - ITS RESULT (-9) IS HARD-CODED IN OTHER TESTS !!
@@ -29,13 +35,19 @@
// Subtraction
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=1-10');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1,
- RPNNumber(10,
- RPNFunc(fekSub, nil)))));
+ formula := '1-10';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(10.0,
+ RPNFunc(fekSub, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, -9);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1-10); // B2 = -9
+ sollValues[Row] := FloatResult(1 - 10); // B2 = -9
+ cellB2 := 1 - 10;
// don't refer to the cell contents here because they have not yet been calculated!
{!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! DO NOT CHANGE THIS FORMULA - ITS RESULT (-9) IS HARD-CODED IN OTHER TESTS !!
@@ -43,826 +55,1341 @@
// Add cell values - relative addresses
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=B1+B2');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNCellValue('B2',
- RPNFunc(fekAdd, nil)))));
+ formula := 'B1+B2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNCellValue('B2',
+ RPNFunc(fekAdd, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 + cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(-7);
+ sollValues[Row] := FloatResult(cellB1 + cellB2);
// Add cell values - absolute addresses
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=$B$1+$B$2');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('$B$1',
- RPNCellValue('$B$2',
- RPNFunc(fekAdd, nil)))));
+ formula := '$B$1+$B$2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('$B$1',
+ RPNCellValue('$B$2',
+ RPNFunc(fekAdd, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 + cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(-7);
+ sollValues[Row] := FloatResult(cellB1 + cellB2);
// don't refer to the cell contents here because they have not yet been calculated!
// Add cell values - mixed absolute and relative addresses
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=B$1+$B2');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B$1',
- RPNCellValue('$B2',
- RPNFunc(fekAdd, nil)))));
+ formula := 'B$1+$B2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B$1',
+ RPNCellValue('$B2',
+ RPNFunc(fekAdd, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 + cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(-7);
+ sollValues[Row] := FloatResult(cellB1 + cellB2);
// don't refer to the cell contents here because they have not yet been calculated!
- // Multiplication
+ // Subtract cell values - relative addresses
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=10*-3');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(10,
- RPNNumber(-3,
- RPNFunc(fekMul, nil)))));
+ formula := 'B1-B2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNCellValue('B2',
+ RPNFunc(fekSub, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 - cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(10*(-3));
+ sollValues[Row] := FloatResult(cellB1 - cellB2);
+
+ // Subtract cell values - absolute addresses
+ inc(Row);
+ formula := '$B$1-$B$2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('$B$1',
+ RPNCellValue('$B$2',
+ RPNFunc(fekSub, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 - cellB2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(cellB1 - cellB2);
+
+ // Subtract cell values - mixed absolute and relative addresses
+ inc(Row);
+ formula := 'B$1-$B2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B$1',
+ RPNCellValue('$B2',
+ RPNFunc(fekSub, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 - cellB2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(cellB1 - cellB2);
+ // don't refer to the cell contents here because they have not yet been calculated!
+
+ // Multiplication w/o Parenthesis
+ inc(Row);
+ formula := '10*-3';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(10.0,
+ RPNNumber(-3.0,
+ RPNFunc(fekMul, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 10*(-3));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(10*(-3));
// Multiplication w/Parenthesis
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=10*(-3)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(10,
- RPNNumber(-3,
- RPNParenthesis(
- RPNFunc(fekMul, nil))))));
+ formula := '10*(-3)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(10.0,
+ RPNNumber(-3.0,
+ RPNParenthesis(
+ RPNFunc(fekMul, nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 10*(-3));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(10*(-3));
+ sollValues[Row] := FloatResult(10*(-3));
// Multiplication of cell values
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=B1*B2');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNCellValue('B2',
- RPNFunc(fekMul, nil)))));
+ formula := 'B1*B2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNCellValue('B2',
+ RPNFunc(fekMul, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 * cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2*(-9));
+ sollValues[Row] := FloatResult(cellB1 * cellB2);
// Division
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=10/200');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(10,
- RPNNumber(200,
- RPNFunc(fekDiv, nil)))));
+ formula := '10/200';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(10.0,
+ RPNNumber(200.0,
+ RPNFunc(fekDiv, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 10/200);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(10/200);
+ sollValues[Row] := FloatResult(10/200);
// Division: Error case - divide by zero
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=10/0');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(10,
- RPNNumber(0,
- RPNFunc(fekDiv, nil)))));
+ formula := '10/0';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(10.0,
+ RPNNumber(0,
+ RPNFunc(fekDiv, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteErrorValue(Row, 2, errDivideByZero);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errDivideByZero);
+ sollValues[Row] := ErrorResult(errDivideByZero);
// Division of cell values
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=B1/B2');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNcellValue('B2',
- RPNFunc(fekDiv, nil)))));
+ formula := 'B1/B2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNcellValue('B2',
+ RPNFunc(fekDiv, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, cellB1 / cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2/(-9));
+ sollValues[Row] := FloatResult(cellB1 / cellB2);
// Percentage
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=10%');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(10,
- RPNFunc(fekPercent, nil))));
+ formula := '10%';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(10.0,
+ RPNFunc(fekPercent, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 10*0.01);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(10*0.01);
+ sollValues[Row] := FloatResult(10*0.01);
// Percentage of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=B1%');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekPercent, nil))));
+ formula := 'B1%';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc(fekPercent, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, cellB1*0.01);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2*0.01);
+ sollValues[Row] := FloatResult(cellB1*0.01);
- // Power
+ // Power symbol
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=power(2.0,0.5)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(2.0,
- RPNNumber(0.5,
- RPNFunc(fekPower, nil)))));
+ formula := '2^0.5';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2.0,
+ RPNNumber(0.5,
+ RPNFunc(fekPower, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, power(2, 0.5));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(power(2, 0.5));
+ sollValues[Row] := FloatResult(power(2, 0.5));
- // Power of cell values
+ // Power symbol - precedence of operations
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=power(B1,B2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNCellValue('B2',
- RPNFunc(fekPower, nil)))));
+ formula := '2^0.5*4';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2.0,
+ RPNNumber(0.5,
+ RPNFunc(fekPower,
+ RPNNumber(4,
+ RPNFunc(fekMul, nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, power(2, 0.5)*4);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(power(2, -9));
+ sollValues[Row] := FloatResult(power(2, 0.5)*4);
+
+ // Power symbol - precedence of operators
+ inc(Row);
+ formula := '4*2^0.5';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(4.0,
+ RPNNumber(2.0,
+ RPNNumber(0.5,
+ RPNFunc(fekPower,
+ RPNFunc(fekMul, nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 4 * power(2, 0.5));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(4 * power(2, 0.5));
+
+ // Power function
+ if AFormat <> sfExcel2 then begin
+ // Power of constant
+ inc(Row);
+ formula := 'POWER(2,0.5)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2.0,
+ RPNNumber(0.5,
+ RPNFunc('POWER', nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, power(2, 0.5));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(power(2, 0.5));
+
+ // Power of cell values
+ inc(Row);
+ formula := 'POWER(B1,B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNCellValue('B2',
+ RPNFunc('POWER', nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, power(cellB1, cellB2));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(power(cellB1, cellB2));
+ end;
{$IFDEF ENABLE_CALC_RPN_EXCEPTIONS}
// Power: Error case "power( (negative number), (fractional number) )"
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=power(-2.0, 0.5)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-2.0,
- RPNNumber(0.5,
- RPNFunc(fekPower, nil)))));
+ formula := 'POWER(-2.0, 0.5)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-2.0,
+ RPNNumber(0.5,
+ RPNFunc('POWER', nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ with sollValues[Row] do begin
+ ResultType := rtError;
+ ResError := errOverflow;
+ end;
{$ENDIF}
// Unary minus
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=-(-1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-1,
- RPNParenthesis(
- RPNFunc(fekUMinus, nil)))));
+ formula := '-(-1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNformula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-1.0,
+ RPNParenthesis(
+ RPNFunc(fekUMinus, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, -(-1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(-(-1));
// Unary minus of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=-B1');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekUMinus, nil))));
+ formula := '-B1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc(fekUMinus, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, -cellB1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(-(2));
+ sollValues[Row] := FloatResult(-cellB1);
// Unary plus
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=+(-1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-1,
- RPNParenthesis(
- RPNFunc(fekUPlus, nil)))));
+ formula := '+(-1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-1.0,
+ RPNParenthesis(
+ RPNFunc(fekUPlus, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, -1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(-1);
+ sollValues[Row] := FloatResult(-1);
- // Unary plus of cell value
+ // Unary plus of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=+(B2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellvalue('B2',
- RPNFunc(fekUPlus, nil))));
+ formula := '+(B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellvalue('B2',
+ RPNParenthesis((
+ RPNFunc(fekUPlus, nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, +cellB2);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(+(-9));
+ sollValues[Row] := FloatResult(+cellB2);
// String result
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '="Hallo"');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo', nil)));
+ formula := '"Hallo"';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo', nil)))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'Hallo');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('Hallo');
+ sollValues[Row] := StringResult('Hallo');
// String concatenation
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '="Hallo"&" world"');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo',
- RPNString(' world',
- RPNFunc(fekConcat, nil)))));
+ formula := '"Hallo"&" world"';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo',
+ RPNString(' world',
+ RPNFunc(fekConcat, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'Hallo' + ' world');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('Hallo' + ' world');
+ sollValues[Row] := StringResult('Hallo' + ' world');
// Equal (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(true=false)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNFunc(fekEqual, nil)))));
+ formula := 'TRUE=FALSE';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNFunc(fekEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true = false);
+ sollValues[Row] := BooleanResult(true = false);
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Equal (strings)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=("Hallo"="world")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo',
- RPNString('world',
- RPNFunc(fekEqual, nil)))));
+ formula := '"Hallo"="world"';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo',
+ RPNString('world',
+ RPNFunc(fekEqual, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg('Hallo' = 'world');
+ sollValues[Row] := BooleanResult('Hallo' = 'world');
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Equal (numbers)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(1=1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.0,
- RPNFunc(fekEqual, nil)))));
+ formula := '1=1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNNumber(1,
+ RPNFunc(fekEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(1=1);
+ sollValues[Row] := BooleanResult(1 = 1);
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Equal (cell)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M1=1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M1',
- RPNNumber(1.0,
- RPNFunc(fekEqual, nil)))));
+ formula := 'M1=1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M1',
+ RPNNumber(1,
+ RPNFunc(fekEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // M1 is "A"
+ sollValues[Row] := BooleanResult(false); // M1 is "A"
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2=1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNNumber(1.0,
- RPNFunc(fekEqual, nil)))));
+ formula := 'M2=1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNNumber(1.0,
+ RPNFunc(fekEqual, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true); // M2 is 1
+ sollValues[Row] := BooleanResult(true); // M2 is 1
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2=N2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNCellValue('N2',
- RPNFunc(fekEqual, nil)))));
+ formula := 'M2=N2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNCellValue('N2',
+ RPNFunc(fekEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // M2 is 1, N2 is 2
+ sollValues[Row] := BooleanResult(false); // M2 is 1, N2 is 2
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(true>false)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNFunc(fekGreater, nil)))));
+ formula := 'TRUE>FALSE';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNFunc(fekGreater, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true > false);
+ sollValues[Row] := BooleanResult(true > false);
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater (strings)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=("Hallo">"world")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo',
- RPNString('world',
- RPNFunc(fekGreater, nil)))));
+ formula := '"Hallo">"world"';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo',
+ RPNString('world',
+ RPNFunc(fekGreater, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg('Hallo' > 'world');
+ sollValues[Row] := BooleanResult('Hallo' > 'world');
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater (numbers)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(1>1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.0,
- RPNFunc(fekGreater, nil)))));
+ formula := '1>1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNNumber(1,
+ RPNFunc(fekGreater, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(1>1);
+ sollValues[Row] := BooleanResult(1 > 1);
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater (cell)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2>1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNNumber(1.0,
- RPNFunc(fekGreater, nil)))));
+ formula := 'M2>1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNNumber(1,
+ RPNFunc(fekGreater, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteBoolvalue(Row, 2, SollValues[Row].ResBoolean);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // M2 is 1
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2>N2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNCellValue('N2',
- RPNFunc(fekGreater, nil)))));
+ formula := 'M2>N2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNCellValue('N2',
+ RPNFunc(fekGreater, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // M2 is 1, N2 is 2
+ sollValues[Row] := BooleanResult(false); // M2 is 1, N2 is 2
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater equal (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(true>=false)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNParenthesis(
- RPNFunc(fekGreaterEqual, nil))))));
+ formula := 'TRUE>=FALSE';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNFunc(fekGreaterEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true >= false);
+ sollValues[Row] := BooleanResult(true >= false);
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater equal (strings)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=("Hallo">="world")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo',
- RPNString('world',
- RPNParenthesis(
- RPNFunc(fekGreaterEqual, nil))))));
+ formula := '"Hallo">="world"';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo',
+ RPNString('world',
+ RPNFunc(fekGreaterEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg('Hallo' >= 'world');
+ sollValues[Row] := BooleanResult('Hallo' >= 'world');
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater equal (numbers)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(1>=1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.0,
- RPNParenthesis(
- RPNFunc(fekGreaterEqual, nil))))));
+ formula := '1>=1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNNumber(1,
+ RPNFunc(fekGreaterEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(1>=1);
+ sollValues[Row] := BooleanResult(1 >= 1);
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Greater equal(cell)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2>=1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNNumber(1.0,
- RPNFunc(fekGreaterEqual, nil)))));
+ formula := 'M2>=1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNNumber(1,
+ RPNFunc(fekGreaterEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true); // M2 is 1
+ sollValues[Row] := BooleanResult(true); // M2 is 1
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2>=N2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNCellValue('N2',
- RPNFunc(fekGreaterEqual, nil)))));
+ formula := 'M2>=N2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNCellValue('N2',
+ RPNFunc(fekGreaterEqual, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // M2 is 1, N2 is 2
+ sollValues[Row] := BooleanResult(false); // M2 is 1, N2 is 2
+ Myworksheet.WriteUTF8Text(Row, 2, BoolToStr(SollValues[Row].ResBoolean, 'TRUE','FALSE'));
// Less (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(truefalse)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNParenthesis(
- RPNFunc(fekNotEqual, nil))))));
+ formula := 'TRUE<>FALSE';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNFunc(fekNotEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true <> false);
+ sollValues[Row] := BooleanResult(true <> false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
// Not equal (strings)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=("Hallo"<>"world")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo',
- RPNString('world',
- RPNParenthesis(
- RPNFunc(fekNotEqual, nil))))));
+ formula := '"Hallo"<>"world"';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo',
+ RPNString('world',
+ RPNFunc(fekNotEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg('Hallo' <> 'world');
+ sollValues[Row] := BooleanResult('Hallo' <> 'world');
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
// Not equal (numbers)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(1<>1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.0,
- RPNParenthesis(
- RPNFunc(fekNotEqual, nil))))));
+ formula := '1<>1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNNumber(1,
+ RPNFunc(fekNotEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(1<>1);
+ sollValues[Row] := BooleanResult(1 <> 1);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
// Not equal (cell)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2<>1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNNumber(1.0,
- RPNFunc(fekNotEqual, nil)))));
+ formula := 'M2<>1';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNNumber(1,
+ RPNFunc(fekNotEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // M2 is 1
+ sollValues[Row] := BooleanResult(false); // M2 is 1
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=(M2<>N2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('M2',
- RPNCellValue('N2',
- RPNFunc(fekNotEqual, nil)))));
+ formula := 'M2<>N2';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('M2',
+ RPNCellValue('N2',
+ RPNFunc(fekNotEqual, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true); // M2 is 1, N2 is 2
+ sollValues[Row] := BooleanResult(true); // M2 is 1, N2 is 2
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
{------------------------------------------------------------------------------}
{ Math }
{------------------------------------------------------------------------------}
// ABS
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=abs(-1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-1,
- RPNFunc(fekABS, nil))));
+ formula := 'ABS(-1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-1,
+ RPNFunc('ABS', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(abs(-1));
+ sollValues[Row] := FloatResult(abs(-1));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ARCCOS - valid result
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=acos(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekACOS, nil))));
+ formula := 'ACOS(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('ACOS', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(arccos(number));
+ sollValues[Row] := FloatResult(arccos(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// Don't explicitly use the constant here because this will be "extended"
// which is slightly different from the double value used in the formula
// ACOS - error result (arccos is not defined outside the interval [-1..1]
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=acos(-2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-2,
- RPNFunc(fekACOS, nil))));
+ formula := 'ACOS(-2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-2,
+ RPNFunc('ACOS', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverFlow);
+ sollValues[Row] := ErrorResult(errOverFlow);
+ MyWorksheet.WriteErrorValue(Row, 2, errOverflow);
// ARCCOSH - valid result
inc(Row);
number := 1.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=acosh(1.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(number,
- RPNFunc(fekACOSH, nil))));
+ formula := 'ACOSH(1.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(number,
+ RPNFunc('ACOSH', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(arccosh(number));
+ sollValues[Row] := FloatResult(arccosh(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ACOSH - error result (arccos is not defined below 1
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=acosh(0.9)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.9,
- RPNFunc(fekACOSH, nil))));
+ formula := 'ACOSH(0.9)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.9,
+ RPNFunc('ACOSH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverFlow);
+ sollValues[Row] := ErrorResult(errOverFlow);
+ MyWorksheet.WriteErrorValue(Row, 2, errOverflow);
// ASIN
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=asin(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekASIN, nil))));
+ formula := 'ASIN(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('ASIN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(arcsin(number));
+ sollValues[Row] := FloatResult(arcsin(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ASIN - error result (arcsin is not defined outside the interval [-1..1]
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=asin(-2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-2,
- RPNFunc(fekASIN, nil))));
+ formula := 'ASIN(-2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-2,
+ RPNFunc('ASIN', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverFlow);
+ sollValues[Row] := ErrorResult(errOverFlow);
+ MyWorksheet.WriteErrorValue(Row, 2, errOverflow);
// ARCSINH
inc(Row);
number := 1.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=asinh(1.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.1,
- RPNFunc(fekASINH, nil))));
+ formula := 'ASINH(1.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.1,
+ RPNFunc('ASINH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(arcsinh(number));
+ sollValues[Row] := FloatResult(arcsinh(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ATAN
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=atan(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekATAN, nil))));
+ formula := 'ATAN(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('ATAN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(arctan(number));
+ sollValues[Row] := FloatResult(arctan(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ATANH - valid result
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=atanh(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekATANH, nil))));
+ formula := 'ATANH(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('ATANH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(arctanh(number));
+ sollValues[Row] := FloatResult(arctanh(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ATANH - error result (arctan is only defined within ]-1,1[
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=atanh(1.0)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNFunc(fekATANH, nil))));
+ formula := 'ATANH(1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNFunc('ATANH', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverFlow);
+ sollValues[Row] := ErrorResult(errOverFlow);
+ MyWorksheet.WriteErrorValue(Row, 2, errOverFlow);
// COS
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=cos(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekCOS, nil))));
+ formula := 'COS(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('COS', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(cos(number));
+ sollValues[Row] := FloatResult(cos(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// COSH
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=cosh(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekCOSH, nil))));
+ formula := 'COSH(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('COSH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(cosh(number));
+ sollValues[Row] := FloatResult(cosh(number));
+ MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// DEGREES
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=degrees(1.0)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNFunc(fekDEGREES, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(radToDeg(1.0));
+ if AFormat <> sfExcel2 then
+ begin
+ inc(Row);
+ formula := 'DEGREES(1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNFunc('DEGREES', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(radToDeg(1.0));
+ myWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
+ end;
// EXP
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=exp(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekEXP, nil))));
+ formula := 'EXP(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('EXP', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, exp(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(exp(number));
+ sollValues[Row] := FloatResult(exp(number));
// INT (positive argument)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=int(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekINT, nil))));
+ number := 0.1;
+ formula := 'INT(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('INT', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, floor(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(floor(0.1));
+ sollValues[Row] := FloatResult(floor(number));
// INT (negative argument)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=int(-0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-0.1,
- RPNFunc(fekINT, nil))));
+ formula := 'INT(-0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-0.1,
+ RPNFunc('INT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, floor(-0.1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(floor(-0.1));
+ sollValues[Row] := FloatResult(floor(-0.1));
// LN - valid result
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=ln(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekLN, nil))));
+ formula := 'LN(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('LN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, ln(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(ln(number));
+ sollValues[Row] := FloatResult(ln(number));
// LN - error due to argument = 0
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ln(0.0)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.0,
- RPNFunc(fekLN, nil))));
+ formula := 'LN(0)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.0,
+ RPNFunc('LN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// LN - error due to argument < 0
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ln(-0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-0.1,
- RPNFunc(fekLN, nil))));
+ formula := 'LN(-0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-0.1,
+ RPNFunc('LN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// LOG10 - valid result
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=log10(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekLOG10, nil))));
+ formula := 'LOG10(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('LOG10', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, log10(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(log10(number));
+ sollValues[Row] := FloatResult(log10(number));
// LOG10 - error due to argument = 0
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=log10(0.0)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.0,
- RPNFunc(fekLOG10, nil))));
+ formula := 'LOG10(0)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.0,
+ RPNFunc('LOG10', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// LOG10 - error due to argument < 0
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=log10(-0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-0.1,
- RPNFunc(fekLOG10, nil))));
+ formula := 'LOG10(-0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-0.1,
+ RPNFunc('LOG10', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverFlow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// LOG - valid result (2 arguments)
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, 2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNNumber(2,
- RPNFunc(fekLOG, 2, nil)))));
+ formula := 'LOG(0.1,2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNNumber(2,
+ RPNFunc('LOG', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, logn(2, number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(logn(2, number));
+ sollValues[Row] := FloatResult(logn(2, number));
+
+{ removed until new formula system supports missing arguments...
// LOG - valid result (2 arguments, base missing)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, )');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNMissingArg(
- RPNFunc(fekLOG, 2, nil)))));
+ formula := 'LOG(0.1, )';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNMissingArg(
+ RPNFunc('LOG', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
+}
// LOG - valid result (1 argument)
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekLOG, 1, nil))));
+ formula := 'LOG(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('LOG', 1, nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, logn(10, number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(logn(10, number));
+ sollValues[Row] := FloatResult(logn(10, number));
// LOG - negative base
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=log(0.1, -2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNNumber(-2,
- RPNFunc(fekLOG, 2, nil)))));
+ formula := 'LOG(0.1,-2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNNumber(-2,
+ RPNFunc('LOG', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// LOG - negative value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=log(-0.1, 2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-0.1,
- RPNNumber(2.0,
- RPNFunc(fekLOG, 2, nil)))));
+ formula := 'LOG(-0.1,2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-0.1,
+ RPNNumber(2.0,
+ RPNFunc('LOG', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// LOG of cell
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=log(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekLOG, 1, nil))));
+ formula := 'LOG(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('LOG', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, logn(10, 2));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(logn(10, 2));
+ sollValues[Row] := FloatResult(logn(10, 2));
// PI
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=pi()');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI, nil)));
+ formula := 'PI()';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNFunc('PI', nil)))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, pi);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(pi);
+ sollValues[Row] := FloatResult(pi);
- // RADIANS
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=radians(60)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(60,
- RPNFunc(fekRADIANS, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(degtorad(60));
+ if AFormat <> sfExcel2 then
+ begin
+ // RADIANS
+ inc(Row);
+ formula := 'RADIANS(60)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(60,
+ RPNFunc('RADIANS', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, DegToRad(60));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(degtorad(60));
- // RADIANS of cell value
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=radians(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekRADIANS, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(degtorad(2));
+ // RADIANS of cell value
+ inc(Row);
+ formula := 'RADIANS(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('RADIANS', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, DegToRad(cellB1));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(degtorad(cellB1));
+ end;
// RAND
// Test omitted because we cannot enforce getting the same random number back
@@ -870,138 +1397,209 @@
// ROUND
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ROUND(pi(), 2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekPI,
- RPNNumber(2,
- RPNFunc(fekROUND, nil)))));
+ formula := 'ROUND(PI(),2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNFunc('PI',
+ RPNNumber(2,
+ RPNFunc('ROUND', nil)))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, RoundTo(pi, 2));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(RoundTo(pi, 2));
+ sollValues[Row] := FloatResult(RoundTo(pi, 2));
// SIGN
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=sign(-0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-0.1,
- RPNFunc(fekSIGN, nil))));
+ number := -0.1;
+ formula := 'SIGN(-0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-0.1,
+ RPNFunc('SIGN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, sign(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sign(-0.1));
+ sollValues[Row] := FloatResult(sign(number));
// SIGN of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=sign(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekSIGN, nil))));
+ formula := 'SIGN(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('SIGN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, sign(cellB1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sign(2));
+ sollValues[Row] := FloatResult(sign(cellB1));
// SIN
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=sin(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekSIN, nil))));
+ formula := 'SIN(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('SIN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, sin(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sin(number));
+ sollValues[Row] := FloatResult(sin(number));
- // SIN of cell value
+ // SIN of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=sin(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekSIN, nil))));
+ formula := 'SIN(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('SIN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, sin(cellB1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sin(2));
+ sollValues[Row] := FloatResult(sin(cellB1));
// SINH
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=sinh(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekSINH, nil))));
+ formula := 'SINH(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('SINH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, sinh(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sinh(number));
+ sollValues[Row] := FloatResult(sinh(number));
// SINH of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=sinh(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekSINH, nil))));
+ formula := 'SINH(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('SINH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, sinh(cellB1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sinh(2));
+ sollValues[Row] := FloatResult(sinh(cellB1));
// SQRT - valid result
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekSQRT, nil))));
+ formula := 'SQRT(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('SQRT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, sqrt(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sqrt(number));
+ sollValues[Row] := FloatResult(sqrt(number));
// SQRT - error (negative argument)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(-0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(-0.1,
- RPNFunc(fekSQRT, nil))));
+ formula := 'SQRT(-0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(-0.1,
+ RPNFunc('SQRT', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, GetErrorValueStr(errOverflow));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateErrorArg(errOverflow);
+ sollValues[Row] := ErrorResult(errOverflow);
// SQRT of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=sqrt(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellvalue('B1',
- RPNFunc(fekSQRT, nil))));
+ formula := 'SQRT(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellvalue('B1',
+ RPNFunc('SQRT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, sqrt(cellB1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(sqrt(2));
+ sollValues[Row] := FloatResult(sqrt(cellB1));
// TAN - valid result
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=tan(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekTAN, nil))));
+ formula := 'TAN(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('TAN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, tan(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(tan(number));
+ sollValues[Row] := FloatResult(tan(number));
// TAN - error (argument = pi/2)
// This test is omitted because it is difficult to exactly hit pi/2.
// TAN of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=tan(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekTAN, nil))));
+ formula := 'TAN(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('TAN', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, tan(cellB1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(tan(2));
+ sollValues[Row] := FloatResult(tan(cellB1));
// TANH
inc(Row);
number := 0.1;
- MyWorksheet.WriteUTF8Text(Row, 0, '=tanh(0.1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0.1,
- RPNFunc(fekTANH, nil))));
+ formula := 'TANH(0.1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0.1,
+ RPNFunc('TANH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, tanh(number));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(tanh(number));
+ sollValues[Row] := FloatResult(tanh(number));
// TANH of cell value
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=tanh(B1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('B1',
- RPNFunc(fekTANH, nil))));
+ formula := 'TANH(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('B1',
+ RPNFunc('TANH', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, tanh(cellB1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(tanh(2));
+ sollValues[Row] := FloatResult(tanh(cellB1));
{------------------------------------------------------------------------------}
@@ -1010,221 +1608,351 @@
// DATE
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=DATE(2014, 7, 1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(2014,
- RPNNumber(7,
- RPNNumber(1,
- RPNFunc(fekDATE, nil))))));
+ formula := 'DATE(2014,7,1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2014,
+ RPNNumber(7,
+ RPNNumber(1,
+ RPNFunc('DATE', nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteDateTime(Row, 2, EncodeDate(2014,7,1));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(EncodeDate(2014,7,1));
+ sollValues[Row] := DateTimeResult(EncodeDate(2014,7,1));
+
+ // DATEDIF
+ if AFormat <> sfExcel2 then begin
+ inc(Row);
+ formula := 'DATEDIF(DATE(2014,7,1),DATE(2014,1,1),"D")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2014,
+ RPNNumber(7,
+ RPNNumber(1,
+ RPNFunc('DATE',
+ RPNNumber(2014,
+ RPNNumber(1,
+ RPNNumber(1,
+ RPNFunc('DATE',
+ RPNString('D',
+ RPNFUNC('DATEDIF',
+ nil))))))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, EncodeDate(2014,7,1)-EncodeDate(2014,1,1));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(round(EncodeDate(2014,7,1)-EncodeDate(2014,1,1)));
+ end;
// DATEVALUE
inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=DATEVALUE("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekDATEVALUE, nil))));
+ formula := 'DATEVALUE("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('DATEVALUE', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteDateTimeFormat(Row, 1, nfShortDate);
+ MyWorksheet.WriteDateTime(Row, 2, StrToDate(s), nfShortDate);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(EncodeDate(2014,7,1));
+ sollValues[Row] := DateTimeResult(EncodeDate(2014,7,1));
// DAY / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=DAY(DATE(2014,7,1))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(2014,
- RPNNumber(7,
- RPNNumber(1,
- RPNFunc(fekDATE,
- RPNFunc(fekDAY, nil)))))));
+ formula := 'DAY(DATE(2014,7,1))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2014,
+ RPNNumber(7,
+ RPNNumber(1,
+ RPNFunc('DATE',
+ RPNFunc('DAY', nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := IntegerResult(1);
// DAY / argument string
inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=DAY("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekDAY, nil))));
+ formula := 'DAY("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('DAY', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := IntegerResult(1);
// HOUR / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=HOUR(TIME(9, 59, 20))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(9,
- RPNNumber(59,
- RPNNumber(20,
- RPNFunc(fekTIME,
- RPNFunc(fekHOUR, nil)))))));
+ formula := 'HOUR(TIME(9,59,20))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(9,
+ RPNNumber(59,
+ RPNNumber(20,
+ RPNFunc('TIME',
+ RPNFunc('HOUR', nil)))))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 9);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(9);
+ sollValues[Row] := IntegerResult(9);
// HOUR / argument string
inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=HOUR("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekHOUR, nil))));
+ formula := 'HOUR("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('HOUR', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, 9);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(9);
+ sollValues[Row] := IntegerResult(9);
// MINUTE / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MINUTE(TIME(9, 59, 20))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(9,
- RPNNumber(59,
- RPNNumber(20,
- RPNFunc(fekTIME,
- RPNFunc(fekMINUTE, nil)))))));
+ formula := 'MINUTE(TIME(9,59,20))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(9,
+ RPNNumber(59,
+ RPNNumber(20,
+ RPNFunc('TIME',
+ RPNFunc('MINUTE', nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, 59);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(59);
+ sollValues[Row] := IntegerResult(59);
// MINUTE / argument string
inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=MINUTE("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekMINUTE, nil))));
+ formula := 'MINUTE("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('MINUTE', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 59);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(59);
+ sollValues[Row] := IntegerResult(59);
// MONTH / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MONTH(DATE(2014,7,1))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(2014,
- RPNNumber(7,
- RPNNumber(1,
- RPNFunc(fekDATE,
- RPNFunc(fekMONTH, nil)))))));
+ formula := 'MONTH(DATE(2014,7,1))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2014,
+ RPNNumber(7,
+ RPNNumber(1,
+ RPNFunc('DATE',
+ RPNFunc('MONTH', nil)))))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 7);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(7);
+ sollValues[Row] := IntegerResult(7);
// MONTH / argument string
inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=MONTH("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekMONTH, nil))));
+ formula := 'MONTH("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('MONTH', nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 7);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(7);
+ sollValues[Row] := IntegerResult(7);
// NOW
inc(Row);
+ formula := 'NOW()';
// Make sure that, when the file is read we still have the same second
// Otherwise there would be a mismatch.
repeat
- t := now();
- DecodeTime(t, hr, min, sec, msec);
+ number := now();
+ DecodeTime(number, hr, min, sec, msec);
until msec < 500;
- MyWorksheet.WriteUTF8Text(Row, 0, '=NOW()');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekNOW, nil)));
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNFunc('NOW', nil)))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteDateTimeFormat(Row, 1, nfShortDateTime);
+ MyWorksheet.WriteDateTime(Row, 2, number, nfShortDateTime);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(t);
+ sollValues[Row] := DateTimeResult(number);
// SECOND / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SECOND(TIME(9, 59, 20))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(9,
- RPNNumber(59,
- RPNNumber(20,
- RPNFunc(fekTIME,
- RPNFunc(fekSECOND, nil)))))));
+ formula := 'SECOND(TIME(9,59,20))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(9,
+ RPNNumber(59,
+ RPNNumber(20,
+ RPNFunc('TIME',
+ RPNFunc('SECOND', nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 1, 20);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(20);
+ sollValues[Row] := IntegerResult(20);
// SECOND / argument string
inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=SECOND("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekSECOND, nil))));
+ formula := 'SECOND("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('SECOND', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 1, 20);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(20);
+ sollValues[Row] := IntegerResult(20);
// TIME
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=TIME(9, 59, 20)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(9,
- RPNNumber(59,
- RPNNumber(20,
- RPNFunc(fekTIME, nil))))));
+ formula := 'TIME(9,59,20)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(9,
+ RPNNumber(59,
+ RPNNumber(20,
+ RPNFunc('TIME', nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteDateTimeFormat(Row, 1, nfLongTime);
+ MyWorksheet.WriteDateTime(Row, 2, EncodeTime(9, 50, 20, 0), nfLongTime);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(EncodeTime(9, 59, 20, 0));
+ sollValues[Row] := DateTimeResult(EncodeTime(9, 59, 20, 0));
// TIMEVALUE
inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization!
- MyWorksheet.WriteUTF8Text(Row, 0, '=TIMEVALUE("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekTIMEVALUE, nil))));
+ formula := 'TIMEVALUE("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('TIMEVALUE', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteDateTimeFormat(Row, 1, nfLongTime);
+ MyWorksheet.WriteDateTime(Row, 2, EncodeTime(9, 59, 20, 0), nfLongTime);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(EncodeTime(9, 59, 20, 0));
+ sollValues[Row] := DateTimeResult(EncodeTime(9, 59, 20, 0));
// TODAY
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=TODAY()');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekTODAY, nil)));
+ formula := 'TODAY()';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNFunc('TODAY', nil)))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteDateTimeFormat(Row, 1, nfShortDate);
+ Myworksheet.WriteDateTime(Row, 2, Date(), nfShortDate);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(Date());
+ sollValues[Row] := DateTimeResult(Date());
// WEEKDAY / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=WEEKDAY(DATE(2014,7,1))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(2014,
- RPNNumber(7,
- RPNNumber(1,
- RPNFunc(fekDATE,
- RPNFunc(fekWEEKDAY, 1, nil)))))));
+ formula := 'WEEKDAY(DATE(2014,7,1))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2014,
+ RPNNumber(7,
+ RPNNumber(1,
+ RPNFunc('DATE',
+ RPNFunc('WEEKDAY', 1, nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, DayOfWeek(EncodeDate(2014,7,1)));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(DayOfWeek(EncodeDate(2014,7,1)));
+ sollValues[Row] := IntegerResult(DayOfWeek(EncodeDate(2014,7,1)));
// WEEKDAY / argument string
inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=WEEKDAY("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekWEEKDAY, 1, nil))));
+ formula := 'WEEKDAY("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('WEEKDAY', 1, nil))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, DayOfWeek(EncodeDate(2014,7,1)));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(DayOfWeek(EncodeDate(2014,7,1)));
+ sollValues[Row] := IntegerResult(DayOfWeek(EncodeDate(2014,7,1)));
// YEAR / argument number
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=YEAR(DATE(2014,7,1))');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(2014,
- RPNNumber(7,
- RPNNumber(1,
- RPNFunc(fekDATE,
- RPNFunc(fekYEAR, nil)))))));
+ formula := 'YEAR(DATE(2014,7,1))';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(2014,
+ RPNNumber(7,
+ RPNNumber(1,
+ RPNFunc('DATE',
+ RPNFunc('YEAR', nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 2014);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2014);
+ sollValues[Row] := IntegerResult(2014);
// YEAR / argument string
inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
- MyWorksheet.WriteUTF8Text(Row, 0, '=YEAR("'+s+'")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(s,
- RPNFunc(fekYEAR, nil))));
+ formula := 'YEAR("'+s+'")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(s,
+ RPNFunc('YEAR', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 2014);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2014);
+ sollValues[Row] := IntegerResult(2014);
{------------------------------------------------------------------------------}
@@ -1232,369 +1960,568 @@
{------------------------------------------------------------------------------}
// AVEDEV
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=AVEDEV(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekAVEDEV, 5, nil))))))));
- SetLength(sollValues, Row+1);
- number := mean(STATS_NUMBERS);
- for k := 0 to High(STATS_NUMBERS) do numberArray[k] := abs(STATS_NUMBERS[k] - number);
- // these values are the absolute deviations from mean (1.0)
- sollValues[Row] := CreateNumberArg(mean(numberArray));
-
- // AVERAGE
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=AVERAGE(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekAVERAGE, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(mean(STATS_NUMBERS));
-
- // AVERAGE of cell block
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=AVERAGE(B1:B2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('B1:B2',
- RPNFunc(fekAVERAGE, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(mean([2.0, -9.0]));
-
- // COUNT
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNT(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekCOUNT, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(5);
-
- // COUNT of cell range (no empty cells)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNT(B1:B2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellrange('B1:B2',
- RPNFunc(fekCOUNT, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2);
-
- // COUNT of cell range (no empty cells, but with non-numeric cells)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNT(A1:B2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellrange('A1:B2',
- RPNFunc(fekCOUNT, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2);
-
- // COUNT of cell range (with empty cells)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNT(B1:C2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellrange('B1:C2',
- RPNFunc(fekCOUNT, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2);
-
- // COUNTA
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTA(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekCOUNTA, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(5);
-
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTA(1, 1.1, 1.2, 0.9, 0.8, "A", "B")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNString('A',
- RPNString('B',
- RPNFunc(fekCOUNTA, 7, nil))))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(7);
-
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTA(A1:C2,1,2,"A")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('A1:C2',
- RPNNumber(1,
- RPNNumber(2,
- RPNString('A',
- RPNFunc(fekCOUNTA, 4, nil)))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(7);
-
if AFormat <> sfExcel2 then begin
- // COUNTBLANK
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTBLANK(A1:D2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('A1:D2',
- RPNFunc(fekCOUNTBLANK, nil))));
+ formula := 'AVEDEV(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('AVEDEV', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(4);
+ number := mean(STATS_NUMBERS);
+ for k := 0 to High(STATS_NUMBERS) do numberArray[k] := abs(STATS_NUMBERS[k] - number);
+ // these values are the absolute deviations from mean (1.0)
+ sollValues[Row] := FloatResult(mean(numberArray));
+ MyWorksheet.WriteNumber(Row, 2, mean(numberArray));
end;
- if AFormat <> sfExcel2 then begin
- // COUNTIF
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTIF(M1:N3,1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNNumber(1, // "1" is in M2
- RPNFunc(fekCOUNTIF, nil)))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
-
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTIF(M1:N3,">=1")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNString('>=1', // M2=1, N2=2 --> 2
- RPNFunc(fekCOUNTIF, nil)))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2);
-
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTIF(M1:N3,"<2")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNString('<2', // M2=1, N2=2 --> 1
- RPNFunc(fekCOUNTIF, nil)))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
-
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTIF(M1:N3,"<>2")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNString('<>2', // N2=2, 6 other cells --> 5
- RPNFunc(fekCOUNTIF, nil)))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(5);
-
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTIF(M1:N3,M1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNCellValue('M1', // M1="A", N1="A" -> 2
- RPNFunc(fekCOUNTIF, nil)))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(2);
- end;
-
- // MAX
+// AVERAGE
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MAX(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekMAX, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(MaxValue(STATS_NUMBERS));
-
- // MAX of cell range (no empty cells)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MAX(B1:B2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('B1:B2',
- RPNFunc(fekMAX, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(MaxValue([2.0, -9.0]));
-
- // MAX of cell range (incl empty cells)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MAX(B1:C2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('B1:C2',
- RPNFunc(fekMAX, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(MaxValue([2.0, -9.0]));
-
- // MIN
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MIN(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekMIN, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(MinValue(STATS_NUMBERS));
-
- // PRODUCT
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=PRODUCT(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekPRODUCT, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(STATS_NUMBERS[0]*STATS_NUMBERS[1]*STATS_NUMBERS[2]*STATS_NUMBERS[3]*STATS_NUMBERS[4]);
-
- // STDEV
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=STDEV(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekSTDEV, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(StdDev(STATS_NUMBERS));
-
- // STDEVP
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=STDEVP(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekSTDEVP, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(PopnStdDev(STATS_NUMBERS));
-
- // SUM
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUM(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekSUM, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(Sum(STATS_NUMBERS));
-
- if AFormat <> sfExcel2 then begin
- // SUMSQ
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMSQ(1, 1.1, 1.2, 0.9, 0.8)');
+ formula := 'AVERAGE(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNNumber(1.0,
RPNNumber(1.1,
RPNNumber(1.2,
RPNNumber(0.9,
RPNNumber(0.8,
- RPNFunc(fekSUMSQ, 5, nil))))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(SumOfSquares(STATS_NUMBERS));
+ RPNFunc('AVERAGE', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, mean(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(mean(STATS_NUMBERS));
+ // AVERAGE of cell block
+ inc(Row);
+ formula := 'AVERAGE(B1:B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('B1:B2',
+ RPNFunc('AVERAGE', 1, nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, mean([cellB1, cellB2]));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(mean([cellB1, cellB2]));
+
+ // COUNT
+ inc(Row);
+ formula := 'COUNT(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('COUNT', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 5);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(5);
+
+ // COUNT of cell range (no empty cells)
+ inc(Row);
+ formula := 'COUNT(B1:B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellrange('B1:B2',
+ RPNFunc('COUNT', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(2);
+
+ // COUNT of cell range (no empty cells, but with non-numeric cells)
+ inc(Row);
+ formula := 'COUNT(A1:B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellrange('A1:B2',
+ RPNFunc('COUNT', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, 2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(2);
+
+ // COUNT of cell range (with empty cells)
+ inc(Row);
+ formula := 'COUNT(B1:C2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellrange('B1:C2',
+ RPNFunc('COUNT', 1, nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(2);
+
+ // COUNTA
+ formula := 'COUNTA(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('COUNTA', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 5);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(5);
+
+ formula := 'COUNTA(1,1.1,1.2,0.9,0.8,"A","B")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNString('A',
+ RPNString('B',
+ RPNFunc('COUNTA', 7, nil))))))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 7);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(7);
+
+ inc(Row);
+ formula := 'COUNTA(A1:D2,1,2,"A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('A1:D2',
+ RPNNumber(1,
+ RPNNumber(2,
+ RPNString('A',
+ RPNFunc('COUNTA', 4, nil)))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 9);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(9);
+
+ // COUNTBLANK
+ if AFormat <> sfExcel2 then begin
+ inc(Row);
+ formula := 'COUNTBLANK(A1:D2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('A1:D2',
+ RPNFunc('COUNTBLANK', nil))))
+ else
+ myworksheet.WriteFormula(row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(2);
+ end;
+
+ // COUNTIF
+{ currently no support for COUNTIF
+
+ if AFormat <> sfExcel2 then begin
+ inc(Row);
+ formula := 'COUNTIF(M1:N3,1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNNumber(1, // "1" is in M2
+ RPNFunc('COUNTIF', nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 1);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(1);
+
+ inc(Row);
+ formula := 'COUNTIF(M1:N3,">=1")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNString('>=1', // M2=1, N2=2 --> 2
+ RPNFunc('COUNTIF', nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(2);
+
+ inc(Row);
+ formula := 'COUNTIF(M1:N3,"<2")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNString('<2', // M2=1, N2=2 --> 1
+ RPNFunc('COUNTIF', nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 1);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(1);
+
+ inc(Row);
+ formula := 'COUNTIF(M1:N3,"<>2")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNString('<>2', // N2=2, 6 other cells --> 5
+ RPNFunc('COUNTIF', nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 5);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(5);
+
+ inc(Row);
+ formula := 'COUNTIF(M1:N3,M1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNCellValue('M1', // M1="A", N1="A" -> 2
+ RPNFunc('COUNTIF', nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNubmer(Row, 2, 2);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(2);
+ end;
+ }
+ // MAX
+ inc(Row);
+ formula := 'MAX(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('MAX', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, MaxValue(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(MaxValue(STATS_NUMBERS));
+
+ // MAX of cell range (no empty cells)
+ inc(Row);
+ formula := 'MAX(B1:B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('B1:B2',
+ RPNFunc('MAX', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, MaxValue([cellB1, cellB2]));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(MaxValue([cellB1, cellB2]));
+
+ // MAX of cell range (incl empty cells)
+ inc(Row);
+ formula := 'MAX(B1:C2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('B1:C2',
+ RPNFunc('MAX', 1, nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, MaxValue([cellB1, cellB2]));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(MaxValue([cellB1, cellB2]));
+
+ // MIN
+ inc(Row);
+ formula := 'MIN(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('MIN', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, MinValue(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(MinValue(STATS_NUMBERS));
+
+ // MIN of cell range (no empty cells)
+ inc(Row);
+ formula := 'MIN(B1:B2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('B1:B2',
+ RPNFunc('MIN', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, MinValue([cellB1, cellB2]));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(MinValue([cellB1, cellB2]));
+
+ // MIN of cell range (incl empty cells)
+ inc(Row);
+ formula := 'MIN(B1:C2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('B1:C2',
+ RPNFunc('MIN', 1, nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, MinValue([cellB1, cellB2]));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(MinValue([cellB1, cellB2]));
+
+ // PRODUCT
+ inc(Row);
+ formula := 'PRODUCT(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('PRODUCT', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(STATS_NUMBERS[0]*STATS_NUMBERS[1]*STATS_NUMBERS[2]*STATS_NUMBERS[3]*STATS_NUMBERS[4]);
+ MyWorksheet.WriteNumber(Row, 2, sollvalues[Row].ResFloat);
+
+ // STDEV
+ inc(Row);
+ formula := 'STDEV(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('STDEV', 5, nil))))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, StdDev(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(StdDev(STATS_NUMBERS));
+
+ // STDEVP
+ inc(Row);
+ formula := 'STDEVP(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('STDEVP', 5, nil))))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, PopnStdDev(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(PopnStdDev(STATS_NUMBERS));
+
+ // SUM
+ inc(Row);
+ formula := 'SUM(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('SUM', 5, nil))))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, Sum(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(Sum(STATS_NUMBERS));
+
+ // SUMSQ
+ if AFormat <> sfExcel2 then begin
+ inc(Row);
+ formula := 'SUMSQ(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('SUMSQ', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, SumOfSquares(STATS_NUMBERS));
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(SumOfSquares(STATS_NUMBERS));
+
+ { ---- SUMIF currently not supported
// SUMIF
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMIF(M1:N3,1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNNumber(1, // "1" is in M2
- RPNFunc(fekSUMIF, 2, nil)))));
+ formula = 'SUMIF(M1:N3,1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNNumber(1, // "1" is in M2
+ RPNFunc('SUMIF', 2, nil)))))
+ else
+ MyWorksheet.WriteRPNFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMIF(M1:N3,">=1")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNString('>=1', // M2=1, N2=2 --> 2
- RPNFunc(fekSUMIF, 2, nil)))));
+ formula := 'SUMIF(M1:N3,">=1")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formul);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNString('>=1', // M2=1, N2=2 --> 2
+ RPNFunc('SUMIF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1 formula);
+ MyWorksheet.WriteNumber(Row, 2, 1.0+2.0);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1+2);
+ sollValues[Row] := FloatResult(1.0 + 2.0);
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMIF(M1:N3,"<2")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNString('<2', // M2=1, N2=2 --> 1
- RPNFunc(fekSUMIF, 2, nil)))));
+ formula := 'SUMIF(M1:N3,"<2")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNString('<2', // M2=1, N2=2 --> 1
+ RPNFunc('SUMIF', 2, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteFormula(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMIF(M1:N3,"<>2")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNString('<>2', // N2=2, 6 other cells --> 5
- RPNFunc(fekSUMIF, 2, nil)))));
+ formula := 'SUMIF(M1:N3,"<>2")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNString('<>2', // N2=2, 6 other cells --> 5
+ RPNFunc('SUMIF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMIF(M1:N3,M1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNCellValue('M1', // M1="A", N1="A" -> 2
- RPNFunc(fekSUMIF, 2, nil)))));
+ formula := 'SUMIF(M1:N3,M1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNCellValue('M1', // M1="A", N1="A" -> 2
+ RPNFunc('SUMIF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 0);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(0); // no numbers!
+ sollValues[Row] := FloatResult(0); // no numbers!
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUMIF(M1:N3,M2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRange('M1:N3',
- RPNCellValue('M2', // M2=1
- RPNFunc(fekSUMIF, 2, nil)))));
+ formula := 'SUMIF(M1:N3,M2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRange('M1:N3',
+ RPNCellValue('M2', // M2=1
+ RPNFunc('SUMIF', 2, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
+ }
end;
// VAR
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=VAR(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekVAR, 5, nil))))))));
+ formula := 'VAR(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('VAR', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, Variance(STATS_NUMBERS));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(Variance(STATS_NUMBERS));
+ sollValues[Row] := FloatResult(Variance(STATS_NUMBERS));
// VARP
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=VARP(1, 1.1, 1.2, 0.9, 0.8)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1.0,
- RPNNumber(1.1,
- RPNNumber(1.2,
- RPNNumber(0.9,
- RPNNumber(0.8,
- RPNFunc(fekVARP, 5, nil))))))));
+ formula := 'VARP(1,1.1,1.2,0.9,0.8)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1.0,
+ RPNNumber(1.1,
+ RPNNumber(1.2,
+ RPNNumber(0.9,
+ RPNNumber(0.8,
+ RPNFunc('VARP', 5, nil))))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, PopnVariance(STATS_NUMBERS));
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(PopnVariance(STATS_NUMBERS));
+ sollValues[Row] := FloatResult(PopnVariance(STATS_NUMBERS));
{------------------------------------------------------------------------------}
@@ -1603,172 +2530,257 @@
// AND of one values (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=AND(true)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNFunc(fekAND, 1, nil))));
+ formula := 'AND(TRUE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNFunc('AND', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'TRUE');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true);
+ sollValues[Row] := BooleanResult(true);
// AND of two values (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=AND(true,false)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNFunc(fekAND, 2, nil)))));
+ formula := 'AND(TRUE,FALSE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNFunc('AND', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'FALSE');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true and false);
+ sollValues[Row] := BooleanResult(true and false);
// AND of three values (bool)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=AND(true,false,true)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNBool(true,
- RPNFunc(fekAND, 3, nil))))));
+ formula := 'AND(TRUE,FALSE,TRUE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNBool(true,
+ RPNFunc('AND', 3, nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true and false and true);
-
- // OR of one values (bool)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=OR(true)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNFunc(fekOR, 1, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true);
-
- // OR of two values (bool)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=OR(true,false)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNFunc(fekOR, 2, nil)))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true or false);
-
- // OR of three values (bool)
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=OR(true,false,true)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNBool(false,
- RPNBool(true,
- RPNFunc(fekOR, 3, nil))))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true or false or true);
+ sollValues[Row] := BooleanResult(true and false and true);
// function =FALSE()
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=FALSE()');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekFALSE, nil)));
+ formula := 'FALSE()';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNFunc('FALSE', nil)))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'FALSE');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false);
-
- // function =TRUE()
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=TRUE()');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNFunc(fekTRUE, nil)));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true);
-
- // NOT
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=NOT(false)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(false,
- RPNFunc(fekNOT, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(not false);
+ sollValues[Row] := BooleanResult(false);
// IF (2 parameters)/strings/case true
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,"A")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNString('A',
- RPNFunc(fekIF, 2, nil)))));
+ formula := 'IF(TRUE,"A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNString('A',
+ RPNFunc('IF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'A');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('A');
+ sollValues[Row] := StringResult('A');
// IF (2 parameters) /floats/case true
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNNumber(1.0,
- RPNFunc(fekIF, 2, nil)))));
+ formula := 'IF(TRUE,1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNNumber(1.0,
+ RPNFunc('IF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
- // IF (2 parameters)/strings/case falsee
+ // IF (2 parameters)/strings/case false
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(false,"A")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(false,
- RPNString('A',
- RPNFunc(fekIF, 2, nil)))));
+ formula := 'IF(FALSE,"A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(false,
+ RPNString('A',
+ RPNFunc('IF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false);
+ sollValues[Row] := BooleanResult(false);
- // IF (2 parameters) /floats/case tfalse
+ // IF (2 parameters) /floats/case false
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(false,1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(false,
- RPNNumber(1.0,
- RPNFunc(fekIF, 2, nil)))));
+ formula := 'IF(FALSE,1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(false,
+ RPNNumber(1.0,
+ RPNFunc('IF', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteUTF8Text(Row, 2, 'FALSE');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false);
+ sollValues[Row] := BooleanResult(false);
// IF (3 parameters)/strings
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,"A","B")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNString('A',
- RPNString('B',
- RPNFunc(fekIF, 3, nil))))));
+ formula := 'IF(TRUE,"A","B")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNString('A',
+ RPNString('B',
+ RPNFunc('IF', 3, nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'A');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('A');
+ sollValues[Row] := StringResult('A');
// IF (3 parameters) /floats
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1,2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNNumber(1.0,
- RPNNumber(2.0,
- RPNFunc(fekIF,3, nil))))));
+ formula := 'IF(TRUE,1,2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNNumber(1.0,
+ RPNNumber(2.0,
+ RPNFunc('IF',3, nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
// IF (3 parameters) /floats / mixed types, case true
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1,"A")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(true,
- RPNNumber(1.0,
- RPNString('A',
- RPNFunc(fekIF,3, nil))))));
+ formula := 'IF(TRUE,1,"A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNNumber(1.0,
+ RPNString('A',
+ RPNFunc('IF',3, nil))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteNumber(Row, 2, 1);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(1);
+ sollValues[Row] := FloatResult(1);
// IF (3 parameters) /floats / mixed types, case false
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(false,1,"A")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNBool(false,
- RPNNumber(1.0,
- RPNString('A',
- RPNFunc(fekIF, 3, nil))))));
+ formula := 'IF(FALSE,1,"A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(false,
+ RPNNumber(1.0,
+ RPNString('A',
+ RPNFunc('IF', 3, nil))))))
+ else
+ myWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'A');
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('A');
+ sollValues[Row] := StringResult('A');
+
+ // NOT
+ inc(Row);
+ formula := 'NOT(FALSE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(false,
+ RPNFunc('NOT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'TRUE');
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(not false);
+
+ // OR of one values (bool)
+ inc(Row);
+ formula := 'OR(TRUE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNFunc('OR', 1, nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteUTF8Text(Row, 2, 'TRUE');
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+
+ // OR of two values (bool)
+ inc(Row);
+ formula := 'OR(TRUE,FALSE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNFunc('OR', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := Booleanresult(true or false);
+
+ // OR of three values (bool)
+ inc(Row);
+ formula := 'OR(TRUE,FALSE,TRUE)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNBool(true,
+ RPNBool(false,
+ RPNBool(true,
+ RPNFunc('OR', 3, nil))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true or false or true);
+
+ // function =TRUE()
+ inc(Row);
+ formula := 'TRUE()';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNFunc('TRUE', nil)))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'TRUE');
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
{------------------------------------------------------------------------------}
@@ -1777,195 +2789,355 @@
// CHAR
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=CHAR(72)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(72,
- RPNFunc(fekCHAR, nil))));
+ formula := 'CHAR(72)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNInteger(72,
+ RPNFunc('CHAR', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg(char(72));
+ sollValues[Row] := StringResult(char(72));
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// CODE
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=CODE("Hallo word")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNFunc(fekCODE, nil))));
+ formula := 'CODE("Hallo world")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNFunc('CODE', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(ord('H'));
+ sollValues[Row] := IntegerResult(ord('H'));
+ Myworksheet.WriteNumber(Row, 2, sollValues[Row].ResInteger);
+
+ // CONCATENATE
+ inc(Row);
+ formula := 'CONCATENATE("A","B","C")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('A',
+ RPNString('B',
+ RPNString('C',
+ RPNFunc('CONCATENATE', 3, nil))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := StringResult('ABC');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// LEFT (2 parameters)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Hallo word", 2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNNumber(2,
- RPNFunc(fekLEFT, 2, nil)))));
+ formula := 'LEFT("Hallo world",2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNInteger(2,
+ RPNFunc('LEFT', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('Ha');
+ sollValues[Row] := StringResult('Ha');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// LEFT (2 parameters, utf8)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Ändern", 3)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Ändern',
- RPNNumber(3,
- RPNFunc(fekLEFT, 2, nil)))));
+ formula := 'LEFT("Ändern",3)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Ändern',
+ RPNInteger(3,
+ RPNFunc('LEFT', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('Änd');
+ sollValues[Row] := StringResult('Änd');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+ { -- at the momement no missing arguments support
// LEFT (2 parameters, 1 of them missing)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Hallo word", )');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNMissingArg(
- RPNFunc(fekLEFT, 2, nil)))));
+ formula := 'LEFT("Hallo world",)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNMissingArg(
+ RPNFunc('LEFT', 2, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('H');
-
+ sollValues[Row] := StringResult('H');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+}
// LEFT (1 parameter)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Hallo word")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNFunc(fekLEFT, 1, nil))));
+ formula := 'LEFT("Hallo world")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNFunc('LEFT', 1, nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('H');
+ sollValues[Row] := StringResult('H');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+
+ // Len
+ inc(Row);
+ formula := 'LEN("Hallo world")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNFunc('LEN', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := IntegerResult(Length('Hallo world'));
+ Myworksheet.WriteNumber(Row, 2, sollValues[Row].ResInteger);
// Lower case
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=LOWER("Hallo word")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNFunc(fekLOWER, nil))));
+ formula := 'LOWER("Hallo world")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNFunc('LOWER', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg(LowerCase('Hallo world'));
+ sollValues[Row] := StringResult(LowerCase('Hallo world'));
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// Lower case / utf8
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=LOWER("Viele Grüße")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Viele Grüße',
- RPNFunc(fekLOWER, nil))));
+ formula := 'LOWER("Viele Grüße")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Viele Grüße',
+ RPNFunc('LOWER', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg(UTF8LowerCase('Viele Grüße'));
+ sollValues[Row] := StringResult(UTF8LowerCase('Viele Grüße'));
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// MID
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=MID("Hallo word", 3, 2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNNumber(3,
- RPNNumber(2,
- RPNFunc(fekMID, nil))))));
+ formula := 'MID("Hallo world",3,2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNInteger(3,
+ RPNInteger(2,
+ RPNFunc('MID', nil))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('ll');
+ sollValues[Row] := StringResult('ll');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// REPLACE
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=REPLACE("weather", 2, 2, "he")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('weather',
- RPNNumber(2,
- RPNNumber(2,
- RPNString('he',
- RPNFunc(fekREPLACE, nil)))))));
+ formula := 'REPLACE("weather",2,2,"he")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('weather',
+ RPNInteger(2,
+ RPNInteger(2,
+ RPNString('he',
+ RPNFunc('REPLACE', nil)))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('whether');
+ sollValues[Row] := StringResult('whether');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// REPLACE / utf8
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=REPLACE("würde", 2, 1, "u")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('würde',
- RPNNumber(2,
- RPNNumber(1,
- RPNString('u',
- RPNFunc(fekREPLACE, nil)))))));
+ formula := 'REPLACE("würde",2,1,"u")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('würde',
+ RPNInteger(2,
+ RPNInteger(1,
+ RPNString('u',
+ RPNFunc('REPLACE', nil)))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('wurde');
+ sollValues[Row] := StringResult('wurde');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// RIGHT (2 parameters)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=RIGHT("Hallo word", 2)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNNumber(2,
- RPNFunc(fekRIGHT, 2, nil)))));
+ formula := 'RIGHT("Hallo world",2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNInteger(2,
+ RPNFunc('RIGHT', 2, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('ld');
+ sollValues[Row] := StringResult('ld');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+{ --- no missing parameter support now
// RIGHT (2 parameters, one of them missing)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=RIGHT("Hallo word", )');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNMissingArg(
- RPNFunc(fekRIGHT, 2, nil)))));
+ formula := 'RIGHT("Hallo world", )';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNMissingArg(
+ RPNFunc('RIGHT', 2, nil)))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('d');
+ sollValues[Row] := StringResult('d');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+ }
// RIGHT (1 parameter)
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=RIGHT("Hallo word")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNFunc(fekRIGHT, 1, nil))));
+ formula := 'RIGHT("Hallo world")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNFunc('RIGHT', 1, nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('d');
+ sollValues[Row] := StringResult('d');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// SUBSTITUTE
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=SUBSTITUTE("lAzArus", "A", "a")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('lAzArus',
- RPNString('A',
- RPNString('a',
- RPNFunc(fekSUBSTITUTE, 3, nil))))));
+ formula := 'SUBSTITUTE("lAzArus","A","a")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('lAzArus',
+ RPNString('A',
+ RPNString('a',
+ RPNFunc('SUBSTITUTE', 3, nil))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('lazarus');
+ sollValues[Row] := StringResult('lazarus');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+
+ // SUBSTITUTE /nth appearance
+ inc(Row);
+ formula := 'SUBSTITUTE("lazarus","a","A",2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('lazarus',
+ RPNString('a',
+ RPNString('A',
+ RPNInteger(2,
+ RPNFunc('SUBSTITUTE', 4, nil)))))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := StringResult('lazArus');
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// Trim
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=TRIM(" Hallo word ")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString(' Hallo world ',
- RPNFunc(fekTRIM, nil))));
+ formula := 'TRIM(" Hallo world ")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString(' Hallo world ',
+ RPNFunc('TRIM', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg(UTF8Trim(' Hallo world '));
+ sollValues[Row] := StringResult(UTF8Trim(' Hallo world '));
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// Upper case
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=UPPER("Hallo word")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Hallo world',
- RPNFunc(fekUPPER, nil))));
+ formula := 'UPPER("Hallo world")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Hallo world',
+ RPNFunc('UPPER', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg(UpperCase('Hallo world'));
+ sollValues[Row] := StringResult(UpperCase('Hallo world'));
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
// Upper case / utf8
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=UPPER("Viele Grüße")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('Viele Grüße',
- RPNFunc(fekUPPER, nil))));
+ formula := 'UPPER("Viele Grüße")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('Viele Grüße',
+ RPNFunc('UPPER', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg(UTF8UpperCase('Viele Grüße'));
+ sollValues[Row] := StringResult(UTF8UpperCase('Viele Grüße'));
+ Myworksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
+
+ // VALUE
+ inc(Row);
+ formula := 'VALUE("100")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('100',
+ RPNFunc('VALUE', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ myWorksheet.WriteNumber(Row, 2, 100);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := FloatResult(100);
+
+
+
+
+
+
{------------------------------------------------------------------------------}
-{ Lookup / referece functions }
+{ Lookup / reference functions }
{------------------------------------------------------------------------------}
-
+ (*
// COLUMN
- MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMN(A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'COLUMN(A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRef('A1',
RPNFunc(fekCOLUMN, 1, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(1);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMN(C1:D3)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'COLUMN(C1:D3)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange('C1:D3',
RPNFunc(fekCOLUMN, 1, nil))));
@@ -1973,14 +3145,14 @@
sollValues[Row] := CreateNumberArg(3);
// COLUMNS
- MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMNS(A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'COLUMNS(A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRef('A1',
RPNFunc(fekCOLUMNS, 1, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(1);
- MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMNS(C1:D3)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'COLUMNS(C1:D3)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange('C1:D3',
RPNFunc(fekCOLUMNS, 1, nil))));
@@ -1988,14 +3160,14 @@
sollValues[Row] := CreateNumberArg(2);
// ROW
- MyWorksheet.WriteUTF8Text(Row, 0, '=ROW(A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'ROW(A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRef('A1',
RPNFunc(fekROW, 1, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(1);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ROW(C2:D3)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'ROW(C2:D3)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange('C2:D3',
RPNFunc(fekROW, 1, nil))));
@@ -2003,35 +3175,40 @@
sollValues[Row] := CreateNumberArg(2);
// ROWS
- MyWorksheet.WriteUTF8Text(Row, 0, '=ROWS(A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'ROWS(A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRef('A1',
RPNFunc(fekROWS, 1, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(1);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ROWS(C2:D3)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'ROWS(C2:D3)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNCellRange('C2:D3',
RPNFunc(fekROWS, 1, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(2);
-
+ *)
{------------------------------------------------------------------------------}
{ Information functions }
{------------------------------------------------------------------------------}
-
+ { cell function will be removed because it requires localized string parameters
// CELL
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("address", A1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('address',
- RPNCellRef('A1',
- RPNFunc(fekCELLINFO, 2, nil)))));
+ formula := 'CELL("address",A1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('address',
+ RPNCellRef('A1',
+ RPNFunc('CELL', 2, nil)))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateStringArg('$A$1');
+ sollValues[Row] := StringResult('$A$1');
+ MyWorksheet.WriteUTF8Text(Row, 2, sollValues[Row].ResString);
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("col", B1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'CELL("col", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('col',
RPNCellRef('B1',
@@ -2039,7 +3216,7 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(2); // Excel starts counting at 1
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("format", B1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'CELL("format", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('format',
RPNCellRef('B1',
@@ -2047,7 +3224,7 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateStringArg('G');
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("prefix", A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'CELL("prefix", A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('prefix',
RPNCellRef('A1',
@@ -2055,7 +3232,7 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateStringArg('''');
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("row", B1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'CELL("row", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('row',
RPNCellRef('B1',
@@ -2063,7 +3240,7 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(1); // Excel starts counting at 1
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("type", A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'CELL("type", A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('type',
RPNCellRef('A1',
@@ -2071,113 +3248,334 @@
SetLength(sollValues, Row+1);
sollValues[Row] := CreateStringArg('l');
- MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("type", B1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'CELL("type", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('type',
RPNCellRef('B1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateStringArg('v');
+}
+(*
// INFO
- MyWorksheet.WriteUTF8Text(Row, 0, '=INFO("directory", A1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'INFO("directory", A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('directory',
RPNFunc(fekINFO, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateStringArg(ExtractFilePath(TempFile));
- MyWorksheet.WriteUTF8Text(Row, 0, '=INFO("numfile")');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'INFO("numfile")');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('numfile',
RPNFunc(fekINFO, nil))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumberArg(MyWorkbook.GetWorksheetCount);
-
+ *)
// IsBlank
- inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISBLANK(A1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRef('A1',
- RPNFunc(fekISBLANK, nil))));
- SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false); // cell contains text --> not blank
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISBLANK(G1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRef('G1',
- RPNFunc(fekISBLANK, nil))));
+ formula := 'ISBLANK(A1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('A1',
+ RPNFunc('ISBLANK', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true); // the cell does not exist --> blank
+ sollValues[Row] := BooleanResult(false); // cell contains text --> not blank
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISBLANK(H1)');
+ formula := 'ISBLANK(G1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('G1',
+ RPNFunc('ISBLANK', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true); // the cell does not exist --> blank
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ inc(Row);
+ formula := 'ISBLANK(H1)';
MyWorksheet.WriteBlank(0, 7); // A11 is an empty cell
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRef('H1',
- RPNFunc(fekISBLANK, nil))));
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('H1',
+ RPNFunc('ISBLANK', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true); // the cell exists, but it is empty
+ sollValues[Row] := BooleanResult(true); // the cell exists, but it is empty
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ // IsErr
+ inc(Row);
+ formula := 'ISERR(H2)';
+ MyWorksheet.WriteFormula(1, 7, '1/0'); // Create an error in H2
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ myWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('H2',
+ RPNFunc('ISERR', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true); // there is an error in H2
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ formula := 'ISERR(H3)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ myWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('H3',
+ RPNFunc('ISERR', nil))))
+ else
+ Myworksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(false); // no error in H3
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
// IsError
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISERROR(1/0)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1,
- RPNNumber(0,
- RPNFunc(fekDiv,
- RPNFunc(fekISERROR, nil))))));
+ formula := 'ISERROR(1/0)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNNumber(0,
+ RPNFunc(fekDiv,
+ RPNFunc('ISERROR', nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
- // IsError
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISERROR(0/1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(0,
- RPNNumber(1,
- RPNFunc(fekDiv,
- RPNFunc(fekISERROR, nil))))));
+ formula := 'ISERROR(0/1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(0,
+ RPNNumber(1,
+ RPNFunc(fekDiv,
+ RPNFunc('ISERROR', nil))))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
+ inc(Row);
+ formula := 'ISERROR(H2)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('H2',
+ RPNFunc('ISERROR', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ inc(Row);
+ formula := 'ISERROR(H3)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('H3',
+ RPNFunc('ISERROR', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
+ // IsLogical
+ inc(Row);
+ formula := 'ISLOGICAL(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('B1',
+ RPNFunc('ISLOGICAL', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
+ // IsNONTEXT
+ inc(Row);
+ formula := 'ISNONTEXT(1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNFunc('ISNONTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ inc(Row);
+ formula := 'ISNONTEXT("A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('A',
+ RPNFunc('ISNONTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
+ inc(Row);
+ formula := 'ISNONTEXT(A1)'; // A1 contains a text
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('A1',
+ RPNFunc('ISNONTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
+ inc(Row);
+ formula := 'ISNONTEXT(B1)'; // B1 contains a number
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('B1',
+ RPNFunc('ISNONTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ // IsNumber
+ inc(Row);
+ formula := 'ISNUMBER(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('B1',
+ RPNFunc('ISNUMBER', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
// IsRef
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISREF(1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNNumber(1,
- RPNFunc(fekISREF, nil))));
+ formula := 'ISREF(1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNFunc('ISREF', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(false);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISREF(A1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellRef('A1',
- RPNFunc(fekISREF, nil))));
+ formula := 'ISREF(A1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('A1',
+ RPNFunc('ISREF', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateBoolArg(true);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=ISREF(A1)');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNCellValue('A1', // we use a cell value here !
- RPNFunc(fekISREF, nil))));
+ formula := 'ISREF(A1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellValue('A1', // we use a cell value here !
+ RPNFunc('ISREF', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
// The correct result would be "false" because a cell value is not the same as
// a cell reference. But Excel seems to ignore this difference here and
// accepts only a "true".
- sollValues[Row] := CreateBoolArg(true);
+ sollValues[Row] := BooleanResult(true);
+ MyWorksheet.WriteUTF8Text(Row, 2, 'TRUE');
- // VALUE
+ // IsText
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=VALUE("100")');
- MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
- RPNString('100',
- RPNFunc(fekVALUE, nil))));
+ formula := 'ISTEXT(1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNNumber(1,
+ RPNFunc('ISTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
- sollValues[Row] := CreateNumberArg(100);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
+ inc(Row);
+ formula := 'ISTEXT("A")';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNString('A',
+ RPNFunc('ISTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ inc(Row);
+ formula := 'ISTEXT(A1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('A1',
+ RPNFunc('ISTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(true);
+ Myworksheet.WriteUTF8Text(Row, 2, 'TRUE');
+
+ inc(Row);
+ formula := 'ISTEXT(B1)';
+ MyWorksheet.WriteUTF8Text(Row, 0, formula);
+ if UseRPNFormula then
+ MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
+ RPNCellRef('B1',
+ RPNFunc('ISTEXT', nil))))
+ else
+ MyWorksheet.WriteFormula(Row, 1, formula);
+ SetLength(sollValues, Row+1);
+ sollValues[Row] := BooleanResult(false);
+ Myworksheet.WriteUTF8Text(Row, 2, 'FALSE');
+
{------------------------------------------------------------------------------}
{ Error cases }
@@ -2185,7 +3583,7 @@
{$IFDEF ENABLE_DEFECTIVE_FORMULAS }
// Using less parameters than specified
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1)');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'IF(true,1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNBool(true,
RPNNumber(1.0,
@@ -2195,7 +3593,7 @@
// Using more parameters than specified
inc(Row);
- MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1,"A")');
+ MyWorksheet.WriteUTF8Text(Row, 0, 'IF(true,1,"A")');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNBool(true,
RPNNumber(1.0,
diff --git a/components/fpspreadsheet/tests/testsutility.pas b/components/fpspreadsheet/tests/testsutility.pas
index adb0f2bde..200a5028b 100644
--- a/components/fpspreadsheet/tests/testsutility.pas
+++ b/components/fpspreadsheet/tests/testsutility.pas
@@ -111,7 +111,10 @@ begin
if not(assigned(Worksheet)) then
result:='CellNotation: error getting worksheet.'
else
- result:=WorkSheet.Name+'!'+ColumnToLetter(Column)+inttostr(Row+1)
+ if Worksheet.Name <> '' then
+ result := WorkSheet.Name + '!' + ColumnToLetter(Column) + inttostr(Row+1)
+ else
+ Result := ColumnToLetter(Column) + IntToStr(Row + 1);
end;
function ColNotation(WorkSheet: TsWorksheet; Column:Integer): String;
@@ -119,7 +122,10 @@ begin
if not Assigned(Worksheet) then
Result := 'ColNotation: error getting worksheet.'
else
- Result := WorkSheet.Name + '!' + ColumnToLetter(Column);
+ if Worksheet.Name <> '' then
+ Result := WorkSheet.Name + '!' + ColumnToLetter(Column)
+ else
+ Result := ColumnToLetter(Column);
end;
function RowNotation(Worksheet: TsWorksheet; Row: Integer): String;
@@ -127,7 +133,10 @@ begin
if not Assigned(Worksheet) then
Result := 'RowNotation: error getting worksheet.'
else
- Result := Worksheet.Name + '!' + IntToStr(Row+1);
+ if Worksheet.Name <> '' then
+ Result := Worksheet.Name + '!' + IntToStr(Row+1)
+ else
+ Result := IntToStr(Row+1);
end;
end.
diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas
index 07da28268..c354d6357 100755
--- a/components/fpspreadsheet/xlsbiff2.pas
+++ b/components/fpspreadsheet/xlsbiff2.pas
@@ -605,7 +605,7 @@ begin
end;
{ Formula token array }
- if boReadFormulas in FWorkbook.Options then begin
+ if (boReadFormulas in FWorkbook.Options) then begin
ok := ReadRPNTokenArray(AStream, cell);
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
end;
@@ -1230,10 +1230,9 @@ end;
}
procedure TsSpreadBIFF2Writer.WriteToStream(AStream: TStream);
var
- sheet: TsWorksheet;
pane: Byte;
begin
- sheet := Workbook.GetFirstWorksheet;
+ FWorksheet := Workbook.GetFirstWorksheet;
WriteBOF(AStream);
WriteFonts(AStream);
@@ -1241,21 +1240,21 @@ begin
WriteFormats(AStream);
WriteXFRecords(AStream);
WriteColWidths(AStream);
- WriteDimensions(AStream, sheet);
- WriteRows(AStream, sheet);
+ WriteDimensions(AStream, FWorksheet);
+ WriteRows(AStream, FWorksheet);
if (boVirtualMode in Workbook.Options) then
WriteVirtualCells(AStream)
else begin
- WriteRows(AStream, sheet);
- WriteCellsToStream(AStream, sheet.Cells);
+ WriteRows(AStream, FWorksheet);
+ WriteCellsToStream(AStream, FWorksheet.Cells);
end;
WriteWindow1(AStream);
// { -- currently not working
- WriteWindow2(AStream, sheet);
- WritePane(AStream, sheet, false, pane); // false for "is not BIFF5 or BIFF8"
- WriteSelections(AStream, sheet);
+ WriteWindow2(AStream, FWorksheet);
+ WritePane(AStream, FWorksheet, false, pane); // false for "is not BIFF5 or BIFF8"
+ WriteSelections(AStream, FWorksheet);
//}
WriteEOF(AStream);
end;
@@ -1630,11 +1629,6 @@ begin
else
WriteRPNTokenArray(AStream, AFormula, true, RPNLength);
-(*
-{ Formula data (RPN token array) }
- WriteRPNTokenArray(AStream, AFormula, true, RPNLength);
-*)
-
{ Finally write sizes after we know them }
FinalPos := AStream.Position;
AStream.Position := RecordSizePos;
@@ -1665,13 +1659,12 @@ var
i: Integer;
formula: TsRPNFormula;
begin
- SetLength(formula, Length(ACell^.SharedFormulaBase^.RPNFormulaValue));
- for i:=0 to Length(formula)-1 do begin
- // Copy formula
- formula[i] := ACell^.SharedFormulaBase^.RPNFormulaValue[i];
- // Adapt relative cell references
+ // Create RPN formula from the shared formula base's string formula
+ formula := FWorksheet.BuildRPNFormula(ACell^.SharedFormulaBase);
+
+ // Adapt relative cell references
+ for i:=0 to Length(formula)-1 do
FixRelativeReferences(ACell, formula[i]);
- end;
// Write adapted copy of shared formula to stream.
WriteRPNTokenArray(AStream, formula, true, RPNLength);
diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas
index c2c77f236..452d810f8 100755
--- a/components/fpspreadsheet/xlsbiff8.pas
+++ b/components/fpspreadsheet/xlsbiff8.pas
@@ -222,7 +222,7 @@ var
implementation
uses
- fpsStreams;
+ fpsStreams, fpsExprParser;
const
{ Excel record IDs }
@@ -1247,6 +1247,14 @@ begin
inherited;
end;
+
+var
+
+
+ counter: Integer = 0;
+
+
+
function TsSpreadBIFF8Reader.ReadWideString(const AStream: TStream;
const ALength: WORD): WideString;
var
@@ -1264,16 +1272,15 @@ var
begin
StringFlags:=AStream.ReadByte;
Dec(PendingRecordSize);
+ if StringFlags and 4 = 4 then begin
+ //Asian phonetics
+ //Read Asian phonetics Length (not used)
+ AsianPhoneticBytes:=DWordLEtoN(AStream.ReadDWord);
+ end;
if StringFlags and 8 = 8 then begin
//Rich string
RunsCounter:=WordLEtoN(AStream.ReadWord);
- dec(PendingRecordSize, 2);
- end;
- if StringFlags and 4 = 4 then begin
- //Asian phonetics
- //Read Asian phonetics length (not used)
- AsianPhoneticBytes:=DWordLEtoN(AStream.ReadDWord);
- dec(PendingRecordSize, 4);
+ dec(PendingRecordSize,2);
end;
if StringFlags and 1 = 1 Then begin
//String is WideStringLE
@@ -1290,6 +1297,11 @@ begin
end else begin
//String is 1 byte per char, this is UTF-16 with the high byte ommited because it is zero
//so decompress and then convert
+
+
+ inc(Counter);
+
+
lLen:=ALength;
SetLength(DecomprStrValue, lLen);
for i := 1 to lLen do
@@ -1298,7 +1310,7 @@ begin
DecomprStrValue[i] := C;
Dec(PendingRecordSize);
if (PendingRecordSize<=0) and (iINT_EXCEL_ID_CONTINUE then begin
@@ -1310,13 +1322,14 @@ begin
end;
end;
end;
+
Result := DecomprStrValue;
end;
if StringFlags and 8 = 8 then begin
- //Rich string (This only happens in BIFF8)
+ //Rich string (This only happened in BIFF8)
for j := 1 to RunsCounter do begin
if (PendingRecordSize<=0) then begin
- //A CONTINUE may happen here
+ //A CONTINUE may happend here
RecordType := WordLEToN(AStream.ReadWord);
RecordSize := WordLEToN(AStream.ReadWord);
if RecordType<>INT_EXCEL_ID_CONTINUE then begin
diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas
index 2d90b2b5e..0b0ded825 100644
--- a/components/fpspreadsheet/xlscommon.pas
+++ b/components/fpspreadsheet/xlscommon.pas
@@ -67,176 +67,6 @@ const
INT_FONT_WEIGHT_NORMAL = $0190;
INT_FONT_WEIGHT_BOLD = $02BC;
- { Formula constants TokenID values }
-
- { Binary Operator Tokens 3.6}
- INT_EXCEL_TOKEN_TADD = $03;
- 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_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)
- INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis
-
- { Constant Operand Tokens, 3.8}
- INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand
- INT_EXCEL_TOKEN_TSTR = $17; //string
- INT_EXCEL_TOKEN_TERR = $1C; //error value
- INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
- INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer
- INT_EXCEL_TOKEN_TNUM = $1F; //floating-point
-
- { Operand Tokens }
- // _R: reference; _V: value; _A: array
- 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;
- INT_EXCEL_TOKEN_TREFN_R = $2C;
- INT_EXCEL_TOKEN_TREFN_V = $4C;
- INT_EXCEL_TOKEN_TREFN_A = $6C;
-
- { Function Tokens }
- // _R: reference; _V: value; _A: array
- // Offset 0: token; offset 1: index to a built-in sheet function ( ➜ 3.111)
- INT_EXCEL_TOKEN_FUNC_R = $21;
- INT_EXCEL_TOKEN_FUNC_V = $41;
- INT_EXCEL_TOKEN_FUNC_A = $61;
-
- //VAR: variable number of arguments:
- INT_EXCEL_TOKEN_FUNCVAR_R = $22;
- INT_EXCEL_TOKEN_FUNCVAR_V = $42;
- INT_EXCEL_TOKEN_FUNCVAR_A = $62;
-
- { Special tokens }
- INT_EXCEL_TOKEN_TEXP = $01; // cell belongs to shared formula
-
- { 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
-// 02H tTbl Multiple operation table
-// 15H tParen Parentheses
-// 18H tNlr Natural language reference (BIFF8)
- INT_EXCEL_TOKEN_TATTR = $19; // tAttr Special attribute
-// 1AH tSheet Start of external sheet reference (BIFF2-BIFF4)
-// 1BH tEndSheet End of external sheet reference (BIFF2-BIFF4)
-
{ CODEPAGE record constants }
WORD_ASCII = 367;
WORD_UTF_16 = 1200; // BIFF 8
@@ -475,7 +305,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): Word;
+// function FormulaElementKindToExcelTokenID(AElementKind: TFEKind; out ASecondaryID: Word): Word;
// Helper function for writing a string with 8-bit length }
function WriteString_8BitLen(AStream: TStream; AString: String): Integer; virtual;
@@ -555,11 +385,11 @@ type
implementation
uses
- AVL_Tree, Math, Variants, fpsNumFormatParser;
+ AVL_Tree, Math, Variants, xlsConst, fpsNumFormatParser, fpsExprParser;
-{ Helper table for rpn formulas:
- Assignment of FormulaElementKinds (fekXXXX) to EXCEL_TOKEN IDs. }
const
+ { Helper table for rpn formulas:
+ Assignment of FormulaElementKinds (fekXXXX) to EXCEL_TOKEN IDs. }
TokenIDs: array[TFEKind] of Word = (
// Basic operands
INT_EXCEL_TOKEN_TREFV, {fekCell}
@@ -590,6 +420,9 @@ const
INT_EXCEL_TOKEN_TLE, {fekLessEqual, <=}
INT_EXCEL_TOKEN_TNE, {fekNotEqual, <>}
INT_EXCEL_TOKEN_TPAREN, {Operator in parenthesis}
+ Word(-1) {fekFunc}
+ );
+ (*
// Math functions
INT_EXCEL_SHEET_FUNC_ABS, {fekABS}
@@ -711,7 +544,7 @@ const
// Other operations
INT_EXCEL_TOKEN_TATTR {fekOpSum}
);
-
+ *)
type
TBIFF58BlankRecord = packed record
RecordID: Word;
@@ -1188,7 +1021,6 @@ var
err: TsErrorValue;
ok: Boolean;
cell: PCell;
-
begin
{ Index to XF Record }
ReadRowColXF(AStream, ARow, ACol, XF);
@@ -1247,7 +1079,7 @@ begin
if IsDateTime(ResultFormula, nf, nfs, dt) then
FWorksheet.WriteDateTime(cell, dt, nf, nfs)
else
- FWorksheet.WriteNumber(cell, ResultFormula, nf, nfs); //, nd, ncs);
+ FWorksheet.WriteNumber(cell, ResultFormula, nf, nfs);
end;
{ Formula token array }
@@ -1274,7 +1106,7 @@ var
begin
ARow := WordLEtoN(AStream.ReadWord);
fc := WordLEtoN(AStream.ReadWord);
- pending := RecordSize - Sizeof(fc) - Sizeof(ARow);
+ pending := RecordSize - SizeOf(fc) - SizeOf(ARow);
if FIsVirtualMode then begin
InitCell(ARow, 0, FVirtualCell);
cell := @FVirtualCell;
@@ -1626,7 +1458,7 @@ begin
end;
{ Reads the array of rpn tokens from the current stream position, creates an
- rpn formula and stores it in the cell. }
+ rpn formula, converts it to a string formula and stores it in the cell. }
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
ACell: PCell): Boolean;
var
@@ -1640,9 +1472,11 @@ var
r, c, r2, c2: Cardinal;
dr, dc: Integer;
fek: TFEKind;
- func: Word;
+ exprDef: TsBuiltInExprIdentifierDef;
+ funcCode: Word;
b: Byte;
found: Boolean;
+ formula: TsRPNformula;
begin
rpnItem := nil;
n := ReadRPNTokenArraySize(AStream);
@@ -1698,16 +1532,11 @@ begin
INT_EXCEL_TOKEN_FUNC_A:
// functions with fixed argument count
begin
- func := ReadRPNFunc(AStream);
- found := false;
- for fek in TFuncTokens do begin
- if (TokenIDs[fek] = func) and FixedParamCount(fek) then begin
- rpnItem := RPNFunc(fek, rpnItem);
- found := true;
- break;
- end;
- end;
- if not found then
+ funcCode := ReadRPNFunc(AStream);
+ exprDef := BuiltInIdentifiers.IdentifierByExcelCode(funcCode);
+ if exprDef <> nil then
+ rpnItem := RPNFunc(exprDef.Name, rpnItem)
+ else
supported := false;
end;
@@ -1717,15 +1546,11 @@ begin
// functions with variable argument count
begin
b := AStream.ReadByte;
- func := ReadRPNFunc(AStream);
- found := false;
- for fek in TFuncTokens do
- if (TokenIDs[fek] = func) and not FixedParamCount(fek) then begin
- rpnItem := RPNFunc(fek, b, rpnItem);
- found := true;
- break;
- end;
- if not found then
+ funcCode := ReadRPNFunc(AStream);
+ exprDef := BuiltinIdentifiers.IdentifierByExcelCode(funcCode);
+ if exprDef <> nil then
+ rpnItem := RPNFunc(exprDef.Name, b, rpnItem)
+ else
supported := false;
end;
@@ -1751,11 +1576,11 @@ begin
end;
if not supported then begin
DestroyRPNFormula(rpnItem);
- SetLength(ACell^.RPNFormulaValue, 0);
Result := false;
end
else begin
- ACell^.RPNFormulaValue := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items!
+ formula := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items!
+ ACell^.FormulaValue := FWorksheet.ConvertRPNFormulaToStringFormula(formula);
Result := true;
end;
end;
@@ -1938,10 +1763,11 @@ begin
end else
Result := AColor;
end;
-
+ (*
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
AElementKind: TFEKind; out ASecondaryID: Word): Word;
begin
+ if AElementKind = fekFunc then
if (AElementKind >= Low(TFuncTokens)) and (AElementKind <= High(TFuncTokens))
then begin
if FixedParamCount(AElementKind) then
@@ -1955,7 +1781,7 @@ begin
ASecondaryID := 0;
end;
end;
-
+ *)
procedure TsSpreadBIFFWriter.GetLastRowCallback(ACell: PCell; AStream: TStream);
begin
Unused(AStream);
@@ -2174,12 +2000,17 @@ end;
{ Writes an Excel FORMULA record.
Note: The formula is already stored in the cell.
- Since BIFF files contain RPN formulas the method calls WriteRPNFormula.
+ Since BIFF files contain RPN formulas the string formula of the cell is
+ converted to an RPN formula and the method calls WriteRPNFormula.
}
procedure TsSpreadBIFFWriter.WriteFormula(AStream: TStream;
const ARow, ACol: Cardinal; ACell: PCell);
+var
+ formula: TsRPNFormula;
begin
- WriteRPNFormula(AStream, ARow, ACol, ACell^.RPNFormulaValue, ACell);
+ formula := FWorksheet.BuildRPNFormula(ACell);
+ WriteRPNFormula(AStream, ARow, ACol, formula, ACell);
+ SetLength(formula, 0);
end;
{ Writes a 64-bit floating point NUMBER record.
@@ -2586,10 +2417,12 @@ procedure TsSpreadBIFFWriter.WriteRPNTokenArray(AStream: TStream;
const AFormula: TsRPNFormula; WriteTokenArraySize: Boolean; var RPNLength: Word);
var
i: Integer;
- tokenID, secondaryID: Word;
n: Word;
TokenArraySizePos: Int64;
FinalPos: Int64;
+ exprDef: TsExprIdentifierDef;
+ excelCode: Word;
+ primaryExcelCode, secondaryExcelCode: Word;
begin
RPNLength := 0;
@@ -2604,12 +2437,22 @@ begin
for i := 0 to Length(AFormula) - 1 do begin
{ Token identifier }
- tokenID := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind, secondaryID);
- AStream.WriteByte(tokenID);
+ if AFormula[i].ElementKind = fekFunc then begin
+ exprDef := BuiltinIdentifiers.IdentifierByName(Aformula[i].FuncName);
+ if exprDef.HasFixedArgumentCount then
+ primaryExcelCode := INT_EXCEL_TOKEN_FUNC_V
+ else
+ primaryExcelCode := INT_EXCEL_TOKEN_FUNCVAR_V;
+ secondaryExcelCode := exprDef.ExcelCode;
+ end else begin
+ primaryExcelCode := TokenIDs[AFormula[i].ElementKind];
+ secondaryExcelCode := 0;
+ end;
+ AStream.WriteByte(primaryExcelCode);
inc(RPNLength);
{ Token data }
- case tokenID of
+ case primaryExcelCode of
{ Operand Tokens }
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell }
// INT_EXCEL_TOKEN_TREFN_R, INT_EXCEL_TOKEN_TREFN_V, INT_EXCEL_TOKEN_TREFN_A: { fekCellOffset}
@@ -2651,6 +2494,12 @@ begin
inc(RPNLength, 8);
end;
+ INT_EXCEL_TOKEN_TINT: { fekNum, but integer }
+ begin
+ AStream.WriteBuffer(AFormula[i].IntValue, 2);
+ inc(RPNLength, 2);
+ end;
+
INT_EXCEL_TOKEN_TSTR: { fekString }
{ string constant is stored as widestring in BIFF8, otherwise as ansistring
Writing is done by the virtual method WriteString_8bitLen. }
@@ -2667,7 +2516,7 @@ begin
// Functions with fixed parameter count
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
begin
- n := WriteRPNFunc(AStream, secondaryID);
+ n := WriteRPNFunc(AStream, secondaryExcelCode);
inc(RPNLength, n);
end;
@@ -2675,7 +2524,7 @@ begin
INT_EXCEL_TOKEN_FUNCVAR_V:
begin
AStream.WriteByte(AFormula[i].ParamsNum);
- n := WriteRPNFunc(AStream, secondaryID);
+ n := WriteRPNFunc(AStream, secondaryExcelCode);
inc(RPNLength, 1 + n);
end;
@@ -2933,14 +2782,13 @@ begin
// Number of existing formula records
AStream.WriteByte((r2-r1+1) * (c2-c1+1));
- // Copy the formula (we don't want to overwrite the cell formulas)
+ // Create an RPN formula from the shared formula base's string formula
// and adjust relative references
- SetLength(formula, Length(ACell^.SharedFormulaBase^.RPNFormulaValue));
- for i:=0 to Length(ACell^.SharedFormulaBase^.RPNFormulaValue)-1 do begin
- formula[i] := ACell^.SharedFormulaBase^.RPNFormulaValue[i];
+ formula := FWorksheet.BuildRPNFormula(ACell^.SharedFormulaBase);
+ for i:=0 to Length(formula)-1 do
FixRelativeReferences(ACell, formula[i]);
- end;
- // Writes the (copied) rpn token array
+
+ // Writes the rpn token array
WriteRPNTokenArray(AStream, formula, true, RPNLength);
{ Write record size at the end after we known it }
diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas
index 24c58c038..b18505353 100755
--- a/components/fpspreadsheet/xlsxooxml.pas
+++ b/components/fpspreadsheet/xlsxooxml.pas
@@ -114,7 +114,7 @@ type
function GetStyleIndex(ACell: PCell): Cardinal;
procedure ListAllBorders;
procedure ListAllFills;
- function PrepareFormula(const AFormula: TsFormula): String;
+ function PrepareFormula(const AFormula: String): String;
procedure ResetStreams;
procedure WriteBorderList(AStream: TStream);
procedure WriteCols(AStream: TStream; AWorksheet: TsWorksheet);
@@ -648,7 +648,7 @@ begin
if datanode.NodeName = 'v' then
dataStr := GetNodeValue(datanode)
else
- if datanode.NodeName = 'f' then
+ if (boReadFormulas in FWorkbook.Options) and (datanode.NodeName = 'f') then
begin
// Formula to cell
formulaStr := GetNodeValue(datanode);
@@ -660,12 +660,12 @@ begin
s := GetAttrValue(datanode, 'ref');
if (s <>'') then
begin
- cell^.FormulaValue.FormulaStr := '=' + formulaStr;
+ cell^.FormulaValue := formulaStr;
FWorksheet.UseSharedFormula(s, cell);
end;
end
else
- cell^.FormulaValue.FormulaStr := '=' + formulaStr;
+ cell^.FormulaValue := formulaStr;
end;
end;
datanode := datanode.NextSibling;
@@ -2407,10 +2407,11 @@ begin
end;
{ Prepares a string formula for writing }
-function TsSpreadOOXMLWriter.PrepareFormula(const AFormula: TsFormula): String;
+function TsSpreadOOXMLWriter.PrepareFormula(const AFormula: String): String;
begin
- Result := AFormula.FormulaStr;
+ Result := AFormula;
if (Result <> '') and (Result[1] = '=') then Delete(Result, 1, 1);
+ Result := UTF8TextToXMLText(Result)
end;
{ Is called before zipping the individual file parts. Rewinds the streams. }
@@ -2426,15 +2427,6 @@ begin
ResetStream(FSSharedStrings_complete);
for i := 0 to High(FSSheets) do
ResetStream(FSSheets[i]);
- {
- FSContentTypes.Position := 0;
- FSRelsRels.Position := 0;
- FSWorkbookRels.Position := 0;
- FSWorkbook.Position := 0;
- FSStyles.Position := 0;
- FSSharedStrings_complete.Position := 0;
- for stream in FSSheets do stream.Position := 0;
- }
end;
{
@@ -2544,6 +2536,7 @@ var
r, c, r2, c2: Cardinal;
cell: PCell;
id: Cardinal;
+ t, v: String;
begin
cellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell);
@@ -2596,15 +2589,53 @@ begin
CellPosText, lStyleIndex,
PtrInt(ACell^.SharedFormulaBase) // ID of the shared formula
]));
- end else
+ end else begin
// "normal" formula
+ case ACell^.ContentType of
+ cctFormula:
+ begin
+ t := '';
+ v := '';
+ end;
+ cctUTF8String:
+ begin
+ t := ' t="str"';
+ v := Format('%s', [UTF8TextToXMLText(ACell^.UTF8StringValue)]);
+ end;
+ cctNumber:
+ begin
+ t := '';
+ v := Format('%g', [ACell^.NumberValue], FPointSeparatorSettings);
+ end;
+ cctDateTime:
+ begin
+ t := '';
+ v := Format('%g', [ACell^.DateTimeValue], FPointSeparatorSettings);
+ end;
+ cctBool:
+ begin
+ t := ' t="b"';
+ if ACell^.BoolValue then
+ v := '1'
+ else
+ v := '0';
+ end;
+ cctError:
+ begin
+ t := ' t="e"';
+ v := Format('%s', [GetErrorValueStr(ACell^.ErrorValue)]);
+ end;
+ end;
AppendToStream(AStream, Format(
- '' +
+ '' +
'%s' +
+ '%s' +
'', [
- CellPosText, lStyleIndex,
- PrepareFormula(ACell^.FormulaValue)
+ CellPosText, lStyleIndex, t,
+ PrepareFormula(ACell^.FormulaValue),
+ v
]));
+ end;
end;
{*******************************************************************