fpspreadsheet: Refactor dialects in formula parser.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7067 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2019-07-22 17:49:17 +00:00
parent bc94843c24
commit 47e9e05399
8 changed files with 178 additions and 134 deletions

View File

@ -1551,8 +1551,8 @@ begin
proc := @fixInsertedCol; proc := @fixInsertedCol;
for formula in self do for formula in self do
if formula^.Parser.IterateNodes(proc, Pointer(PtrInt(AIndex)), InSheet) then if formula^.Parser.IterateNodes(proc, {%H-}Pointer(PtrInt(AIndex)), InSheet) then
formula^.Text := formula^.Parser.Expression; formula^.Text := formula^.Parser.Expression[fdExcelA1];
end; end;
// Formula enumerators (use in "for ... in" syntax) // Formula enumerators (use in "for ... in" syntax)

View File

@ -673,6 +673,7 @@ type
end; end;
EExprScanner = class(Exception); EExprScanner = class(Exception);
PFormatSettings = ^TFormatSettings;
{ TsExpressionParser } { TsExpressionParser }
TsExpressionParser = class TsExpressionParser = class
@ -695,25 +696,27 @@ type
function GetAsFloat: TsExprFloat; function GetAsFloat: TsExprFloat;
function GetAsInteger: Int64; function GetAsInteger: Int64;
function GetAsString: String; function GetAsString: String;
function GetDecimalSeparator: Char;
function GetExpression(ADialect: TsFormulaDialect): String;
function GetFormatSettings: TFormatSettings;
function GetR1C1Expression(ACell: PCell): String;
function GetRPNFormula: TsRPNFormula; function GetRPNFormula: TsRPNFormula;
procedure SetBuiltIns(const AValue: TsBuiltInExprCategories); procedure SetBuiltIns(const AValue: TsBuiltInExprCategories);
procedure SetDialect(const AValue: TsFormulaDialect); procedure SetDialect(const AValue: TsFormulaDialect);
procedure SetExpression(ADialect: TsFormulaDialect; const AValue: String);
procedure SetIdentifiers(const AValue: TsExprIdentifierDefs); procedure SetIdentifiers(const AValue: TsExprIdentifierDefs);
procedure SetR1C1Expression(ACell: PCell; const AValue: String);
procedure SetRPNFormula(const AFormula: TsRPNFormula); procedure SetRPNFormula(const AFormula: TsRPNFormula);
protected protected
FFormatSettings: TFormatSettings; FFormatSettings: PFormatSettings;
FContains3DRef: Boolean; FContains3DRef: Boolean;
class function BuiltinExpressionManager: TsBuiltInExpressionManager; class function BuiltinExpressionManager: TsBuiltInExpressionManager;
function BuildStringFormula: String; function BuildStringFormula: String;
procedure ParserError(Msg: String); procedure ParserError(Msg: String);
function GetExpression: String; //function GetLocalizedExpression: String; virtual;
function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual; procedure InternalSetExpression(ADialect: TsFormulaDialect; const AValue: String);
function GetR1C1Expression(ACell: PCell): String; // procedure SetLocalizedExpression(const AValue: String); virtual;
procedure SetExpression(const AValue: String);
procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings;
const AValue: String); virtual;
procedure SetR1C1Expression(ACell: PCell; const AValue: String);
procedure UpdateExprFormatSettings; procedure UpdateExprFormatSettings;
procedure CheckResultType(const Res: TsExpressionResult; procedure CheckResultType(const Res: TsExpressionResult;
@ -753,14 +756,22 @@ type
property AsString: String read GetAsString; property AsString: String read GetAsString;
property AsBoolean: Boolean read GetAsBoolean; property AsBoolean: Boolean read GetAsBoolean;
property AsDateTime: TDateTime read GetAsDateTime; property AsDateTime: TDateTime read GetAsDateTime;
// The expression to parse // The expression to parse
property Expression: String read GetExpression write SetExpression; property Expression[ADialect: TsFormulaDialect]: String
property ListSeparator: Char read FListSep; read GetExpression write SetExpression;
property LocalizedExpression[AFormatSettings: TFormatSettings]: String {
property LocalizedExpression: String
read GetLocalizedExpression write SetLocalizedExpression; read GetLocalizedExpression write SetLocalizedExpression;
}
property R1C1Expression[ACell: PCell]: String property R1C1Expression[ACell: PCell]: String
read GetR1C1Expression write SetR1C1Expression; read GetR1C1Expression write SetR1C1Expression;
property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula; property RPNFormula: TsRPNFormula
read GetRPNFormula write SetRPNFormula;
property DecimalSeparator: Char read GetDecimalSeparator;
property ListSeparator: Char read FListSep;
property FormatSettings: TFormatSettings read GetFormatSettings;
property Identifiers: TsExprIdentifierDefs read FIdentifiers write SetIdentifiers; property Identifiers: TsExprIdentifierDefs read FIdentifiers write SetIdentifiers;
property BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns; property BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns;
property Worksheet: TsBasicWorksheet read FWorksheet; property Worksheet: TsBasicWorksheet read FWorksheet;
@ -1197,7 +1208,7 @@ var
begin begin
C := CurrentChar; C := CurrentChar;
prevC := #0; prevC := #0;
while (not IsWordDelim(C) or (prevC = 'E') or (C = FParser.FFormatSettings.DecimalSeparator)) and (C <> cNull) do while (not IsWordDelim(C) or (prevC = 'E') or (C = FParser.DecimalSeparator)) and (C <> cNull) do
begin begin
if not ( IsDigit(C) if not ( IsDigit(C)
or ((FToken <> '') and (Upcase(C) = 'E')) or ((FToken <> '') and (Upcase(C) = 'E'))
@ -1209,7 +1220,7 @@ begin
prevC := Upcase(C); prevC := Upcase(C);
C := NextPos; C := NextPos;
end; end;
if not TryStrToFloat(FToken, X, FParser.FFormatSettings) then if not TryStrToFloat(FToken, X, FParser.FFormatSettings^) then
ScanError(Format(rsInvalidNumber, [FToken])); ScanError(Format(rsInvalidNumber, [FToken]));
Result := ttNumber; Result := ttNumber;
end; end;
@ -1382,7 +1393,7 @@ end;
function TsExpressionScanner.IsDigit(C: Char): Boolean; function TsExpressionScanner.IsDigit(C: Char): Boolean;
begin begin
Result := (C in Digits) or (C = FParser.FFormatSettings.DecimalSeparator); Result := (C in Digits) or (C = FParser.DecimalSeparator);
end; end;
function TsExpressionScanner.IsWordDelim(C: Char): Boolean; function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
@ -1440,8 +1451,12 @@ begin
FIdentifiers.FParser := Self; FIdentifiers.FParser := Self;
FScanner := TsExpressionScanner.Create(self); FScanner := TsExpressionScanner.Create(self);
FHashList := TFPHashObjectList.Create(False); FHashList := TFPHashObjectList.Create(False);
SetDialect(fdExcelA1);
FFormatSettings := InitFormatSettings(TsWorksheet(FWorksheet).Workbook); // Prepare for ExcelA1 dialect which is the default dialect. Can't call
// SetDialect(fdExcelA1) because it exits immediately at default dialect.
FDialect := fdExcelA1;
FListSep := ',';
FFormatSettings := @ExprFormatSettings;
UpdateExprFormatSettings; UpdateExprFormatSettings;
end; end;
@ -1616,23 +1631,6 @@ begin
Result := Res.ResString; Result := Res.ResString;
end; end;
{ Returns the expression in R1C1 notation.
ACell is the cell to which the expression is assumed to be relative. }
function TsExpressionParser.GetR1C1Expression(ACell: PCell): String;
var
oldDialect: TsFormulaDialect;
begin
oldDialect := FDialect;
try
FDialect := fdExcelR1C1;
PrepareCopyMode(ACell, ACell);
Result := Expression;
finally
PrepareCopyMode(nil, nil);
FDialect := oldDialect;
end;
end;
function TsExpressionParser.GetRPNFormula: TsRPNFormula; function TsExpressionParser.GetRPNFormula: TsRPNFormula;
begin begin
Result := CreateRPNFormula(FExprNode.AsRPNItem(nil), true); Result := CreateRPNFormula(FExprNode.AsRPNItem(nil), true);
@ -1877,7 +1875,7 @@ begin
if TryStrToInt64(CurrentToken, I) then if TryStrToInt64(CurrentToken, I) then
Result := TsConstExprNode.CreateInteger(self, I) Result := TsConstExprNode.CreateInteger(self, I)
else else
if TryStrToFloat(CurrentToken, X, FFormatSettings) then if TryStrToFloat(CurrentToken, X, FFormatSettings^) then
Result := TsConstExprNode.CreateFloat(self, X) Result := TsConstExprNode.CreateFloat(self, X)
else else
ParserError(Format(rsInvalidFloat, [CurrentToken])); ParserError(Format(rsInvalidFloat, [CurrentToken]));
@ -2007,19 +2005,61 @@ begin
FDirty := true; FDirty := true;
end; end;
function TsExpressionParser.GetExpression: String; function TsExpressionParser.GetDecimalSeparator: Char;
begin begin
Result := BuildStringFormula; //(FFormatSettings); Result := FFormatSettings^.DecimalSeparator;
end; end;
function TsExpressionParser.GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; { Builds an expression string for the currently loaded parser tree. The string
is created for the specified formula dialect. The formula dialect used by
the parser is restored afterwards. }
function TsExpressionParser.GetExpression(ADialect: TsFormulaDialect): String;
var
oldDialect: TsFormulaDialect;
begin begin
// ExprFormatSettings := AFormatSettings; if ADialect = fdExcelR1C1 then
FFormatSettings := AFormatSettings; raise Exception.Create('Please use R1C1Expression');
UpdateExprFormatSettings;
Result := BuildStringFormula; //(AFormatSettings); oldDialect := FDialect;
try
SetDialect(ADialect);
Result := BuildStringFormula;
finally
SetDialect(oldDialect);
end;
end; end;
function TsExpressionParser.GetFormatSettings: TFormatSettings;
begin
Result := FFormatSettings^;
end;
{ Builds an expression string for the currently loaded parser tree. The string
is created for Excel's R1C1 notation. ACell points to the cell to which cell
references are relative. The formula dialect used by the parser is
restored afterwards. }
function TsExpressionParser.GetR1C1Expression(ACell: PCell): String;
var
oldDialect: TsFormulaDialect;
begin
oldDialect := FDialect;
try
SetDialect(fdExcelR1C1);
PrepareCopyMode(ACell, ACell);
Result := BuildStringFormula;
finally
PrepareCopyMode(nil, nil);
SetDialect(oldDialect);
end;
end;
{
function TsExpressionParser.GetLocalizedExpression: String;
begin
SetDialect(fdLocalized);
Result := BuildStringFormula;
end;
}
function TsExpressionParser.Has3DLinks: Boolean; function TsExpressionParser.Has3DLinks: Boolean;
begin begin
Result := FExprNode.Has3DLink; Result := FExprNode.Has3DLink;
@ -2034,42 +2074,73 @@ end;
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect); procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
begin begin
if FDialect = AValue then
exit;
FDialect := AValue; FDialect := AValue;
case FDialect of case FDialect of
fdExcelA1, fdExcelA1,
fdExcelR1C1 : FListSep := ','; fdExcelR1C1:
fdOpenDocument : FListSep := ';' begin
FListSep := ',';
FFormatSettings := @ExprFormatSettings;
UpdateExprFormatSettings;
end;
fdOpenDocument:
begin
FListSep := ';';
FFormatSettings := @ExprFormatSettings;
UpdateExprFormatSettings;
end;
fdLocalized:
begin
FFormatSettings := @TsWorksheet(FWorksheet).Workbook.FormatSettings;
FListSep := FFormatSettings^.ListSeparator;
end;
end; end;
end; end;
procedure TsExpressionParser.SetExpression(const AValue: String); procedure TsExpressionParser.InternalSetExpression(ADialect: TsFormulaDialect;
begin
SetLocalizedExpression(FFormatSettings, AValue);
end;
procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings;
const AValue: String); const AValue: String);
begin begin
if FExpression = AValue then if FExpression = AValue then
exit; exit;
FFormatSettings := AFormatSettings;
UpdateExprFormatSettings;
FExpression := AValue; FExpression := AValue;
if (AValue <> '') and (AValue[1] = '=') then if (AValue <> '') and (AValue[1] = '=') then
FScanner.Source := Copy(AValue, 2, Length(AValue)) Delete(FExpression, 1, 1);
else
FScanner.Source := AValue; SetDialect(ADialect);
FreeAndNil(FExprNode); FreeAndNil(FExprNode);
if (FExpression <> '') then FScanner.Source := FExpression;
begin if FExpression <> '' then begin
GetToken; GetToken;
FExprNode := Level1; FExprNode := Level1;
if (TokenType <> ttEOF) then if TokenType <> ttEOF then
ParserError(Format(rsUnterminatedExpression, [Scanner.Pos, CurrentToken])); ParserError(Format(rsUnTerminatedExpression, [Scanner.Pos, CurrentToken]));
FExprNode.Check; FExprNode.Check;
end; end;
end; end;
{ Makes the parser analyze the given expression string. The expression string
is assumed to be valid for the specified formula dialect. }
procedure TsExpressionParser.SetExpression(ADialect: TsFormulaDialect;
const AValue: String);
begin
if FDialect = fdExcelR1C1 then
raise Exception.Create('Please use R1C1Expression');
InternalSetExpression(ADialect, AValue);
end;
(*
{ Sets a localized Excel expression in A1 syntax. The format settings needed
for localization are taken from the workbook. }
procedure TsExpressionParser.SetLocalizedExpression(const AValue: String);
begin
InternalSetExpression(fdLocalized, AValue);
end; *)
procedure TsExpressionParser.SetIdentifiers(const AValue: TsExprIdentifierDefs); procedure TsExpressionParser.SetIdentifiers(const AValue: TsExprIdentifierDefs);
begin begin
FIdentifiers.Assign(AValue) FIdentifiers.Assign(AValue)
@ -2078,16 +2149,11 @@ end;
{ Parses an expression in which cell references are given in Excel's R1C1 notation { Parses an expression in which cell references are given in Excel's R1C1 notation
ACell is the cell to which the created expression will be relative. } ACell is the cell to which the created expression will be relative. }
procedure TsExpressionParser.SetR1C1Expression(ACell: PCell; const AValue: String); procedure TsExpressionParser.SetR1C1Expression(ACell: PCell; const AValue: String);
var
oldDialect: TsFormulaDialect;
begin begin
oldDialect := FDialect; PrepareCopyMode(ACell, ACell);
try try
FDialect := fdExcelR1C1; InternalSetExpression(fdExcelR1C1, AValue);
PrepareCopyMode(ACell, ACell);
Expression := AValue;
finally finally
FDialect := oldDialect;
PrepareCopyMode(nil, nil); PrepareCopyMode(nil, nil);
end; end;
end; end;
@ -2554,7 +2620,7 @@ end;
function TsExprIdentifierDef.GetFormatSettings: TFormatSettings; function TsExprIdentifierDef.GetFormatSettings: TFormatSettings;
begin begin
Result := TsExprIdentifierDefs(Collection).Parser.FFormatSettings; Result := TsExprIdentifierDefs(Collection).Parser.FFormatSettings^;
end; end;
function TsExprIdentifierDef.GetResultType: TsResultType; function TsExprIdentifierDef.GetResultType: TsResultType;
@ -3017,9 +3083,9 @@ begin
case NodeType of case NodeType of
rtString : Result := cDoubleQuote + FValue.ResString + cDoubleQuote; rtString : Result := cDoubleQuote + FValue.ResString + cDoubleQuote;
rtInteger : Result := IntToStr(FValue.ResInteger); rtInteger : Result := IntToStr(FValue.ResInteger);
rtDateTime : Result := '''' + FormatDateTime('cccc', FValue.ResDateTime, Parser.FFormatSettings) + ''''; // Probably wrong !!! rtDateTime : Result := '''' + FormatDateTime('cccc', FValue.ResDateTime, Parser.FFormatSettings^) + ''''; // Probably wrong !!!
rtBoolean : if FValue.ResBoolean then Result := 'TRUE' else Result := 'FALSE'; rtBoolean : if FValue.ResBoolean then Result := 'TRUE' else Result := 'FALSE';
rtFloat : Result := FloatToStr(FValue.ResFloat, Parser.FFormatSettings); rtFloat : Result := FloatToStr(FValue.ResFloat, Parser.FFormatSettings^);
rtError : Result := GetErrorValueStr(FValue.ResError); rtError : Result := GetErrorValueStr(FValue.ResError);
end; end;
end; end;
@ -4039,7 +4105,7 @@ begin
c := GetCol; c := GetCol;
if Has3dLink then begin if Has3dLink then begin
case FParser.Dialect of case FParser.Dialect of
fdExcelA1: fdExcelA1, fdLocalized:
Result := Format('%s!%s', [GetQuotedSheetName, GetCellString(r, c, FFlags)]); Result := Format('%s!%s', [GetQuotedSheetName, GetCellString(r, c, FFlags)]);
fdExcelR1C1: fdExcelR1C1:
Result := Format('%s!%s', [GetQuotedSheetName, Result := Format('%s!%s', [GetQuotedSheetName,
@ -4053,7 +4119,7 @@ begin
end end
end else end else
case FParser.Dialect of case FParser.Dialect of
fdExcelA1: fdExcelA1, fdLocalized:
Result := GetCellString(GetRow, GetCol, FFlags); Result := GetCellString(GetRow, GetCol, FFlags);
fdExcelR1C1: fdExcelR1C1:
Result := GetCellString_R1C1(GetRow, GetCol, FFlags, FParser.FSourceCell^.Row, FParser.FSourceCell^.Col); Result := GetCellString_R1C1(GetRow, GetCol, FFlags, FParser.FSourceCell^.Row, FParser.FSourceCell^.Col);
@ -4308,7 +4374,7 @@ begin
if F3dRange then if F3dRange then
case FParser.Dialect of case FParser.Dialect of
fdExcelA1: fdExcelA1, fdLocalized:
Result := GetCellRangeString(s1, s2, r1, c1, r2, c2, FFlags, true); Result := GetCellRangeString(s1, s2, r1, c1, r2, c2, FFlags, true);
fdExcelR1C1: fdExcelR1C1:
Result := GetCellRangeString_R1C1(s1, s2, r1, c1, r2, c2, FFlags, Result := GetCellRangeString_R1C1(s1, s2, r1, c1, r2, c2, FFlags,
@ -4322,7 +4388,7 @@ begin
end end
else else
case FParser.Dialect of case FParser.Dialect of
fdExcelA1: fdExcelA1, fdLocalized:
Result := GetCellRangeString(r1, c1, r2, c2, FFlags, true); Result := GetCellRangeString(r1, c1, r2, c2, FFlags, true);
fdExcelR1C1: fdExcelR1C1:
Result := GetCellRangeString_R1C1(r1, c1, r2, c2, FFlags, Result := GetCellRangeString_R1C1(r1, c1, r2, c2, FFlags,

View File

@ -2482,23 +2482,14 @@ begin
Delete(formulaStr, 1, p); Delete(formulaStr, 1, p);
end; end;
// ... and store in cell's FormulaValue field. // ... and store in cell's FormulaValue field. Convert from ODS to ExcelA1 dialect.
formula := TsWorksheet(FWorksheet).Formulas.AddFormula(ARow, ACol); formula := TsWorksheet(FWorksheet).Formulas.AddFormula(ARow, ACol);
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet); formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
formula^.Parser.Dialect := fdOpenDocument; // Parse in ODS dialect formula^.Parser.Expression[fdOpenDocument] := formulaStr; // Parse in ODS dialect
formula^.Parser.Expression := formulaStr; formula^.Text := formula^.Parser.Expression[fdExcelA1]; // Convert to Excel A1 dialect
formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect
formula^.Text := formula^.Parser.Expression;
cell^.Flags := cell^.Flags + [cfHasFormula]; cell^.Flags := cell^.Flags + [cfHasFormula];
hasFormula := true; hasFormula := true;
{
cell^.FormulaValue := formula;
// Note: This formula is still in OpenDocument dialect. Conversion to
// ExcelA1 dialect (used by fps) is postponed until all sheets have beeon
// read (--> FixFormulas) because of possible references to other sheets
// which might not have been loaded yet at this moment.
}
{$IFDEF FPSpreadDebug} {$IFDEF FPSpreadDebug}
DebugLn(' Formula found: ' + formula); DebugLn(' Formula found: ' + formula);
{$ENDIF} {$ENDIF}
@ -7834,30 +7825,18 @@ begin
valueStr := ''; valueStr := '';
if formula^.Parser = nil then begin if formula^.Parser = nil then begin
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet); formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
formula^.Parser.Expression := formula^.Text; formula^.Parser.Expression[fdExcelA1] := formula^.Text; // the formula text is in ExcelA1 dialect
end; end;
// Convert string formula to the format needed by ods // Convert string formula to the format needed by ods
oldDialect := formula^.Parser.Dialect; oldDialect := formula^.Parser.Dialect;
try try
formula^.Parser.Dialect := fdOpenDocument; formulaStr := formula^.Parser.Expression[fdOpenDocument]; // Formula converted to ODS dialect
formulaStr := formula^.Parser.Expression; // Formula converted to ODS dialect
if (formulaStr <> '') and (formulastr[1] <> '=') then if (formulaStr <> '') and (formulastr[1] <> '=') then
formulaStr := '=' + formulaStr; formulaStr := '=' + formulaStr;
finally finally
formula^.Parser.Dialect := oldDialect; formula^.Parser.Dialect := oldDialect;
end; end;
{
parser := TsSpreadsheetParser.Create(FWorksheet);
try
parser.Expression := ACell^.FormulaValue; // Formula still in Excel dialect
parser.Dialect := fdOpenDocument; // Now convert to ODS dialect
formula := Parser.LocalizedExpression[FPointSeparatorSettings];
if (formula <> '') and (formula[1] <> '=') then
formula := '=' + formula;
finally
parser.Free;
end;
}
case ACell^.ContentType of case ACell^.ContentType of
cctNumber: cctNumber:
begin begin

View File

@ -1274,7 +1274,7 @@ begin
if AFormula^.Parser = nil then begin if AFormula^.Parser = nil then begin
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
parser.Expression := AFormula^.Text; parser.Expression[fdExcelA1] := AFormula^.Text;
AFormula^.Parser := parser; AFormula^.Parser := parser;
except except
on E:ECalcEngine do begin on E:ECalcEngine do begin
@ -1288,7 +1288,7 @@ begin
try try
res := AFormula^.Parser.Evaluate; res := AFormula^.Parser.Evaluate;
if AFormula^.Text = '' then if AFormula^.Text = '' then
AFormula^.Text := AFormula^.Parser.Expression; AFormula^.Text := AFormula^.Parser.Expression[fdExcelA1];
except except
on E: ECalcEngine do on E: ECalcEngine do
begin begin
@ -2049,7 +2049,7 @@ begin
end; end;
end; end;
destFormula^.Parser.RPNFormula := rpn; destFormula^.Parser.RPNFormula := rpn;
destFormula^.Text := destFormula^.Parser.Expression; destFormula^.Text := destFormula^.Parser.Expression[fdExcelA1];
UseFormulaInCell(AToCell, destFormula); UseFormulaInCell(AToCell, destFormula);
finally finally
srcFormula^.Parser.PrepareCopyMode(nil, nil); srcFormula^.Parser.PrepareCopyMode(nil, nil);
@ -2987,9 +2987,9 @@ begin
if HasFormula(ACell) then begin if HasFormula(ACell) then begin
formula := FFormulas.FindFormula(ACell^.Row, ACell^.Col); formula := FFormulas.FindFormula(ACell^.Row, ACell^.Col);
if ALocalized then if ALocalized then
Result := formula^.Parser.LocalizedExpression[Workbook.FormatSettings] Result := formula^.Parser.Expression[fdLocalized]
else else
Result := formula^.Parser.Expression; Result := formula^.Parser.Expression[fdExcelA1];
end; end;
end; end;
@ -3029,7 +3029,6 @@ end;
function TsWorksheet.ConvertFormulaDialect(ACell: PCell; function TsWorksheet.ConvertFormulaDialect(ACell: PCell;
ADialect: TsFormulaDialect): String; ADialect: TsFormulaDialect): String;
var var
oldDialect: TsFormulaDialect;
formula: PsFormula; formula: PsFormula;
begin begin
Result := ''; Result := '';
@ -3037,17 +3036,10 @@ begin
exit; exit;
formula := FFormulas.FindFormula(ACell^.Row, ACell^.Col); formula := FFormulas.FindFormula(ACell^.Row, ACell^.Col);
oldDialect := formula^.Parser.Dialect; if ADialect = fdExcelR1C1 then
if oldDialect <> ADialect then begin Result := formula^.Parser.R1C1Expression[ACell]
try else
formula^.Parser.Dialect := ADialect; Result := formula^.Parser.Expression[ADialect];
formula^.Parser.PrepareCopyMode(ACell, nil);
Result := formula^.Parser.Expression;
finally
formula^.Parser.PrepareCopyMode(nil, nil);
formula^.Parser.Dialect := oldDialect;
end;
end;
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -3066,7 +3058,7 @@ begin
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
parser.RPNFormula := AFormula; parser.RPNFormula := AFormula;
Result := parser.Expression; Result := parser.Expression[fdExcelA1];
finally finally
parser.Free; parser.Free;
end; end;
@ -3751,7 +3743,7 @@ begin
Result := formula^.Text; Result := formula^.Text;
if (Result = '') and (formula^.Parser <> nil) then if (Result = '') and (formula^.Parser <> nil) then
Result := formula^.Parser.Expression; Result := formula^.Parser.Expression[fdExcelA1];
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -5913,13 +5905,14 @@ begin
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
if ALocalized then if ALocalized then
parser.LocalizedExpression[Workbook.FormatSettings] := AFormula parser.Expression[fdLocalized] := AFormula
else else
if R1C1Mode then if R1C1Mode then
parser.R1C1Expression[ACell] := AFormula parser.R1C1Expression[ACell] := AFormula
else else
parser.Expression := AFormula; parser.Expression[fdExcelA1] := AFormula;
AFormula := parser.Expression;
AFormula := parser.Expression[fdExcelA1];
formula := FFormulas.AddFormula(ACell^.Row, ACell^.Col, AFormula); formula := FFormulas.AddFormula(ACell^.Row, ACell^.Col, AFormula);
except except
@ -6201,7 +6194,7 @@ begin
formula^.Parser := TsSpreadsheetParser.Create(self); formula^.Parser := TsSpreadsheetParser.Create(self);
end; end;
formula^.Parser.RPNFormula := ARPNFormula; formula^.Parser.RPNFormula := ARPNFormula;
formula^.Text := formula^.Parser.Expression; formula^.Text := formula^.Parser.Expression[fdExcelA1];
UseFormulaInCell(ACell, formula); UseFormulaInCell(ACell, formula);
ACell^.ContentType := cctFormula; ACell^.ContentType := cctFormula;
@ -8500,7 +8493,7 @@ var
begin begin
Unused(Arg); Unused(Arg);
for formula in TsWorksheet(Data).Formulas do for formula in TsWorksheet(Data).Formulas do
formula^.Text := formula^.Parser.Expression; formula^.Text := formula^.Parser.Expression[fdExcelA1];
end; end;

View File

@ -229,7 +229,7 @@ type
TsRPNFormula = array of TsFormulaElement; TsRPNFormula = array of TsFormulaElement;
{@@ Formula dialect } {@@ Formula dialect }
TsFormulaDialect = (fdExcelA1, fdExcelR1C1, fdOpenDocument); TsFormulaDialect = (fdExcelA1, fdExcelR1C1, fdOpenDocument, fdLocalized);
{@@ Describes the <b>type of content</b> in a cell of a TsWorksheet } {@@ Describes the <b>type of content</b> in a cell of a TsWorksheet }
TCellContentType = (cctEmpty, cctFormula, cctNumber, cctUTF8String, TCellContentType = (cctEmpty, cctFormula, cctNumber, cctUTF8String,

View File

@ -3016,7 +3016,7 @@ begin
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet); formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
end; end;
formula^.Parser.RPNFormula := rpnFormula; formula^.Parser.RPNFormula := rpnFormula;
formula^.Text := formula^.Parser.Expression; formula^.Text := formula^.Parser.Expression[fdExcelA1];
TsWorksheet(FWorksheet).UseFormulaInCell(ACell, formula); TsWorksheet(FWorksheet).UseFormulaInCell(ACell, formula);
{ {
if formula^.Parser.Has3dLinks then if formula^.Parser.Has3dLinks then

View File

@ -2266,6 +2266,8 @@ end;
Checks valididty of the provided formula and creates a corresponding Checks valididty of the provided formula and creates a corresponding
error message. error message.
It is assumed that the formula is localized.
Returns TRUE if the provided string is a valid formula or no formula, FALSE Returns TRUE if the provided string is a valid formula or no formula, FALSE
otherwise. In the latter case an error message string is returned as well. otherwise. In the latter case an error message string is returned as well.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -2281,7 +2283,7 @@ begin
parser := TsSpreadsheetParser.Create(Worksheet); parser := TsSpreadsheetParser.Create(Worksheet);
try try
try try
parser.LocalizedExpression[Workbook.FormatSettings] := AFormula; parser.Expression[fdLocalized] := AFormula;
except except
on E: Exception do begin on E: Exception do begin
AErrMsg := E.Message; AErrMsg := E.Message;

View File

@ -6200,9 +6200,11 @@ begin
Result := GetTextRotation(ALeft, ATop); Result := GetTextRotation(ALeft, ATop);
textrot := Result; textrot := Result;
for c := ALeft to ARight do for c := ALeft to ARight do
for r := ATop to ABottom do begin for r := ATop to ABottom do
begin
Result := GetTextRotation(c, r); Result := GetTextRotation(c, r);
if Result <> textrot then begin if Result <> textrot then
begin
Result := trHorizontal; Result := trHorizontal;
exit; exit;
end; end;
@ -6212,7 +6214,8 @@ end;
function TsCustomWorksheetGrid.GetWorkbookSource: TsWorkbookSource; function TsCustomWorksheetGrid.GetWorkbookSource: TsWorkbookSource;
begin begin
if FWorkbookSource <> nil then if FWorkbookSource <> nil then
Result := FWorkbookSource else Result := FWorkbookSource
else
Result := FInternalWorkbookSource; Result := FInternalWorkbookSource;
end; end;
@ -6221,7 +6224,8 @@ var
cell: PCell; cell: PCell;
begin begin
Result := vaDefault; Result := vaDefault;
if Assigned(Worksheet) then begin if Assigned(Worksheet) then
begin
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol)); cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Result := Worksheet.ReadVertAlignment(cell); Result := Worksheet.ReadVertAlignment(cell);
end; end;
@ -7089,7 +7093,7 @@ begin
parser := TsSpreadsheetParser.Create(Worksheet); parser := TsSpreadsheetParser.Create(Worksheet);
try try
try try
parser.LocalizedExpression[Worksheet.FormatSettings] := AExpression; parser.Expression[fdLocalized] := AExpression;
except except
on E: Exception do begin on E: Exception do begin
AErrMsg := E.Message; AErrMsg := E.Message;