* Aggregate Functions & ExtractNode

git-svn-id: trunk@34377 -
This commit is contained in:
michael 2016-08-26 07:17:03 +00:00
parent be9e097841
commit 2ef1a423fe
2 changed files with 856 additions and 88 deletions

View File

@ -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;
@ -322,6 +337,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,7 +419,7 @@ 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);
@ -411,6 +429,7 @@ Type
TFPExprIdentifierDef = Class(TCollectionItem)
private
FNodeType: TFPExprFunctionClass;
FOnGetVarValue: TFPExprVariableEvent;
FOnGetVarValueCB: TFPExprVariableCallBack;
FStringValue : String;
@ -459,10 +478,11 @@ Type
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 }
@ -500,6 +520,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;
@ -541,6 +562,42 @@ 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;
{ 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)
@ -610,8 +667,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;
@ -647,22 +708,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
@ -737,13 +799,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));
@ -765,7 +827,7 @@ end;
Var
BuiltIns : TExprBuiltInManager;
Function BuiltinIdentifiers : TExprBuiltInManager;
function BuiltinIdentifiers: TExprBuiltInManager;
begin
If (BuiltIns=Nil) then
@ -779,6 +841,85 @@ begin
FreeAndNil(Builtins);
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
---------------------------------------------------------------------}
@ -1014,7 +1155,7 @@ end;
TFPExpressionParser
---------------------------------------------------------------------}
Function TFPExpressionParser.TokenType : TTokenType;
function TFPExpressionParser.TokenType: TTokenType;
begin
Result:=FScanner.TokenType;
@ -1085,13 +1226,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
@ -1113,6 +1254,17 @@ 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);
@ -1224,7 +1376,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);
@ -1238,7 +1390,7 @@ begin
FDirty:=True;
end;
Function TFPExpressionParser.Level1 : TFPExprNode;
function TFPExpressionParser.Level1: TFPExprNode;
var
tt: TTokenType;
@ -1458,7 +1610,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;
@ -1510,6 +1662,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;
@ -1557,7 +1710,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;
{ ---------------------------------------------------------------------
@ -1717,6 +1887,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
---------------------------------------------------------------------}
@ -2042,6 +2224,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
@ -2075,6 +2265,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
@ -2096,6 +2313,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
@ -2249,6 +2488,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);
@ -2354,6 +2613,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;
@ -2432,6 +2712,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;
@ -2886,7 +3205,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;
@ -3406,80 +3725,101 @@ 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);
end;
end;
end;

View File

@ -792,6 +792,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)
@ -814,8 +853,10 @@ type
TTestBuiltins = Class(TTestExpressionParser)
private
FValue : Integer;
FM : TExprBuiltInManager;
FExpr : String;
procedure DoAverage(Var Result : TFPExpressionResult; ConstRef AName : ShortString);
Protected
procedure Setup; override;
procedure Teardown; override;
@ -827,6 +868,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;
@ -893,12 +936,335 @@ type
Procedure TestFunctionstrtotimedef;
Procedure TestFunctionstrtodatetime;
Procedure TestFunctionstrtodatetimedef;
Procedure TestFunctionAggregateSum;
Procedure TestFunctionAggregateCount;
Procedure TestFunctionAggregateAvg;
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);
@ -5055,6 +5421,7 @@ procedure TTestBuiltins.Setup;
begin
inherited Setup;
FM:=TExprBuiltInManager.Create(Nil);
FValue:=0;
end;
procedure TTestBuiltins.Teardown;
@ -5063,7 +5430,7 @@ begin
inherited Teardown;
end;
procedure TTestBuiltins.SetExpression(Const AExpression : String);
procedure TTestBuiltins.SetExpression(const AExpression: String);
Var
Msg : String;
@ -5148,11 +5515,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',67,FM.IdentifierCount);
Assertvariable('pi',rtFloat);
AssertFunction('cos','F','F',bcMath);
AssertFunction('sin','F','F',bcMath);
@ -5217,6 +5614,9 @@ 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);
end;
procedure TTestBuiltins.TestVariablepi;
@ -5667,6 +6067,33 @@ 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.TestFunctionAggregateAvg;
begin
FP.Identifiers.AddVariable('S',rtInteger,@DoAverage);
AssertAggregateExpression('avg(S)',5.5,10);
end;
{ TTestNotNode }
procedure TTestNotNode.TearDown;
@ -6113,6 +6540,7 @@ initialization
TTestParserExpressions, TTestParserBooleanOperations,
TTestParserOperands, TTestParserTypeMatch,
TTestParserVariables,TTestParserFunctions,
TTestParserAggregate,
TTestBuiltinsManager,TTestBuiltins]);
end.