summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2>2017-06-19 09:01:02 +0000
committermarco <marco@3ad0048d-3df7-0310-abae-a5850022a9f2>2017-06-19 09:01:02 +0000
commit916559cb1fd5bb6ece7cfe9400e58a221b51bb3f (patch)
treed5787cd4f51b00894e3df00b667f5badfab4e844
parent2323b6a565015e94bf92d97cba2dbc7f444d00e1 (diff)
downloadfpc-916559cb1fd5bb6ece7cfe9400e58a221b51bb3f.tar.gz
--- Merging r33947 into '.':
U packages/fcl-base/src/fpexprpars.pp U packages/fcl-base/tests/fclbase-unittests.lpi U packages/fcl-base/tests/testexprpars.pp U packages/fcl-base/tests/fclbase-unittests.pp --- Recording mergeinfo for merge of r33947 into '.': U . --- Merging r33986 into '.': G packages/fcl-base/src/fpexprpars.pp --- Recording mergeinfo for merge of r33986 into '.': G . --- Merging r34377 into '.': G packages/fcl-base/src/fpexprpars.pp G packages/fcl-base/tests/testexprpars.pp --- Recording mergeinfo for merge of r34377 into '.': G . --- Merging r34422 into '.': G packages/fcl-base/tests/testexprpars.pp G packages/fcl-base/src/fpexprpars.pp --- Recording mergeinfo for merge of r34422 into '.': G . --- Merging r34423 into '.': G packages/fcl-base/src/fpexprpars.pp G packages/fcl-base/tests/testexprpars.pp --- Recording mergeinfo for merge of r34423 into '.': G . --- Merging r34967 into '.': G packages/fcl-base/tests/testexprpars.pp G packages/fcl-base/src/fpexprpars.pp --- Recording mergeinfo for merge of r34967 into '.': G . --- Merging r35006 into '.': G packages/fcl-base/src/fpexprpars.pp --- Recording mergeinfo for merge of r35006 into '.': G . # revisions: 33947,33986,34377,34422,34423,34967,35006 git-svn-id: https://svn.freepascal.org/svn/fpc/branches/fixes_3_0@36537 3ad0048d-3df7-0310-abae-a5850022a9f2
-rw-r--r--packages/fcl-base/src/fpexprpars.pp823
-rw-r--r--packages/fcl-base/tests/fclbase-unittests.lpi23
-rw-r--r--packages/fcl-base/tests/fclbase-unittests.pp2
-rw-r--r--packages/fcl-base/tests/testexprpars.pp771
4 files changed, 1486 insertions, 133 deletions
diff --git a/packages/fcl-base/src/fpexprpars.pp b/packages/fcl-base/src/fpexprpars.pp
index 956d026809..5629300690 100644
--- a/packages/fcl-base/src/fpexprpars.pp
+++ b/packages/fcl-base/src/fpexprpars.pp
@@ -27,15 +27,15 @@ Type
TTokenType = (ttPlus, ttMinus, ttLessThan, ttLargerThan, ttEqual, ttDiv,
ttMul, ttLeft, ttRight, ttLessThanEqual, ttLargerThanEqual,
ttunequal, ttNumber, ttString, ttIdentifier,
- ttComma, ttand, ttOr,ttXor,ttTrue,ttFalse,ttnot,ttif,
- ttCase,ttEOF);
+ ttComma, ttAnd, ttOr, ttXor, ttTrue, ttFalse, ttNot, ttif,
+ ttCase, ttPower, ttEOF); // keep ttEOF last
TExprFloat = Double;
Const
ttDelimiters = [ttPlus, ttMinus, ttLessThan, ttLargerThan, ttEqual, ttDiv,
ttMul, ttLeft, ttRight, ttLessThanEqual, ttLargerThanEqual,
- ttunequal];
+ ttunequal, ttPower];
ttComparisons = [ttLargerThan,ttLessthan,
ttLargerThanEqual,ttLessthanEqual,
ttEqual,ttUnequal];
@@ -44,6 +44,8 @@ Type
TFPExpressionParser = Class;
TExprBuiltInManager = Class;
+ TFPExprFunction = Class;
+ TFPExprFunctionClass = Class of TFPExprFunction;
{ TFPExpressionScanner }
@@ -106,6 +108,10 @@ Type
Procedure GetNodeValue(var Result : TFPExpressionResult); virtual; abstract;
Public
Procedure Check; virtual; abstract;
+ Procedure InitAggregate; virtual;
+ Procedure UpdateAggregate; virtual;
+ Class Function IsAggregate : Boolean; virtual;
+ Function HasAggregate : Boolean; virtual;
Function NodeType : TResultType; virtual; abstract;
Function NodeValue : TFPExpressionResult;
Function AsString : string; virtual; abstract;
@@ -123,6 +129,9 @@ Type
Public
Constructor Create(ALeft,ARight : TFPExprNode);
Destructor Destroy; override;
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ Function HasAggregate : Boolean; override;
Procedure Check; override;
Property left : TFPExprNode Read FLeft;
Property Right : TFPExprNode Read FRight;
@@ -245,6 +254,9 @@ Type
Procedure GetNodeValue(var Result : TFPExpressionResult); override;
Public
Procedure Check; override;
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ Function HasAggregate : Boolean; override;
Function NodeType : TResultType; override;
Constructor Create(ACondition,ALeft,ARight : TFPExprNode);
Destructor destroy; override;
@@ -262,6 +274,9 @@ Type
Procedure GetNodeValue(var Result : TFPExpressionResult); override;
Public
Procedure Check; override;
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ function HasAggregate: Boolean; override;
Function NodeType : TResultType; override;
Constructor Create(Args : TExprArgumentArray);
Destructor destroy; override;
@@ -314,6 +329,16 @@ Type
Procedure GetNodeValue(var Result : TFPExpressionResult); override;
end;
+ { TFPPowerOperation }
+ TFPPowerOperation = class(TMathOperation)
+ public
+ Procedure Check; override;
+ Function AsString : string ; override;
+ Function NodeType : TResultType; override;
+ Procedure GetNodeValue(var Result : TFPExpressionResult); override;
+ end;
+
+
{ TFPUnaryOperator }
TFPUnaryOperator = Class(TFPExprNode)
@@ -322,6 +347,9 @@ Type
Public
Constructor Create(AOperand : TFPExprNode);
Destructor Destroy; override;
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ Function HasAggregate : Boolean; override;
Procedure Check; override;
Property Operand : TFPExprNode Read FOperand;
end;
@@ -401,14 +429,19 @@ Type
end;
- TIdentifierType = (itVariable,itFunctionCallBack,itFunctionHandler);
+ TIdentifierType = (itVariable,itFunctionCallBack,itFunctionHandler,itFunctionNode);
TFPExprFunctionCallBack = Procedure (Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
TFPExprFunctionEvent = Procedure (Var Result : TFPExpressionResult; Const Args : TExprParameterArray) of object;
+ TFPExprVariableCallBack = Procedure (Var Result : TFPExpressionResult; ConstRef AName : ShortString);
+ TFPExprVariableEvent = Procedure (Var Result : TFPExpressionResult; ConstRef AName : ShortString) of Object;
{ TFPExprIdentifierDef }
TFPExprIdentifierDef = Class(TCollectionItem)
private
+ FNodeType: TFPExprFunctionClass;
+ FOnGetVarValue: TFPExprVariableEvent;
+ FOnGetVarValueCB: TFPExprVariableCallBack;
FStringValue : String;
FValue : TFPExpressionResult;
FArgumentTypes: String;
@@ -435,15 +468,18 @@ Type
Protected
Procedure CheckResultType(Const AType : TResultType);
Procedure CheckVariable;
+ Procedure FetchValue;
Public
Function ArgumentCount : Integer;
Procedure Assign(Source : TPersistent); override;
+ Function EventBasedVariable : Boolean; Inline;
Property AsFloat : TExprFloat Read GetAsFloat Write SetAsFloat;
Property AsInteger : Int64 Read GetAsInteger Write SetAsInteger;
Property AsString : String Read GetAsString Write SetAsString;
Property AsBoolean : Boolean Read GetAsBoolean Write SetAsBoolean;
Property AsDateTime : TDateTime Read GetAsDateTime Write SetAsDateTime;
Property OnGetFunctionValueCallBack : TFPExprFunctionCallBack Read FOnGetValueCB Write FOnGetValueCB;
+ Property OnGetVariableValueCallBack : TFPExprVariableCallBack Read FOnGetVarValueCB Write FOnGetVarValueCB;
Published
Property IdentifierType : TIdentifierType Read FIDType Write FIDType;
Property Name : ShortString Read FName Write SetName;
@@ -451,10 +487,12 @@ Type
Property ParameterTypes : String Read FArgumentTypes Write SetArgumentTypes;
Property ResultType : TResultType Read GetResultType Write SetResultType;
Property OnGetFunctionValue : TFPExprFunctionEvent Read FOnGetValue Write FOnGetValue;
+ Property OnGetVariableValue : TFPExprVariableEvent Read FOnGetVarValue Write FOnGetVarValue;
+ Property NodeType : TFPExprFunctionClass Read FNodeType Write FNodeType;
end;
- TBuiltInCategory = (bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser);
+ TBuiltInCategory = (bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser,bcAggregate);
TBuiltInCategories = Set of TBuiltInCategory;
{ TFPBuiltInExprIdentifierDef }
@@ -482,6 +520,8 @@ Type
Function IndexOfIdentifier(Const AName : ShortString) : Integer;
Function FindIdentifier(Const AName : ShortString) : TFPExprIdentifierDef;
Function IdentifierByName(Const AName : ShortString) : TFPExprIdentifierDef;
+ Function AddVariable(Const AName : ShortString; AResultType : TResultType; ACallback : TFPExprVariableCallBack) : TFPExprIdentifierDef;
+ Function AddVariable(Const AName : ShortString; AResultType : TResultType; ACallback : TFPExprVariableEvent) : TFPExprIdentifierDef;
Function AddVariable(Const AName : ShortString; AResultType : TResultType; AValue : String) : TFPExprIdentifierDef;
Function AddBooleanVariable(Const AName : ShortString; AValue : Boolean) : TFPExprIdentifierDef;
Function AddIntegerVariable(Const AName : ShortString; AValue : Integer) : TFPExprIdentifierDef;
@@ -490,6 +530,7 @@ Type
Function AddDateTimeVariable(Const AName : ShortString; AValue : TDateTime) : TFPExprIdentifierDef;
Function AddFunction(Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ACallBack : TFPExprFunctionCallBack) : TFPExprIdentifierDef;
Function AddFunction(Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ACallBack : TFPExprFunctionEvent) : TFPExprIdentifierDef;
+ Function AddFunction(Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ANodeClass : TFPExprFunctionClass) : TFPExprIdentifierDef;
property Identifiers[AIndex : Integer] : TFPExprIdentifierDef Read GetI Write SetI; Default;
end;
@@ -531,6 +572,62 @@ Type
Function AsString : String; override;
end;
+ { TAggregateExpr }
+
+ TAggregateExpr = Class(TFPExprFunction)
+ Protected
+ FResult : TFPExpressionResult;
+ Class Function IsAggregate : Boolean; override;
+ Procedure GetNodeValue(var Result : TFPExpressionResult); override;
+ end;
+
+ { TAggregateMin }
+
+ TAggregateMin = Class(TAggregateExpr)
+ Public
+ FFirst: Boolean;
+ Public
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ end;
+
+ { TAggregateMax }
+
+ TAggregateMax = Class(TAggregateExpr)
+ Public
+ FFirst: Boolean;
+ Public
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ end;
+
+ { TAggregateSum }
+
+ TAggregateSum = Class(TAggregateExpr)
+ Public
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ end;
+
+ { TAggregateAvg }
+
+ TAggregateAvg = Class(TAggregateSum)
+ Protected
+ FCount : Integer;
+ Public
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ Procedure GetNodeValue(var Result : TFPExpressionResult); override;
+ end;
+
+ { TAggregateCount }
+
+ TAggregateCount = Class(TAggregateExpr)
+ Public
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ end;
+
{ TFPFunctionCallBack }
TFPFunctionCallBack = Class(TFPExprFunction)
@@ -586,6 +683,7 @@ Type
Function Level4 : TFPExprNode;
Function Level5 : TFPExprNode;
Function Level6 : TFPExprNode;
+ Function Level7 : TFPExprNode;
Function Primitive : TFPExprNode;
function GetToken: TTokenType;
Function TokenType : TTokenType;
@@ -600,8 +698,12 @@ Type
Function IdentifierByName(const AName : ShortString) : TFPExprIdentifierDef; virtual;
Procedure Clear;
Procedure EvaluateExpression(Var Result : TFPExpressionResult);
+ function ExtractNode(var N: TFPExprNode): Boolean;
Function Evaluate : TFPExpressionResult;
Function ResultType : TResultType;
+ Function HasAggregate : Boolean;
+ Procedure InitAggregate;
+ Procedure UpdateAggregate;
Property AsFloat : TExprFloat Read GetAsFloat;
Property AsInteger : Int64 Read GetAsInteger;
Property AsString : String Read GetAsString;
@@ -637,22 +739,23 @@ Type
Function AddDateTimeVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : TDateTime) : TFPBuiltInExprIdentifierDef;
Function AddFunction(Const ACategory : TBuiltInCategory; Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ACallBack : TFPExprFunctionCallBack) : TFPBuiltInExprIdentifierDef;
Function AddFunction(Const ACategory : TBuiltInCategory; Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ACallBack : TFPExprFunctionEvent) : TFPBuiltInExprIdentifierDef;
+ Function AddFunction(Const ACategory : TBuiltInCategory; Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ANodeClass : TFPExprFunctionClass) : TFPBuiltInExprIdentifierDef;
Property IdentifierCount : Integer Read GetCount;
Property Identifiers[AIndex : Integer] :TFPBuiltInExprIdentifierDef Read GetI;
end;
EExprParser = Class(Exception);
+Const
+ AllBuiltIns = [bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser,bcAggregate];
Function TokenName (AToken : TTokenType) : String;
Function ResultTypeName (AResult : TResultType) : String;
Function CharToResultType(C : Char) : TResultType;
Function BuiltinIdentifiers : TExprBuiltInManager;
-Procedure RegisterStdBuiltins(AManager : TExprBuiltInManager);
+Procedure RegisterStdBuiltins(AManager : TExprBuiltInManager; Categories : TBuiltInCategories = AllBuiltIns);
function ArgToFloat(Arg: TFPExpressionResult): TExprFloat;
-Const
- AllBuiltIns = [bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser];
implementation
@@ -667,9 +770,9 @@ const
Digits = ['0'..'9','.'];
WhiteSpace = [' ',#13,#10,#9];
- Operators = ['+','-','<','>','=','/','*'];
+ Operators = ['+','-','<','>','=','/','*','^'];
Delimiters = Operators+[',','(',')'];
- Symbols = ['%','^']+Delimiters;
+ Symbols = ['%']+Delimiters;
WordDelimiters = WhiteSpace + Symbols;
Resourcestring
@@ -689,6 +792,7 @@ Resourcestring
SErrCommaExpected = 'Expected comma (,) at position %d, but got %s';
SErrInvalidNumberChar = 'Unexpected character in number : %s';
SErrInvalidNumber = 'Invalid numerical value : %s';
+ SErrUnterminatedIdentifier = 'Unterminated quoted identifier: %s';
SErrNoOperand = 'No operand for unary operation %s';
SErrNoleftOperand = 'No left operand for binary operation %s';
SErrNoRightOperand = 'No right operand for binary operation %s';
@@ -727,13 +831,13 @@ begin
Raise EExprParser.CreateFmt(Fmt,Args);
end;
-Function TokenName (AToken : TTokenType) : String;
+function TokenName(AToken: TTokenType): String;
begin
Result:=GetEnumName(TypeInfo(TTokenType),Ord(AToken));
end;
-Function ResultTypeName (AResult : TResultType) : String;
+function ResultTypeName(AResult: TResultType): String;
begin
Result:=GetEnumName(TypeInfo(TResultType),Ord(AResult));
@@ -755,7 +859,7 @@ end;
Var
BuiltIns : TExprBuiltInManager;
-Function BuiltinIdentifiers : TExprBuiltInManager;
+function BuiltinIdentifiers: TExprBuiltInManager;
begin
If (BuiltIns=Nil) then
@@ -769,6 +873,157 @@ begin
FreeAndNil(Builtins);
end;
+{ TAggregateMax }
+
+procedure TAggregateMax.InitAggregate;
+begin
+ inherited InitAggregate;
+ FFirst:=True;
+ FResult.ResultType:=rtFloat;
+ FResult.resFloat:=0;
+end;
+
+procedure TAggregateMax.UpdateAggregate;
+
+Var
+ OK : Boolean;
+ N : TFPExpressionResult;
+
+begin
+ FArgumentNodes[0].GetNodeValue(N);
+ if FFirst then
+ begin
+ FFirst:=False;
+ OK:=True;
+ end
+ else
+ Case N.ResultType of
+ rtFloat: OK:=N.ResFloat>FResult.ResFloat;
+ rtinteger: OK:=N.ResInteger>FResult.ResFloat;
+ end;
+ if OK then
+ Case N.ResultType of
+ rtFloat: FResult.ResFloat:=N.ResFloat;
+ rtinteger: FResult.ResFloat:=N.ResInteger;
+ end;
+end;
+
+{ TAggregateMin }
+
+procedure TAggregateMin.InitAggregate;
+begin
+ inherited InitAggregate;
+ FFirst:=True;
+ FResult.ResultType:=rtFloat;
+ FResult.resFloat:=0;
+end;
+
+procedure TAggregateMin.UpdateAggregate;
+
+Var
+ OK : Boolean;
+ N : TFPExpressionResult;
+
+begin
+ FArgumentNodes[0].GetNodeValue(N);
+ if FFirst then
+ begin
+ FResult.ResultType:=N.ResultType;
+ FFirst:=False;
+ OK:=True;
+ end
+ else
+ Case N.ResultType of
+ rtFloat: OK:=N.ResFloat<FResult.ResFloat;
+ rtinteger: OK:=N.ResInteger<FResult.ResFloat;
+ end;
+ if OK then
+ Case FResult.ResultType of
+ rtFloat: FResult.ResFloat:=N.ResFloat;
+ rtinteger: FResult.ResFloat:=N.ResInteger;
+ end;
+ inherited UpdateAggregate;
+end;
+
+{ TAggregateAvg }
+
+procedure TAggregateAvg.InitAggregate;
+begin
+ inherited InitAggregate;
+ FCount:=0;
+end;
+
+procedure TAggregateAvg.UpdateAggregate;
+begin
+ inherited UpdateAggregate;
+ Inc(FCount);
+end;
+
+procedure TAggregateAvg.GetNodeValue(var Result: TFPExpressionResult);
+begin
+ inherited GetNodeValue(Result);
+ Result.ResultType:=rtFloat;
+ if FCount=0 then
+ Result.ResFloat:=0
+ else
+ Case FResult.ResultType of
+ rtInteger:
+ Result.ResFloat:=FResult.ResInteger/FCount;
+ rtFloat:
+ Result.ResFloat:=FResult.ResFloat/FCount;
+ end;
+end;
+
+{ TAggregateCount }
+
+procedure TAggregateCount.InitAggregate;
+begin
+ FResult.ResultType:=rtInteger;
+ FResult.ResInteger:=0;
+end;
+
+procedure TAggregateCount.UpdateAggregate;
+begin
+ Inc(FResult.ResInteger);
+end;
+
+{ TAggregateExpr }
+
+class function TAggregateExpr.IsAggregate: Boolean;
+begin
+ Result:=True;
+end;
+
+procedure TAggregateExpr.GetNodeValue(var Result: TFPExpressionResult);
+begin
+ Result:=FResult;
+end;
+
+{ TAggregateSum }
+
+
+procedure TAggregateSum.InitAggregate;
+begin
+ FResult.ResultType:=FArgumentNodes[0].NodeType;
+ Case FResult.ResultType of
+ rtFloat: FResult.ResFloat:=0.0;
+ rtinteger: FResult.ResInteger:=0;
+ end;
+end;
+
+procedure TAggregateSum.UpdateAggregate;
+
+Var
+ R : TFPExpressionResult;
+
+begin
+ FArgumentNodes[0].GetNodeValue(R);
+ Case FResult.ResultType of
+ rtFloat: FResult.ResFloat:=FResult.ResFloat+R.ResFloat;
+ rtinteger: FResult.ResInteger:=FResult.ResInteger+R.ResInteger;
+ end;
+end;
+
{ ---------------------------------------------------------------------
TFPExpressionScanner
---------------------------------------------------------------------}
@@ -863,6 +1118,7 @@ begin
'(' : Result := ttLeft;
')' : Result := ttRight;
',' : Result := ttComma;
+ '^' : Result := ttPower;
else
ScanError(Format(SUnknownDelimiter,[D]));
end;
@@ -925,7 +1181,7 @@ Var
begin
C:=CurrentChar;
prevC := #0;
- while (not IsWordDelim(C) or (prevC='E')) and (C<>cNull) do
+ while (not IsWordDelim(C) or (prevC in ['E','-','+'])) and (C<>cNull) do
begin
If Not ( IsDigit(C)
or ((FToken<>'') and (Upcase(C)='E'))
@@ -952,7 +1208,19 @@ begin
C:=CurrentChar;
while (not IsWordDelim(C)) and (C<>cNull) do
begin
- FToken:=FToken+C;
+ if (C<>'"') then
+ FToken:=FToken+C
+ else
+ begin
+ C:=NextPos;
+ While Not (C in [cNull,'"']) do
+ begin
+ FToken:=FToken+C;
+ C:=NextPos;
+ end;
+ if (C<>'"') then
+ ScanError(Format(SErrUnterminatedIdentifier,[FToken]));
+ end;
C:=NextPos;
end;
S:=LowerCase(Token);
@@ -993,7 +1261,7 @@ begin
Result:=DoString
else if IsDigit(C) then
Result:=DoNumber
- else if IsAlpha(C) then
+ else if IsAlpha(C) or (C='"') then
Result:=DoIdentifier
else
ScanError(Format(SErrUnknownCharacter,[FPos,C])) ;
@@ -1004,7 +1272,7 @@ end;
TFPExpressionParser
---------------------------------------------------------------------}
-Function TFPExpressionParser.TokenType : TTokenType;
+function TFPExpressionParser.TokenType: TTokenType;
begin
Result:=FScanner.TokenType;
@@ -1075,13 +1343,13 @@ begin
inherited Destroy;
end;
-Function TFPExpressionParser.GetToken : TTokenType;
+function TFPExpressionParser.GetToken: TTokenType;
begin
Result:=FScanner.GetToken;
end;
-Procedure TFPExpressionParser.CheckEOF;
+procedure TFPExpressionParser.CheckEOF;
begin
If (TokenType=ttEOF) then
@@ -1103,14 +1371,23 @@ begin
FExprNode.GetNodeValue(Result);
end;
+function TFPExpressionParser.ExtractNode(Var N : TFPExprNode) : Boolean;
+begin
+ Result:=Assigned(FExprNode);
+ if Result then
+ begin
+ N:=FExprNode;
+ FExprNode:=Nil;
+ FExpression:='';
+ end;
+end;
+
procedure TFPExpressionParser.ParserError(Msg: String);
begin
Raise EExprParser.Create(Msg);
end;
function TFPExpressionParser.ConvertNode(Todo : TFPExprNode; ToType : TResultType): TFPExprNode;
-
-
begin
Result:=ToDo;
Case ToDo.NodeType of
@@ -1214,7 +1491,7 @@ end;
if the result types differ, they are converted to a common type if possible.
}
-Procedure TFPExpressionParser.CheckNodes(Var Left,Right : TFPExprNode);
+procedure TFPExpressionParser.CheckNodes(var Left, Right: TFPExprNode);
begin
Left:=MatchNodes(Left,Right);
@@ -1228,7 +1505,7 @@ begin
FDirty:=True;
end;
-Function TFPExpressionParser.Level1 : TFPExprNode;
+function TFPExpressionParser.Level1: TFPExprNode;
var
tt: TTokenType;
@@ -1379,8 +1656,28 @@ begin
end;
function TFPExpressionParser.Level6: TFPExprNode;
+var
+ right: TFPExprNode;
begin
-{$ifdef debugexpr} Writeln('Level 6 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+{$ifdef debugexpr} Writeln('Level 6 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
+ Result := Level7;
+ try
+ while (TokenType = ttPower) do
+ begin
+ GetToken;
+ right := Level5; // Accept '(', unary '+', '-' as next tokens
+ CheckNodes(Result, right);
+ Result := TFPPowerOperation.Create(Result, right);
+ end;
+ except
+ Result.Free;
+ Raise;
+ end;
+end;
+
+function TFPExpressionParser.Level7: TFPExprNode;
+begin
+{$ifdef debugexpr} Writeln('Level 7 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
if (TokenType=ttLeft) then
begin
GetToken;
@@ -1448,7 +1745,7 @@ begin
ACount:=3
else if IfC then
ACount:=-4
- else if (ID.IdentifierType in [itFunctionCallBack,itFunctionHandler]) then
+ else if (ID.IdentifierType in [itFunctionCallBack,itFunctionHandler,itFunctionNode]) then
ACount:=ID.ArgumentCount
else
ACount:=0;
@@ -1500,6 +1797,7 @@ begin
itVariable : Result:= TFPExprVariable.CreateIdentifier(ID);
itFunctionCallBack : Result:= TFPFunctionCallback.CreateFunction(ID,Args);
itFunctionHandler : Result:= TFPFunctionEventHandler.CreateFunction(ID,Args);
+ itFunctionNode : Result:= ID.NodeType.CreateFunction(ID,Args);
end;
end;
GetToken;
@@ -1547,7 +1845,24 @@ function TFPExpressionParser.ResultType: TResultType;
begin
if not Assigned(FExprNode) then
ParserError(SErrInExpression);
- Result:=FExprNode.NodeType;;
+ Result:=FExprNode.NodeType;
+end;
+
+function TFPExpressionParser.HasAggregate: Boolean;
+begin
+ Result:=Assigned(FExprNode) and FExprNode.HasAggregate;
+end;
+
+procedure TFPExpressionParser.InitAggregate;
+begin
+ If Assigned(FExprNode) then
+ FExprNode.InitAggregate;
+end;
+
+procedure TFPExpressionParser.UpdateAggregate;
+begin
+ If Assigned(FExprNode) then
+ FExprNode.UpdateAggregate;
end;
{ ---------------------------------------------------------------------
@@ -1601,7 +1916,29 @@ begin
RaiseParserError(SErrUnknownIdentifier,[AName]);
end;
-function TFPExprIdentifierDefs.AddVariable(Const AName: ShortString;
+function TFPExprIdentifierDefs.AddVariable(const AName: ShortString;
+ AResultType: TResultType; ACallback: TFPExprVariableCallBack
+ ): TFPExprIdentifierDef;
+begin
+ Result:=Add as TFPExprIdentifierDef;
+ Result.IdentifierType:=itVariable;
+ Result.Name:=AName;
+ Result.ResultType:=AResultType;
+ Result.OnGetVariableValueCallBack:=ACallBack
+end;
+
+function TFPExprIdentifierDefs.AddVariable(const AName: ShortString;
+ AResultType: TResultType; ACallback: TFPExprVariableEvent
+ ): TFPExprIdentifierDef;
+begin
+ Result:=Add as TFPExprIdentifierDef;
+ Result.IdentifierType:=itVariable;
+ Result.Name:=AName;
+ Result.ResultType:=AResultType;
+ Result.OnGetVariableValue:=ACallBack
+end;
+
+function TFPExprIdentifierDefs.AddVariable(const AName: ShortString;
AResultType: TResultType; AValue: String): TFPExprIdentifierDef;
begin
Result:=Add as TFPExprIdentifierDef;
@@ -1611,8 +1948,8 @@ begin
Result.Value:=AValue;
end;
-function TFPExprIdentifierDefs.AddBooleanVariable(Const AName: ShortString; AValue: Boolean
- ): TFPExprIdentifierDef;
+function TFPExprIdentifierDefs.AddBooleanVariable(const AName: ShortString;
+ AValue: Boolean): TFPExprIdentifierDef;
begin
Result:=Add as TFPExprIdentifierDef;
Result.IdentifierType:=itVariable;
@@ -1621,8 +1958,8 @@ begin
Result.FValue.ResBoolean:=AValue;
end;
-function TFPExprIdentifierDefs.AddIntegerVariable(Const AName: ShortString; AValue: Integer
- ): TFPExprIdentifierDef;
+function TFPExprIdentifierDefs.AddIntegerVariable(const AName: ShortString;
+ AValue: Integer): TFPExprIdentifierDef;
begin
Result:=Add as TFPExprIdentifierDef;
Result.IdentifierType:=itVariable;
@@ -1631,8 +1968,8 @@ begin
Result.FValue.ResInteger:=AValue;
end;
-function TFPExprIdentifierDefs.AddFloatVariable(Const AName: ShortString; AValue: TExprFloat
- ): TFPExprIdentifierDef;
+function TFPExprIdentifierDefs.AddFloatVariable(const AName: ShortString;
+ AValue: TExprFloat): TFPExprIdentifierDef;
begin
Result:=Add as TFPExprIdentifierDef;
Result.IdentifierType:=itVariable;
@@ -1641,8 +1978,8 @@ begin
Result.FValue.ResFloat:=AValue;
end;
-function TFPExprIdentifierDefs.AddStringVariable(Const AName: ShortString; AValue: String
- ): TFPExprIdentifierDef;
+function TFPExprIdentifierDefs.AddStringVariable(const AName: ShortString;
+ AValue: String): TFPExprIdentifierDef;
begin
Result:=Add as TFPExprIdentifierDef;
Result.IdentifierType:=itVariable;
@@ -1651,8 +1988,8 @@ begin
Result.FValue.ResString:=AValue;
end;
-function TFPExprIdentifierDefs.AddDateTimeVariable(Const AName: ShortString; AValue: TDateTime
- ): TFPExprIdentifierDef;
+function TFPExprIdentifierDefs.AddDateTimeVariable(const AName: ShortString;
+ AValue: TDateTime): TFPExprIdentifierDef;
begin
Result:=Add as TFPExprIdentifierDef;
Result.IdentifierType:=itVariable;
@@ -1685,6 +2022,18 @@ begin
Result.FOnGetValue:=ACallBack;
end;
+function TFPExprIdentifierDefs.AddFunction(const AName: ShortString;
+ const AResultType: Char; const AParamTypes: String;
+ ANodeClass: TFPExprFunctionClass): TFPExprIdentifierDef;
+begin
+ Result:=Add as TFPExprIdentifierDef;
+ Result.Name:=Aname;
+ Result.IdentifierType:=itFunctionNode;
+ Result.ParameterTypes:=AParamTypes;
+ Result.ResultType:=CharToResultType(AResultType);
+ Result.FNodeType:=ANodeClass;
+end;
+
{ ---------------------------------------------------------------------
TFPExprIdentifierDef
---------------------------------------------------------------------}
@@ -1739,6 +2088,8 @@ procedure TFPExprIdentifierDef.CheckVariable;
begin
If Identifiertype<>itvariable then
RaiseParserError(SErrNotVariable,[Name]);
+ if EventBasedVariable then
+ FetchValue;
end;
function TFPExprIdentifierDef.ArgumentCount: Integer;
@@ -1762,6 +2113,8 @@ begin
FName:=EID.FName;
FOnGetValue:=EID.FOnGetValue;
FOnGetValueCB:=EID.FOnGetValueCB;
+ FOnGetVarValue:=EID.FOnGetVarValue;
+ FOnGetVarValueCB:=EID.FOnGetVarValueCB;
end
else
inherited Assign(Source);
@@ -1828,6 +2181,46 @@ begin
end;
end;
+procedure TFPExprIdentifierDef.FetchValue;
+
+Var
+ RT,RT2 : TResultType;
+ I : Integer;
+
+begin
+ RT:=FValue.ResultType;
+ if Assigned(FOnGetVarValue) then
+ FOnGetVarValue(FValue,FName)
+ else
+ FOnGetVarValueCB(FValue,FName);
+ RT2:=FValue.ResultType;
+ if RT2<>RT then
+ begin
+ // Automatically convert integer to float.
+ if (rt2=rtInteger) and (rt=rtFLoat) then
+ begin
+ FValue.ResultType:=RT;
+ I:=FValue.resInteger;
+ FValue.resFloat:=I;
+ end
+ else
+ begin
+ // Restore
+ FValue.ResultType:=RT;
+ Raise EExprParser.CreateFmt('Value handler for variable %s returned wrong type, expected "%s", got "%s"',[
+ FName,
+ GetEnumName(TypeInfo(TResultType),Ord(rt)),
+ GetEnumName(TypeInfo(TResultType),Ord(rt2))
+ ]);
+ end;
+ end;
+end;
+
+function TFPExprIdentifierDef.EventBasedVariable: Boolean;
+begin
+ Result:=Assigned(FOnGetVarValue) or Assigned(FOnGetVarValueCB);
+end;
+
function TFPExprIdentifierDef.GetResultType: TResultType;
begin
Result:=FValue.ResultType;
@@ -1977,6 +2370,14 @@ begin
Result.Category:=ACategory;
end;
+function TExprBuiltInManager.AddFunction(const ACategory: TBuiltInCategory;
+ const AName: ShortString; const AResultType: Char; const AParamTypes: String;
+ ANodeClass: TFPExprFunctionClass): TFPBuiltInExprIdentifierDef;
+begin
+ Result:=TFPBuiltInExprIdentifierDef(FDefs.AddFunction(AName,AResultType,AParamTypes,ANodeClass));
+ Result. Category:=ACategory;
+end;
+
{ ---------------------------------------------------------------------
Various Nodes
@@ -2010,6 +2411,33 @@ begin
inherited Destroy;
end;
+procedure TFPBinaryOperation.InitAggregate;
+begin
+ inherited InitAggregate;
+ if Assigned(Left) then
+ Left.InitAggregate;
+ if Assigned(Right) then
+ Right.InitAggregate;
+end;
+
+procedure TFPBinaryOperation.UpdateAggregate;
+begin
+ inherited UpdateAggregate;
+ if Assigned(Left) then
+ Left.UpdateAggregate;
+ if Assigned(Right) then
+ Right.UpdateAggregate;
+end;
+
+function TFPBinaryOperation.HasAggregate: Boolean;
+begin
+ Result:=inherited HasAggregate;
+ if Assigned(Left) then
+ Result:=Result or Left.HasAggregate;
+ if Assigned(Right) then
+ Result:=Result or Right.HasAggregate;
+end;
+
procedure TFPBinaryOperation.Check;
begin
If Not Assigned(Left) then
@@ -2031,6 +2459,28 @@ begin
inherited Destroy;
end;
+procedure TFPUnaryOperator.InitAggregate;
+begin
+ inherited InitAggregate;
+ if Assigned(FOperand) then
+ FOperand.InitAggregate;
+
+end;
+
+procedure TFPUnaryOperator.UpdateAggregate;
+begin
+ inherited UpdateAggregate;
+ if Assigned(FOperand) then
+ FOperand.UpdateAggregate;
+end;
+
+function TFPUnaryOperator.HasAggregate: Boolean;
+begin
+ Result:=inherited HasAggregate;
+ if Assigned(FOperand) then
+ Result:=Result or FOperand.HasAggregate;
+end;
+
procedure TFPUnaryOperator.Check;
begin
If Not Assigned(Operand) then
@@ -2184,6 +2634,26 @@ begin
end;
end;
+procedure TFPExprNode.InitAggregate;
+begin
+ // Do nothing
+end;
+
+procedure TFPExprNode.UpdateAggregate;
+begin
+ // Do nothing
+end;
+
+function TFPExprNode.HasAggregate: Boolean;
+begin
+ Result:=IsAggregate;
+end;
+
+class function TFPExprNode.IsAggregate: Boolean;
+begin
+ Result:=False;
+end;
+
function TFPExprNode.NodeValue: TFPExpressionResult;
begin
GetNodeValue(Result);
@@ -2289,6 +2759,27 @@ begin
CheckSameNodeTypes;
end;
+procedure TIfOperation.InitAggregate;
+begin
+ inherited InitAggregate;
+ If Assigned(FCondition) then
+ fCondition.InitAggregate;
+end;
+
+procedure TIfOperation.UpdateAggregate;
+begin
+ inherited UpdateAggregate;
+ If Assigned(FCondition) then
+ FCondition.UpdateAggregate;
+end;
+
+function TIfOperation.HasAggregate: Boolean;
+begin
+ Result:=inherited HasAggregate;
+ if Assigned(Condition) then
+ Result:=Result or Condition.HasAggregate;
+end;
+
function TIfOperation.NodeType: TResultType;
begin
Result:=Left.NodeType;
@@ -2367,6 +2858,45 @@ begin
end;
end;
+procedure TCaseOperation.InitAggregate;
+
+Var
+ I : Integer;
+
+begin
+ inherited InitAggregate;
+ if Assigned(FCondition) then
+ FCondition.InitAggregate;
+ For I:=0 to Length(Fargs)-1 do
+ FArgs[i].InitAggregate;
+end;
+
+procedure TCaseOperation.UpdateAggregate;
+Var
+ I : Integer;
+begin
+ inherited UpdateAggregate;
+ if Assigned(FCondition) then
+ FCondition.UpdateAggregate;
+ For I:=0 to Length(Fargs)-1 do
+ FArgs[i].InitAggregate;
+end;
+
+Function TCaseOperation.HasAggregate : Boolean;
+
+Var
+ I,L : Integer;
+begin
+ Result:=inherited HasAggregate;
+ L:=Length(Fargs);
+ I:=0;
+ While (Not Result) and (I<L) do
+ begin
+ Result:=Result or FArgs[i].HasAggregate;
+ Inc(I)
+ end;
+end;
+
function TCaseOperation.NodeType: TResultType;
begin
Result:=FArgs[1].NodeType;
@@ -2692,6 +3222,55 @@ begin
Result.ResultType:=rtFloat;
end;
+{ TFPPowerOperation }
+
+procedure TFPPowerOperation.Check;
+const
+ AllowedTypes = [rtInteger, rtFloat];
+begin
+ CheckNodeType(Left, AllowedTypes);
+ CheckNodeType(Right, AllowedTypes);
+end;
+
+function TFPPowerOperation.AsString: String;
+begin
+ Result := Left.AsString + '^' + Right.AsString;
+end;
+
+function TFPPowerOperation.NodeType: TResultType;
+begin
+ Result := rtFloat;
+end;
+
+function power(base,exponent: TExprFloat): TExprFloat;
+// Adapted from unit "math"
+var
+ ex: Integer;
+begin
+ if Exponent = 0.0 then
+ result := 1.0
+ else if (base = 0.0) and (exponent > 0.0) then
+ result := 0.0
+ else if (base < 0.0) and (frac(exponent) = 0.0) then
+ begin
+ ex := round(exponent);
+ result := exp( exponent * ln(-base));
+ if odd(ex) then result := -result;
+ end
+ else
+ result := exp( exponent * ln(base) );
+end;
+
+procedure TFPPowerOperation.GetNodeValue(var Result: TFPExpressionResult);
+var
+ RRes: TFPExpressionResult;
+begin
+ Left.GetNodeValue(Result);
+ Right.GetNodeValue(RRes);
+ Result.ResFloat := power(ArgToFloat(Result), ArgToFloat(RRes));
+ Result.ResultType := rtFloat;
+end;
+
{ TFPConvertNode }
function TFPConvertNode.AsString: String;
@@ -2771,6 +3350,8 @@ end;
Procedure TFPExprIdentifierNode.GetNodeValue(var Result : TFPExpressionResult);
begin
+ if Identifier.EventBasedVariable then
+ Identifier.FetchValue;
Result:=PResult^;
Result.ResultType:=FResultType;
end;
@@ -2796,7 +3377,9 @@ Var
begin
For I:=0 to Length(FArgumentParams)-1 do
+ begin
FArgumentNodes[i].GetNodeValue(FArgumentParams[i]);
+ end;
end;
procedure TFPExprFunction.Check;
@@ -2817,7 +3400,7 @@ begin
// Automatically convert integers to floats in functions that return
// a float
if (rta = rtInteger) and (rtp = rtFloat) then begin
- FArgumentNodes[i] := TIntToFloatNode(FArgumentNodes[i]);
+ FArgumentNodes[i] := TIntToFloatNode.Create(FArgumentNodes[i]);
exit;
end;
@@ -2877,6 +3460,7 @@ Procedure TFPFunctionCallBack.GetNodeValue(var Result : TFPExpressionResult);
begin
If Length(FArgumentParams)>0 then
CalcParams;
+
FCallBack(Result,FArgumentParams);
Result.ResultType:=NodeType;
end;
@@ -3336,80 +3920,103 @@ begin
Result.resDateTime:=Args[2].resDateTime
end;
-Procedure RegisterStdBuiltins(AManager : TExprBuiltInManager);
+procedure RegisterStdBuiltins(AManager: TExprBuiltInManager; Categories: TBuiltInCategories = AllBuiltIns);
begin
With AManager do
begin
- AddFloatVariable(bcMath,'pi',Pi);
- // Math functions
- AddFunction(bcMath,'cos','F','F',@BuiltinCos);
- AddFunction(bcMath,'sin','F','F',@BuiltinSin);
- AddFunction(bcMath,'arctan','F','F',@BuiltinArctan);
- AddFunction(bcMath,'abs','F','F',@BuiltinAbs);
- AddFunction(bcMath,'sqr','F','F',@BuiltinSqr);
- AddFunction(bcMath,'sqrt','F','F',@BuiltinSqrt);
- AddFunction(bcMath,'exp','F','F',@BuiltinExp);
- AddFunction(bcMath,'ln','F','F',@BuiltinLn);
- AddFunction(bcMath,'log','F','F',@BuiltinLog);
- AddFunction(bcMath,'frac','F','F',@BuiltinFrac);
- AddFunction(bcMath,'int','F','F',@BuiltinInt);
- AddFunction(bcMath,'round','I','F',@BuiltinRound);
- AddFunction(bcMath,'trunc','I','F',@BuiltinTrunc);
- // String
- AddFunction(bcStrings,'length','I','S',@BuiltinLength);
- AddFunction(bcStrings,'copy','S','SII',@BuiltinCopy);
- AddFunction(bcStrings,'delete','S','SII',@BuiltinDelete);
- AddFunction(bcStrings,'pos','I','SS',@BuiltinPos);
- AddFunction(bcStrings,'lowercase','S','S',@BuiltinLowercase);
- AddFunction(bcStrings,'uppercase','S','S',@BuiltinUppercase);
- AddFunction(bcStrings,'stringreplace','S','SSSBB',@BuiltinStringReplace);
- AddFunction(bcStrings,'comparetext','I','SS',@BuiltinCompareText);
- // Date/Time
- AddFunction(bcDateTime,'date','D','',@BuiltinDate);
- AddFunction(bcDateTime,'time','D','',@BuiltinTime);
- AddFunction(bcDateTime,'now','D','',@BuiltinNow);
- AddFunction(bcDateTime,'dayofweek','I','D',@BuiltinDayofweek);
- AddFunction(bcDateTime,'extractyear','I','D',@BuiltinExtractYear);
- AddFunction(bcDateTime,'extractmonth','I','D',@BuiltinExtractMonth);
- AddFunction(bcDateTime,'extractday','I','D',@BuiltinExtractDay);
- AddFunction(bcDateTime,'extracthour','I','D',@BuiltinExtractHour);
- AddFunction(bcDateTime,'extractmin','I','D',@BuiltinExtractMin);
- AddFunction(bcDateTime,'extractsec','I','D',@BuiltinExtractSec);
- AddFunction(bcDateTime,'extractmsec','I','D',@BuiltinExtractMSec);
- AddFunction(bcDateTime,'encodedate','D','III',@BuiltinEncodedate);
- AddFunction(bcDateTime,'encodetime','D','IIII',@BuiltinEncodeTime);
- AddFunction(bcDateTime,'encodedatetime','D','IIIIIII',@BuiltinEncodeDateTime);
- AddFunction(bcDateTime,'shortdayname','S','I',@BuiltinShortDayName);
- AddFunction(bcDateTime,'shortmonthname','S','I',@BuiltinShortMonthName);
- AddFunction(bcDateTime,'longdayname','S','I',@BuiltinLongDayName);
- AddFunction(bcDateTime,'longmonthname','S','I',@BuiltinLongMonthName);
- AddFunction(bcDateTime,'formatdatetime','S','SD',@BuiltinFormatDateTime);
- // Boolean
- AddFunction(bcBoolean,'shl','I','II',@BuiltinShl);
- AddFunction(bcBoolean,'shr','I','II',@BuiltinShr);
- AddFunction(bcBoolean,'IFS','S','BSS',@BuiltinIFS);
- AddFunction(bcBoolean,'IFF','F','BFF',@BuiltinIFF);
- AddFunction(bcBoolean,'IFD','D','BDD',@BuiltinIFD);
- AddFunction(bcBoolean,'IFI','I','BII',@BuiltinIFI);
- // Conversion
- AddFunction(bcConversion,'inttostr','S','I',@BuiltInIntToStr);
- AddFunction(bcConversion,'strtoint','I','S',@BuiltInStrToInt);
- AddFunction(bcConversion,'strtointdef','I','SI',@BuiltInStrToIntDef);
- AddFunction(bcConversion,'floattostr','S','F',@BuiltInFloatToStr);
- AddFunction(bcConversion,'strtofloat','F','S',@BuiltInStrToFloat);
- AddFunction(bcConversion,'strtofloatdef','F','SF',@BuiltInStrToFloatDef);
- AddFunction(bcConversion,'booltostr','S','B',@BuiltInBoolToStr);
- AddFunction(bcConversion,'strtobool','B','S',@BuiltInStrToBool);
- AddFunction(bcConversion,'strtobooldef','B','SB',@BuiltInStrToBoolDef);
- AddFunction(bcConversion,'datetostr','S','D',@BuiltInDateToStr);
- AddFunction(bcConversion,'timetostr','S','D',@BuiltInTimeToStr);
- AddFunction(bcConversion,'strtodate','D','S',@BuiltInStrToDate);
- AddFunction(bcConversion,'strtodatedef','D','SD',@BuiltInStrToDateDef);
- AddFunction(bcConversion,'strtotime','D','S',@BuiltInStrToTime);
- AddFunction(bcConversion,'strtotimedef','D','SD',@BuiltInStrToTimeDef);
- AddFunction(bcConversion,'strtodatetime','D','S',@BuiltInStrToDateTime);
- AddFunction(bcConversion,'strtodatetimedef','D','SD',@BuiltInStrToDateTimeDef);
+ if bcMath in Categories then
+ begin
+ AddFloatVariable(bcMath,'pi',Pi);
+ // Math functions
+ AddFunction(bcMath,'cos','F','F',@BuiltinCos);
+ AddFunction(bcMath,'sin','F','F',@BuiltinSin);
+ AddFunction(bcMath,'arctan','F','F',@BuiltinArctan);
+ AddFunction(bcMath,'abs','F','F',@BuiltinAbs);
+ AddFunction(bcMath,'sqr','F','F',@BuiltinSqr);
+ AddFunction(bcMath,'sqrt','F','F',@BuiltinSqrt);
+ AddFunction(bcMath,'exp','F','F',@BuiltinExp);
+ AddFunction(bcMath,'ln','F','F',@BuiltinLn);
+ AddFunction(bcMath,'log','F','F',@BuiltinLog);
+ AddFunction(bcMath,'frac','F','F',@BuiltinFrac);
+ AddFunction(bcMath,'int','F','F',@BuiltinInt);
+ AddFunction(bcMath,'round','I','F',@BuiltinRound);
+ AddFunction(bcMath,'trunc','I','F',@BuiltinTrunc);
+ end;
+ if bcStrings in Categories then
+ begin
+ // String
+ AddFunction(bcStrings,'length','I','S',@BuiltinLength);
+ AddFunction(bcStrings,'copy','S','SII',@BuiltinCopy);
+ AddFunction(bcStrings,'delete','S','SII',@BuiltinDelete);
+ AddFunction(bcStrings,'pos','I','SS',@BuiltinPos);
+ AddFunction(bcStrings,'lowercase','S','S',@BuiltinLowercase);
+ AddFunction(bcStrings,'uppercase','S','S',@BuiltinUppercase);
+ AddFunction(bcStrings,'stringreplace','S','SSSBB',@BuiltinStringReplace);
+ AddFunction(bcStrings,'comparetext','I','SS',@BuiltinCompareText);
+ end;
+ if bcDateTime in Categories then
+ begin
+ // Date/Time
+ AddFunction(bcDateTime,'date','D','',@BuiltinDate);
+ AddFunction(bcDateTime,'time','D','',@BuiltinTime);
+ AddFunction(bcDateTime,'now','D','',@BuiltinNow);
+ AddFunction(bcDateTime,'dayofweek','I','D',@BuiltinDayofweek);
+ AddFunction(bcDateTime,'extractyear','I','D',@BuiltinExtractYear);
+ AddFunction(bcDateTime,'extractmonth','I','D',@BuiltinExtractMonth);
+ AddFunction(bcDateTime,'extractday','I','D',@BuiltinExtractDay);
+ AddFunction(bcDateTime,'extracthour','I','D',@BuiltinExtractHour);
+ AddFunction(bcDateTime,'extractmin','I','D',@BuiltinExtractMin);
+ AddFunction(bcDateTime,'extractsec','I','D',@BuiltinExtractSec);
+ AddFunction(bcDateTime,'extractmsec','I','D',@BuiltinExtractMSec);
+ AddFunction(bcDateTime,'encodedate','D','III',@BuiltinEncodedate);
+ AddFunction(bcDateTime,'encodetime','D','IIII',@BuiltinEncodeTime);
+ AddFunction(bcDateTime,'encodedatetime','D','IIIIIII',@BuiltinEncodeDateTime);
+ AddFunction(bcDateTime,'shortdayname','S','I',@BuiltinShortDayName);
+ AddFunction(bcDateTime,'shortmonthname','S','I',@BuiltinShortMonthName);
+ AddFunction(bcDateTime,'longdayname','S','I',@BuiltinLongDayName);
+ AddFunction(bcDateTime,'longmonthname','S','I',@BuiltinLongMonthName);
+ AddFunction(bcDateTime,'formatdatetime','S','SD',@BuiltinFormatDateTime);
+ end;
+ if bcBoolean in Categories then
+ begin
+ // Boolean
+ AddFunction(bcBoolean,'shl','I','II',@BuiltinShl);
+ AddFunction(bcBoolean,'shr','I','II',@BuiltinShr);
+ AddFunction(bcBoolean,'IFS','S','BSS',@BuiltinIFS);
+ AddFunction(bcBoolean,'IFF','F','BFF',@BuiltinIFF);
+ AddFunction(bcBoolean,'IFD','D','BDD',@BuiltinIFD);
+ AddFunction(bcBoolean,'IFI','I','BII',@BuiltinIFI);
+ end;
+ if (bcConversion in Categories) then
+ begin
+ // Conversion
+ AddFunction(bcConversion,'inttostr','S','I',@BuiltInIntToStr);
+ AddFunction(bcConversion,'strtoint','I','S',@BuiltInStrToInt);
+ AddFunction(bcConversion,'strtointdef','I','SI',@BuiltInStrToIntDef);
+ AddFunction(bcConversion,'floattostr','S','F',@BuiltInFloatToStr);
+ AddFunction(bcConversion,'strtofloat','F','S',@BuiltInStrToFloat);
+ AddFunction(bcConversion,'strtofloatdef','F','SF',@BuiltInStrToFloatDef);
+ AddFunction(bcConversion,'booltostr','S','B',@BuiltInBoolToStr);
+ AddFunction(bcConversion,'strtobool','B','S',@BuiltInStrToBool);
+ AddFunction(bcConversion,'strtobooldef','B','SB',@BuiltInStrToBoolDef);
+ AddFunction(bcConversion,'datetostr','S','D',@BuiltInDateToStr);
+ AddFunction(bcConversion,'timetostr','S','D',@BuiltInTimeToStr);
+ AddFunction(bcConversion,'strtodate','D','S',@BuiltInStrToDate);
+ AddFunction(bcConversion,'strtodatedef','D','SD',@BuiltInStrToDateDef);
+ AddFunction(bcConversion,'strtotime','D','S',@BuiltInStrToTime);
+ AddFunction(bcConversion,'strtotimedef','D','SD',@BuiltInStrToTimeDef);
+ AddFunction(bcConversion,'strtodatetime','D','S',@BuiltInStrToDateTime);
+ AddFunction(bcConversion,'strtodatetimedef','D','SD',@BuiltInStrToDateTimeDef);
+ end;
+ if bcAggregate in Categories then
+ begin
+ AddFunction(bcAggregate,'count','I','',TAggregateCount);
+ AddFunction(bcAggregate,'sum','F','F',TAggregateSum);
+ AddFunction(bcAggregate,'avg','F','F',TAggregateAvg);
+ AddFunction(bcAggregate,'min','F','F',TAggregateMin);
+ AddFunction(bcAggregate,'max','F','F',TAggregateMax);
+ end;
end;
end;
diff --git a/packages/fcl-base/tests/fclbase-unittests.lpi b/packages/fcl-base/tests/fclbase-unittests.lpi
index babd8c6440..225b475c27 100644
--- a/packages/fcl-base/tests/fclbase-unittests.lpi
+++ b/packages/fcl-base/tests/fclbase-unittests.lpi
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="9"/>
@@ -6,7 +6,6 @@
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
- <UseDefaultCompilerOptions Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
@@ -31,35 +30,35 @@
<RunParams>
<local>
<FormatVersion Value="1"/>
- <LaunchingApplication PathPlusParams="/usr/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+ <CommandLineParams Value="--suite=TTestParserVariables.TestVariable31"/>
</local>
</RunParams>
- <Units Count="2">
+ <Units Count="3">
<Unit0>
<Filename Value="fclbase-unittests.pp"/>
<IsPartOfProject Value="True"/>
+ <UnitName Value="fclbase_unittests"/>
</Unit0>
<Unit1>
<Filename Value="tchashlist.pp"/>
<IsPartOfProject Value="True"/>
- <UnitName Value="tchashlist"/>
</Unit1>
+ <Unit2>
+ <Filename Value="testexprpars.pp"/>
+ <IsPartOfProject Value="True"/>
+ </Unit2>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<Target>
- <Filename Value="project1"/>
+ <Filename Value="fclbase-unittests"/>
</Target>
<SearchPaths>
+ <IncludeFiles Value="$(ProjOutDir)"/>
+ <OtherUnitFiles Value="../src"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
- <Other>
- <CompilerMessages>
- <MsgFileName Value=""/>
- </CompilerMessages>
- <CompilerPath Value="$(CompPath)"/>
- </Other>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
diff --git a/packages/fcl-base/tests/fclbase-unittests.pp b/packages/fcl-base/tests/fclbase-unittests.pp
index 19bd30e3bc..0add823423 100644
--- a/packages/fcl-base/tests/fclbase-unittests.pp
+++ b/packages/fcl-base/tests/fclbase-unittests.pp
@@ -10,6 +10,8 @@ var
Application: TTestRunner;
begin
+ DefaultFormat:=fPlain;
+ DefaultRunAllTests:=True;
Application := TTestRunner.Create(nil);
Application.Initialize;
Application.Title := 'FCL-Base unittests';
diff --git a/packages/fcl-base/tests/testexprpars.pp b/packages/fcl-base/tests/testexprpars.pp
index 7528c76534..7526a96a4d 100644
--- a/packages/fcl-base/tests/testexprpars.pp
+++ b/packages/fcl-base/tests/testexprpars.pp
@@ -20,7 +20,7 @@ unit testexprpars;
interface
uses
- Classes, SysUtils, fpcunit, testutils, testregistry,fpexprpars;
+ Classes, SysUtils, fpcunit, testutils, testregistry, math, fpexprpars;
type
@@ -31,6 +31,7 @@ type
FP : TFPExpressionScanner;
FInvalidString : String;
procedure DoInvalidNumber(AString: String);
+ procedure TestIdentifier(const ASource, ATokenName: string);
procedure TestInvalidNumber;
protected
procedure SetUp; override;
@@ -46,6 +47,7 @@ type
Procedure TestInvalidCharacter;
Procedure TestUnterminatedString;
Procedure TestQuotesInString;
+ Procedure TestIdentifiers;
end;
{ TMyFPExpressionParser }
@@ -412,6 +414,27 @@ type
Procedure TestAsString;
end;
+ { TTestPowerNode }
+
+ TTestPowerNode = Class(TTestBaseParser)
+ Private
+ FN : TFPPowerOperation;
+ FE : TFPExpressionParser;
+ Protected
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ procedure Calc(AExpr: String; Expected: Double = NaN);
+ Published
+ Procedure TestCreateInteger;
+ Procedure TestCreateFloat;
+ Procedure TestCreateDateTime;
+ Procedure TestCreateString;
+ Procedure TestCreateBoolean;
+ Procedure TestDestroy;
+ Procedure TestAsString;
+ Procedure TestCalc;
+ end;
+
{ TTestDivideNode }
TTestDivideNode = Class(TTestBaseParser)
@@ -701,6 +724,12 @@ type
TTestParserVariables = Class(TTestExpressionParser)
private
FAsWrongType : TResultType;
+ FEventName: String;
+ FBoolValue : Boolean;
+ FTest33 : TFPExprIdentifierDef;
+ procedure DoGetBooleanVar(var Res: TFPExpressionResult; ConstRef AName: ShortString);
+ procedure DoGetBooleanVarWrong(var Res: TFPExpressionResult; ConstRef AName: ShortString);
+ procedure DoTestVariable33;
procedure TestAccess(Skip: TResultType);
Protected
procedure AddVariabletwice;
@@ -741,6 +770,10 @@ type
procedure TestVariable28;
procedure TestVariable29;
procedure TestVariable30;
+ procedure TestVariable31;
+ procedure TestVariable32;
+ procedure TestVariable33;
+ procedure TestVariable34;
end;
{ TTestParserFunctions }
@@ -782,6 +815,45 @@ type
procedure TestFunction29;
end;
+ { TAggregateNode }
+
+ TAggregateNode = Class(TFPExprNode)
+ Public
+ InitCount : Integer;
+ UpdateCount : Integer;
+ Class Function IsAggregate: Boolean; override;
+ Function NodeType: TResultType; override;
+ Procedure InitAggregate; override;
+ Procedure UpdateAggregate; override;
+ procedure GetNodeValue(var Result: TFPExpressionResult); override;
+ end;
+
+ { TTestParserAggregate }
+
+ TTestParserAggregate = Class(TTestExpressionParser)
+ private
+ FVarValue : Integer;
+ FLeft : TAggregateNode;
+ FRight : TAggregateNode;
+ FFunction : TFPExprIdentifierDef;
+ FFunction2 : TFPExprIdentifierDef;
+ Protected
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ public
+ procedure GetVar(var Result: TFPExpressionResult; ConstRef AName: ShortString);
+ Published
+ Procedure TestIsAggregate;
+ Procedure TestHasAggregate;
+ Procedure TestBinaryAggregate;
+ Procedure TestUnaryAggregate;
+ Procedure TestCountAggregate;
+ Procedure TestSumAggregate;
+ Procedure TestSumAggregate2;
+ Procedure TestAvgAggregate;
+ Procedure TestAvgAggregate2;
+ Procedure TestAvgAggregate3;
+ end;
{ TTestBuiltinsManager }
TTestBuiltinsManager = Class(TTestExpressionParser)
@@ -804,8 +876,11 @@ type
TTestBuiltins = Class(TTestExpressionParser)
private
+ FValue : Integer;
FM : TExprBuiltInManager;
FExpr : String;
+ procedure DoAverage(Var Result : TFPExpressionResult; ConstRef AName : ShortString);
+ procedure DoSeries(var Result: TFPExpressionResult; ConstRef AName: ShortString);
Protected
procedure Setup; override;
procedure Teardown; override;
@@ -817,6 +892,8 @@ type
procedure AssertExpression(Const AExpression : String; Const AResult : TExprFloat);
procedure AssertExpression(Const AExpression : String; Const AResult : Boolean);
procedure AssertDateTimeExpression(Const AExpression : String; Const AResult : TDateTime);
+ procedure AssertAggregateExpression(Const AExpression : String; AResult : Int64; AUpdateCount : integer);
+ procedure AssertAggregateExpression(Const AExpression : String; AResult : TExprFloat; AUpdateCount : integer);
Published
procedure TestRegister;
Procedure TestVariablepi;
@@ -883,12 +960,337 @@ type
Procedure TestFunctionstrtotimedef;
Procedure TestFunctionstrtodatetime;
Procedure TestFunctionstrtodatetimedef;
+ Procedure TestFunctionAggregateSum;
+ Procedure TestFunctionAggregateCount;
+ Procedure TestFunctionAggregateAvg;
+ Procedure TestFunctionAggregateMin;
+ Procedure TestFunctionAggregateMax;
end;
implementation
uses typinfo;
+{ TTestParserAggregate }
+
+procedure TTestParserAggregate.Setup;
+begin
+ inherited Setup;
+ FVarValue:=0;
+ FFunction:=TFPExprIdentifierDef.Create(Nil);
+ FFunction.Name:='Count';
+ FFunction2:=TFPExprIdentifierDef.Create(Nil);
+ FFunction2.Name:='MyVar';
+ FFunction2.ResultType:=rtInteger;
+ FFunction2.IdentifierType:=itVariable;
+ FFunction2.OnGetVariableValue:=@GetVar;
+ FLeft:=TAggregateNode.Create;
+ FRight:=TAggregateNode.Create;
+end;
+
+procedure TTestParserAggregate.TearDown;
+begin
+ FreeAndNil(FFunction);
+ FreeAndNil(FLeft);
+ FreeAndNil(FRight);
+ inherited TearDown;
+end;
+
+procedure TTestParserAggregate.GetVar(var Result: TFPExpressionResult; ConstRef
+ AName: ShortString);
+begin
+ Result.ResultType:=FFunction2.ResultType;
+ Case Result.ResultType of
+ rtInteger : Result.ResInteger:=FVarValue;
+ rtFloat : Result.ResFloat:=FVarValue / 2;
+ end;
+end;
+
+procedure TTestParserAggregate.TestIsAggregate;
+begin
+ AssertEquals('ExprNode',False,TFPExprNode.IsAggregate);
+ AssertEquals('TAggregateExpr',True,TAggregateExpr.IsAggregate);
+ AssertEquals('TAggregateExpr',False,TFPBinaryOperation.IsAggregate);
+end;
+
+procedure TTestParserAggregate.TestHasAggregate;
+
+Var
+ N : TFPExprNode;
+
+begin
+ N:=TFPExprNode.Create;
+ try
+ AssertEquals('ExprNode',False,N.HasAggregate);
+ finally
+ N.Free;
+ end;
+ N:=TAggregateExpr.Create;
+ try
+ AssertEquals('ExprNode',True,N.HasAggregate);
+ finally
+ N.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestBinaryAggregate;
+
+Var
+ B : TFPBinaryOperation;
+
+begin
+ B:=TFPBinaryOperation.Create(Fleft,TFPConstExpression.CreateInteger(1));
+ try
+ FLeft:=Nil;
+ AssertEquals('Binary',True,B.HasAggregate);
+ finally
+ B.Free;
+ end;
+ B:=TFPBinaryOperation.Create(TFPConstExpression.CreateInteger(1),FRight);
+ try
+ FRight:=Nil;
+ AssertEquals('Binary',True,B.HasAggregate);
+ finally
+ B.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestUnaryAggregate;
+Var
+ B : TFPUnaryOperator;
+
+begin
+ B:=TFPUnaryOperator.Create(Fleft);
+ try
+ FLeft:=Nil;
+ AssertEquals('Unary',True,B.HasAggregate);
+ finally
+ B.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestCountAggregate;
+
+Var
+ C : TAggregateCount;
+ I : Integer;
+ R : TFPExpressionResult;
+
+begin
+ FFunction.ResultType:=rtInteger;
+ FFunction.ParameterTypes:='';
+ C:=TAggregateCount.CreateFunction(FFunction,Nil);
+ try
+ C.Check;
+ C.InitAggregate;
+ For I:=1 to 11 do
+ C.UpdateAggregate;
+ C.GetNodeValue(R);
+ AssertEquals('Correct type',rtInteger,R.ResultType);
+ AssertEquals('Correct value',11,R.ResInteger);
+ finally
+ C.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestSumAggregate;
+
+Var
+ C : TAggregateSum;
+ V : TFPExprVariable;
+ I : Integer;
+ R : TFPExpressionResult;
+ A : TExprArgumentArray;
+
+begin
+ FFunction.ResultType:=rtInteger;
+ FFunction.ParameterTypes:='I';
+ FFunction.Name:='SUM';
+ FFunction2.ResultType:=rtInteger;
+ C:=Nil;
+ V:=TFPExprVariable.CreateIdentifier(FFunction2);
+ try
+ SetLength(A,1);
+ A[0]:=V;
+ C:=TAggregateSum.CreateFunction(FFunction,A);
+ C.Check;
+ C.InitAggregate;
+ For I:=1 to 10 do
+ begin
+ FVarValue:=I;
+ C.UpdateAggregate;
+ end;
+ C.GetNodeValue(R);
+ AssertEquals('Correct type',rtInteger,R.ResultType);
+ AssertEquals('Correct value',55,R.ResInteger);
+ finally
+ C.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestSumAggregate2;
+Var
+ C : TAggregateSum;
+ V : TFPExprVariable;
+ I : Integer;
+ R : TFPExpressionResult;
+ A : TExprArgumentArray;
+
+begin
+ FFunction.ResultType:=rtFloat;
+ FFunction.ParameterTypes:='F';
+ FFunction.Name:='SUM';
+ FFunction2.ResultType:=rtFloat;
+ C:=Nil;
+ V:=TFPExprVariable.CreateIdentifier(FFunction2);
+ try
+ SetLength(A,1);
+ A[0]:=V;
+ C:=TAggregateSum.CreateFunction(FFunction,A);
+ C.Check;
+ C.InitAggregate;
+ For I:=1 to 10 do
+ begin
+ FVarValue:=I;
+ C.UpdateAggregate;
+ end;
+ C.GetNodeValue(R);
+ AssertEquals('Correct type',rtFloat,R.ResultType);
+ AssertEquals('Correct value',55/2,R.ResFloat,0.1);
+ finally
+ C.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestAvgAggregate;
+
+Var
+ C : TAggregateAvg;
+ V : TFPExprVariable;
+ I : Integer;
+ R : TFPExpressionResult;
+ A : TExprArgumentArray;
+
+begin
+ FFunction.ResultType:=rtInteger;
+ FFunction.ParameterTypes:='F';
+ FFunction.Name:='AVG';
+ FFunction2.ResultType:=rtInteger;
+ C:=Nil;
+ V:=TFPExprVariable.CreateIdentifier(FFunction2);
+ try
+ SetLength(A,1);
+ A[0]:=V;
+ C:=TAggregateAvg.CreateFunction(FFunction,A);
+ C.Check;
+ C.InitAggregate;
+ For I:=1 to 10 do
+ begin
+ FVarValue:=I;
+ C.UpdateAggregate;
+ end;
+ C.GetNodeValue(R);
+ AssertEquals('Correct type',rtFloat,R.ResultType);
+ AssertEquals('Correct value',5.5,R.ResFloat,0.1);
+ finally
+ C.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestAvgAggregate2;
+
+Var
+ C : TAggregateAvg;
+ V : TFPExprVariable;
+ I : Integer;
+ R : TFPExpressionResult;
+ A : TExprArgumentArray;
+
+begin
+ FFunction.ResultType:=rtInteger;
+ FFunction.ParameterTypes:='F';
+ FFunction.Name:='AVG';
+ FFunction2.ResultType:=rtFloat;
+ C:=Nil;
+ V:=TFPExprVariable.CreateIdentifier(FFunction2);
+ try
+ SetLength(A,1);
+ A[0]:=V;
+ C:=TAggregateAvg.CreateFunction(FFunction,A);
+ C.Check;
+ C.InitAggregate;
+ For I:=1 to 10 do
+ begin
+ FVarValue:=I;
+ C.UpdateAggregate;
+ end;
+ C.GetNodeValue(R);
+ AssertEquals('Correct type',rtFloat,R.ResultType);
+ AssertEquals('Correct value',5.5/2,R.ResFloat,0.1);
+ finally
+ C.Free;
+ end;
+end;
+
+procedure TTestParserAggregate.TestAvgAggregate3;
+Var
+ C : TAggregateAvg;
+ V : TFPExprVariable;
+ I : Integer;
+ R : TFPExpressionResult;
+ A : TExprArgumentArray;
+
+begin
+ FFunction.ResultType:=rtInteger;
+ FFunction.ParameterTypes:='F';
+ FFunction.Name:='AVG';
+ FFunction2.ResultType:=rtFloat;
+ C:=Nil;
+ V:=TFPExprVariable.CreateIdentifier(FFunction2);
+ try
+ SetLength(A,1);
+ A[0]:=V;
+ C:=TAggregateAvg.CreateFunction(FFunction,A);
+ C.Check;
+ C.InitAggregate;
+ C.GetNodeValue(R);
+ AssertEquals('Correct type',rtFloat,R.ResultType);
+ AssertEquals('Correct value',0.0,R.ResFloat,0.1);
+ finally
+ C.Free;
+ end;
+end;
+
+{ TAggregateNode }
+
+class function TAggregateNode.IsAggregate: Boolean;
+begin
+ Result:=True
+end;
+
+function TAggregateNode.NodeType: TResultType;
+begin
+ Result:=rtInteger;
+end;
+
+procedure TAggregateNode.InitAggregate;
+begin
+ inherited InitAggregate;
+ inc(InitCount)
+end;
+
+procedure TAggregateNode.UpdateAggregate;
+begin
+ inherited UpdateAggregate;
+ inc(UpdateCount);
+end;
+
+procedure TAggregateNode.GetNodeValue(var Result: TFPExpressionResult);
+begin
+ Result.ResultType:=rtInteger;
+ Result.ResInteger:=updateCount;
+end;
+
procedure TTestExpressionScanner.TestCreate;
begin
AssertEquals('Empty source','',FP.Source);
@@ -921,7 +1323,7 @@ Const
= ('+','-','<','>','=','/',
'*','(',')','<=','>=',
'<>','1','''abc''','abc',',','and',
- 'or','xor','true','false','not','if','case','');
+ 'or','xor','true','false','not','if','case','^','');
var
t : TTokenType;
@@ -941,28 +1343,27 @@ procedure TTestExpressionScanner.DoInvalidNumber(AString : String);
begin
FInvalidString:=AString;
- AssertException('Invalid number "'+AString+'"',EExprScanner,@TestInvalidNumber);
+ AssertException('Invalid number "'+AString+'" ',EExprScanner,@TestInvalidNumber);
end;
procedure TTestExpressionScanner.TestNumber;
begin
- TestString('123',ttNumber);
+ {TestString('123',ttNumber);
TestString('123.4',ttNumber);
TestString('123.E4',ttNumber);
TestString('1.E4',ttNumber);
TestString('1e-2',ttNumber);
DoInvalidNumber('1..1');
+}
DoInvalidNumber('1.E--1');
- DoInvalidNumber('.E-1');
+// DoInvalidNumber('.E-1');
end;
procedure TTestExpressionScanner.TestInvalidCharacter;
begin
DoInvalidNumber('~');
- DoInvalidNumber('^');
DoInvalidNumber('#');
DoInvalidNumber('$');
- DoInvalidNumber('^');
end;
procedure TTestExpressionScanner.TestUnterminatedString;
@@ -977,6 +1378,27 @@ begin
TestString('''s it''''''',ttString);
end;
+procedure TTestExpressionScanner.TestIdentifier(Const ASource,ATokenName : string);
+
+begin
+ FP.Source:=ASource;
+ AssertEquals('Token type',ttIdentifier,FP.GetToken);
+ AssertEquals('Token name',ATokenName,FP.Token);
+end;
+
+procedure TTestExpressionScanner.TestIdentifiers;
+begin
+ TestIdentifier('a','a');
+ TestIdentifier(' a','a');
+ TestIdentifier('a ','a');
+ TestIdentifier('a^b','a');
+ TestIdentifier('a-b','a');
+ TestIdentifier('a.b','a.b');
+ TestIdentifier('"a b"','a b');
+ TestIdentifier('c."a b"','c.a b');
+ TestIdentifier('c."ab"','c.ab');
+end;
+
procedure TTestExpressionScanner.SetUp;
begin
FP:=TFPExpressionScanner.Create;
@@ -1118,15 +1540,16 @@ end;
procedure TTestConstExprNode.TestCreateFloat;
Var
- S : String;
+ F : Double;
+ C : Integer;
begin
FN:=TFPConstExpression.CreateFloat(2.34);
AssertEquals('Correct type',rtFloat,FN.NodeType);
AssertEquals('Correct result',2.34,FN.ConstValue.ResFloat);
AssertEquals('Correct result',2.34,FN.NodeValue.ResFloat);
- Str(TExprFLoat(2.34),S);
- AssertEquals('AsString ok',S,FN.AsString);
+ Val(FN.AsString,F,C);
+ AssertEquals('AsString ok',2.34,F,0.001);
end;
procedure TTestConstExprNode.TestCreateBoolean;
@@ -2026,6 +2449,130 @@ begin
end;
+{ TTestPowerNode }
+
+procedure TTestPowerNode.TearDown;
+begin
+ FreeAndNil(FN);
+ inherited TearDown;
+end;
+
+procedure TTestPowerNode.Setup;
+begin
+ inherited ;
+ FE:=TFpExpressionParser.Create(Nil);
+ FE.Builtins := [bcMath];
+end;
+
+procedure TTestPowerNode.Calc(AExpr: String; Expected: Double =NaN);
+const
+ EPS = 1e-9;
+var
+ res: TFpExpressionResult;
+ x: Double;
+begin
+ FE.Expression := AExpr;
+ res:=FE.Evaluate;
+ x:= ArgToFloat(res);
+ if not IsNaN(Expected) then
+ AssertEquals('Expression '+AExpr+' result',Expected,X,Eps);
+end;
+
+procedure TTestPowerNode.TestCalc;
+
+begin
+ Calc('2^2', Power(2, 2));
+ Calc('2^-2', Power(2, -2));
+ Calc('2^(-2)', Power(2, -2));
+ Calc('sqrt(3)^2', Power(sqrt(3), 2));
+ Calc('-sqrt(3)^2', -Power(sqrt(3), 2));
+ Calc('-2^2', -Power(2, 2));
+ Calc('(-2.0)^2', Power(-2.0, 2));
+ Calc('(-2.0)^-2', Power(-2.0, -2));
+ // Odd integer exponent
+ Calc('2^3', Power(2, 3));
+ Calc('-2^3', -Power(2, 3));
+ Calc('-2^-3', -Power(2, -3));
+ Calc('-2^(-3)', -Power(2, -3));
+ Calc('(-2.0)^3', Power(-2.0, 3));
+ Calc('(-2.0)^-3', Power(-2.0, -3));
+ // Fractional exponent
+ Calc('10^2.5', power(10, 2.5));
+ Calc('10^-2.5', Power(10, -2.5));
+ // Expressions
+ Calc('(1+1)^3', Power(1+1, 3));
+ Calc('1+2^3', 1 + Power(2, 3));
+ calc('2^3+1', Power(2, 3) + 1);
+ Calc('2^3*2', Power(2, 3) * 2);
+ Calc('2^3*-2', Power(2, 3) * -2);
+ Calc('2^(1+1)', Power(2, 1+1));
+ Calc('2^-(1+1)', Power(2, -(1+1)));
+ WriteLn;
+ // Special cases
+ Calc('0^0', power(0, 0));
+ calc('0^1', power(0, 1));
+ Calc('0^2.5', Power(0, 2.5));
+ calc('2.5^0', power(2.5, 0));
+ calc('2^3^4', 2417851639229258349412352); // according to Wolfram Alpha, 2^(3^4)
+
+ // These expressions should throw expections
+
+ //Calc('(-10)^2.5', NaN); // base must be positive in case of fractional exponent
+ //Calc('0^-2', NaN); // is 1/0^2 = 1/0
+end;
+
+procedure TTestPowerNode.TestCreateInteger;
+begin
+ FN:=TFPPowerOperation.Create(CreateIntNode(4),CreateIntNode(2));
+ AssertEquals('Power has correct type',rtfloat,FN.NodeType);
+ AssertEquals('Power has correct result',16.0,FN.NodeValue.ResFloat);
+end;
+
+procedure TTestPowerNode.TestCreateFloat;
+begin
+ FN:=TFPPowerOperation.Create(CreateFloatNode(2.0),CreateFloatNode(3.0));
+ AssertEquals('Power has correct type',rtFloat,FN.NodeType);
+ AssertEquals('Power has correct result',8.0,FN.NodeValue.ResFloat);
+end;
+
+procedure TTestPowerNode.TestCreateDateTime;
+
+Var
+ D,T : TDateTime;
+
+begin
+ D:=Date;
+ T:=Time;
+ FN:=TFPPowerOperation.Create(CreateDateTimeNode(D+T),CreateDateTimeNode(T));
+ AssertNodeNotOK('No datetime Power',FN);
+end;
+
+procedure TTestPowerNode.TestCreateString;
+begin
+ FN:=TFPPowerOperation.Create(CreateStringNode('alo'),CreateStringNode('ha'));
+ AssertNodeNotOK('No string Power',FN);
+end;
+
+procedure TTestPowerNode.TestCreateBoolean;
+begin
+ FN:=TFPPowerOperation.Create(CreateBoolNode(True),CreateBoolNode(False));
+ AssertNodeNotOK('No boolean Power',FN);
+end;
+
+procedure TTestPowerNode.TestDestroy;
+begin
+ FN:=TFPPowerOperation.Create(TMyDestroyNode.CreateTest(Self),TMyDestroyNode.CreateTest(Self));
+ FreeAndNil(FN);
+ AssertEquals('Destroy called for left and right nodes',2,self.FDestroyCalled)
+end;
+
+procedure TTestPowerNode.TestAsString;
+begin
+ FN:=TFPPowerOperation.Create(CreateIntNode(1),CreateIntNode(2));
+ AssertEquals('Asstring works ok','1^2',FN.AsString);
+end;
+
+
{ TTestDivideNode }
procedure TTestDivideNode.TearDown;
@@ -4196,6 +4743,114 @@ begin
AssertEquals('Correct value',False,I.AsBoolean);
end;
+procedure TTestParserVariables.DoGetBooleanVar(var Res: TFPExpressionResult;
+ ConstRef AName: ShortString);
+
+begin
+ FEventName:=AName;
+ Res.ResBoolean:=FBoolValue;
+end;
+
+procedure TTestParserVariables.TestVariable31;
+
+Var
+ I : TFPExprIdentifierDef;
+
+begin
+ I:=FP.Identifiers.AddVariable('a',rtBoolean,@DoGetBooleanVar);
+ AssertEquals('Correct name','a',i.Name);
+ AssertEquals('Correct type',Ord(rtBoolean),Ord(i.ResultType));
+ AssertSame(TMethod(I.OnGetVariableValue).Code,TMethod(@DoGetBooleanVar).Code);
+ FBoolValue:=True;
+ FEventName:='';
+ AssertEquals('Correct value 1',True,I.AsBoolean);
+ AssertEquals('Correct name passed','a',FEventName);
+ FBoolValue:=False;
+ FEventName:='';
+ AssertEquals('Correct value 2',False,I.AsBoolean);
+ AssertEquals('Correct name passed','a',FEventName);
+end;
+
+Var
+ FVarCallBackName:String;
+ FVarBoolValue : Boolean;
+
+procedure DoGetBooleanVar2(var Res: TFPExpressionResult; ConstRef AName: ShortString);
+
+begin
+ FVarCallBackName:=AName;
+ Res.ResBoolean:=FVarBoolValue;
+end;
+
+procedure TTestParserVariables.DoGetBooleanVarWrong(var Res: TFPExpressionResult; ConstRef AName: ShortString);
+
+begin
+ FEventName:=AName;
+ Res.ResultType:=rtInteger;
+ Res.ResInteger:=33;
+end;
+
+procedure TTestParserVariables.TestVariable32;
+Var
+ I : TFPExprIdentifierDef;
+
+begin
+ I:=FP.Identifiers.AddVariable('a',rtBoolean,@DoGetBooleanVar2);
+ AssertEquals('Correct name','a',i.Name);
+ AssertEquals('Correct type',Ord(rtBoolean),Ord(i.ResultType));
+ AssertSame(I.OnGetVariableValueCallBack,@DoGetBooleanVar2);
+ FVarBoolValue:=True;
+ FVarCallBackName:='';
+ AssertEquals('Correct value 1',True,I.AsBoolean);
+ AssertEquals('Correct name passed','a',FVarCallBackName);
+ FVarBoolValue:=False;
+ FVarCallBackName:='';
+ AssertEquals('Correct value 2',False,I.AsBoolean);
+ AssertEquals('Correct name passed','a',FVarCallBackName);
+end;
+
+procedure TTestParserVariables.DoTestVariable33;
+
+Var
+ B : Boolean;
+
+begin
+ B:=FTest33.AsBoolean;
+end;
+
+procedure TTestParserVariables.TestVariable33;
+
+Var
+ I : TFPExprIdentifierDef;
+
+begin
+ I:=FP.Identifiers.AddVariable('a',rtBoolean,@DoGetBooleanVarWrong);
+ FTest33:=I;
+ AssertException('Changing type results in exception',EExprParser,@DoTestVariable33);
+ AssertEquals('Type is unchanged',Ord(rtBoolean),Ord(i.ResultType));
+end;
+
+
+procedure DoGetBooleanVar2Wrong(var Res: TFPExpressionResult; ConstRef AName: ShortString);
+
+begin
+ FVarCallBackName:=AName;
+ Res.ResultType:=rtInteger;
+ Res.ResInteger:=34;
+end;
+
+procedure TTestParserVariables.TestVariable34;
+
+Var
+ I : TFPExprIdentifierDef;
+
+begin
+ I:=FP.Identifiers.AddVariable('a',rtBoolean,@DoGetBooleanVar2Wrong);
+ FTest33:=I;
+ AssertException('Changing type results in exception',EExprParser,@DoTestVariable33);
+ AssertEquals('Type is unchanged',Ord(rtBoolean),Ord(i.ResultType));
+end;
+
Procedure EchoDate(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
@@ -4937,6 +5592,7 @@ procedure TTestBuiltins.Setup;
begin
inherited Setup;
FM:=TExprBuiltInManager.Create(Nil);
+ FValue:=0;
end;
procedure TTestBuiltins.Teardown;
@@ -4945,7 +5601,7 @@ begin
inherited Teardown;
end;
-procedure TTestBuiltins.SetExpression(Const AExpression : String);
+procedure TTestBuiltins.SetExpression(const AExpression: String);
Var
Msg : String;
@@ -5030,11 +5686,41 @@ begin
AssertDatetimeResult(AResult);
end;
+procedure TTestBuiltins.AssertAggregateExpression(const AExpression: String;
+ AResult: Int64; AUpdateCount: integer);
+begin
+ FP.BuiltIns:=AllBuiltIns;
+ SetExpression(AExpression);
+ AssertEquals('Has aggregate',True,FP.ExprNode.HasAggregate);
+ FP.InitAggregate;
+ While AUpdateCount>0 do
+ begin
+ FP.UpdateAggregate;
+ Dec(AUpdateCount);
+ end;
+ AssertResult(AResult);
+end;
+
+procedure TTestBuiltins.AssertAggregateExpression(const AExpression: String;
+ AResult: TExprFloat; AUpdateCount: integer);
+begin
+ FP.BuiltIns:=AllBuiltIns;
+ SetExpression(AExpression);
+ AssertEquals('Has aggregate',True,FP.ExprNode.HasAggregate);
+ FP.InitAggregate;
+ While AUpdateCount>0 do
+ begin
+ FP.UpdateAggregate;
+ Dec(AUpdateCount);
+ end;
+ AssertResult(AResult);
+end;
+
procedure TTestBuiltins.TestRegister;
begin
RegisterStdBuiltins(FM);
- AssertEquals('Correct number of identifiers',64,FM.IdentifierCount);
+ AssertEquals('Correct number of identifiers',69,FM.IdentifierCount);
Assertvariable('pi',rtFloat);
AssertFunction('cos','F','F',bcMath);
AssertFunction('sin','F','F',bcMath);
@@ -5099,6 +5785,11 @@ begin
AssertFunction('strtotimedef','D','SD',bcConversion);
AssertFunction('strtodatetime','D','S',bcConversion);
AssertFunction('strtodatetimedef','D','SD',bcConversion);
+ AssertFunction('sum','F','F',bcAggregate);
+ AssertFunction('count','I','',bcAggregate);
+ AssertFunction('avg','F','F',bcAggregate);
+ AssertFunction('min','F','F',bcAggregate);
+ AssertFunction('max','F','F',bcAggregate);
end;
procedure TTestBuiltins.TestVariablepi;
@@ -5549,6 +6240,59 @@ begin
AssertExpression('StrToDateTimeDef('''+S+''',S)',T);
end;
+procedure TTestBuiltins.TestFunctionAggregateSum;
+begin
+ FP.Identifiers.AddIntegerVariable('S',2);
+ AssertAggregateExpression('sum(S)',10.0,5);
+end;
+
+procedure TTestBuiltins.TestFunctionAggregateCount;
+begin
+ AssertAggregateExpression('count',5,5);
+end;
+
+
+procedure TTestBuiltins.DoAverage(var Result: TFPExpressionResult; ConstRef
+ AName: ShortString);
+
+begin
+ Inc(FValue);
+ Result.ResInteger:=FValue;
+ Result.ResultType:=rtInteger;
+end;
+
+procedure TTestBuiltins.DoSeries(var Result: TFPExpressionResult; ConstRef
+ AName: ShortString);
+
+Const
+ Values : Array[1..10] of double =
+ (1.3,1.8,1.1,9.9,1.4,2.4,5.8,6.5,7.8,8.1);
+
+
+begin
+ Inc(FValue);
+ Result.ResFloat:=Values[FValue];
+ Result.ResultType:=rtFloat;
+end;
+
+procedure TTestBuiltins.TestFunctionAggregateAvg;
+begin
+ FP.Identifiers.AddVariable('S',rtInteger,@DoAverage);
+ AssertAggregateExpression('avg(S)',5.5,10);
+end;
+
+procedure TTestBuiltins.TestFunctionAggregateMin;
+begin
+ FP.Identifiers.AddVariable('S',rtFloat,@DoSeries);
+ AssertAggregateExpression('Min(S)',1.1,10);
+end;
+
+procedure TTestBuiltins.TestFunctionAggregateMax;
+begin
+ FP.Identifiers.AddVariable('S',rtFloat,@DoSeries);
+ AssertAggregateExpression('Max(S)',9.9,10);
+end;
+
{ TTestNotNode }
procedure TTestNotNode.TearDown;
@@ -5989,12 +6733,13 @@ initialization
TTestLessThanNode,TTestLessThanEqualNode,
TTestLargerThanNode,TTestLargerThanEqualNode,
TTestAddNode,TTestSubtractNode,
- TTestMultiplyNode,TTestDivideNode,
+ TTestMultiplyNode,TTestDivideNode,TTestPowerNode,
TTestIntToFloatNode,TTestIntToDateTimeNode,
TTestFloatToDateTimeNode,
TTestParserExpressions, TTestParserBooleanOperations,
TTestParserOperands, TTestParserTypeMatch,
TTestParserVariables,TTestParserFunctions,
+ TTestParserAggregate,
TTestBuiltinsManager,TTestBuiltins]);
end.