fpspreadsheet: Beginning to calculate rpn formulas

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3248 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-06-27 14:24:23 +00:00
parent 47e01cf753
commit 9bcb71a1fc
5 changed files with 538 additions and 134 deletions

View File

@ -8,6 +8,7 @@
<MainUnit Value="0"/>
<Title Value="spready"/>
<UseXPManifest Value="True"/>
<Icon Value="0"/>
</General>
<VersionInfo>
<Language Value=""/>

View File

@ -0,0 +1,264 @@
unit fpsmath;
{$mode objfpc}
interface
uses
Classes, SysUtils, fpspreadsheet;
type
TsArgumentType = (atNumber, atString, atBool, atError);
TsArgumentError = (aeOK, aeWrongType, aeDivideByZero, aeFuncNotDefined);
TsArgument = record
IsMissing: Boolean;
case ArgumentType: TsArgumentType of
atNumber : (NumberValue: Double);
atString : (StringValue: String);
atBool : (BoolValue: Boolean);
atError : (ErrorValue: TsArgumentError);
end;
PsArgument = ^TsArgument;
TsArgumentStack = class(TFPList)
public
destructor Destroy; override;
function Pop: TsArgument;
procedure Push(AValue: TsArgument);
procedure PushBool(AValue: Boolean);
procedure PushMissing;
procedure PushNumber(AValue: Double);
procedure PushString(AValue: String);
procedure Clear;
procedure Delete(AIndex: Integer);
end;
procedure CheckMissingBool (var Arg: TsArgument; ABool: Boolean);
procedure CheckMissingNumber(var Arg: TsArgument; ANumber: Double);
procedure CheckMissingString(var Arg: TsArgument; AString: String);
type
TsFormulaFunc = function(Args: TsArgumentStack): TsArgument;
function fpsAdd(Args: TsArgumentStack): TsArgument;
function fpsSub(Args: TsArgumentStack): TsArgument;
function fpsMul(Args: TsArgumentStack): TsArgument;
function fpsDiv(Args: TsArgumentStack): TsArgument;
implementation
{ TsArgumentStack }
destructor TsArgumentStack.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TsArgumentStack.Clear;
var
i: Integer;
begin
for i := Count-1 downto 0 do
Delete(i);
inherited Clear;
end;
procedure TsArgumentStack.Delete(AIndex: Integer);
var
P: PsArgument;
begin
P := PsArgument(Items[AIndex]);
P^.StringValue := '';
FreeMem(P, SizeOf(P));
inherited Delete(AIndex);
end;
function TsArgumentStack.Pop: TsArgument;
var
P: PsArgument;
begin
P := PsArgument(Items[Count-1]);
Result := P^;
Result.StringValue := P^.StringValue; // necessary?
Delete(Count-1);
end;
procedure TsArgumentStack.Push(AValue: TsArgument);
var
P: PsArgument;
begin
GetMem(P, SizeOf(TsArgument));
P^ := AValue;
P^.StringValue := AValue.StringValue;
Add(P);
end;
procedure TsArgumentStack.PushBool(AValue: Boolean);
var
arg: TsArgument;
begin
arg.ArgumentType := atBool;
arg.BoolValue := AValue;
arg.IsMissing := false;
Push(arg);
end;
procedure TsArgumentStack.PushMissing;
var
arg: TsArgument;
begin
arg.IsMissing := true;
Push(arg);
end;
procedure TsArgumentStack.PushNumber(AValue: Double);
var
arg: TsArgument;
begin
arg.ArgumentType := atNumber;
arg.NumberValue := AValue;
arg.IsMissing := false;
Push(arg);
end;
procedure TsArgumentStack.PushString(AValue: String);
var
arg: TsArgument;
begin
arg.ArgumentType := atString;
arg.StringValue := AValue;
arg.IsMissing := false;
Push(arg);
end;
{ Missing arguments }
{@@
Replaces a missing boolean argument by the passed boolean value
@param Arg Argument to be considered
@param ABool Replacement for the missing value
}
procedure CheckMissingBool(var Arg: TsArgument; ABool: Boolean);
begin
if Arg.IsMissing then Arg.BoolValue := ABool;
end;
{@@
Replaces a missing number argument by the passed number value
@param Arg Argument to be considered
@param ANumber Replacement for the missing value
}
procedure CheckMissingNumber(var Arg: TsArgument; ANumber: Double);
begin
if Arg.IsMissing then Arg.NumberValue := ANumber;
end;
{@@
Replaces a missing string argument by the passed string value
@param Arg Argument to be considered
@param AString Replacement for the missing value
}
procedure CheckMissingString(var Arg: TsArgument; AString: String);
begin
if Arg.IsMissing then Arg.StringValue := AString;
end;
{ Preparing arguments }
function GetNumberFromArgument(Arg: TsArgument; var ANumber: Double): TsArgumentError;
begin
Result := aeOK;
case Arg.ArgumentType of
atNumber : ANumber := Arg.NumberValue;
atString : if not TryStrToFloat(arg.StringValue, ANumber) then Result := aeWrongType;
atBool : if Arg.BoolValue then ANumber := 1.0 else ANumber := 0.0;
end;
end;
function CreateNumber(AValue: Double): TsArgument;
begin
Result.ArgumentType := atNumber;
Result.NumberValue := AValue;
end;
function CreateError(AError: TsArgumentError): TsArgument;
begin
Result.ArgumentType := atError;
Result.ErrorValue := AError;
end;
{ Operations }
function fpsAdd(Args: TsArgumentStack): TsArgument;
var
a, b: Double;
erra, errb: TsArgumentError;
begin
errb := GetNumberFromArgument(Args.Pop, b);
erra := GetNumberFromArgument(Args.Pop, a);
if erra <> aeOK then
Result := CreateError(erra)
else if errb <> aeOK then
Result := CreateError(errb)
else
Result := CreateNumber(a + b);
end;
function fpsSub(Args: TsArgumentStack): TsArgument;
var
a, b: Double;
erra, errb: TsArgumentError;
begin
// Pop the data in reverse order they were pushed!
errb := GetNumberFromArgument(Args.Pop, b);
erra := GetNumberFromArgument(Args.Pop, a);
if erra <> aeOK then
Result := CreateError(erra)
else if errb <> aeOK then
Result := CreateError(errb)
else
Result := CreateNumber(a - b);
end;
function fpsMul(Args: TsArgumentStack): TsArgument;
var
a, b: Double;
erra, errb: TsArgumentError;
begin
errb := GetNumberFromArgument(Args.Pop, b);
erra := GetNumberFromArgument(Args.Pop, a);
if erra <> aeOK then
Result := CreateError(erra)
else if errb <> aeOK then
Result := CreateError(errb)
else
Result := CreateNumber(a * b);
end;
function fpsDiv(Args: TsArgumentStack): TsArgument;
var
a, b: Double;
erra, errb: TsArgumentError;
begin
// Pop the data in reverse order they were pushed!
errb := GetNumberFromArgument(Args.Pop, b);
erra := GetNumberFromArgument(Args.Pop, a);
if erra <> aeOK then
Result := CreateError(erra)
else if errb <> aeOK then
Result := CreateError(errb)
else if b = 0 then
Result := CreateError(aeDivideByZero)
else
Result := CreateNumber(a / b);
end;
end.

