diff --git a/components/fpspreadsheet/fpsfunc.pas b/components/fpspreadsheet/fpsfunc.pas index f17407d8a..28f5192a5 100644 --- a/components/fpspreadsheet/fpsfunc.pas +++ b/components/fpspreadsheet/fpsfunc.pas @@ -41,22 +41,21 @@ type function PopString(out AValue: String; out AErrArg: TsArgument): Boolean; function PopStringValues(ANumArgs: Integer; ARangeAllowed:Boolean; out AValues: TsArgStringArray; out AErrArg: TsArgument): Boolean; - procedure Push(AValue: TsArgument); - procedure PushBool(AValue: Boolean); + procedure Push(AValue: TsArgument; AWorksheet: TsWorksheet); + procedure PushBool(AValue: Boolean; AWorksheet: TsWorksheet); procedure PushCell(AValue: PCell; AWorksheet: TsWorksheet); procedure PushCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal; AWorksheet: TsWorksheet); - procedure PushMissing; - procedure PushNumber(AValue: Double); - procedure PushString(AValue: String); + procedure PushMissing(AWorksheet: TsWorksheet); + procedure PushNumber(AValue: Double; AWorksheet: TsWorksheet); + procedure PushString(AValue: String; AWorksheet: TsWorksheet); procedure Clear; procedure Delete(AIndex: Integer); end; function CreateBool(AValue: Boolean): TsArgument; -function CreateCell(AValue: PCell; AWorksheet: TsWorksheet): TsArgument; -function CreateCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal; - AWorksheet: TsWorksheet): TsArgument; +function CreateCell(AValue: PCell): TsArgument; +function CreateCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal): TsArgument; function CreateNumber(AValue: Double): TsArgument; function CreateString(AValue: String): TsArgument; function CreateError(AError: TsErrorValue): TsArgument; @@ -156,14 +155,22 @@ function fpsRIGHT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsSUBSTITUTE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTRIM (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsUPPER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ lookup / reference } +function fpsCOLUMN (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsCOLUMNS (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsROW (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsROWS (Args: TsArgumentStack; NumArgs: Integer): TsArgument; { info functions } function fpsCELLINFO (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsINFO (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsISBLANK (Args: TsArgumentStack; NumArgs: Integer): TsArgument; 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 fpsISREF (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsISTEXT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsVALUE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; @@ -188,16 +195,14 @@ begin Result.Boolvalue := AValue; end; -function CreateCell(AValue: PCell; AWorksheet: TsWorksheet): TsArgument; +function CreateCell(AValue: PCell): TsArgument; begin Result := CreateArgument; Result.ArgumentType := atCell; Result.Cell := AValue; - Result.Worksheet := AWorksheet; end; -function CreateCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal; - AWorksheet: TsWorksheet): TsArgument; +function CreateCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal): TsArgument; begin Result := CreateArgument; Result.ArgumentType := atCellRange; @@ -205,7 +210,6 @@ begin Result.FirstCol := AFirstCol; Result.LastRow := ALastRow; Result.LastCol := ALastCol; - Result.Worksheet := AWorksheet; end; function CreateNumber(AValue: Double): TsArgument; @@ -289,7 +293,7 @@ begin Result := TsArgumentStack.Create; for counter := 1 to ACount do begin arg := Pop; - Result.Push(arg); + Result.Push(arg, arg.Worksheet); end; end; @@ -588,7 +592,7 @@ begin end; end; -procedure TsArgumentStack.Push(AValue: TsArgument); +procedure TsArgumentStack.Push(AValue: TsArgument; AWorksheet: TsWorksheet); var arg: PsArgument; begin @@ -596,42 +600,43 @@ begin arg^ := AValue; arg^.StringValue := AValue.StringValue; arg^.Cell := AValue.Cell; + arg^.Worksheet := AWorksheet; Add(arg); end; -procedure TsArgumentStack.PushBool(AValue: Boolean); +procedure TsArgumentStack.PushBool(AValue: Boolean; AWorksheet: TsWorksheet); begin - Push(CreateBool(AValue)); + Push(CreateBool(AValue), AWorksheet); end; procedure TsArgumentStack.PushCell(AValue: PCell; AWorksheet: TsWorksheet); begin - Push(CreateCell(AValue, AWorksheet)); + Push(CreateCell(AValue), AWorksheet); end; procedure TsArgumentStack.PushCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal; AWorksheet: TsWorksheet); begin - Push(CreateCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol, AWorksheet)); + Push(CreateCellRange(AFirstRow, AFirstCol, ALastRow, ALastCol), AWorksheet); end; -procedure TsArgumentStack.PushMissing; +procedure TsArgumentStack.PushMissing(AWorksheet: TsWorksheet); var arg: TsArgument; begin arg := CreateArgument; arg.IsMissing := true; - Push(arg); + Push(arg, AWorksheet); end; -procedure TsArgumentStack.PushNumber(AValue: Double); +procedure TsArgumentStack.PushNumber(AValue: Double; AWorksheet: TsWorksheet); begin - Push(CreateNumber(AValue)); + Push(CreateNumber(AValue), AWorksheet); end; -procedure TsArgumentStack.PushString(AValue: String); +procedure TsArgumentStack.PushString(AValue: String; AWorksheet: TsWorksheet); begin - Push(CreateString(AValue)); + Push(CreateString(AValue), AWorksheet); end; @@ -1811,6 +1816,80 @@ begin end; +{ Lookup / refernence functions } + +function fpsCOLUMN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ COLUMN( [reference] ) + Returns the column number of a cell reference (starting at 1). + "reference" is a reference to a cell or range of cells. + If omitted, it is assumed that the reference is the cell address in which the + COLUMN function has been entered in. } +var + arg: TsArgument; +begin + if NumArgs = 0 then + Result := CreateError(errArgError); + // We don't know here which cell contains the formula. + + arg := Args.Pop; + case arg.ArgumentType of + atCell : Result := CreateNumber(arg.Cell^.Col + 1); + atCellRange: Result := CreateNumber(arg.FirstCol + 1); + else Result := CreateError(errWrongType); + end; +end; + +function fpsCOLUMNS(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ COLUMNS( [reference] ) + returns the number of column in a cell reference. } +var + arg: TsArgument; +begin + arg := Args.Pop; + case arg.ArgumentType of + atCell : Result := CreateNumber(1); + atCellRange: Result := CreateNumber(arg.LastCol - arg.FirstCol + 1); + else Result := CreateError(errWrongType); + end; +end; + +function fpsROW(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ ROW( [reference] ) + Returns the row number of a cell reference (starting at 1!) + "reference" is a reference to a cell or range of cells. + If omitted, it is assumed that the reference is the cell address in which the + ROW function has been entered in. } +var + arg: TsArgument; +begin + if NumArgs = 0 then + Result := CreateError(errArgError); + // We don't know here which cell contains the formula. + + arg := Args.Pop; + case arg.ArgumentType of + atCell : Result := CreateNumber(arg.Cell^.Row + 1); + atCellRange: Result := CreateNumber(arg.FirstRow + 1); + else Result := CreateError(errWrongType); + end; +end; + + +function fpsROWS(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ ROWS( [reference] ) + returns the number of rows in a cell reference. } +var + arg: TsArgument; +begin + arg := Args.Pop; + case arg.ArgumentType of + atCell : Result := CreateNumber(1); + atCellRange: Result := CreateNumber(arg.LastRow - arg.FirstRow + 1); + else Result := CreateError(errWrongType); + end; +end; + + { Info functions } function fpsCELLINFO(Args: TsArgumentStack; NumArgs: Integer): TsArgument; @@ -1978,6 +2057,51 @@ begin end; end; +function fpsINFO(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +{ INFO( type ) + returns information about the operating environment. + type can be one of the following values: + + "directory" Path of the current directory. + + "numfile" Number of active worksheets. + - "origin" The cell that is in the top, left-most cell visible in the current Excel spreadsheet. + - "osversion" Operating system version. + - "recalc" Returns the recalculation mode - either Automatic or Manual. + - "release" Version of Excel that you are running. + - "system" Name of the operating environment. + ONLY THOSE MARKED BY "+" ARE SUPPORTED! } +var + arg: TsArgument; + workbook: TsWorkbook; + s: String; +begin + arg := Args.Pop; + if arg.ArgumentType <> atString then + Result := CreateError(errWrongType) + else begin + s := Lowercase(arg.StringValue); + workbook := arg.Worksheet.Workbook; + if s = 'directory' then + Result := CreateString(ExtractFilePath(workbook.FileName)) + else + if s = 'numfile' then + Result := CreateNumber(workbook.GetWorksheetCount) + else + Result := CreateError(errFormulaNotSupported); + end; +end; + +function fpsISBLANK(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// ISBLANK( value ) +// Checks for blank cell +var + arg: TsArgument; +begin + arg := Args.Pop; + Result := CreateBool((arg.ArgumentType = atCell) and + ((arg.Cell = nil) or (arg.Cell^.ContentType = cctEmpty)) + ); +end; + function fpsISERR(Args: TsArgumentStack; NumArgs: Integer): TsArgument; // ISERR( value ) // If value is an error value (except #N/A), this function will return TRUE. @@ -2038,6 +2162,15 @@ begin Result := CreateBool(arg.ArgumentType = atNumber); end; +function fpsISREF(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// ISREF( value ) +var + arg: TsArgument; +begin + arg := Args.Pop; + Result := CreateBool(arg.ArgumentType in [atCell, atCellRange]); +end; + function fpsISTEXT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; // ISTEXT( value ) var diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 9529e4432..42b98716c 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -697,6 +697,7 @@ type FReadFormulas: Boolean; FDefaultColWidth: Single; // in "characters". Excel uses the width of char "0" in 1st font FDefaultRowHeight: Single; // in "character heights", i.e. line count + FFileName: String; { Internal methods } procedure PrepareBeforeSaving; @@ -768,6 +769,8 @@ type {@@ This property is only used for formats which don't support unicode and support a single encoding for the whole document, like Excel 2 to 5 } property Encoding: TsEncoding read FEncoding write FEncoding; + {@@ Filename of the saved workbook } + property FileName: String read FFileName; {@@ Identifies the file format which was detected when reading the file } property FileFormat: TsSpreadsheetFormat read FFormat; {@@ This property allows to turn off reading of rpn formulas; this is a @@ -1211,11 +1214,11 @@ var (Symbol:'VAR'; MinParams:1; MaxParams:30; Func:fpsVAR), // fekVAR (Symbol:'VARP'; MinParams:1; MaxParams:30; Func:fpsVARP), // fekVARP { financial } - (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 + (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; Func:fpsAND), // fekAND (Symbol:'FALSE'; MinParams:0; MaxParams:0; Func:fpsFALSE), // fekFALSE @@ -1224,37 +1227,37 @@ var (Symbol:'OR'; MinParams:1; MaxParams:30; Func:fpsOR), // fekOR (Symbol:'TRUE'; MinParams:0; MaxParams:0; Func:fpsTRUE), // fekTRUE { string } - (Symbol:'CHAR'; MinParams:1; MaxParams:1; Func:fpsCHAR), // fekCHAR - (Symbol:'CODE'; MinParams:1; MaxParams:1; Func:fpsCODE), // fekCODE - (Symbol:'LEFT'; MinParams:1; MaxParams:2; Func:fpsLEFT), // fekLEFT - (Symbol:'LOWER'; MinParams:1; MaxParams:1; Func:fpsLOWER), // fekLOWER - (Symbol:'MID'; MinParams:3; MaxParams:3; Func:fpsMID), // fekMID - (Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER - (Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:fpsREPLACE), // fekREPLACE - (Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:fpsRIGHT), // fekRIGHT - (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:fpsSUBSTITUTE), // fekSUBSTITUTE (*) - (Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM - (Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER + (Symbol:'CHAR'; MinParams:1; MaxParams:1; Func:fpsCHAR), // fekCHAR + (Symbol:'CODE'; MinParams:1; MaxParams:1; Func:fpsCODE), // fekCODE + (Symbol:'LEFT'; MinParams:1; MaxParams:2; Func:fpsLEFT), // fekLEFT + (Symbol:'LOWER'; MinParams:1; MaxParams:1; Func:fpsLOWER), // fekLOWER + (Symbol:'MID'; MinParams:3; MaxParams:3; Func:fpsMID), // fekMID + (Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER + (Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:fpsREPLACE), // fekREPLACE + (Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:fpsRIGHT), // fekRIGHT + (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:fpsSUBSTITUTE), // fekSUBSTITUTE (*) + (Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM + (Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER { lookup/reference } - (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 + (Symbol:'COLUMN'; MinParams:0; MaxParams:1; Func:fpsCOLUMN), // fekCOLUMN + (Symbol:'COLUMNS'; MinParams:1; MaxParams:1; Func:fpsCOLUMNS), // fekCOLUMNS + (Symbol:'ROW'; MinParams:0; MaxParams:1; Func:fpsROW), // fekROW + (Symbol:'ROWS'; MinParams:1; MaxParams:1; Func:fpsROWS), // fekROWS { info } - (Symbol:'CELL'; MinParams:1; MaxParams:2; Func:fpsCELLINFO), // fekCELLINFO - (Symbol:'INFO'; MinParams:1; MaxParams:1; Func:nil), // fekINFO - (Symbol:'ISBLANK'; MinParams:1; MaxParams:1; Func:nil), // fekIsBLANK - (Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:fpsISERR), // fekIsERR - (Symbol:'ISERROR'; MinParams:1; MaxParams:1; Func:fpsISERROR), // fekIsERROR - (Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1; Func:fpsISLOGICAL), // fekIsLOGICAL - (Symbol:'ISNA'; MinParams:1; MaxParams:1; Func:fpsISNA), // fekIsNA - (Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1; Func:fpsISNONTEXT), // fekIsNONTEXT - (Symbol:'ISNUMBER'; MinParams:1; MaxParams:1; Func:fpsISNUMBER), // fekIsNUMBER - (Symbol:'ISREF'; MinParams:1; MaxParams:1; Func:nil), // fekIsRef - (Symbol:'ISTEXT'; MinParams:1; MaxParams:1; Func:fpsISTEXT), // fekIsTEXT - (Symbol:'VALUE'; MinParams:1; MaxParams:1; Func:fpsVALUE), // fekValue + (Symbol:'CELL'; MinParams:1; MaxParams:2; Func:fpsCELLINFO), // fekCELLINFO (*) + (Symbol:'INFO'; MinParams:1; MaxParams:1; Func:fpsINFO), // fekINFO (*) + (Symbol:'ISBLANK'; MinParams:1; MaxParams:1; Func:fpsISBLANK), // fekIsBLANK + (Symbol:'ISERR'; MinParams:1; MaxParams:1; Func:fpsISERR), // fekIsERR + (Symbol:'ISERROR'; MinParams:1; MaxParams:1; Func:fpsISERROR), // fekIsERROR + (Symbol:'ISLOGICAL'; MinParams:1; MaxParams:1; Func:fpsISLOGICAL), // fekIsLOGICAL + (Symbol:'ISNA'; MinParams:1; MaxParams:1; Func:fpsISNA), // fekIsNA + (Symbol:'ISNONTEXT'; MinParams:1; MaxParams:1; Func:fpsISNONTEXT), // fekIsNONTEXT + (Symbol:'ISNUMBER'; MinParams:1; MaxParams:1; Func:fpsISNUMBER), // fekIsNUMBER + (Symbol:'ISREF'; MinParams:1; MaxParams:1; Func:fpsISREF), // fekIsRef + (Symbol:'ISTEXT'; MinParams:1; MaxParams:1; Func:fpsISTEXT), // fekIsTEXT + (Symbol:'VALUE'; MinParams:1; MaxParams:1; Func:fpsVALUE), // fekValue { 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} ); {@@ @@ -1510,15 +1513,16 @@ begin args := TsArgumentStack.Create; try for i := 0 to Length(ACell^.RPNFormulaValue) - 1 do begin - fe := ACell^.RPNFormulaValue[i]; // fe = "formula element" + fe := ACell^.RPNFormulaValue[i]; // "fe" means "formula element" case fe.ElementKind of fekCell, fekCellRef: begin cell := FindCell(fe.Row, fe.Col); - case cell^.CalcState of - csNotCalculated: CalcRPNFormula(cell); - csCalculating : raise Exception.Create(lpCircularReference); - end; + if cell <> nil then + case cell^.CalcState of + csNotCalculated: CalcRPNFormula(cell); + csCalculating : raise Exception.Create(lpCircularReference); + end; args.PushCell(cell, self); end; fekCellRange: @@ -1535,15 +1539,15 @@ begin args.PushCellRange(fe.Row, fe.Col, fe.Row2, fe.Col2, self); end; fekNum: - args.PushNumber(fe.DoubleValue); + args.PushNumber(fe.DoubleValue, self); fekInteger: - args.PushNumber(1.0*fe.IntValue); + args.PushNumber(1.0*fe.IntValue, self); fekString: - args.PushString(fe.StringValue); + args.PushString(fe.StringValue, self); fekBool: - args.PushBool(fe.DoubleValue <> 0.0); + args.PushBool(fe.DoubleValue <> 0.0, self); fekMissingArg: - args.PushMissing; + args.PushMissing(self); fekParen: ; // visual effect only fekErr: exit; @@ -1562,7 +1566,7 @@ begin // Result of function val := func(args, fe.ParamsNum); // Push result on stack for usage by next function or as final result - args.Push(val); + args.Push(val, self); end; // case end; // for @@ -3958,6 +3962,7 @@ var begin AReader := CreateSpreadReader(AFormat); try + FFileName := AFileName; AReader.ReadFromFile(AFileName, Self); FFormat := AFormat; finally @@ -4067,6 +4072,7 @@ var begin AWriter := CreateSpreadWriter(AFormat); try + FFileName := AFileName; PrepareBeforeSaving; AWriter.WriteToFile(AFileName, AOverwriteExisting); finally diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index b90fa07a1..f86e14d79 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -1662,11 +1662,76 @@ sollValues[Row] := CreateString(UTF8UpperCase('Viele Grüße')); +{------------------------------------------------------------------------------} +{ Lookup / referece functions } +{------------------------------------------------------------------------------} + + // COLUMN + MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMN(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('A1', + RPNFunc(fekCOLUMN, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(1); + + MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMN(C1:D3)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRange('C1:D3', + RPNFunc(fekCOLUMN, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(3); + + // COLUMNS + MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMNS(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('A1', + RPNFunc(fekCOLUMNS, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(1); + + MyWorksheet.WriteUTF8Text(Row, 0, '=COLUMNS(C1:D3)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRange('C1:D3', + RPNFunc(fekCOLUMNS, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(2); + + // ROW + MyWorksheet.WriteUTF8Text(Row, 0, '=ROW(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('A1', + RPNFunc(fekROW, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(1); + + MyWorksheet.WriteUTF8Text(Row, 0, '=ROW(C2:D3)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRange('C2:D3', + RPNFunc(fekROW, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(2); + + // ROWS + MyWorksheet.WriteUTF8Text(Row, 0, '=ROWS(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('A1', + RPNFunc(fekROWS, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(1); + + MyWorksheet.WriteUTF8Text(Row, 0, '=ROWS(C2:D3)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRange('C2:D3', + RPNFunc(fekROWS, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(2); + + {------------------------------------------------------------------------------} { Information functions } {------------------------------------------------------------------------------} - // INFO + // CELL MyWorksheet.WriteUTF8Text(Row, 0, '=CELL("address", A1)'); MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( RPNString('address', @@ -1723,6 +1788,47 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateString('v'); + // INFO + MyWorksheet.WriteUTF8Text(Row, 0, '=INFO("directory", A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('directory', + RPNFunc(fekINFO, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(ExtractFilePath(TempFile)); + + MyWorksheet.WriteUTF8Text(Row, 0, '=INFO("numfile")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('numfile', + RPNFunc(fekINFO, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(MyWorkbook.GetWorksheetCount); + + // IsBlank + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ISBLANK(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('A1', + RPNFunc(fekISBLANK, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateBool(false); // cell contains text --> not blank + + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ISBLANK(G1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('G1', + RPNFunc(fekISBLANK, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateBool(true); // the cell does not exist --> blank + + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ISBLANK(H1)'); + MyWorksheet.WriteBlank(0, 7); // A11 is an empty cell + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('H1', + RPNFunc(fekISBLANK, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateBool(true); // the cell exists, but it is empty + // IsError inc(Row); MyWorksheet.WriteUTF8Text(Row, 0, '=ISERROR(1/0)'); @@ -1745,6 +1851,34 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateBool(false); + // IsRef + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ISREF(1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1, + RPNFunc(fekISREF, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateBool(false); + + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ISREF(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRef('A1', + RPNFunc(fekISREF, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateBool(true); + + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=ISREF(A1)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellValue('A1', // we use a cell value here ! + RPNFunc(fekISREF, nil)))); + SetLength(sollValues, Row+1); + // The correct result would be "false" because a cell value is not the same as + // a cell reference. But Excel seems to ignore this difference here and + // accepts only a "true". + sollValues[Row] := CreateBool(true); + // VALUE inc(Row); MyWorksheet.WriteUTF8Text(Row, 0, '=VALUE("100")');