浏览代码

* Aggregate Functions & ExtractNode

git-svn-id: trunk@34377 -
michael 9 年之前
父节点
当前提交
2ef1a423fe
共有 2 个文件被更改,包括 856 次插入88 次删除
  1. 426 86
      packages/fcl-base/src/fpexprpars.pp
  2. 430 2
      packages/fcl-base/tests/testexprpars.pp

+ 426 - 86
packages/fcl-base/src/fpexprpars.pp

@@ -44,6 +44,8 @@ Type
 
 
   TFPExpressionParser = Class;
   TFPExpressionParser = Class;
   TExprBuiltInManager = Class;
   TExprBuiltInManager = Class;
+  TFPExprFunction = Class;
+  TFPExprFunctionClass = Class of TFPExprFunction;
 
 
   { TFPExpressionScanner }
   { TFPExpressionScanner }
 
 
@@ -106,6 +108,10 @@ Type
     Procedure GetNodeValue(var Result : TFPExpressionResult); virtual; abstract;
     Procedure GetNodeValue(var Result : TFPExpressionResult); virtual; abstract;
   Public
   Public
     Procedure Check; virtual; abstract;
     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 NodeType : TResultType; virtual; abstract;
     Function NodeValue : TFPExpressionResult;
     Function NodeValue : TFPExpressionResult;
     Function AsString : string; virtual; abstract;
     Function AsString : string; virtual; abstract;
@@ -123,6 +129,9 @@ Type
   Public
   Public
     Constructor Create(ALeft,ARight : TFPExprNode);
     Constructor Create(ALeft,ARight : TFPExprNode);
     Destructor Destroy; override;
     Destructor Destroy; override;
+    Procedure InitAggregate; override;
+    Procedure UpdateAggregate; override;
+    Function HasAggregate : Boolean; override;
     Procedure Check; override;
     Procedure Check; override;
     Property left : TFPExprNode Read FLeft;
     Property left : TFPExprNode Read FLeft;
     Property Right : TFPExprNode Read FRight;
     Property Right : TFPExprNode Read FRight;
@@ -245,6 +254,9 @@ Type
     Procedure GetNodeValue(var Result : TFPExpressionResult); override;
     Procedure GetNodeValue(var Result : TFPExpressionResult); override;
   Public
   Public
     Procedure Check; override;
     Procedure Check; override;
+    Procedure InitAggregate; override;
+    Procedure UpdateAggregate; override;
+    Function HasAggregate : Boolean; override;
     Function NodeType : TResultType; override;
     Function NodeType : TResultType; override;
     Constructor Create(ACondition,ALeft,ARight : TFPExprNode);
     Constructor Create(ACondition,ALeft,ARight : TFPExprNode);
     Destructor destroy; override;
     Destructor destroy; override;
@@ -262,6 +274,9 @@ Type
     Procedure GetNodeValue(var Result : TFPExpressionResult); override;
     Procedure GetNodeValue(var Result : TFPExpressionResult); override;
   Public
   Public
     Procedure Check; override;
     Procedure Check; override;
+    Procedure InitAggregate; override;
+    Procedure UpdateAggregate; override;
+    function HasAggregate: Boolean; override;
     Function NodeType : TResultType; override;
     Function NodeType : TResultType; override;
     Constructor Create(Args : TExprArgumentArray);
     Constructor Create(Args : TExprArgumentArray);
     Destructor destroy; override;
     Destructor destroy; override;
@@ -322,6 +337,9 @@ Type
   Public
   Public
     Constructor Create(AOperand : TFPExprNode);
     Constructor Create(AOperand : TFPExprNode);
     Destructor Destroy; override;
     Destructor Destroy; override;
+    Procedure InitAggregate; override;
+    Procedure UpdateAggregate; override;
+    Function HasAggregate : Boolean; override;
     Procedure Check; override;
     Procedure Check; override;
     Property Operand : TFPExprNode Read FOperand;
     Property Operand : TFPExprNode Read FOperand;
   end;
   end;
@@ -401,7 +419,7 @@ Type
   end;
   end;
 
 
 
 
