fpspreadsheet: Beginning to add cells to calculation of rpn formulas.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3267 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-07-02 15:14:58 +00:00
parent c82e915262
commit 2f338a50a8
6 changed files with 379 additions and 94 deletions

View File

@ -8,11 +8,12 @@ uses
Classes, SysUtils, fpspreadsheet; Classes, SysUtils, fpspreadsheet;
type type
TsArgumentType = (atNumber, atString, atBool, atError, atEmpty); TsArgumentType = (atCell, atNumber, atString, atBool, atError, atEmpty);
TsArgument = record TsArgument = record
IsMissing: Boolean; IsMissing: Boolean;
case ArgumentType: TsArgumentType of case ArgumentType: TsArgumentType of
atCell : (Cell: PCell);
atNumber : (NumberValue: Double); atNumber : (NumberValue: Double);
atString : (StringValue: String); atString : (StringValue: String);
atBool : (BoolValue: Boolean); atBool : (BoolValue: Boolean);
@ -26,6 +27,7 @@ type
function Pop: TsArgument; function Pop: TsArgument;
procedure Push(AValue: TsArgument); procedure Push(AValue: TsArgument);
procedure PushBool(AValue: Boolean); procedure PushBool(AValue: Boolean);
procedure PushCell(AValue: PCell);
procedure PushMissing; procedure PushMissing;
procedure PushNumber(AValue: Double); procedure PushNumber(AValue: Double);
procedure PushString(AValue: String); procedure PushString(AValue: String);
@ -133,6 +135,7 @@ function fpsSUBSTITUTE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsTRIM (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTRIM (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsUPPER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsUPPER (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
{ info functions } { info functions }
function fpsCELLINFO (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsISERR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsISERR (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsISERROR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsISERROR (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
function fpsISLOGICAL (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsISLOGICAL (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
@ -152,6 +155,55 @@ type
TFloatArray = array of double; TFloatArray = array of double;
TStrArray = array of string; TStrArray = array of string;
{ Helpers }
function CreateArgument: TsArgument;
begin
FillChar(Result, SizeOf(Result), 0);
end;
function CreateBool(AValue: Boolean): TsArgument;
begin
Result := CreateArgument;
Result.ArgumentType := atBool;
Result.Boolvalue := AValue;
end;
function CreateCell(AValue: PCell): TsArgument;
begin
Result := CreateArgument;
Result.ArgumentType := atCell;
Result.Cell := AValue;
end;
function CreateNumber(AValue: Double): TsArgument;
begin
Result := CreateArgument;
Result.ArgumentType := atNumber;
Result.NumberValue := AValue;
end;
function CreateString(AValue: String): TsArgument;
begin
Result := CreateArgument;
Result.ArgumentType := atString;
Result.StringValue := AValue;
end;
function CreateError(AError: TsErrorValue): TsArgument;
begin
Result := CreateArgument;
Result.ArgumentType := atError;
Result.ErrorValue := AError;
end;
function CreateEmpty: TsArgument;
begin
Result := CreateArgument;
Result.ArgumentType := atEmpty;
end;
{ TsArgumentStack } { TsArgumentStack }
destructor TsArgumentStack.Destroy; destructor TsArgumentStack.Destroy;
@ -181,60 +233,53 @@ end;
function TsArgumentStack.Pop: TsArgument; function TsArgumentStack.Pop: TsArgument;
var var
P: PsArgument; arg: PsArgument;
begin begin
P := PsArgument(Items[Count-1]); arg := PsArgument(Items[Count-1]);
Result := P^; Result := arg^;
Result.StringValue := P^.StringValue; // necessary? Result.StringValue := arg^.StringValue; // necessary?
Result.Cell := arg^.Cell;
Delete(Count-1); Delete(Count-1);
end; end;
procedure TsArgumentStack.Push(AValue: TsArgument); procedure TsArgumentStack.Push(AValue: TsArgument);
var var
P: PsArgument; arg: PsArgument;
begin begin
GetMem(P, SizeOf(TsArgument)); GetMem(arg, SizeOf(TsArgument));
P^ := AValue; arg^ := AValue;
P^.StringValue := AValue.StringValue; arg^.StringValue := AValue.StringValue;
Add(P); arg^.Cell := AValue.Cell;
Add(arg);
end; end;
procedure TsArgumentStack.PushBool(AValue: Boolean); procedure TsArgumentStack.PushBool(AValue: Boolean);
var
arg: TsArgument;
begin begin
arg.ArgumentType := atBool; Push(CreateBool(AValue));
arg.BoolValue := AValue; end;
arg.IsMissing := false;
Push(arg); procedure TsArgumentStack.PushCell(AValue: PCell);
begin
Push(CreateCell(AValue));
end; end;
procedure TsArgumentStack.PushMissing; procedure TsArgumentStack.PushMissing;
var var
arg: TsArgument; arg: TsArgument;
begin begin
arg := CreateArgument;
arg.IsMissing := true; arg.IsMissing := true;
Push(arg); Push(arg);
end; end;
procedure TsArgumentStack.PushNumber(AValue: Double); procedure TsArgumentStack.PushNumber(AValue: Double);
var
arg: TsArgument;
begin begin
arg.ArgumentType := atNumber; Push(CreateNumber(AValue));
arg.NumberValue := AValue;
arg.IsMissing := false;
Push(arg);
end; end;
procedure TsArgumentStack.PushString(AValue: String); procedure TsArgumentStack.PushString(AValue: String);
var
arg: TsArgument;
begin begin
arg.ArgumentType := atString; Push(CreateString(AValue));
arg.StringValue := AValue;
arg.IsMissing := false;
Push(arg);
end; end;
@ -242,11 +287,12 @@ end;
function GetBoolFromArgument(Arg: TsArgument; var AValue: Boolean): TsErrorValue; function GetBoolFromArgument(Arg: TsArgument; var AValue: Boolean): TsErrorValue;
begin begin
Result := errOK;
case Arg.ArgumentType of case Arg.ArgumentType of
atBool : begin atBool : AValue := Arg.BoolValue;
AValue := Arg.BoolValue; atCell : if (Arg.Cell <> nil) and (Arg.Cell^.ContentType = cctBool)
Result := errOK; then AValue := Arg.Cell^.BoolValue
end; else Result := errWrongType;
atError: Result := Arg.ErrorValue; atError: Result := Arg.ErrorValue;
else Result := errWrongType; else Result := errWrongType;
end; end;
@ -259,56 +305,31 @@ begin
atNumber : ANumber := Arg.NumberValue; atNumber : ANumber := Arg.NumberValue;
atString : if not TryStrToFloat(arg.StringValue, ANumber) then Result := errWrongType; atString : if not TryStrToFloat(arg.StringValue, ANumber) then Result := errWrongType;
atBool : if Arg.BoolValue then ANumber := 1.0 else ANumber := 0.0; atBool : if Arg.BoolValue then ANumber := 1.0 else ANumber := 0.0;
atCell : if (Arg.Cell <> nil) then
case Arg.Cell^.ContentType of
cctNumber : ANumber := Arg.Cell^.NumberValue;
cctDateTime: ANumber := Arg.Cell^.DateTimeValue;
cctBool : if Arg.Cell^.BoolValue then ANumber := 1.0 else ANumber := 0.0;
else Result := errWrongType;
end;
atError : Result := Arg.ErrorValue; atError : Result := Arg.ErrorValue;
end; end;
end; end;
function GetStringFromArgument(Arg: TsArgument; var AString: String): TsErrorValue; function GetStringFromArgument(Arg: TsArgument; var AString: String): TsErrorValue;
begin begin
Result := errOK;
case Arg.ArgumentType of case Arg.ArgumentType of
atString : begin atString : AString := Arg.StringValue;
AString := Arg.StringValue; atCell : if (Arg.Cell <> nil) and (Arg.Cell^.ContentType = cctUTF8String) then
Result := errOK; AString := Arg.Cell^.UTF8StringValue
end; else
Result := errWrongType;
atError : Result := Arg.ErrorValue; atError : Result := Arg.ErrorValue;
else Result := errWrongType; else Result := errWrongType;
end; end;
end; end;
function CreateBool(AValue: Boolean): TsArgument;
begin
Result.ArgumentType := atBool;
Result.Boolvalue := AValue;
Result.IsMissing := false;
end;
function CreateNumber(AValue: Double): TsArgument;
begin
Result.ArgumentType := atNumber;
Result.NumberValue := AValue;
Result.IsMissing := false;
end;
function CreateString(AValue: String): TsArgument;
begin
Result.ArgumentType := atString;
Result.StringValue := AValue;
Result.IsMissing := false;
end;
function CreateError(AError: TsErrorValue): TsArgument;
begin
Result.ArgumentType := atError;
Result.ErrorValue := AError;
Result.IsMissing := false;
end;
function CreateEmpty: TsArgument;
begin
Result.ArgumentType := atEmpty;
Result.IsMissing := false;
end;
{@@ {@@
Pops boolean values from the argument stack. Is called when calculating rpn Pops boolean values from the argument stack. Is called when calculating rpn
formulas. formulas.
@ -381,7 +402,17 @@ begin
begin begin
Result := TryStrToDate(arg.StringValue, ADate); Result := TryStrToDate(arg.StringValue, ADate);
if not Result then AErrArg := CreateError(errWrongType); if not Result then AErrArg := CreateError(errWrongType);
end end;
atCell:
if (arg.Cell <> nil) then begin
Result := true;
case arg.Cell^.ContentType of
cctDateTime: ADate := arg.Cell^.DateTimeValue;
cctNumber : ADate := arg.Cell^.NumberValue;
else Result := false;
AErrArg := CreateError(errWrongType);
end;
end;
end; end;
end; end;
@ -509,7 +540,17 @@ begin
begin begin
Result := TryStrToTime(arg.StringValue, ATime); Result := TryStrToTime(arg.StringValue, ATime);
if not Result then AErrArg := CreateError(errWrongType); if not Result then AErrArg := CreateError(errWrongType);
end end;
atCell:
if (arg.Cell <> nil) then begin
Result := true;
case arg.Cell^.ContentType of
cctDateTime: ATime := frac(arg.Cell^.DateTimeValue);
cctNumber : ATime := frac(arg.Cell^.NumberValue);
else Result := false;
AErrArg := CreateError(errWrongType);
end;
end;
end; end;
end; end;
@ -1506,6 +1547,155 @@ end;
{ Info functions } { Info functions }
function fpsCELLINFO(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
// 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 next table:
"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
arg: TsArgument;
cell: PCell;
sname: String;
data: TStrArray;
begin
if NumArgs < 2 then begin
Result := CreateError(errArgError);
exit;
end;
arg := Args.Pop;
if (arg.ArgumentType <> atCell) or (arg.Cell = nil) then begin
Result := CreateError(errArgError);
exit;
end;
cell := arg.Cell;
if PopStringValues(Args, 1, data, Result) then begin
sname := Lowercase(data[0]);
if sname = 'address' then
Result := CreateString(GetCellString(cell^.Row, cell^.Col, []))
else if sname = 'col' then
Result := CreateNumber(cell^.Col + 1)
else if sname = 'color' then begin
if (cell^.NumberFormat = nfCurrencyRed)
then Result := CreateNumber(1)
else Result := CreateNumber(0);
end else if sname = 'contents' then
case cell^.ContentType of
cctNumber : Result := CreateNumber(cell^.NumberValue);
cctDateTime : Result := CreateNumber(cell^.DateTimeValue);
cctUTF8String : Result := CreateString(cell^.UTF8StringValue);
cctBool : Result := CreateString(BoolToStr(cell^.BoolValue));
cctError : Result := CreateString('Error');
end
else if sname = 'format' then begin
Result := CreateString('');
case cell^.NumberFormat of
nfGeneral:
Result := CreateString('G');
nfFixed:
if cell^.NumberFormatStr= '0' then Result := CreateString('0') else
if cell^.NumberFormatStr = '0.00' then Result := CreateString('F0');
nfFixedTh:
if cell^.NumberFormatStr = '#,##0' then Result := CreateString(',0') else
if cell^.NumberFormatStr = '#,##0.00' then Result := CreateString(',2');
nfPercentage:
if cell^.NumberFormatStr = '0%' then Result := CreateString('P0') else
if cell^.NumberFormatStr = '0.00%' then Result := CreateString('P2');
nfExp:
if cell^.NumberFormatStr = '0.00E+00' then Result := CreateString('S2');
nfShortDate, nfLongDate, nfShortDateTime:
Result := CreateString('D4');
nfLongTimeAM:
Result := CreateString('D6');
nfShortTimeAM:
Result := CreateString('D7');
nfLongTime:
Result := CreateString('D8');
nfShortTime:
Result := CreateString('D9');
end;
end else
if (sname = 'prefix') then begin
Result := CreateString('');
if (cell^.ContentType = cctUTF8String) then
case cell^.HorAlignment of
haLeft : Result := CreateString('''');
haCenter: Result := CreateString('^');
haRight : Result := CreateString('"');
end;
end else
if sname = 'row' then
Result := CreateNumber(cell^.Row + 1)
else if sname = 'type' then begin
if (cell^.ContentType = cctEmpty) then
Result := CreateString('b')
else if cell^.ContentType = cctUTF8String then begin
if (cell^.UTF8StringValue = '')
then Result := CreateString('b')
else Result := CreateString('l');
end else
Result := CreateString('v');
end;
end;
end;
function fpsISERR(Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsISERR(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
// ISERR( value ) // ISERR( value )
// If value is an error value (except #N/A), this function will return TRUE. // If value is an error value (except #N/A), this function will return TRUE.

View File

@ -1220,7 +1220,7 @@ const
(Symbol:'ROW'; MinParams:0; MaxParams:1; Func:nil), // fekROW (Symbol:'ROW'; MinParams:0; MaxParams:1; Func:nil), // fekROW
(Symbol:'ROWS'; MinParams:1; MaxParams:1; Func:nil), // fekROWS (Symbol:'ROWS'; MinParams:1; MaxParams:1; Func:nil), // fekROWS
{ info } { info }
(Symbol:'CELL'; MinParams:1; MaxParams:2; Func:nil), // fekCELLINFO (Symbol:'CELL'; MinParams:1; MaxParams:2; Func:fpsCELLINFO), // fekCELLINFO
(Symbol:'INFO'; MinParams:1; MaxParams:1; Func:nil), // fekINFO (Symbol:'INFO'; MinParams:1; MaxParams:1; Func:nil), // fekINFO
(Symbol:'ISBLANK'; MinParams:1; MaxParams:1; Func:nil), // fekIsBLANK (Symbol:'ISBLANK'; MinParams:1; MaxParams:1; Func:nil), // fekIsBLANK
(Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:fpsISERR), // fekIsERR (Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:fpsISERR), // fekIsERR
@ -1430,6 +1430,7 @@ var
func: TsFormulaFunc; func: TsFormulaFunc;
val: TsArgument; val: TsArgument;
fe: TsFormulaElement; fe: TsFormulaElement;
cell: PCell;
begin begin
if (Length(ACell^.RPNFormulaValue) = 0) or if (Length(ACell^.RPNFormulaValue) = 0) or
(ACell^.ContentType = cctError) (ACell^.ContentType = cctError)
@ -1441,8 +1442,11 @@ begin
for i := 0 to Length(ACell^.RPNFormulaValue) - 1 do begin for i := 0 to Length(ACell^.RPNFormulaValue) - 1 do begin
fe := ACell^.RPNFormulaValue[i]; // "formula element" fe := ACell^.RPNFormulaValue[i]; // "formula element"
case fe.ElementKind of case fe.ElementKind of
fekCell: ; fekCell, fekCellRef:
fekCellRef: ; begin
cell := FindCell(fe.Row, fe.Col);
args.PushCell(cell);
end;
fekCellRange: ; fekCellRange: ;
fekNum: fekNum:
args.PushNumber(fe.DoubleValue); args.PushNumber(fe.DoubleValue);
@ -5352,7 +5356,7 @@ end;
} }
function RPNCellValue(ACellAddress: String; ANext: PRPNItem): PRPNItem; function RPNCellValue(ACellAddress: String; ANext: PRPNItem): PRPNItem;
var var
r,c: Integer; r,c: Cardinal;
flags: TsRelFlags; flags: TsRelFlags;
begin begin
if not ParseCellString(ACellAddress, r, c, flags) then if not ParseCellString(ACellAddress, r, c, flags) then
@ -5392,7 +5396,7 @@ end;
} }
function RPNCellRef(ACellAddress: String; ANext: PRPNItem): PRPNItem; function RPNCellRef(ACellAddress: String; ANext: PRPNItem): PRPNItem;
var var
r,c: Integer; r,c: Cardinal;
flags: TsRelFlags; flags: TsRelFlags;
begin begin
if not ParseCellString(ACellAddress, r, c, flags) then if not ParseCellString(ACellAddress, r, c, flags) then
@ -5433,7 +5437,7 @@ end;
} }
function RPNCellRange(ACellRangeAddress: String; ANext: PRPNItem): PRPNItem; function RPNCellRange(ACellRangeAddress: String; ANext: PRPNItem): PRPNItem;
var var
r1,c1, r2,c2: Integer; r1,c1, r2,c2: Cardinal;
flags: TsRelFlags; flags: TsRelFlags;
begin begin
if not ParseCellRangeString(ACellRangeAddress, r1,c1, r2,c2, flags) then if not ParseCellRangeString(ACellRangeAddress, r1,c1, r2,c2, flags) then

View File

@ -57,19 +57,19 @@ function LongRGBToExcelPhysical(const RGB: DWord): DWord;
// Other routines // Other routines
function ParseIntervalString(const AStr: string; function ParseIntervalString(const AStr: string;
out AFirstCellRow, AFirstCellCol, ACount: Integer; out AFirstCellRow, AFirstCellCol, ACount: Cardinal;
out ADirection: TsSelectionDirection): Boolean; out ADirection: TsSelectionDirection): Boolean;
function ParseCellRangeString(const AStr: string; function ParseCellRangeString(const AStr: string;
out AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer; out AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Cardinal;
out AFlags: TsRelFlags): Boolean; out AFlags: TsRelFlags): Boolean;
function ParseCellString(const AStr: string; function ParseCellString(const AStr: string;
out ACellRow, ACellCol: Integer; out AFlags: TsRelFlags): Boolean; overload; out ACellRow, ACellCol: Cardinal; out AFlags: TsRelFlags): Boolean; overload;
function ParseCellString(const AStr: string; function ParseCellString(const AStr: string;
out ACellRow, ACellCol: Integer): Boolean; overload; out ACellRow, ACellCol: Cardinal): Boolean; overload;
function ParseCellRowString(const AStr: string; function ParseCellRowString(const AStr: string;
out AResult: Integer): Boolean; out AResult: Cardinal): Boolean;
function ParseCellColString(const AStr: string; function ParseCellColString(const AStr: string;
out AResult: Integer): Boolean; out AResult: Cardinal): Boolean;
function GetColString(AColIndex: Integer): String; function GetColString(AColIndex: Integer): String;
function GetCellString(ARow,ACol: Cardinal; AFlags: TsRelFlags): String; function GetCellString(ARow,ACol: Cardinal; AFlags: TsRelFlags): String;
@ -316,11 +316,11 @@ end;
@return false if the string is not a valid cell range @return false if the string is not a valid cell range
} }
function ParseIntervalString(const AStr: string; function ParseIntervalString(const AStr: string;
out AFirstCellRow, AFirstCellCol, ACount: Integer; out AFirstCellRow, AFirstCellCol, ACount: Cardinal;
out ADirection: TsSelectionDirection): Boolean; out ADirection: TsSelectionDirection): Boolean;
var var
//Cells: TStringList; //Cells: TStringList;
LastCellRow, LastCellCol: Integer; LastCellRow, LastCellCol: Cardinal;
p: Integer; p: Integer;
s1, s2: String; s1, s2: String;
begin begin
@ -381,7 +381,7 @@ end;
@return false if the string is not a valid cell range @return false if the string is not a valid cell range
} }
function ParseCellRangeString(const AStr: string; function ParseCellRangeString(const AStr: string;
out AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer; out AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Cardinal;
out AFlags: TsRelFlags): Boolean; out AFlags: TsRelFlags): Boolean;
var var
p: Integer; p: Integer;
@ -424,7 +424,7 @@ end;
@example "AMP$200" --> (rel) column 1029 (= 26*26*1 + 26*16 + 26 - 1) @example "AMP$200" --> (rel) column 1029 (= 26*26*1 + 26*16 + 26 - 1)
(abs) row = 199 (abs) (abs) row = 199 (abs)
} }
function ParseCellString(const AStr: String; out ACellRow, ACellCol: Integer; function ParseCellString(const AStr: String; out ACellRow, ACellCol: Cardinal;
out AFlags: TsRelFlags): Boolean; out AFlags: TsRelFlags): Boolean;
function Scan(AStartPos: Integer): Boolean; function Scan(AStartPos: Integer): Boolean;
@ -505,7 +505,7 @@ end;
@return False if the string is not a valid cell range @return False if the string is not a valid cell range
} }
function ParseCellString(const AStr: string; function ParseCellString(const AStr: string;
out ACellRow, ACellCol: Integer): Boolean; out ACellRow, ACellCol: Cardinal): Boolean;
var var
flags: TsRelFlags; flags: TsRelFlags;
begin begin
@ -519,7 +519,7 @@ end;
@param AResult Index of the row (zero-based!) (putput) @param AResult Index of the row (zero-based!) (putput)
@return False if the string is not a valid cell row string @return False if the string is not a valid cell row string
} }
function ParseCellRowString(const AStr: string; out AResult: Integer): Boolean; function ParseCellRowString(const AStr: string; out AResult: Cardinal): Boolean;
begin begin
try try
AResult := StrToInt(AStr) - 1; AResult := StrToInt(AStr) - 1;
@ -537,7 +537,7 @@ end;
@param AResult Zero-based index of the column (output) @param AResult Zero-based index of the column (output)
@return False if the string is not a valid cell column string @return False if the string is not a valid cell column string
} }
function ParseCellColString(const AStr: string; out AResult: Integer): Boolean; function ParseCellColString(const AStr: string; out AResult: Cardinal): Boolean;
const const
INT_NUM_LETTERS = 26; INT_NUM_LETTERS = 26;
begin begin

View File

@ -30,7 +30,7 @@ const
var var
Row: Integer; Row: Integer;
value: Double; value: Double;
r,c: integer; r,c: Cardinal;
celladdr: String; celladdr: String;
fs: TFormatSettings; fs: TFormatSettings;
ls: char; ls: char;

View File

@ -124,6 +124,7 @@
<Unit12> <Unit12>
<Filename Value="rpnformulaunit.pas"/> <Filename Value="rpnformulaunit.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="rpnFormulaUnit"/>
</Unit12> </Unit12>
<Unit13> <Unit13>
<Filename Value="formulatests.pas"/> <Filename Value="formulatests.pas"/>

View File

@ -12,7 +12,7 @@
RPNNumber(1.0, RPNNumber(1.0,
RPNFunc(fekAdd, nil))))); RPNFunc(fekAdd, nil)))));
SetLength(sollValues, Row+1); SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(1.0+1.0); sollValues[Row] := CreateNumber(1.0+1.0); // B1 = 2
// Subtraction // Subtraction
inc(Row); inc(Row);
@ -22,7 +22,40 @@
RPNNumber(10, RPNNumber(10,
RPNFunc(fekSub, nil))))); RPNFunc(fekSub, nil)))));
SetLength(sollValues, Row+1); SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(1-10); sollValues[Row] := CreateNumber(1-10); // B2 = -9
// 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)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(-7);
// don't refer to the cell contents here because they have not yet been calculated!
// 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)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(-7);
// 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)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(-7);
// don't refer to the cell contents here because they have not yet been calculated!
// Multiplication // Multiplication
inc(Row); inc(Row);
@ -1459,6 +1492,63 @@
{ Information functions } { Information functions }
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
// INFO
MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("address", A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('address',
RPNCellRef('A1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateString('$A$1');
MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("col", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('col',
RPNCellRef('B1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(2); // Excel starts counting at 1
MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("format", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('format',
RPNCellRef('B1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateString('G');
MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("prefix", A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('prefix',
RPNCellRef('A1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateString('''');
MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("row", B1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('row',
RPNCellRef('B1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateNumber(1); // Excel starts counting at 1
MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("type", A1)');
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
RPNString('type',
RPNCellRef('A1',
RPNFunc(fekCELLINFO, 2, nil)))));
SetLength(sollValues, Row+1);
sollValues[Row] := CreateString('l');
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] := CreateString('v');
// IsError // IsError
inc(Row); inc(Row);
MyWorksheet.WriteUTF8Text(Row, 0, '=ISERROR(1/0)'); MyWorksheet.WriteUTF8Text(Row, 0, '=ISERROR(1/0)');