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;
for formula in self do
if formula^.Parser.IterateNodes(proc, Pointer(PtrInt(AIndex)), InSheet) then
formula^.Text := formula^.Parser.Expression;
if formula^.Parser.IterateNodes(proc, {%H-}Pointer(PtrInt(AIndex)), InSheet) then
formula^.Text := formula^.Parser.Expression[fdExcelA1];
end;
// Formula enumerators (use in "for ... in" syntax)

View File

@ -673,6 +673,7 @@ type
end;
EExprScanner = class(Exception);
PFormatSettings = ^TFormatSettings;
{ TsExpressionParser }
TsExpressionParser = class
@ -695,25 +696,27 @@ type
function GetAsFloat: TsExprFloat;
function GetAsInteger: Int64;
function GetAsString: String;
function GetDecimalSeparator: Char;
function GetExpression(ADialect: TsFormulaDialect): String;
function GetFormatSettings: TFormatSettings;
function GetR1C1Expression(ACell: PCell): String;
function GetRPNFormula: TsRPNFormula;
procedure SetBuiltIns(const AValue: TsBuiltInExprCategories);
procedure SetDialect(const AValue: TsFormulaDialect);
procedure SetExpression(ADialect: TsFormulaDialect; const AValue: String);
procedure SetIdentifiers(const AValue: TsExprIdentifierDefs);
procedure SetR1C1Expression(ACell: PCell; const AValue: String);
procedure SetRPNFormula(const AFormula: TsRPNFormula);
protected
FFormatSettings: TFormatSettings;
FFormatSettings: PFormatSettings;
FContains3DRef: Boolean;
class function BuiltinExpressionManager: TsBuiltInExpressionManager;
function BuildStringFormula: String;
procedure ParserError(Msg: String);
function GetExpression: String;
function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual;
function GetR1C1Expression(ACell: PCell): String;
procedure SetExpression(const AValue: String);
procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings;
const AValue: String); virtual;
procedure SetR1C1Expression(ACell: PCell; const AValue: String);
//function GetLocalizedExpression: String; virtual;
procedure InternalSetExpression(ADialect: TsFormulaDialect; const AValue: String);
// procedure SetLocalizedExpression(const AValue: String); virtual;
procedure UpdateExprFormatSettings;
procedure CheckResultType(const Res: TsExpressionResult;
@ -753,14 +756,22 @@ type
property AsString: String read GetAsString;
property AsBoolean: Boolean read GetAsBoolean;
property AsDateTime: TDateTime read GetAsDateTime;
// The expression to parse
property Expression: String read GetExpression write SetExpression;
property ListSeparator: Char read FListSep;
property LocalizedExpression[AFormatSettings: TFormatSettings]: String
property Expression[ADialect: TsFormulaDialect]: String
read GetExpression write SetExpression;
{
property LocalizedExpression: String
read GetLocalizedExpression write SetLocalizedExpression;
}
property R1C1Expression[ACell: PCell]: String
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 BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns;
property Worksheet: TsBasicWorksheet read FWorksheet;
@ -1197,7 +1208,7 @@ var
begin
C := CurrentChar;
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
if not ( IsDigit(C)
or ((FToken <> '') and (Upcase(C) = 'E'))
@ -1209,7 +1220,7 @@ begin
prevC := Upcase(C);
C := NextPos;
end;
if not TryStrToFloat(FToken, X, FParser.FFormatSettings) then
if not TryStrToFloat(FToken, X, FParser.FFormatSettings^) then
ScanError(Format(rsInvalidNumber, [FToken]));
Result := ttNumber;
end;
@ -1382,7 +1393,7 @@ end;
function TsExpressionScanner.IsDigit(C: Char): Boolean;
begin
Result := (C in Digits) or (C = FParser.FFormatSettings.DecimalSeparator);
Result := (C in Digits) or (C = FParser.DecimalSeparator);
end;
function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
@ -1440,8 +1451,12 @@ begin
FIdentifiers.FParser := Self;
FScanner := TsExpressionScanner.Create(self);
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;
end;
@ -1616,23 +1631,6 @@ begin
Result := Res.ResString;
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;
begin
Result := CreateRPNFormula(FExprNode.AsRPNItem(nil), true);
@ -1877,7 +1875,7 @@ begin
if TryStrToInt64(CurrentToken, I) then
Result := TsConstExprNode.CreateInteger(self, I)
else
if TryStrToFloat(CurrentToken, X, FFormatSettings) then
if TryStrToFloat(CurrentToken, X, FFormatSettings^) then
Result := TsConstExprNode.CreateFloat(self, X)
else
ParserError(Format(rsInvalidFloat, [CurrentToken]));
@ -2007,19 +2005,61 @@ begin
FDirty := true;
end;
function TsExpressionParser.GetExpression: String;
function TsExpressionParser.GetDecimalSeparator: Char;
begin
Result := BuildStringFormula; //(FFormatSettings);
Result := FFormatSettings^.DecimalSeparator;
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
// ExprFormatSettings := AFormatSettings;
FFormatSettings := AFormatSettings;
UpdateExprFormatSettings;
Result := BuildStringFormula; //(AFormatSettings);
if ADialect = fdExcelR1C1 then
raise Exception.Create('Please use R1C1Expression');
oldDialect := FDialect;
try
SetDialect(ADialect);
Result := BuildStringFormula;
finally
SetDialect(oldDialect);
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;
begin
Result := FExprNode.Has3DLink;
@ -2034,42 +2074,73 @@ end;
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
begin
if FDialect = AValue then
exit;
FDialect := AValue;
case FDialect of
fdExcelA1,
fdExcelR1C1 : FListSep := ',';
fdOpenDocument : FListSep := ';'
fdExcelR1C1:
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;
procedure TsExpressionParser.SetExpression(const AValue: String);
begin
SetLocalizedExpression(FFormatSettings, AValue);
end;
procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings;
procedure TsExpressionParser.InternalSetExpression(ADialect: TsFormulaDialect;
const AValue: String);
begin
if FExpression = AValue then
exit;
FFormatSettings := AFormatSettings;
UpdateExprFormatSettings;
FExpression := AValue;
if (AValue <> '') and (AValue[1] = '=') then
FScanner.Source := Copy(AValue, 2, Length(AValue))
else
FScanner.Source := AValue;
Delete(FExpression, 1, 1);
SetDialect(ADialect);
FreeAndNil(FExprNode);
if (FExpression <> '') then
begin
FScanner.Source := FExpression;
if FExpression <> '' then begin
GetToken;
FExprNode := Level1;
if (TokenType <> ttEOF) then
ParserError(Format(rsUnterminatedExpression, [Scanner.Pos, CurrentToken]));
if TokenType <> ttEOF then
ParserError(Format(rsUnTerminatedExpression, [Scanner.Pos, CurrentToken]));
FExprNode.Check;
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);
begin
FIdentifiers.Assign(AValue)
@ -2078,16 +2149,11 @@ end;
{ 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. }
procedure TsExpressionParser.SetR1C1Expression(ACell: PCell; const AValue: String);
var
oldDialect: TsFormulaDialect;
begin
oldDialect := FDialect;
PrepareCopyMode(ACell, ACell);
try
FDialect := fdExcelR1C1;
PrepareCopyMode(ACell, ACell);
Expression := AValue;
InternalSetExpression(fdExcelR1C1, AValue);
finally
FDialect := oldDialect;
PrepareCopyMode(nil, nil);
end;
end;
@ -2554,7 +2620,7 @@ end;
function TsExprIdentifierDef.GetFormatSettings: TFormatSettings;
begin
Result := TsExprIdentifierDefs(Collection).Parser.FFormatSettings;
Result := TsExprIdentifierDefs(Collection).Parser.FFormatSettings^;
end;
function TsExprIdentifierDef.GetResultType: TsResultType;
@ -3017,9 +3083,9 @@ begin
case NodeType of
rtString : Result := cDoubleQuote + FValue.ResString + cDoubleQuote;
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';
rtFloat : Result := FloatToStr(FValue.ResFloat, Parser.FFormatSettings);
rtFloat : Result := FloatToStr(FValue.ResFloat, Parser.FFormatSettings^);
rtError : Result := GetErrorValueStr(FValue.ResError);
end;
end;
@ -4039,7 +4105,7 @@ begin
c := GetCol;
if Has3dLink then begin
case FParser.Dialect of
fdExcelA1:
fdExcelA1, fdLocalized:
Result := Format('%s!%s', [GetQuotedSheetName, GetCellString(r, c, FFlags)]);
fdExcelR1C1:
Result := Format('%s!%s', [GetQuotedSheetName,
@ -4053,7 +4119,7 @@ begin
end
end else
case FParser.Dialect of
fdExcelA1:
fdExcelA1, fdLocalized:
Result := GetCellString(GetRow, GetCol, FFlags);
fdExcelR1C1:
Result := GetCellString_R1C1(GetRow, GetCol, FFlags, FParser.FSourceCell^.Row, FParser.FSourceCell^.Col);
@ -4308,7 +4374,7 @@ begin
if F3dRange then
case FParser.Dialect of
fdExcelA1:
fdExcelA1, fdLocalized:
Result := GetCellRangeString(s1, s2, r1, c1, r2, c2, FFlags, true);
fdExcelR1C1:
Result := GetCellRangeString_R1C1(s1, s2, r1, c1, r2, c2, FFlags,
@ -4322,7 +4388,7 @@ begin
end
else
case FParser.Dialect of
fdExcelA1:
fdExcelA1, fdLocalized:
Result := GetCellRangeString(r1, c1, r2, c2, FFlags, true);
fdExcelR1C1:
Result := GetCellRangeString_R1C1(r1, c1, r2, c2, FFlags,

View File

@ -2482,23 +2482,14 @@ begin
Delete(formulaStr, 1, p);
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^.Parser := TsSpreadsheetParser.Create(FWorksheet);
formula^.Parser.Dialect := fdOpenDocument; // Parse in ODS dialect
formula^.Parser.Expression := formulaStr;
formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect
formula^.Text := formula^.Parser.Expression;
formula^.Parser.Expression[fdOpenDocument] := formulaStr; // Parse in ODS dialect
formula^.Text := formula^.Parser.Expression[fdExcelA1]; // Convert to Excel A1 dialect
cell^.Flags := cell^.Flags + [cfHasFormula];
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}
DebugLn(' Formula found: ' + formula);
{$ENDIF}
@ -7834,30 +7825,18 @@ begin
valueStr := '';
if formula^.Parser = nil then begin
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
formula^.Parser.Expression := formula^.Text;
formula^.Parser.Expression[fdExcelA1] := formula^.Text; // the formula text is in ExcelA1 dialect
end;
// Convert string formula to the format needed by ods
oldDialect := formula^.Parser.Dialect;
try
formula^.Parser.Dialect := fdOpenDocument;
formulaStr := formula^.Parser.Expression; // Formula converted to ODS dialect
formulaStr := formula^.Parser.Expression[fdOpenDocument]; // Formula converted to ODS dialect
if (formulaStr <> '') and (formulastr[1] <> '=') then
formulaStr := '=' + formulaStr;
finally
formula^.Parser.Dialect := oldDialect;
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
cctNumber:
begin

View File

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

View File

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

View File

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

View File

@ -2266,6 +2266,8 @@ end;
Checks valididty of the provided formula and creates a corresponding
error message.
It is assumed that the formula is localized.
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.
-------------------------------------------------------------------------------}
@ -2281,7 +2283,7 @@ begin
parser := TsSpreadsheetParser.Create(Worksheet);
try
try
parser.LocalizedExpression[Workbook.FormatSettings] := AFormula;
parser.Expression[fdLocalized] := AFormula;
except
on E: Exception do begin
AErrMsg := E.Message;

View File

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