-  TIdentifierType = (itVariable,itFunctionCallBack,itFunctionHandler);
+  TIdentifierType = (itVariable,itFunctionCallBack,itFunctionHandler,itFunctionNode);
   TFPExprFunctionCallBack = Procedure (Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
   TFPExprFunctionCallBack = Procedure (Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
   TFPExprFunctionEvent = Procedure (Var Result : TFPExpressionResult; Const Args : TExprParameterArray) of object;
   TFPExprFunctionEvent = Procedure (Var Result : TFPExpressionResult; Const Args : TExprParameterArray) of object;
   TFPExprVariableCallBack = Procedure (Var Result : TFPExpressionResult; ConstRef AName : ShortString);
   TFPExprVariableCallBack = Procedure (Var Result : TFPExpressionResult; ConstRef AName : ShortString);
@@ -411,6 +429,7 @@ Type
 
 
   TFPExprIdentifierDef = Class(TCollectionItem)
   TFPExprIdentifierDef = Class(TCollectionItem)
   private
   private
+    FNodeType: TFPExprFunctionClass;
     FOnGetVarValue: TFPExprVariableEvent;
     FOnGetVarValue: TFPExprVariableEvent;
     FOnGetVarValueCB: TFPExprVariableCallBack;
     FOnGetVarValueCB: TFPExprVariableCallBack;
     FStringValue : String;
     FStringValue : String;
@@ -459,10 +478,11 @@ Type
     Property ResultType : TResultType Read GetResultType Write SetResultType;
     Property ResultType : TResultType Read GetResultType Write SetResultType;
     Property OnGetFunctionValue : TFPExprFunctionEvent Read FOnGetValue Write FOnGetValue;
     Property OnGetFunctionValue : TFPExprFunctionEvent Read FOnGetValue Write FOnGetValue;
     Property OnGetVariableValue : TFPExprVariableEvent Read FOnGetVarValue Write FOnGetVarValue;
     Property OnGetVariableValue : TFPExprVariableEvent Read FOnGetVarValue Write FOnGetVarValue;
+    Property NodeType : TFPExprFunctionClass Read FNodeType Write FNodeType;
   end;
   end;
 
 
 
 
-  TBuiltInCategory = (bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser);
+  TBuiltInCategory = (bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser,bcAggregate);
   TBuiltInCategories = Set of TBuiltInCategory;
   TBuiltInCategories = Set of TBuiltInCategory;
 
 
   { TFPBuiltInExprIdentifierDef }
   { TFPBuiltInExprIdentifierDef }
@@ -500,6 +520,7 @@ Type
     Function AddDateTimeVariable(Const AName : ShortString; AValue : TDateTime) : TFPExprIdentifierDef;
     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 : 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; 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;
     property Identifiers[AIndex : Integer] : TFPExprIdentifierDef Read GetI Write SetI; Default;
   end;
   end;
 
 
@@ -541,6 +562,42 @@ Type
     Function AsString : String; override;
     Function AsString : String; override;
   end;
   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 }
 
 
   TFPFunctionCallBack = Class(TFPExprFunction)
   TFPFunctionCallBack = Class(TFPExprFunction)
@@ -610,8 +667,12 @@ Type
     Function IdentifierByName(const AName : ShortString) : TFPExprIdentifierDef; virtual;
     Function IdentifierByName(const AName : ShortString) : TFPExprIdentifierDef; virtual;
     Procedure Clear;
     Procedure Clear;
     Procedure EvaluateExpression(Var Result : TFPExpressionResult);
     Procedure EvaluateExpression(Var Result : TFPExpressionResult);
+    function ExtractNode(var N: TFPExprNode): Boolean;
     Function Evaluate : TFPExpressionResult;
     Function Evaluate : TFPExpressionResult;
     Function ResultType : TResultType;
     Function ResultType : TResultType;
+    Function HasAggregate : Boolean;
+    Procedure InitAggregate;
+    Procedure UpdateAggregate;
     Property AsFloat : TExprFloat Read GetAsFloat;
     Property AsFloat : TExprFloat Read GetAsFloat;
     Property AsInteger : Int64 Read GetAsInteger;
     Property AsInteger : Int64 Read GetAsInteger;
     Property AsString : String Read GetAsString;
     Property AsString : String Read GetAsString;
@@ -647,22 +708,23 @@ Type
     Function AddDateTimeVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : TDateTime) : TFPBuiltInExprIdentifierDef;
     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 : 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; 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 IdentifierCount : Integer Read GetCount;
     Property Identifiers[AIndex : Integer] :TFPBuiltInExprIdentifierDef Read GetI;
     Property Identifiers[AIndex : Integer] :TFPBuiltInExprIdentifierDef Read GetI;
   end;
   end;
 
 
   EExprParser = Class(Exception);
   EExprParser = Class(Exception);
 
 
+Const
+  AllBuiltIns = [bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser,bcAggregate];
 
 
 Function TokenName (AToken : TTokenType) : String;
 Function TokenName (AToken : TTokenType) : String;
 Function ResultTypeName (AResult : TResultType) : String;
 Function ResultTypeName (AResult : TResultType) : String;
 Function CharToResultType(C : Char) : TResultType;
 Function CharToResultType(C : Char) : TResultType;
 Function BuiltinIdentifiers : TExprBuiltInManager;
 Function BuiltinIdentifiers : TExprBuiltInManager;
