You've already forked lazarus-ccr
fpspreadsheet: Implement calculation of rnp date/time and info formulas. Partial implementation of SUBSTITUTE calculation is back. Add corresponding unit tests, passed.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3262 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -86,6 +86,21 @@ function fpsSINH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
|||||||
function fpsSQRT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsSQRT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
function fpsTAN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsTAN (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
function fpsTANH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsTANH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
{ Date/time functions }
|
||||||
|
function fpsDATE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsDATEDIF (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsDATEVALUE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsDAY (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsHOUR (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsMINUTE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsMONTH (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsNOW (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsSECOND (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsTIME (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsTIMEVALUE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsTODAY (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsWEEKDAY (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsYEAR (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
{ Statistical functions }
|
{ Statistical functions }
|
||||||
function fpsAVEDEV (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsAVEDEV (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
function fpsAVERAGE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsAVERAGE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
@ -114,13 +129,23 @@ function fpsLOWER (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
|||||||
function fpsMID (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsMID (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
function fpsREPLACE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsREPLACE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
function fpsRIGHT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsRIGHT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
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 }
|
||||||
|
function fpsISERR (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsISERROR (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsISLOGICAL (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsISNA (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsISNONTEXT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsISNUMBER (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsISTEXT (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
function fpsVALUE (Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Math, lazutf8, StrUtils, fpsUtils;
|
Math, lazutf8, StrUtils, DateUtils, fpsUtils;
|
||||||
|
|
||||||
type
|
type
|
||||||
TBoolArray = array of boolean;
|
TBoolArray = array of boolean;
|
||||||
@ -222,6 +247,7 @@ begin
|
|||||||
AValue := Arg.BoolValue;
|
AValue := Arg.BoolValue;
|
||||||
Result := errOK;
|
Result := errOK;
|
||||||
end;
|
end;
|
||||||
|
atError: Result := Arg.ErrorValue;
|
||||||
else Result := errWrongType;
|
else Result := errWrongType;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -233,6 +259,7 @@ 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;
|
||||||
|
atError : Result := Arg.ErrorValue;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -243,6 +270,7 @@ begin
|
|||||||
AString := Arg.StringValue;
|
AString := Arg.StringValue;
|
||||||
Result := errOK;
|
Result := errOK;
|
||||||
end;
|
end;
|
||||||
|
atError : Result := Arg.ErrorValue;
|
||||||
else Result := errWrongType;
|
else Result := errWrongType;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -332,6 +360,31 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function PopDateValue(Args: TsArgumentStack; out ADate: TDate;
|
||||||
|
out AErrArg: TsArgument): Boolean;
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
case arg.ArgumentType of
|
||||||
|
atError, atBool, atEmpty:
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
AErrArg := CreateError(errWrongType);
|
||||||
|
end;
|
||||||
|
atNumber:
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
ADate := arg.NumberValue;
|
||||||
|
end;
|
||||||
|
atString:
|
||||||
|
begin
|
||||||
|
Result := TryStrToDate(arg.StringValue, ADate);
|
||||||
|
if not Result then AErrArg := CreateError(errWrongType);
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
Pops floating point values from the argument stack. Is called when
|
Pops floating point values from the argument stack. Is called when
|
||||||
calculating rpn formulas.
|
calculating rpn formulas.
|
||||||
@ -435,6 +488,32 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function PopTimeValue(Args: TsArgumentStack; out ATime: TTime;
|
||||||
|
out AErrArg: TsArgument): Boolean;
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
case arg.ArgumentType of
|
||||||
|
atError, atBool, atEmpty:
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
AErrArg := CreateError(errWrongType);
|
||||||
|
end;
|
||||||
|
atNumber:
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
ATime := frac(arg.NumberValue);
|
||||||
|
end;
|
||||||
|
atString:
|
||||||
|
begin
|
||||||
|
Result := TryStrToTime(arg.StringValue, ATime);
|
||||||
|
if not Result then AErrArg := CreateError(errWrongType);
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Operations }
|
{ Operations }
|
||||||
|
|
||||||
function fpsAdd(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsAdd(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
@ -759,13 +838,13 @@ begin
|
|||||||
base := arg_base.NumberValue;
|
base := arg_base.NumberValue;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
arg_number := Args.Pop;
|
||||||
|
|
||||||
if base < 0 then begin
|
if base < 0 then begin
|
||||||
Result := CreateError(errOverflow);
|
Result := CreateError(errOverflow);
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
arg_number := Args.Pop;
|
|
||||||
if arg_number.ArgumentType <> atNumber then begin
|
if arg_number.ArgumentType <> atNumber then begin
|
||||||
Result := CreateError(errWrongType);
|
Result := CreateError(errWrongType);
|
||||||
exit;
|
exit;
|
||||||
@ -872,6 +951,159 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ Date/time functions }
|
||||||
|
|
||||||
|
function fpsDATE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// DATE( year, month, day )
|
||||||
|
var
|
||||||
|
data: TFloatArray;
|
||||||
|
d: TDate;
|
||||||
|
begin
|
||||||
|
if PopFloatValues(Args, 3, data, Result) then begin
|
||||||
|
d := EncodeDate(round(data[0]), round(data[1]), round(data[2]));
|
||||||
|
Result := CreateNumber(d);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsDATEDIF(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
{ 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
|
||||||
|
data: TStrArray;
|
||||||
|
start_date, end_date: TDate;
|
||||||
|
begin
|
||||||
|
if not PopStringValues(Args, 1, data, Result) then exit;
|
||||||
|
if not PopDateValue(Args, end_date, Result) then exit;
|
||||||
|
if not PopDateValue(Args, start_date, Result) then exit;
|
||||||
|
|
||||||
|
if end_date > start_date then
|
||||||
|
Result := CreateError(errOverflow)
|
||||||
|
else if data[0] = 'Y' then
|
||||||
|
Result := CreateNumber(YearsBetween(end_date, start_date))
|
||||||
|
else if data[0] = 'M' then
|
||||||
|
Result := CreateNumber(MonthsBetween(end_date, start_date))
|
||||||
|
else if data[0] = 'D' then
|
||||||
|
Result := CreateNumber(DaysBetween(end_date, start_date))
|
||||||
|
else
|
||||||
|
Result := CreateError(errFormulaNotSupported);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsDATEVALUE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// DATEVALUE( date ) -- date can be a string or a date/time
|
||||||
|
var
|
||||||
|
d: TDate;
|
||||||
|
begin
|
||||||
|
if PopDateValue(Args, d, Result) then
|
||||||
|
Result := CreateNumber(d);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsDAY(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
var
|
||||||
|
d: TDate;
|
||||||
|
begin
|
||||||
|
if PopDateValue(Args, d, Result) then
|
||||||
|
Result := CreateNumber(DayOf(d));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsHOUR(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
var
|
||||||
|
t: TTime;
|
||||||
|
begin
|
||||||
|
if PopTimeValue(Args, t, Result) then
|
||||||
|
Result := CreateNumber(HourOf(t));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsMINUTE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
var
|
||||||
|
t: TTime;
|
||||||
|
begin
|
||||||
|
if PopTimeValue(Args, t, Result) then
|
||||||
|
Result := CreateNumber(MinuteOf(t));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsMONTH(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
var
|
||||||
|
d: TDate;
|
||||||
|
begin
|
||||||
|
if PopDateValue(Args, d, Result) then
|
||||||
|
Result := CreateNumber(MonthOf(d));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsNOW(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// NOW()
|
||||||
|
begin
|
||||||
|
Result := CreateNumber(now);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsSECOND(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
var
|
||||||
|
t: TTime;
|
||||||
|
begin
|
||||||
|
if PopTimeValue(Args, t, Result) then
|
||||||
|
Result := CreateNumber(SecondOf(t));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsTIME(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// TIME( hour, minute, second )
|
||||||
|
var
|
||||||
|
data: TFloatArray;
|
||||||
|
t: TTime;
|
||||||
|
begin
|
||||||
|
if PopFloatValues(Args, 3, data, Result) then begin
|
||||||
|
t := EncodeTime(round(data[0]), round(data[1]), round(data[2]), 0);
|
||||||
|
Result := CreateNumber(t);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsTIMEVALUE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// TIMEVALUE( time_value )
|
||||||
|
var
|
||||||
|
t: TTime;
|
||||||
|
begin
|
||||||
|
if PopTimeValue(Args, t, Result) then
|
||||||
|
Result := CreateNumber(t);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsToday(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// TODAY()
|
||||||
|
begin
|
||||||
|
Result := CreateNumber(Date());
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsWEEKDAY(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
{ WEEKDAY( serial_number, [return_value]
|
||||||
|
return_value = 1 - Returns a number from 1 (Sunday) to 7 (Saturday).
|
||||||
|
This is the default if parameter is omitted.
|
||||||
|
= 2 - Returns a number from 1 (Monday) to 7 (Sunday).
|
||||||
|
= 3 - Returns a number from 0 (Monday) to 6 (Sunday). }
|
||||||
|
var
|
||||||
|
d: TDate;
|
||||||
|
data: TFloatArray;
|
||||||
|
n: Integer;
|
||||||
|
begin
|
||||||
|
n := 1;
|
||||||
|
if NumArgs = 2 then
|
||||||
|
if PopFloatValues(Args, 1, data, Result) then n := round(data[0])
|
||||||
|
else exit;
|
||||||
|
if PopDateValue(Args, d, Result) then
|
||||||
|
Result := CreateNumber(DayOfWeek(d));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsYEAR(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
var
|
||||||
|
d: TDate;
|
||||||
|
begin
|
||||||
|
if PopDateValue(Args, d, Result) then
|
||||||
|
Result := CreateNumber(YearOf(d));
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ Statistical functions }
|
{ Statistical functions }
|
||||||
|
|
||||||
function fpsAVEDEV(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsAVEDEV(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
@ -901,15 +1133,19 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function fpsCOUNT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsCOUNT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
{ Count non-missing arguments
|
{ counts the number of cells that contain numbers as well as the number of
|
||||||
|
arguments that contain numbers.
|
||||||
COUNT( argument1, [argument2, ... argument_n] )
|
COUNT( argument1, [argument2, ... argument_n] )
|
||||||
}
|
}
|
||||||
var
|
var
|
||||||
n, i: Integer;
|
n, i: Integer;
|
||||||
|
arg: TsArgument;
|
||||||
begin
|
begin
|
||||||
n := 0;
|
n := 0;
|
||||||
for i:=1 to NumArgs do
|
for i:=1 to NumArgs do begin
|
||||||
if not Args.Pop.IsMissing then inc(n);
|
arg := Args.Pop;
|
||||||
|
if (not arg.IsMissing) and (arg.ArgumentType = atNumber) then inc(n);
|
||||||
|
end;
|
||||||
Result := CreateNumber(n);
|
Result := CreateNumber(n);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1221,6 +1457,34 @@ begin
|
|||||||
Result := CreateString(UTF8RightStr(s, count));
|
Result := CreateString(UTF8RightStr(s, count));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function fpsSUBSTITUTE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// SUBSTITUTE( text, old_text, new_text, [nth_appearance] )
|
||||||
|
var
|
||||||
|
n: Integer;
|
||||||
|
arg: TsArgument;
|
||||||
|
data: TStrArray;
|
||||||
|
s, s_old, s_new: String;
|
||||||
|
begin
|
||||||
|
Result := CreateError(errWrongType);
|
||||||
|
n := -1;
|
||||||
|
if (NumArgs = 4) then begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
if not arg.IsMissing and (arg.ArgumentType <> atNumber) then
|
||||||
|
exit;
|
||||||
|
n := round(arg.NumberValue);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if PopStringValues(Args, 3, data, Result) then begin
|
||||||
|
s := data[0];
|
||||||
|
s_old := data[1];
|
||||||
|
s_new := data[2];
|
||||||
|
if n = -1 then
|
||||||
|
Result := CreateString(UTF8StringReplace(s, s_old, s_new, [rfReplaceAll]))
|
||||||
|
else
|
||||||
|
Result := CreateError(errFormulaNotSupported);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function fpsTRIM(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
function fpsTRIM(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
// TRIM( text )
|
// TRIM( text )
|
||||||
var
|
var
|
||||||
@ -1239,4 +1503,92 @@ begin
|
|||||||
Result := CreateString(UTF8UpperCase(data[0]));
|
Result := CreateString(UTF8UpperCase(data[0]));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ Info functions }
|
||||||
|
|
||||||
|
function fpsISERR(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// ISERR( value )
|
||||||
|
// If value is an error value (except #N/A), this function will return TRUE.
|
||||||
|
// Otherwise, it will return FALSE.
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool((arg.ArgumentType = atError) and (arg.ErrorValue <> errArgError));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsISERROR(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// 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
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool((arg.ArgumentType = atError));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsISLOGICAL(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// ISLOGICAL( value )
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool(arg.ArgumentType = atBool);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsISNA(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// ISNA( value )
|
||||||
|
// If value is a #N/A error value , this function will return TRUE.
|
||||||
|
// Otherwise, it will return FALSE.
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool((arg.ArgumentType = atError) and (arg.ErrorValue = errArgError));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsISNONTEXT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// ISNONTEXT( value )
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool(arg.ArgumentType <> atString);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsISNUMBER(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// ISNUMBER( value )
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool(arg.ArgumentType = atNumber);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsISTEXT(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// ISTEXT( value )
|
||||||
|
var
|
||||||
|
arg: TsArgument;
|
||||||
|
begin
|
||||||
|
arg := Args.Pop;
|
||||||
|
Result := CreateBool(arg.ArgumentType = atString);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function fpsVALUE(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
|
||||||
|
// VALUE( text )
|
||||||
|
// text is the text value to convert to a number. If text is not a number, the
|
||||||
|
// VALUE function will return #VALUE!.
|
||||||
|
var
|
||||||
|
data: TStrArray;
|
||||||
|
x: Double;
|
||||||
|
begin
|
||||||
|
if PopStringValues(Args, 1, data, Result) then
|
||||||
|
if TryStrToFloat(data[0], x) then
|
||||||
|
Result := CreateNumber(x)
|
||||||
|
else
|
||||||
|
Result := CreateError(errWrongType);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -1094,8 +1094,8 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
const
|
const
|
||||||
FEProps: array[TFEKind] of TFEProp = (
|
FEProps: array[TFEKind] of TFEProp = ( // functions marked by (*)
|
||||||
{ Operands }
|
{ Operands } // are only partially supported
|
||||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCell
|
(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), // fekCellRef
|
||||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRange
|
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRange
|
||||||
@ -1149,20 +1149,20 @@ const
|
|||||||
(Symbol:'TAN'; MinParams:1; MaxParams:1; Func:fpsTAN), // fekTAN
|
(Symbol:'TAN'; MinParams:1; MaxParams:1; Func:fpsTAN), // fekTAN
|
||||||
(Symbol:'TANH'; MinParams:1; MaxParams:1; Func:fpsTANH), // fekTANH,
|
(Symbol:'TANH'; MinParams:1; MaxParams:1; Func:fpsTANH), // fekTANH,
|
||||||
{ date/time }
|
{ date/time }
|
||||||
(Symbol:'DATE'; MinParams:3; MaxParams:3; Func:nil), // fekDATE
|
(Symbol:'DATE'; MinParams:3; MaxParams:3; Func:fpsDATE), // fekDATE
|
||||||
(Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:nil), // fekDATEDIF
|
(Symbol:'DATEDIF'; MinParams:3; MaxParams:3; Func:fpsDATEDIF), // fekDATEDIF (*)
|
||||||
(Symbol:'DATEVALUE'; MinParams:1; MaxParams:1; Func:nil), // fekDATEVALUE
|
(Symbol:'DATEVALUE'; MinParams:1; MaxParams:1; Func:fpsDATEVALUE), // fekDATEVALUE
|
||||||
(Symbol:'DAY'; MinParams:1; MaxParams:1; Func:nil), // fekDAY
|
(Symbol:'DAY'; MinParams:1; MaxParams:1; Func:fpsDAY), // fekDAY
|
||||||
(Symbol:'HOUR'; MinParams:1; MaxParams:1; Func:nil), // fekHOUR
|
(Symbol:'HOUR'; MinParams:1; MaxParams:1; Func:fpsHOUR), // fekHOUR
|
||||||
(Symbol:'MINUTE'; MinParams:1; MaxParams:1; Func:nil), // fekMINUTE
|
(Symbol:'MINUTE'; MinParams:1; MaxParams:1; Func:fpsMINUTE), // fekMINUTE
|
||||||
(Symbol:'MONTH'; MinParams:1; MaxParams:1; Func:nil), // fekMONTH
|
(Symbol:'MONTH'; MinParams:1; MaxParams:1; Func:fpsMONTH), // fekMONTH
|
||||||
(Symbol:'NOW'; MinParams:0; MaxParams:0; Func:nil), // fekNOW
|
(Symbol:'NOW'; MinParams:0; MaxParams:0; Func:fpsNOW), // fekNOW
|
||||||
(Symbol:'SECOND'; MinParams:1; MaxParams:1; Func:nil), // fekSECOND
|
(Symbol:'SECOND'; MinParams:1; MaxParams:1; Func:fpsSECOND), // fekSECOND
|
||||||
(Symbol:'TIME'; MinParams:3; MaxParams:3; Func:nil), // fekTIME
|
(Symbol:'TIME'; MinParams:3; MaxParams:3; Func:fpsTIME), // fekTIME
|
||||||
(Symbol:'TIMEVALUE'; MinParams:1; MaxParams:1; Func:nil), // fekTIMEVALUE
|
(Symbol:'TIMEVALUE'; MinParams:1; MaxParams:1; Func:fpsTIMEVALUE), // fekTIMEVALUE
|
||||||
(Symbol:'TODAY'; MinParams:0; MaxParams:0; Func:nil), // fekTODAY
|
(Symbol:'TODAY'; MinParams:0; MaxParams:0; Func:fpsTODAY), // fekTODAY
|
||||||
(Symbol:'WEEKDAY'; MinParams:1; MaxParams:2; Func:nil), // fekWEEKDAY
|
(Symbol:'WEEKDAY'; MinParams:1; MaxParams:2; Func:fpsWEEKDAY), // fekWEEKDAY
|
||||||
(Symbol:'YEAR'; MinParams:1; MaxParams:1; Func:nil), // fekYEAR
|
(Symbol:'YEAR'; MinParams:1; MaxParams:1; Func:fpsYEAR), // fekYEAR
|
||||||
{ statistical }
|
{ statistical }
|
||||||
(Symbol:'AVEDEV'; MinParams:1; MaxParams:30; Func:fpsAVEDEV), // fekAVEDEV
|
(Symbol:'AVEDEV'; MinParams:1; MaxParams:30; Func:fpsAVEDEV), // fekAVEDEV
|
||||||
(Symbol:'AVERAGE'; MinParams:1; MaxParams:30; Func:fpsAVERAGE), // fekAVERAGE
|
(Symbol:'AVERAGE'; MinParams:1; MaxParams:30; Func:fpsAVERAGE), // fekAVERAGE
|
||||||
@ -1210,7 +1210,7 @@ const
|
|||||||
(Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER
|
(Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER
|
||||||
(Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:fpsREPLACE), // fekREPLACE
|
(Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:fpsREPLACE), // fekREPLACE
|
||||||
(Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:fpsRIGHT), // fekRIGHT
|
(Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:fpsRIGHT), // fekRIGHT
|
||||||
(Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:nil), // fekSUBSTITUTE
|
(Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:fpsSUBSTITUTE), // fekSUBSTITUTE (*)
|
||||||
(Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM
|
(Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM
|
||||||
(Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER
|
(Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER
|
||||||
{ lookup/reference }
|
{ lookup/reference }
|
||||||
@ -1222,15 +1222,15 @@ const
|
|||||||
(Symbol:'CELL'; MinParams:1; MaxParams:2; Func:nil), // fekCELLINFO
|
(Symbol:'CELL'; MinParams:1; MaxParams:2; Func:nil), // 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:nil), // fekIsERR
|
(Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:fpsISERR), // fekIsERR
|
||||||
(Symbol:'ISERROR'; MinParams:1; MaxParams:1; Func:nil), // fekIsERROR
|
(Symbol:'ISERROR'; MinParams:1; MaxParams:1; Func:fpsISERROR), // fekIsERROR
|
||||||
(Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1; Func:nil), // fekIsLOGICAL
|
(Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1; Func:fpsISLOGICAL), // fekIsLOGICAL
|
||||||
(Symbol:'ISNA'; MinParams:1; MaxParams:1; Func:nil), // fekIsNA
|
(Symbol:'ISNA'; MinParams:1; MaxParams:1; Func:fpsISNA), // fekIsNA
|
||||||
(Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1; Func:nil), // fekIsNONTEXT
|
(Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1; Func:fpsISNONTEXT), // fekIsNONTEXT
|
||||||
(Symbol:'ISNUMBER'; MinParams:1; MaxParams:1; Func:nil), // fekIsNUMBER
|
(Symbol:'ISNUMBER'; MinParams:1; MaxParams:1; Func:fpsISNUMBER), // fekIsNUMBER
|
||||||
(Symbol:'ISREF'; MinParams:1; MaxParams:1; Func:nil), // fekIsRef
|
(Symbol:'ISREF'; MinParams:1; MaxParams:1; Func:nil), // fekIsRef
|
||||||
(Symbol:'ISTEXT'; MinParams:1; MaxParams:1; Func:nil), // fekIsTEXT
|
(Symbol:'ISTEXT'; MinParams:1; MaxParams:1; Func:fpsISTEXT), // fekIsTEXT
|
||||||
(Symbol:'VALUE'; MinParams:1; MaxParams:1; Func:nil), // fekValue
|
(Symbol:'VALUE'; MinParams:1; MaxParams:1; Func:fpsVALUE), // fekValue
|
||||||
{ Other operations }
|
{ Other operations }
|
||||||
(Symbol:'SUM'; MinParams:1; MaxParams:1; Func:nil) // 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}
|
||||||
);
|
);
|
||||||
@ -1460,14 +1460,18 @@ begin
|
|||||||
func := FEProps[fe.ElementKind].Func;
|
func := FEProps[fe.ElementKind].Func;
|
||||||
if not Assigned(func) then begin
|
if not Assigned(func) then begin
|
||||||
// calculation of function not implemented
|
// calculation of function not implemented
|
||||||
|
WriteErrorValue(ACell, errFormulaNotSupported);
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
if args.Count < fe.ParamsNum then begin
|
if args.Count < fe.ParamsNum then begin
|
||||||
// not enough parameters
|
// not enough parameters
|
||||||
|
WriteErrorValue(ACell, errArgError);
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
// Result of function
|
// Result of function
|
||||||
val := func(args, fe.ParamsNum);
|
val := func(args, fe.ParamsNum);
|
||||||
|
args.Push(val);
|
||||||
|
{
|
||||||
// Push valid result on stack, exit in case of error
|
// Push valid result on stack, exit in case of error
|
||||||
case val.ArgumentType of
|
case val.ArgumentType of
|
||||||
atNumber, atString, atBool, atEmpty:
|
atNumber, atString, atBool, atEmpty:
|
||||||
@ -1478,6 +1482,7 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
}
|
||||||
end; // case
|
end; // case
|
||||||
end; // for
|
end; // for
|
||||||
if args.Count = 1 then begin
|
if args.Count = 1 then begin
|
||||||
@ -1486,19 +1491,22 @@ begin
|
|||||||
atNumber: WriteNumber(ACell, val.NumberValue);
|
atNumber: WriteNumber(ACell, val.NumberValue);
|
||||||
atBool : WriteBoolValue(ACell, val.BoolValue);
|
atBool : WriteBoolValue(ACell, val.BoolValue);
|
||||||
atString: WriteUTF8Text(ACell, val.StringValue);
|
atString: WriteUTF8Text(ACell, val.StringValue);
|
||||||
|
atError : WriteErrorValue(ACell, val.ErrorValue);
|
||||||
atEmpty : WriteBlank(ACell);
|
atEmpty : WriteBlank(ACell);
|
||||||
end;
|
end;
|
||||||
end else
|
end else
|
||||||
|
WriteErrorValue(ACell, errArgError);
|
||||||
|
{
|
||||||
// This case is a program error --> raise an exception
|
// This case is a program error --> raise an exception
|
||||||
raise Exception.CreateFmt('Incorrect argument count of the formula in cell %s', [
|
raise Exception.CreateFmt('Incorrect argument count of the formula in cell %s', [
|
||||||
GetCellString(ACell^.Row, ACell^.Col, [])
|
GetCellString(ACell^.Row, ACell^.Col, [])
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
args.Free;
|
args.Free;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
Converts a FPSpreadsheet cell position, which is Row, Col in numbers
|
Converts a FPSpreadsheet cell position, which is Row, Col in numbers
|
||||||
and zero based, to a textual representation which is [Col][Row],
|
and zero based, to a textual representation which is [Col][Row],
|
||||||
|
@ -6,6 +6,10 @@ unit formulatests;
|
|||||||
when the corresponding rpn formula is calculated. }
|
when the corresponding rpn formula is calculated. }
|
||||||
{.$DEFINE ENABLE_CALC_RPN_EXCEPTIONS}
|
{.$DEFINE ENABLE_CALC_RPN_EXCEPTIONS}
|
||||||
|
|
||||||
|
{ Deactivate this define to include errors in the structure of the rpn formulas.
|
||||||
|
Note that Excel report a corrupted file when trying to read this file }
|
||||||
|
{.DEFINE ENABLE_DEFECTIVE_FORMULAS }
|
||||||
|
|
||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
@ -143,6 +147,9 @@ var
|
|||||||
cell: PCell;
|
cell: PCell;
|
||||||
sollValues: array of TsArgument;
|
sollValues: array of TsArgument;
|
||||||
formula: String;
|
formula: String;
|
||||||
|
s: String;
|
||||||
|
t: TTime;
|
||||||
|
hr,min,sec,msec: Word;
|
||||||
begin
|
begin
|
||||||
TempFile := GetTempFileName;
|
TempFile := GetTempFileName;
|
||||||
|
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
<UseExternalDbgSyms Value="True"/>
|
<UseExternalDbgSyms Value="True"/>
|
||||||
</Debugging>
|
</Debugging>
|
||||||
</Linking>
|
</Linking>
|
||||||
|
<Other>
|
||||||
|
<CompilerPath Value="$(CompPath)"/>
|
||||||
|
</Other>
|
||||||
</CompilerOptions>
|
</CompilerOptions>
|
||||||
</Item2>
|
</Item2>
|
||||||
</BuildModes>
|
</BuildModes>
|
||||||
@ -154,6 +157,9 @@
|
|||||||
<OptimizationLevel Value="0"/>
|
<OptimizationLevel Value="0"/>
|
||||||
</Optimizations>
|
</Optimizations>
|
||||||
</CodeGeneration>
|
</CodeGeneration>
|
||||||
|
<Other>
|
||||||
|
<CompilerPath Value="$(CompPath)"/>
|
||||||
|
</Other>
|
||||||
</CompilerOptions>
|
</CompilerOptions>
|
||||||
<Debugging>
|
<Debugging>
|
||||||
<Exceptions Count="6">
|
<Exceptions Count="6">
|
||||||
|
@ -680,6 +680,230 @@
|
|||||||
SetLength(sollValues, Row+1);
|
SetLength(sollValues, Row+1);
|
||||||
sollValues[Row] := CreateNumber(tanh(0.1));
|
sollValues[Row] := CreateNumber(tanh(0.1));
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ Date/time functions }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
|
||||||
|
// 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))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(EncodeDate(2014,7,1));
|
||||||
|
|
||||||
|
// 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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(7);
|
||||||
|
|
||||||
|
// NOW
|
||||||
|
inc(Row);
|
||||||
|
// 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);
|
||||||
|
until msec < 500;
|
||||||
|
MyWorksheet.WriteUTF8Text(Row, 0, '=NOW()');
|
||||||
|
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||||
|
RPNFunc(fekNOW, nil)));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(t);
|
||||||
|
|
||||||
|
// 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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(EncodeTime(9, 59, 20, 0));
|
||||||
|
|
||||||
|
// TODAY
|
||||||
|
inc(Row);
|
||||||
|
MyWorksheet.WriteUTF8Text(Row, 0, '=TODAY()');
|
||||||
|
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||||
|
RPNFunc(fekTODAY, nil)));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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)))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(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))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(2014);
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
{ Statistical functions }
|
{ Statistical functions }
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
@ -1184,6 +1408,17 @@
|
|||||||
SetLength(sollValues, Row+1);
|
SetLength(sollValues, Row+1);
|
||||||
sollValues[Row] := CreateString('d');
|
sollValues[Row] := CreateString('d');
|
||||||
|
|
||||||
|
// 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))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateString('lazarus');
|
||||||
|
|
||||||
// Trim
|
// Trim
|
||||||
inc(Row);
|
inc(Row);
|
||||||
MyWorksheet.WriteUTF8Text(Row, 0, '=TRIM(" Hallo word ")');
|
MyWorksheet.WriteUTF8Text(Row, 0, '=TRIM(" Hallo word ")');
|
||||||
@ -1211,3 +1446,67 @@
|
|||||||
SetLength(sollValues, Row+1);
|
SetLength(sollValues, Row+1);
|
||||||
sollValues[Row] := CreateString(UTF8UpperCase('Viele Grüße'));
|
sollValues[Row] := CreateString(UTF8UpperCase('Viele Grüße'));
|
||||||
|
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ Information functions }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
|
||||||
|
// IsError
|
||||||
|
inc(Row);
|
||||||
|
MyWorksheet.WriteUTF8Text(Row, 0, '=ISERROR(1/0)');
|
||||||
|
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||||
|
RPNNumber(1,
|
||||||
|
RPNNumber(0,
|
||||||
|
RPNFunc(fekDiv,
|
||||||
|
RPNFunc(fekISERROR, nil))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateBool(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))))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateBool(false);
|
||||||
|
|
||||||
|
// VALUE
|
||||||
|
inc(Row);
|
||||||
|
MyWorksheet.WriteUTF8Text(Row, 0, '=VALUE("100")');
|
||||||
|
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||||
|
RPNString('100',
|
||||||
|
RPNFunc(fekVALUE, nil))));
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateNumber(100);
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ Error cases }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{$IFDEF ENABLE_DEFECTIVE_FORMULAS }
|
||||||
|
// Using less parameters than specified
|
||||||
|
inc(Row);
|
||||||
|
MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1)');
|
||||||
|
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||||
|
RPNBool(true,
|
||||||
|
RPNNumber(1.0,
|
||||||
|
RPNFunc(fekIF,3, nil))))); // <-- we have only 2 parameters!
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateError(errArgError);
|
||||||
|
|
||||||
|
// Using more parameters than specified
|
||||||
|
inc(Row);
|
||||||
|
MyWorksheet.WriteUTF8Text(Row, 0, '=IF(true,1,"A")');
|
||||||
|
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||||
|
RPNBool(true,
|
||||||
|
RPNNumber(1.0,
|
||||||
|
RPNString('A',
|
||||||
|
RPNFunc(fekIF,2, nil)))))); // <-- we have 3 parameters, not 2
|
||||||
|
SetLength(sollValues, Row+1);
|
||||||
|
sollValues[Row] := CreateError(errWrongType);
|
||||||
|
{ The first idea was that this should report an ArgError, but in fact it is
|
||||||
|
a WrongType error because popping two values from the stack finds a number,
|
||||||
|
but a bool is expected }
|
||||||
|
{$ENDIF}
|
||||||
|
Reference in New Issue
Block a user