View File

@ -438,7 +438,8 @@ type
@param soHasFrozenPanes If set a number of rows and columns of the spreadsheet
is fixed and does not scroll. The number is defined by
LeftPaneWidth and TopPaneHeight. }
TsSheetOption = (soShowGridLines, soShowHeaders, soHasFrozenPanes);
TsSheetOption = (soShowGridLines, soShowHeaders, soHasFrozenPanes,
soCalcBeforeSaving);
{@@ Set of user interface options
@ see TsSheetOption }
@ -470,10 +471,13 @@ type
FOptions: TsSheetOptions;
FOnChangeCell: TsCellEvent;
FOnChangeFont: TsCellEvent;
procedure CalcFormulaCallback(data, arg: Pointer);
function GetFormatSettings: TFormatSettings;
procedure RemoveCallback(data, arg: pointer);
protected
procedure CalcRPNFormula(ACell: PCell);
procedure ChangedCell(ARow, ACol: Cardinal);
procedure ChangedFont(ARow, ACol: Cardinal);
@ -592,6 +596,7 @@ type
procedure WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean);
{ Data manipulation methods - For Cells }
procedure CalcFormulas;
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); overload;
procedure CopyFormat(AFromCell, AToCell: PCell); overload;
@ -671,6 +676,7 @@ type
FDefaultRowHeight: Single; // in "character heights", i.e. line count
{ Internal methods }
procedure PrepareBeforeSaving;
procedure RemoveWorksheetsCallback(data, arg: pointer);
public
@ -983,7 +989,7 @@ procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer);
implementation
uses
Math, StrUtils, TypInfo, fpsUtils, fpsNumFormatParser;
Math, StrUtils, TypInfo, fpsUtils, fpsNumFormatParser, fpsMath;
{ Translatable strings }
resourcestring
@ -1076,149 +1082,154 @@ type
{@@ Properties of formula elements:
@param Symbol Symbol used in the formula
@param MinParams Minimum count of parameters used in this function
@param MaxParams Maximum count of parameters used in this function }
TFEProp = record Symbol: String; MinParams, MaxParams: Byte; end;
@param MaxParams Maximum count of parameters used in this function
@param Func Function to be calculated }
TFEProp = record
Symbol: String;
MinParams, MaxParams: Byte;
Func: TsFormulaFunc;
end;
const
FEProps: array[TFEKind] of TFEProp = (
{ Operands }
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCell
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellRef
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellRange
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellNum
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellInteger
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellString
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellBool
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellErr
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1)), // fekCellMissingArg
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCell
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRef
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRange
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellNum
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellInteger
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellString
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellBool
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellErr
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellMissingArg
{ Basic operations }
(Symbol:'+'; MinParams:2; MaxParams:2), // fekAdd
(Symbol:'-'; MinParams:2; MaxParams:2), // fekSub
(Symbol:'*'; MinParams:2; MaxParams:2), // fekMul
(Symbol:'/'; MinParams:2; MaxParams:2), // fekDiv
(Symbol:'%'; MinParams:1; MaxParams:1), // fekPercent
(Symbol:'^'; MinParams:2; MaxParams:2), // fekPower
(Symbol:'-'; MinParams:1; MaxParams:1), // fekUMinus
(Symbol:'+'; MinParams:1; MaxParams:1), // fekUPlus
(Symbol:'&'; MinParams:2; MaxParams:2), // fekConcat (string concatenation)
(Symbol:'='; MinParams:2; MaxParams:2), // fekEqual
(Symbol:'>'; MinParams:2; MaxParams:2), // fekGreater
(Symbol:'>='; MinParams:2; MaxParams:2), // fekGreaterEqual
(Symbol:'<'; MinParams:2; MaxParams:2), // fekLess
(Symbol:'<='; MinParams:2; MaxParams:2), // fekLessEqual
(Symbol:'<>'; MinParams:2; MaxParams:2), // fekNotEqual
(Symbol:''; MinParams:1; MaxParams:1), // fekParen
(Symbol:'+'; MinParams:2; MaxParams:2; Func:fpsAdd), // fekAdd
(Symbol:'-'; MinParams:2; MaxParams:2; Func:fpsSub), // fekSub
(Symbol:'*'; MinParams:2; MaxParams:2; Func:fpsMul), // fekMul
(Symbol:'/'; MinParams:2; MaxParams:2; Func:fpsDiv), // fekDiv
(Symbol:'%'; MinParams:1; MaxParams:1; Func:nil), // fekPercent
(Symbol:'^'; MinParams:2; MaxParams:2; Func:nil), // fekPower
(Symbol:'-'; MinParams:1; MaxParams:1; Func:nil), // fekUMinus
(Symbol:'+'; MinParams:1; MaxParams:1; Func:nil), // fekUPlus
(Symbol:'&'; MinParams:2; MaxParams:2; Func:nil), // fekConcat (string concatenation)
(Symbol:'='; MinParams:2; MaxParams:2; Func:nil), // fekEqual
(Symbol:'>'; MinParams:2; MaxParams:2; Func:nil), // fekGreater
(Symbol:'>='; MinParams:2; MaxParams:2; Func:nil), // fekGreaterEqual
(Symbol:'<'; MinParams:2; MaxParams:2; Func:nil), // fekLess
(Symbol:'<='; MinParams:2; MaxParams:2; Func:nil), // fekLessEqual
(Symbol:'<>'; MinParams:2; MaxParams:2; Func:nil), // fekNotEqual
(Symbol:''; MinParams:1; MaxParams:1; Func:nil), // fekParen
{ math }
(Symbol:'ABS'; MinParams:1; MaxParams:1), // fekABS
(Symbol:'ACOS'; MinParams:1; MaxParams:1), // fekACOS
(Symbol:'ACOSH'; MinParams:1; MaxParams:1), // fekACOSH
(Symbol:'ASIN'; MinParams:1; MaxParams:1), // fekASIN
(Symbol:'ASINH'; MinParams:1; MaxParams:1), // fekASINH
(Symbol:'ATAN'; MinParams:1; MaxParams:1), // fekATAN
(Symbol:'ATANH'; MinParams:1; MaxParams:1), // fekATANH,
(Symbol:'COS'; MinParams:1; MaxParams:1), // fekCOS
(Symbol:'COSH'; MinParams:1; MaxParams:1), // fekCOSH
(Symbol:'DEGREES'; MinParams:1; MaxParams:1), // fekDEGREES
(Symbol:'EXP'; MinParams:1; MaxParams:1), // fekEXP
(Symbol:'INT'; MinParams:1; MaxParams:1), // fekINT
(Symbol:'LN'; MinParams:1; MaxParams:1), // fekLN
(Symbol:'LOG'; MinParams:1; MaxParams:2), // fekLOG,
(Symbol:'LOG10'; MinParams:1; MaxParams:1), // fekLOG10
(Symbol:'PI'; MinParams:0; MaxParams:0), // fekPI
(Symbol:'RADIANS'; MinParams:1; MaxParams:1), // fekRADIANS
(Symbol:'RAND'; MinParams:0; MaxParams:0), // fekRAND
(Symbol:'ROUND'; MinParams:2; MaxParams:2), // fekROUND,
(Symbol:'SIGN'; MinParams:1; MaxParams:1), // fekSIGN
(Symbol:'SIN'; MinParams:1; MaxParams:1), // fekSIN
(Symbol:'SINH'; MinParams:1; MaxParams:1), // fekSINH
(Symbol:'SQRT'; MinParams:1; MaxParams:1), // fekSQRT,
(Symbol:'TAN'; MinParams:1; MaxParams:1), // fekTAN
(Symbol:'TANH'; MinParams:1; MaxParams:1), // fekTANH,
(Symbol:'ABS'; MinParams:1; MaxParams:1; Func:nil), // fekABS
(Symbol:'ACOS'; MinParams:1; MaxParams:1; Func:nil), // fekACOS
(Symbol:'ACOSH'; MinParams:1; MaxParams:1; Func:nil), // fekACOSH
(Symbol:'ASIN'; MinParams:1; MaxParams:1; Func:nil), // fekASIN
(Symbol:'ASINH'; MinParams:1; MaxParams:1; Func:nil), // fekASINH
(Symbol:'ATAN'; MinParams:1; MaxParams:1; Func:nil), // fekATAN
(Symbol:'ATANH'; MinParams:1; MaxParams:1; Func:nil), // fekATANH,
(Symbol:'COS'; MinParams:1; MaxParams:1; Func:nil), // fekCOS
(Symbol:'COSH'; MinParams:1; MaxParams:1; Func:nil), // fekCOSH
(Symbol:'DEGREES'; MinParams:1; MaxParams:1; Func:nil), // fekDEGREES
(Symbol:'EXP'; MinParams:1; MaxParams:1; Func:nil), // fekEXP
(Symbol:'INT'; MinParams:1; MaxParams:1; Func:nil), // fekINT
(Symbol:'LN'; MinParams:1; MaxParams:1; Func:nil), // fekLN
(Symbol:'LOG'; MinParams:1; MaxParams:2; Func:nil), // fekLOG,
(Symbol:'LOG10'; MinParams:1; MaxParams:1; Func:nil), // fekLOG10
(Symbol:'PI'; MinParams:0; MaxParams:0; Func:nil), // fekPI
(Symbol:'RADIANS'; MinParams:1; MaxParams:1; Func:nil), // fekRADIANS
(Symbol:'RAND'; MinParams:0; MaxParams:0; Func:nil), // fekRAND
(Symbol:'ROUND'; MinParams:2; MaxParams:2; Func:nil), // fekROUND,
(Symbol:'SIGN'; MinParams:1; MaxParams:1; Func:nil), // fekSIGN
(Symbol:'SIN'; MinParams:1; MaxParams:1; Func:nil), // fekSIN
(Symbol:'SINH'; MinParams:1; MaxParams:1; Func:nil), // fekSINH
(Symbol:'SQRT'; MinParams:1; MaxParams:1; Func:nil), // fekSQRT,
(Symbol:'TAN'; MinParams:1; MaxParams:1; Func:nil), // fekTAN
(Symbol:'TANH'; MinParams:1; MaxParams:1; Func:nil), // fekTANH,
{ date/time }
(Symbol:'DATE'; MinParams:3; MaxParams:3), // fekDATE
(Symbol:'DATEDIF'; MinParams:3; MaxParams:3), // fekDATEDIF
(Symbol:'DATEVALUE'; MinParams:1; MaxParams:1), // fekDATEVALUE
(Symbol:'DAY'; MinParams:1; MaxParams:1), // fekDAY
(Symbol:'HOUR'; MinParams:1; MaxParams:1), // fekHOUR
(Symbol:'MINUTE'; MinParams:1; MaxParams:1), // fekMINUTE
(Symbol:'MONTH'; MinParams:1; MaxParams:1), // fekMONTH
(Symbol:'NOW'; MinParams:0; MaxParams:0), // fekNOW
(Symbol:'SECOND'; MinParams:1; MaxParams:1), // fekSECOND
(Symbol:'TIME'; MinParams:3; MaxParams:3), // fekTIME
(Symbol:'TIMEVALUE'; MinParams:1; MaxParams:1), // fekTIMEVALUE
(Symbol:'TODAY'; MinParams:0; MaxParams:0), // fekTODAY
(Symbol:'WEEKDAY'; MinParams:1; MaxParams:2), // fekWEEKDAY
(Symbol:'YEAR'; MinParams:1; MaxParams:1), // fekYEAR
(Symbol:'DATE'; MinParams:3; MaxParams:3; Func:nil), // fekDATE
(Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:nil), // fekDATEDIF
(Symbol:'DATEVALUE'; MinParams:1; MaxParams:1; Func:nil), // fekDATEVALUE
(Symbol:'DAY'; MinParams:1; MaxParams:1; Func:nil), // fekDAY
(Symbol:'HOUR'; MinParams:1; MaxParams:1; Func:nil), // fekHOUR
(Symbol:'MINUTE'; MinParams:1; MaxParams:1; Func:nil), // fekMINUTE
(Symbol:'MONTH'; MinParams:1; MaxParams:1; Func:nil), // fekMONTH
(Symbol:'NOW'; MinParams:0; MaxParams:0; Func:nil), // fekNOW
(Symbol:'SECOND'; MinParams:1; MaxParams:1; Func:nil), // fekSECOND
(Symbol:'TIME'; MinParams:3; MaxParams:3; Func:nil), // fekTIME
(Symbol:'TIMEVALUE'; MinParams:1; MaxParams:1; Func:nil), // fekTIMEVALUE
(Symbol:'TODAY'; MinParams:0; MaxParams:0; Func:nil), // fekTODAY
(Symbol:'WEEKDAY'; MinParams:1; MaxParams:2; Func:nil), // fekWEEKDAY
(Symbol:'YEAR'; MinParams:1; MaxParams:1; Func:nil), // fekYEAR
{ statistical }
(Symbol:'AVEDEV'; MinParams:1; MaxParams:30), // fekAVEDEV
(Symbol:'AVERAGE'; MinParams:1; MaxParams:30), // fekAVERAGE
(Symbol:'BETADIST'; MinParams:3; MaxParams:5), // fekBETADIST
(Symbol:'BETAINV'; MinParams:3; MaxParams:5), // fekBETAINV
(Symbol:'BINOMDIST'; MinParams:4; MaxParams:4), // fekBINOMDIST
(Symbol:'CHIDIST'; MinParams:2; MaxParams:2), // fekCHIDIST
(Symbol:'CHIINV'; MinParams:2; MaxParams:2), // fekCHIINV
(Symbol:'COUNT'; MinParams:0; MaxParams:30), // fekCOUNT
(Symbol:'COUNTA'; MinParams:0; MaxParams:30), // fekCOUNTA
(Symbol:'COUNTBLANK';MinParams:1; MaxParams:1), // fekCOUNTBLANK
(Symbol:'COUNTIF'; MinParams:2; MaxParams:2), // fekCOUNTIF
(Symbol:'MAX'; MinParams:1; MaxParams:30), // fekMAX
(Symbol:'MEDIAN'; MinParams:1; MaxParams:30), // fekMEDIAN
(Symbol:'MIN'; MinParams:1; MaxParams:30), // fekMIN
(Symbol:'PERMUT'; MinParams:2; MaxParams:2), // fekPERMUT
(Symbol:'POISSON'; MinParams:3; MaxParams:3), // fekPOISSON
(Symbol:'PRODUCT'; MinParams:0; MaxParams:30), // fekPRODUCT
(Symbol:'STDEV'; MinParams:1; MaxParams:30), // fekSTDEV
(Symbol:'STDEVP'; MinParams:1; MaxParams:30), // fekSTDEVP
(Symbol:'SUM'; MinParams:0; MaxParams:30), // fekSUM
(Symbol:'SUMIF'; MinParams:2; MaxParams:3), // fekSUMIF
(Symbol:'SUMSQ'; MinParams:0; MaxParams:30), // fekSUMSQ
(Symbol:'VAR'; MinParams:1; MaxParams:30), // fekVAR
(Symbol:'VARP'; MinParams:1; MaxParams:30), // fekVARP
(Symbol:'AVEDEV'; MinParams:1; MaxParams:30; Func:nil), // fekAVEDEV
(Symbol:'AVERAGE'; MinParams:1; MaxParams:30; Func:nil), // fekAVERAGE
(Symbol:'BETADIST'; MinParams:3; MaxParams:5; Func:nil), // fekBETADIST
(Symbol:'BETAINV'; MinParams:3; MaxParams:5; Func:nil), // fekBETAINV
(Symbol:'BINOMDIST'; MinParams:4; MaxParams:4; Func:nil), // fekBINOMDIST
(Symbol:'CHIDIST'; MinParams:2; MaxParams:2; Func:nil), // fekCHIDIST
(Symbol:'CHIINV'; MinParams:2; MaxParams:2; Func:nil), // fekCHIINV
(Symbol:'COUNT'; MinParams:0; MaxParams:30; Func:nil), // fekCOUNT
(Symbol:'COUNTA'; MinParams:0; MaxParams:30; Func:nil), // fekCOUNTA
(Symbol:'COUNTBLANK';MinParams:1; MaxParams:1; Func:nil), // fekCOUNTBLANK
(Symbol:'COUNTIF'; MinParams:2; MaxParams:2; Func:nil), // fekCOUNTIF
(Symbol:'MAX'; MinParams:1; MaxParams:30; Func:nil), // fekMAX
(Symbol:'MEDIAN'; MinParams:1; MaxParams:30; Func:nil), // fekMEDIAN
(Symbol:'MIN'; MinParams:1; MaxParams:30; Func:nil), // fekMIN
(Symbol:'PERMUT'; MinParams:2; MaxParams:2; Func:nil), // fekPERMUT
(Symbol:'POISSON'; MinParams:3; MaxParams:3; Func:nil), // fekPOISSON
(Symbol:'PRODUCT'; MinParams:0; MaxParams:30; Func:nil), // fekPRODUCT
(Symbol:'STDEV'; MinParams:1; MaxParams:30; Func:nil), // fekSTDEV
(Symbol:'STDEVP'; MinParams:1; MaxParams:30; Func:nil), // fekSTDEVP
(Symbol:'SUM'; MinParams:0; MaxParams:30; Func:nil), // fekSUM
(Symbol:'SUMIF'; MinParams:2; MaxParams:3; Func:nil), // fekSUMIF
(Symbol:'SUMSQ'; MinParams:0; MaxParams:30; Func:nil), // fekSUMSQ
(Symbol:'VAR'; MinParams:1; MaxParams:30; Func:nil), // fekVAR
(Symbol:'VARP'; MinParams:1; MaxParams:30; Func:nil), // fekVARP
{ financial }
(Symbol:'FV'; MinParams:3; MaxParams:5), // fekFV
(Symbol:'NPER'; MinParams:3; MaxParams:5), // fekNPER
(Symbol:'PMT'; MinParams:3; MaxParams:5), // fekPMT
(Symbol:'PV'; MinParams:3; MaxParams:5), // fekPV
(Symbol:'RATE'; MinParams:3; MaxParams:6), // fekRATE
(Symbol:'FV'; MinParams:3; MaxParams:5; Func:nil), // fekFV
(Symbol:'NPER'; MinParams:3; MaxParams:5; Func:nil), // fekNPER
(Symbol:'PMT'; MinParams:3; MaxParams:5; Func:nil), // fekPMT
(Symbol:'PV'; MinParams:3; MaxParams:5; Func:nil), // fekPV
(Symbol:'RATE'; MinParams:3; MaxParams:6; Func:nil), // fekRATE
{ logical }
(Symbol:'AND'; MinParams:0; MaxParams:30), // fekAND
(Symbol:'FALSE'; MinParams:0; MaxParams:0), // fekFALSE
(Symbol:'IF'; MinParams:2; MaxParams:3), // fekIF
(Symbol:'NOT'; MinParams:1; MaxParams:1), // fekNOT
(Symbol:'OR'; MinParams:1; MaxParams:30), // fekOR
(Symbol:'TRUE'; MinParams:0; MaxParams:0), // fekTRUE
(Symbol:'AND'; MinParams:0; MaxParams:30; Func:nil), // fekAND
(Symbol:'FALSE'; MinParams:0; MaxParams:0; Func:nil), // fekFALSE
(Symbol:'IF'; MinParams:2; MaxParams:3; Func:nil), // fekIF
(Symbol:'NOT'; MinParams:1; MaxParams:1; Func:nil), // fekNOT
(Symbol:'OR'; MinParams:1; MaxParams:30; Func:nil), // fekOR
(Symbol:'TRUE'; MinParams:0; MaxParams:0; Func:nil), // fekTRUE
{ string }
(Symbol:'CHAR'; MinParams:1; MaxParams:1), // fekCHAR
(Symbol:'CODE'; MinParams:1; MaxParams:1), // fekCODE
(Symbol:'LEFT'; MinParams:1; MaxParams:2), // fekLEFT
(Symbol:'LOWER'; MinParams:1; MaxParams:1), // fekLOWER
(Symbol:'MID'; MinParams:3; MaxParams:3), // fekMID
(Symbol:'PROPER'; MinParams:1; MaxParams:1), // fekPROPER
(Symbol:'REPLACE'; MinParams:4; MaxParams:4), // fekREPLACE
(Symbol:'RIGHT'; MinParams:1; MaxParams:2), // fekRIGHT
(Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4), // fekSUBSTITUTE
(Symbol:'TRIM'; MinParams:1; MaxParams:1), // fekTRIM
(Symbol:'UPPER'; MinParams:1; MaxParams:1), // fekUPPER
(Symbol:'CHAR'; MinParams:1; MaxParams:1; Func:nil), // fekCHAR
(Symbol:'CODE'; MinParams:1; MaxParams:1; Func:nil), // fekCODE
(Symbol:'LEFT'; MinParams:1; MaxParams:2; Func:nil), // fekLEFT
(Symbol:'LOWER'; MinParams:1; MaxParams:1; Func:nil), // fekLOWER
(Symbol:'MID'; MinParams:3; MaxParams:3; Func:nil), // fekMID
(Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER
(Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:nil), // fekREPLACE
(Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:nil), // fekRIGHT
(Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:nil), // fekSUBSTITUTE
(Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:nil), // fekTRIM
(Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:nil), // fekUPPER
{ lookup/reference }
(Symbol:'COLUMN'; MinParams:0; MaxParams:1), // fekCOLUMN
(Symbol:'COLUMNS'; MinParams:1; MaxParams:1), // fekCOLUMNS
(Symbol:'ROW'; MinParams:0; MaxParams:1), // fekROW
(Symbol:'ROWS'; MinParams:1; MaxParams:1), // fekROWS
(Symbol:'COLUMN'; MinParams:0; MaxParams:1; Func:nil), // fekCOLUMN
(Symbol:'COLUMNS'; MinParams:1; MaxParams:1; Func:nil), // fekCOLUMNS
(Symbol:'ROW'; MinParams:0; MaxParams:1; Func:nil), // fekROW
(Symbol:'ROWS'; MinParams:1; MaxParams:1; Func:nil), // fekROWS
{ info }
(Symbol:'CELL'; MinParams:1; MaxParams:2), // fekCELLINFO
(Symbol:'INFO'; MinParams:1; MaxParams:1), // fekINFO
(Symbol:'ISBLANK'; MinParams:1; MaxParams:1), // fekIsBLANK
(Symbol:'ISERR'; MinParams:1; MaxParams:1), // fekIsERR
(Symbol:'ISERROR'; MinParams:1; MaxParams:1), // fekIsERROR
(Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1), // fekIsLOGICAL
(Symbol:'ISNA'; MinParams:1; MaxParams:1), // fekIsNA
(Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1), // fekIsNONTEXT
(Symbol:'ISNUMBER'; MinParams:1; MaxParams:1), // fekIsNUMBER
(Symbol:'ISREF'; MinParams:1; MaxParams:1), // fekIsRef
(Symbol:'ISTEXT'; MinParams:1; MaxParams:1), // fekIsTEXT
(Symbol:'VALUE'; MinParams:1; MaxParams:1), // fekValue
(Symbol:'CELL'; MinParams:1; MaxParams:2; Func:nil), // fekCELLINFO
(Symbol:'INFO'; MinParams:1; MaxParams:1; Func:nil), // fekINFO
(Symbol:'ISBLANK'; MinParams:1; MaxParams:1; Func:nil), // fekIsBLANK
(Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:nil), // fekIsERR
(Symbol:'ISERROR'; MinParams:1; MaxParams:1; Func:nil), // fekIsERROR
(Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1; Func:nil), // fekIsLOGICAL
(Symbol:'ISNA'; MinParams:1; MaxParams:1; Func:nil), // fekIsNA
(Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1; Func:nil), // fekIsNONTEXT
(Symbol:'ISNUMBER'; MinParams:1; MaxParams:1; Func:nil), // fekIsNUMBER
(Symbol:'ISREF'; MinParams:1; MaxParams:1; Func:nil), // fekIsRef
(Symbol:'ISTEXT'; MinParams:1; MaxParams:1; Func:nil), // fekIsTEXT
(Symbol:'VALUE'; MinParams:1; MaxParams:1; Func:nil), // fekValue
{ Other operations }
(Symbol:'SUM'; MinParams:1; MaxParams:1) // fekOpSUM (Unary sum operation). Note: CANNOT be used for summing sell contents; use fekSUM}
(Symbol:'SUM'; MinParams:1; MaxParams:1; Func:nil) // fekOpSUM (Unary sum operation). Note: CANNOT be used for summing sell contents; use fekSUM}
);
{@@
@ -1372,6 +1383,116 @@ begin
inherited Destroy;
end;
{@@
Helper method for clearing the records in a spreadsheet.
}
procedure TsWorksheet.CalcFormulaCallback(data, arg: pointer);
var
cell: PCell;
begin
Unused(arg);
cell := PCell(data);
// Empty cell or error cell --> nothing to do
if (cell = nil) or (cell^.ContentType = cctError) then
exit;
// Cell contains an RPN formula --> calculate the formula
if Length(cell^.RPNFormulaValue) > 0 then
CalcRPNFormula(cell);
end;
{@@
}
procedure TsWorksheet.CalcFormulas;
var
node: TAVLTreeNode;
begin
Node := FCells.FindLowest;
while Assigned(Node) do begin
CalcFormulaCallback(Node.Data, nil);
node := FCells.FindSuccessor(node);
end;
end;
{@@
}
procedure TsWorksheet.CalcRPNFormula(ACell: PCell);
var
i: Integer;
formula: TsRPNFormula;
args: TsArgumentStack;
func: TsFormulaFunc;
val: TsArgument;
fe: TsFormulaElement;
begin
if (Length(ACell^.RPNFormulaValue) = 0) or
(ACell^.ContentType = cctError)
then
exit;
args := TsArgumentStack.Create;
try
for i := 0 to Length(ACell^.RPNFormulaValue) - 1 do begin
fe := ACell^.RPNFormulaValue[i]; // "formula element"
case fe.ElementKind of
fekCell: ;
fekCellRef: ;
fekCellRange: ;
fekNum:
args.PushNumber(fe.DoubleValue);
fekInteger:
args.PushNumber(1.0*fe.IntValue);
fekString:
args.PushString(fe.StringValue);
fekBool:
args.PushBool(fe.DoubleValue <> 0.0);
fekMissingArg:
args.PushMissing;
fekParen: ; // visual effect only
fekErr:
exit;
else
func := FEProps[fe.ElementKind].Func;
if not Assigned(func) then begin
// calculation of function not implemented
exit;
end; {
if args.Count < FEProps[fe.ElementKind].MinParams then begin
// not enough parameters
exit;
end;
if args.Count > FEProps[fe.ElementKind].MaxParams then begin
// too many parameters
exit;
end; }
// Result of function
val := func(args);
// Push valid result on stack, exit in case of error
case val.ArgumentType of
atNumber, atString, atBool:
args.Push(val);
atError:
exit;
end;
end; // case
end; // for
if args.Count = 1 then begin
val := args.Pop;
case val.ArgumentType of
atNumber: WriteNumber(ACell, val.NumberValue);
atBool : WriteNumber(ACell, 1.0*ord(val.BoolValue));
atString: WriteUTF8Text(ACell, val.StringValue);
end;
end else
WriteErrorValue(ACell, errArgError);
finally
args.Free;
end;
end;
{@@
Converts a FPSpreadsheet cell position, which is Row, Col in numbers
and zero based, to a textual representation which is [Col][Row],
@ -3458,6 +3579,19 @@ end;
{ TsWorkbook }
{@@
Helper method called before saving the workbook. Calculates the formulas
in all worksheets having the option soCalcBeforeSaving set.
}
procedure TsWorkbook.PrepareBeforeSaving;
var
sheet: TsWorksheet;
begin
for sheet in FWorksheets do
if (soCalcBeforeSaving in sheet.Options) then
sheet.CalcFormulas;
end;
{@@
Helper method for clearing the spreadsheet list.
}
@ -3699,6 +3833,7 @@ var
begin
AWriter := CreateSpreadWriter(AFormat);
try
PrepareBeforeSaving;
AWriter.WriteToFile(AFileName, AOverwriteExisting);
finally
AWriter.Free;
@ -3737,8 +3872,8 @@ var
AWriter: TsCustomSpreadWriter;
begin
AWriter := CreateSpreadWriter(AFormat);
try
PrepareBeforeSaving;
AWriter.WriteToStream(AStream);
finally
AWriter.Free;

View File

@ -26,7 +26,7 @@
This package is all you need if you don't want graphical components (like grids and charts)."/>
<License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/>
<Version Major="1" Minor="2"/>
<Files Count="20">
<Files Count="21">
<Item1>
<Filename Value="fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
@ -107,6 +107,10 @@ This package is all you need if you don't want graphical components (like grids
<Filename Value="fpsnumformatparser.pas"/>
<UnitName Value="fpsNumFormatParser"/>
</Item20>
<Item21>
<Filename Value="fpsmath.pas"/>
<UnitName Value="fpsmath"/>
</Item21>
</Files>
<RequiredPkgs Count="2">
<Item1>

View File

@ -11,7 +11,7 @@ uses
xlsbiff5, xlsbiff8, xlsxooxml, fpsutils, fpszipper, uvirtuallayer_types,
uvirtuallayer, uvirtuallayer_ole, uvirtuallayer_ole_helpers,
uvirtuallayer_ole_types, uvirtuallayer_stream, fpolebasic, xlscommon,
wikitable, fpsNumFormatParser;
wikitable, fpsNumFormatParser, fpsmath;
implementation