-Procedure RegisterStdBuiltins(AManager : TExprBuiltInManager);
+Procedure RegisterStdBuiltins(AManager : TExprBuiltInManager; Categories : TBuiltInCategories = AllBuiltIns);
 function ArgToFloat(Arg: TFPExpressionResult): TExprFloat;
 function ArgToFloat(Arg: TFPExpressionResult): TExprFloat;
 
 
-Const
-  AllBuiltIns = [bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser];
 
 
 
 
 implementation
 implementation
@@ -737,13 +799,13 @@ begin
   Raise EExprParser.CreateFmt(Fmt,Args);
   Raise EExprParser.CreateFmt(Fmt,Args);
 end;
 end;
 
 
-Function TokenName (AToken : TTokenType) : String;
+function TokenName(AToken: TTokenType): String;
 
 
 begin
 begin
   Result:=GetEnumName(TypeInfo(TTokenType),Ord(AToken));
   Result:=GetEnumName(TypeInfo(TTokenType),Ord(AToken));
 end;
 end;
 
 
-Function ResultTypeName (AResult : TResultType) : String;
+function ResultTypeName(AResult: TResultType): String;
 
 
 begin
 begin
   Result:=GetEnumName(TypeInfo(TResultType),Ord(AResult));
   Result:=GetEnumName(TypeInfo(TResultType),Ord(AResult));
@@ -765,7 +827,7 @@ end;
 Var
 Var
   BuiltIns : TExprBuiltInManager;
   BuiltIns : TExprBuiltInManager;
 
 
-Function BuiltinIdentifiers : TExprBuiltInManager;
+function BuiltinIdentifiers: TExprBuiltInManager;
 
 
 begin
 begin
   If (BuiltIns=Nil) then
   If (BuiltIns=Nil) then
@@ -779,6 +841,85 @@ begin
   FreeAndNil(Builtins);
   FreeAndNil(Builtins);
 end;
 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
   TFPExpressionScanner
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
@@ -1014,7 +1155,7 @@ end;
   TFPExpressionParser
   TFPExpressionParser
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
 
 
-Function TFPExpressionParser.TokenType : TTokenType;
+function TFPExpressionParser.TokenType: TTokenType;
 
 
 begin
 begin
   Result:=FScanner.TokenType;
   Result:=FScanner.TokenType;
@@ -1085,13 +1226,13 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-Function TFPExpressionParser.GetToken : TTokenType;
+function TFPExpressionParser.GetToken: TTokenType;
 
 
 begin
 begin
   Result:=FScanner.GetToken;
   Result:=FScanner.GetToken;
 end;
 end;
 
 
-Procedure TFPExpressionParser.CheckEOF;
+procedure TFPExpressionParser.CheckEOF;
 
 
 begin
 begin
   If (TokenType=ttEOF) then
   If (TokenType=ttEOF) then
@@ -1113,6 +1254,17 @@ begin
   FExprNode.GetNodeValue(Result);
   FExprNode.GetNodeValue(Result);
 end;
 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);
 procedure TFPExpressionParser.ParserError(Msg: String);
 begin
 begin
   Raise EExprParser.Create(Msg);
   Raise EExprParser.Create(Msg);
@@ -1224,7 +1376,7 @@ end;
   if the result types differ, they are converted to a common type if possible.
   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
 begin
   Left:=MatchNodes(Left,Right);
   Left:=MatchNodes(Left,Right);
@@ -1238,7 +1390,7 @@ begin
   FDirty:=True;
   FDirty:=True;
 end;
 end;
 
 
-Function TFPExpressionParser.Level1 : TFPExprNode;
+function TFPExpressionParser.Level1: TFPExprNode;
 
 
 var
 var
   tt: TTokenType;
   tt: TTokenType;
@@ -1458,7 +1610,7 @@ begin
       ACount:=3
       ACount:=3
     else if IfC then
     else if IfC then
       ACount:=-4
       ACount:=-4
-    else if (ID.IdentifierType in [itFunctionCallBack,itFunctionHandler]) then
+    else if (ID.IdentifierType in [itFunctionCallBack,itFunctionHandler,itFunctionNode]) then
       ACount:=ID.ArgumentCount
       ACount:=ID.ArgumentCount
     else
     else
       ACount:=0;
       ACount:=0;
@@ -1510,6 +1662,7 @@ begin
         itVariable         : Result:= TFPExprVariable.CreateIdentifier(ID);
         itVariable         : Result:= TFPExprVariable.CreateIdentifier(ID);
         itFunctionCallBack : Result:= TFPFunctionCallback.CreateFunction(ID,Args);
         itFunctionCallBack : Result:= TFPFunctionCallback.CreateFunction(ID,Args);
         itFunctionHandler  : Result:= TFPFunctionEventHandler.CreateFunction(ID,Args);
         itFunctionHandler  : Result:= TFPFunctionEventHandler.CreateFunction(ID,Args);
+        itFunctionNode     : Result:= ID.NodeType.CreateFunction(ID,Args);
       end;
       end;
     end;
     end;
   GetToken;
   GetToken;
@@ -1557,7 +1710,24 @@ function TFPExpressionParser.ResultType: TResultType;
 begin
 begin
   if not Assigned(FExprNode) then
   if not Assigned(FExprNode) then
     ParserError(SErrInExpression);
     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;
 end;
 
 
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
@@ -1717,6 +1887,18 @@ begin
   Result.FOnGetValue:=ACallBack;
   Result.FOnGetValue:=ACallBack;
 end;
 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
   TFPExprIdentifierDef
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
@@ -2042,6 +2224,14 @@ begin
   Result.Category:=ACategory;
   Result.Category:=ACategory;
 end;
 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
   Various Nodes
@@ -2075,6 +2265,33 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 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;
 procedure TFPBinaryOperation.Check;
 begin
 begin
   If Not Assigned(Left) then
   If Not Assigned(Left) then
@@ -2096,6 +2313,28 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 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;
 procedure TFPUnaryOperator.Check;
 begin
 begin
   If Not Assigned(Operand) then
   If Not Assigned(Operand) then
@@ -2249,6 +2488,26 @@ begin
     end;
     end;
 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;
 function TFPExprNode.NodeValue: TFPExpressionResult;
 begin
 begin
   GetNodeValue(Result);
   GetNodeValue(Result);
@@ -2354,6 +2613,27 @@ begin
   CheckSameNodeTypes;
   CheckSameNodeTypes;
 end;
 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;
 function TIfOperation.NodeType: TResultType;
 begin
 begin
   Result:=Left.NodeType;
   Result:=Left.NodeType;
@@ -2432,6 +2712,45 @@ begin
     end;
     end;
 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;
 function TCaseOperation.NodeType: TResultType;
 begin
 begin
   Result:=FArgs[1].NodeType;
   Result:=FArgs[1].NodeType;
@@ -2886,7 +3205,7 @@ begin
       // Automatically convert integers to floats in functions that return
       // Automatically convert integers to floats in functions that return
       // a float
       // a float
       if (rta = rtInteger) and (rtp = rtFloat) then begin
       if (rta = rtInteger) and (rtp = rtFloat) then begin
-        FArgumentNodes[i] := TIntToFloatNode(FArgumentNodes[i]);
+        FArgumentNodes[i] := TIntToFloatNode.Create(FArgumentNodes[i]);
         exit;
         exit;
       end;
       end;
 
 
@@ -3406,80 +3725,101 @@ begin
     Result.resDateTime:=Args[2].resDateTime
     Result.resDateTime:=Args[2].resDateTime
 end;
 end;
 
 
-Procedure RegisterStdBuiltins(AManager : TExprBuiltInManager);
+procedure RegisterStdBuiltins(AManager: TExprBuiltInManager;  Categories: TBuiltInCategories = AllBuiltIns);
 
 
 begin
 begin
   With AManager do
   With AManager do
     begin
     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;
 end;
 end;
 
 

+ 430 - 2
packages/fcl-base/tests/testexprpars.pp

@@ -792,6 +792,45 @@ type
     procedure TestFunction29;
     procedure TestFunction29;
   end;
   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 }
 
 
   TTestBuiltinsManager = Class(TTestExpressionParser)
   TTestBuiltinsManager = Class(TTestExpressionParser)
@@ -814,8 +853,10 @@ type
 
 
   TTestBuiltins = Class(TTestExpressionParser)
   TTestBuiltins = Class(TTestExpressionParser)
   private
   private
+    FValue : Integer;
     FM : TExprBuiltInManager;
     FM : TExprBuiltInManager;
     FExpr : String;
     FExpr : String;
+    procedure DoAverage(Var Result : TFPExpressionResult; ConstRef AName : ShortString);
   Protected
   Protected
     procedure Setup; override;
     procedure Setup; override;
     procedure Teardown; override;
     procedure Teardown; override;
@@ -827,6 +868,8 @@ type
     procedure AssertExpression(Const AExpression : String; Const AResult : TExprFloat);
     procedure AssertExpression(Const AExpression : String; Const AResult : TExprFloat);
     procedure AssertExpression(Const AExpression : String; Const AResult : Boolean);
     procedure AssertExpression(Const AExpression : String; Const AResult : Boolean);
     procedure AssertDateTimeExpression(Const AExpression : String; Const AResult : TDateTime);
     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
   Published
     procedure TestRegister;
     procedure TestRegister;
     Procedure TestVariablepi;
     Procedure TestVariablepi;
@@ -893,12 +936,335 @@ type
     Procedure TestFunctionstrtotimedef;
     Procedure TestFunctionstrtotimedef;
     Procedure TestFunctionstrtodatetime;
     Procedure TestFunctionstrtodatetime;
     Procedure TestFunctionstrtodatetimedef;
     Procedure TestFunctionstrtodatetimedef;
+    Procedure TestFunctionAggregateSum;
+    Procedure TestFunctionAggregateCount;
+    Procedure TestFunctionAggregateAvg;
   end;
   end;
 
 
 implementation
 implementation
 
 
 uses typinfo;
 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;
 procedure TTestExpressionScanner.TestCreate;
 begin
 begin
   AssertEquals('Empty source','',FP.Source);
   AssertEquals('Empty source','',FP.Source);
@@ -5055,6 +5421,7 @@ procedure TTestBuiltins.Setup;
 begin
 begin
   inherited Setup;
   inherited Setup;
   FM:=TExprBuiltInManager.Create(Nil);
   FM:=TExprBuiltInManager.Create(Nil);
+  FValue:=0;
 end;
 end;
 
 
 procedure TTestBuiltins.Teardown;
 procedure TTestBuiltins.Teardown;
@@ -5063,7 +5430,7 @@ begin
   inherited Teardown;
   inherited Teardown;
 end;
 end;
 
 
-procedure TTestBuiltins.SetExpression(Const AExpression : String);
+procedure TTestBuiltins.SetExpression(const AExpression: String);
 
 
 Var
 Var
   Msg : String;
   Msg : String;
@@ -5148,11 +5515,41 @@ begin
   AssertDatetimeResult(AResult);
   AssertDatetimeResult(AResult);
 end;
 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;
 procedure TTestBuiltins.TestRegister;
 
 
 begin
 begin
   RegisterStdBuiltins(FM);
   RegisterStdBuiltins(FM);
-  AssertEquals('Correct number of identifiers',64,FM.IdentifierCount);
+  AssertEquals('Correct number of identifiers',67,FM.IdentifierCount);
   Assertvariable('pi',rtFloat);
   Assertvariable('pi',rtFloat);
   AssertFunction('cos','F','F',bcMath);
   AssertFunction('cos','F','F',bcMath);
   AssertFunction('sin','F','F',bcMath);
   AssertFunction('sin','F','F',bcMath);
@@ -5217,6 +5614,9 @@ begin
   AssertFunction('strtotimedef','D','SD',bcConversion);
   AssertFunction('strtotimedef','D','SD',bcConversion);
   AssertFunction('strtodatetime','D','S',bcConversion);
   AssertFunction('strtodatetime','D','S',bcConversion);
   AssertFunction('strtodatetimedef','D','SD',bcConversion);
   AssertFunction('strtodatetimedef','D','SD',bcConversion);
+  AssertFunction('sum','F','F',bcAggregate);
+  AssertFunction('count','I','',bcAggregate);
+  AssertFunction('avg','F','F',bcAggregate);
 end;
 end;
 
 
 procedure TTestBuiltins.TestVariablepi;
 procedure TTestBuiltins.TestVariablepi;
@@ -5667,6 +6067,33 @@ begin
   AssertExpression('StrToDateTimeDef('''+S+''',S)',T);
   AssertExpression('StrToDateTimeDef('''+S+''',S)',T);
 end;
 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 }
 { TTestNotNode }
 
 
 procedure TTestNotNode.TearDown;
 procedure TTestNotNode.TearDown;
@@ -6113,6 +6540,7 @@ initialization
                  TTestParserExpressions, TTestParserBooleanOperations,
                  TTestParserExpressions, TTestParserBooleanOperations,
                  TTestParserOperands, TTestParserTypeMatch,
                  TTestParserOperands, TTestParserTypeMatch,
                  TTestParserVariables,TTestParserFunctions,
                  TTestParserVariables,TTestParserFunctions,
+                 TTestParserAggregate,
                  TTestBuiltinsManager,TTestBuiltins]);
                  TTestBuiltinsManager,TTestBuiltins]);
 end.
 end.