{
    This file is part of the Free Component Library (FCL)
    Copyright (c) 2008 Michael Van Canneyt.

    Expression parser, supports variables, functions and
    float/integer/string/boolean/datetime operations.

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}
{$mode objfpc}
{$h+}
unit fpexprpars;

interface

uses
  Classes, SysUtils, contnrs;

Type
  // tokens
  TTokenType = (ttPlus, ttMinus, ttLessThan, ttLargerThan, ttEqual, ttDiv,
                ttMod, ttMul, ttLeft, ttRight, ttLessThanEqual,
                ttLargerThanEqual, ttunequal, ttNumber, ttString, ttIdentifier,
                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, ttPower];
  ttComparisons = [ttLargerThan,ttLessthan,
                   ttLargerThanEqual,ttLessthanEqual,
                   ttEqual,ttUnequal];

Type

  TFPExpressionParser = Class;
  TExprBuiltInManager = Class;
  TFPExprFunction = Class;
  TFPExprFunctionClass = Class of TFPExprFunction;

  TNumberKind = (nkDecimal, nkHex, nkOctal, nkBinary);

  { TFPExpressionScanner }

  TFPExpressionScanner = Class(TObject)
    FSource : String;
    LSource,
    FPos : Integer;
    FChar : PChar;
    FToken : String;
    FTokenType : TTokenType;
  private
    function GetCurrentChar: Char;
    procedure ScanError(Msg: String);
  protected
    procedure SetSource(const AValue: String); virtual;
    function DoIdentifier: TTokenType;
    function DoNumber(AKind: TNumberKind): TTokenType;
    function DoDelimiter: TTokenType;
    function DoString: TTokenType;
    Function NextPos : Char; // inline;
    procedure SkipWhiteSpace; // inline;
    function IsWordDelim(C : Char) : Boolean; // inline;
    function IsDelim(C : Char) : Boolean; // inline;
    function IsDigit(C : Char; AKind: TNumberKind) : Boolean; // inline;
    function IsAlpha(C : Char) : Boolean; // inline;
  public
    Constructor Create;
    Function GetToken : TTokenType;
    Property Token : String Read FToken;
    Property TokenType : TTokenType Read FTokenType;
    Property Source : String Read FSource Write SetSource;
    Property Pos : Integer Read FPos;
    Property CurrentChar : Char Read GetCurrentChar;
  end;

  EExprScanner = Class(Exception);

  TResultType = (rtBoolean,rtInteger,rtFloat,rtDateTime,rtString,rtCurrency);
  TResultTypes = set of TResultType;

  TFPExpressionResult = record
    ResString   : String;
    Case ResultType : TResultType of
      rtBoolean  : (ResBoolean  : Boolean);
      rtInteger  : (ResInteger  : Int64);
      rtFloat    : (ResFloat    : TExprFloat);
      rtCurrency : (ResCurrency : Currency);
      rtDateTime : (ResDateTime : TDatetime);
      rtString   : ();
  end;
  PFPExpressionResult = ^TFPExpressionResult;
  TExprParameterArray = Array of TFPExpressionResult;

  { TFPExprNode }

  TFPExprNode = Class(TObject)
  Protected
    Procedure CheckNodeType(Anode : TFPExprNode; Allowed : TResultTypes);
    // A procedure with var saves an implicit try/finally in each node
    // A marked difference in execution speed.
    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;
  end;
  TExprArgumentArray = Array of TFPExprNode;

  { TFPBinaryOperation }

  TFPBinaryOperation = Class(TFPExprNode)
  private
    FLeft: TFPExprNode;
    FRight: TFPExprNode;
  Protected
    Procedure CheckSameNodeTypes;
  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;
  end;
  TFPBinaryOperationClass = Class of TFPBinaryOperation;


  { TFPBooleanOperation }

  TFPBooleanOperation = Class(TFPBinaryOperation)
  Public
    Procedure Check; override;
    Function NodeType : TResultType; override;
  end;
  { TFPBinaryAndOperation }

  TFPBinaryAndOperation = Class(TFPBooleanOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPBinaryOrOperation }

  TFPBinaryOrOperation = Class(TFPBooleanOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPBinaryXOrOperation }

  TFPBinaryXOrOperation = Class(TFPBooleanOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPBooleanResultOperation }

  TFPBooleanResultOperation = Class(TFPBinaryOperation)
  Public
    Procedure Check; override;
    Function NodeType : TResultType; override;
  end;
  TFPBooleanResultOperationClass = Class of TFPBooleanResultOperation;


  { TFPEqualOperation }

  TFPEqualOperation = Class(TFPBooleanResultOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPUnequalOperation }

  TFPUnequalOperation = Class(TFPEqualOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPOrderingOperation }

  TFPOrderingOperation = Class(TFPBooleanResultOperation)
  Public
    Procedure Check; override;
  end;

  { TFPLessThanOperation }

  TFPLessThanOperation = Class(TFPOrderingOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPGreaterThanOperation }

  TFPGreaterThanOperation = Class(TFPOrderingOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPLessThanEqualOperation }

  TFPLessThanEqualOperation = Class(TFPGreaterThanOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;


  { TFPGreaterThanEqualOperation }

  TFPGreaterThanEqualOperation = Class(TFPLessThanOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TIfOperation }

  TIfOperation = Class(TFPBinaryOperation)
  private
    FCondition: TFPExprNode;
  protected
    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;
    Function AsString : string ; override;
    Property Condition : TFPExprNode Read FCondition;
  end;

  { TCaseOperation }

  TCaseOperation = Class(TFPExprNode)
  private
    FArgs : TExprArgumentArray;
    FCondition: TFPExprNode;
  protected
    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;
    Function AsString : string ; override;
    Property Condition : TFPExprNode Read FCondition;
  end;

  { TMathOperation }

  TMathOperation = Class(TFPBinaryOperation)
  Public
    Procedure Check; override;
    Function NodeType : TResultType; override;
  end;

  { TFPAddOperation }

  TFPAddOperation = Class(TMathOperation)
  Protected
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  Public
    Function AsString : string ; override;
  end;

  { TFPSubtractOperation }

  TFPSubtractOperation = Class(TMathOperation)
  Public
    Procedure Check; override;
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
    Function AsString : string ; override;
  end;

  { TFPMultiplyOperation }

  TFPMultiplyOperation = Class(TMathOperation)
  Public
    Procedure check; override;
    Function AsString : string ; override;
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  end;

  { TFPDivideOperation }

  TFPDivideOperation = Class(TMathOperation)
  Public
    Procedure Check; override;
    Function AsString : string ; override;
    Function NodeType : TResultType; override;
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
  end;

  { TFPModuloOperation }

  TFPModuloOperation = Class(TMathOperation)
  Public
    Procedure Check; override;
    Function AsString : string ; override;
    Function NodeType : TResultType; override;
    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)
  private
    FOperand: TFPExprNode;
  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;

  { TFPConvertNode }

  TFPConvertNode = Class(TFPUnaryOperator)
    Function AsString : String; override;
  end;

  { TFPNotNode }

  TFPNotNode = Class(TFPUnaryOperator)
  Public
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
    Function AsString : String; override;
  end;

  TIntConvertNode = Class(TFPConvertNode)
  Public
    Procedure Check; override;
  end;

  { TIntToFloatNode }

  TIntToFloatNode = Class(TIntConvertNode)
  Public
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;

  { TIntToCurrencyNode }

  TIntToCurrencyNode = Class(TIntConvertNode)
  Public
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;

  { TIntToDateTimeNode }

  TIntToDateTimeNode = Class(TIntConvertNode)
  Public
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;

  { TFloatToDateTimeNode }

  TFloatToDateTimeNode = Class(TFPConvertNode)
  Public
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;

  { TFloatToCurrencyNode }

  TFloatToCurrencyNode = Class(TFPConvertNode)
  Public
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;

  { TCurrencyToDateTimeNode }

  TCurrencyToDateTimeNode = Class(TFPConvertNode)
  Public
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;

  { TCurrencyToFloatNode }

  TCurrencyToFloatNode = Class(TFPConvertNode)
  Public
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
  end;


  { TFPNegateOperation }

  TFPNegateOperation = Class(TFPUnaryOperator)
  Public
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
    Function AsString : String; override;
  end;

  { TFPConstExpression }

  TFPConstExpression = Class(TFPExprnode)
  private
    FValue : TFPExpressionResult;
  public
    Constructor CreateString(AValue : String);
    Constructor CreateInteger(AValue : Int64);
    Constructor CreateDateTime(AValue : TDateTime);
    Constructor CreateFloat(AValue : TExprFloat);
    Constructor CreateBoolean(AValue : Boolean);
    constructor CreateCurrency(AValue: Currency);
    Procedure Check; override;
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
    Function AsString : string ; override;
   // For inspection
    Property ConstValue : TFPExpressionResult read FValue;
  end;


  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;
    FIDType: TIdentifierType;
    FName: ShortString;
    FVariableArgumentCount: Boolean;
    FOnGetValue: TFPExprFunctionEvent;
    FOnGetValueCB: TFPExprFunctionCallBack;
    function GetAsBoolean: Boolean;
    function GetAsDateTime: TDateTime;
    function GetAsFloat: TExprFloat;
    function GetAsCurrency : Currency;
    function GetAsInteger: Int64;
    function GetAsString: String;
    function GetResultType: TResultType;
    function GetValue: String;
    procedure SetArgumentTypes(const AValue: String);
    procedure SetAsBoolean(const AValue: Boolean);
    procedure SetAsDateTime(const AValue: TDateTime);
    procedure SetAsFloat(const AValue: TExprFloat);
    procedure SetAsCurrency(const AValue: Currency);
    procedure SetAsInteger(const AValue: Int64);
    procedure SetAsString(const AValue: String);
    procedure SetName(const AValue: ShortString);
    procedure SetResultType(const AValue: TResultType);
    procedure SetValue(const AValue: String);
  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 AsCurrency : Currency Read GetAsCurrency Write SetAsCurrency;
    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;
    Property Value : String Read GetValue Write SetValue;
    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;
    property VariableArgumentCount: Boolean read FVariableArgumentCount write FVariableArgumentCount;
  end;


  TBuiltInCategory = (bcStrings,bcDateTime,bcMath,bcBoolean,bcConversion,bcData,bcVaria,bcUser,bcAggregate);
  TBuiltInCategories = Set of TBuiltInCategory;

  { TFPBuiltInExprIdentifierDef }

  TFPBuiltInExprIdentifierDef = Class(TFPExprIdentifierDef)
  private
    FCategory: TBuiltInCategory;
  Public
    Procedure Assign(Source : TPersistent); override;
  Published
    Property Category : TBuiltInCategory Read FCategory Write FCategory;
  end;

  { TFPExprIdentifierDefs }

  TFPExprIdentifierDefs = Class(TCollection)
  private
    FParser: TFPExpressionParser;
    function GetI(AIndex : Integer): TFPExprIdentifierDef;
    procedure SetI(AIndex : Integer; const AValue: TFPExprIdentifierDef);
  Protected
    procedure Update(Item: TCollectionItem); override;
    Property Parser: TFPExpressionParser Read FParser;
  Public
    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;
    Function AddFloatVariable(Const AName : ShortString; AValue : TExprFloat) : TFPExprIdentifierDef;
    Function AddCurrencyVariable(Const AName : ShortString; AValue : Currency) : TFPExprIdentifierDef;
    Function AddStringVariable(Const AName : ShortString; AValue : String) : 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 : 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;

  { TFPExprIdentifierNode }

  TFPExprIdentifierNode = Class(TFPExprNode)
  Private
    FID : TFPExprIdentifierDef;
    PResult : PFPExpressionResult;
    FResultType : TResultType;
  public
    Constructor CreateIdentifier(AID : TFPExprIdentifierDef);
    Function NodeType : TResultType;  override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
    Property Identifier : TFPExprIdentifierDef Read FID;
  end;

  { TFPExprVariable }

  TFPExprVariable = Class(TFPExprIdentifierNode)
    Procedure Check; override;
    function AsString: string; override;
  end;

  { TFPExprFunction }

  TFPExprFunction = Class(TFPExprIdentifierNode)
  private
    FArgumentNodes : TExprArgumentArray;
    FargumentParams : TExprParameterArray;
  Protected
    Procedure CalcParams;
    function ConvertArgument(aIndex: Integer; aNode: TFPExprNode; aType: TResultType): TFPExprNode; virtual;
  Public
    Procedure Check; override;
    Constructor CreateFunction(AID : TFPExprIdentifierDef; Const Args : TExprArgumentArray); virtual;
    Destructor Destroy; override;
    Procedure InitAggregate; override;
    Procedure UpdateAggregate; override;
    Function HasAggregate : Boolean; override;
    Property ArgumentNodes : TExprArgumentArray Read FArgumentNodes;
    Property ArgumentParams : TExprParameterArray Read FArgumentParams;
    Function AsString : String; override;
  end;

  { TAggregateExpr }

  TAggregateExpr = Class(TFPExprFunction)
  Protected
    FResult : TFPExpressionResult;
  public
    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
    function ConvertArgument(aIndex: Integer; aNode: TFPExprNode; aType: TResultType): TFPExprNode; override;
    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)
  Private
    FCallBack : TFPExprFunctionCallBack;
  Public
    Constructor CreateFunction(AID : TFPExprIdentifierDef; Const Args : TExprArgumentArray); override;
    Procedure GetNodeValue(var Result : TFPExpressionResult);  override;
    Property CallBack : TFPExprFunctionCallBack Read FCallBack;
  end;

  { TFPFunctionEventHandler }

  TFPFunctionEventHandler = Class(TFPExprFunction)
  Private
    FCallBack : TFPExprFunctionEvent;
  Public
    Constructor CreateFunction(AID : TFPExprIdentifierDef; Const Args : TExprArgumentArray); override;
    Procedure GetNodeValue(var Result : TFPExpressionResult); override;
    Property CallBack : TFPExprFunctionEvent Read FCallBack;
  end;

  { TFPExpressionParser }

  TFPExpressionParser = class(TComponent)
  private
    FBuiltIns: TBuiltInCategories;
    FExpression: String;
    FScanner : TFPExpressionScanner;
    FExprNode : TFPExprNode;
    FIdentifiers : TFPExprIdentifierDefs;
    FHashList : TFPHashObjectlist;
    FDirty : Boolean;
    procedure CheckEOF;
    function GetAsBoolean: Boolean;
    function GetAsDateTime: TDateTime;
    function GetAsFloat: TExprFloat;
    function GetAsCurrency: Currency;
    function GetAsInteger: Int64;
    function GetAsString: String;
    function MatchNodes(Todo, Match: TFPExprNode): TFPExprNode;
    procedure CheckNodes(var Left, Right: TFPExprNode);
    procedure SetBuiltIns(const AValue: TBuiltInCategories);
    procedure SetIdentifiers(const AValue: TFPExprIdentifierDefs);
  Protected
    procedure ParserError(Msg: String);
    procedure SetExpression(const AValue: String); virtual;
    Procedure CheckResultType(Const Res :TFPExpressionResult; AType : TResultType); inline;
    Procedure CheckResultTypes(Const Res :TFPExpressionResult; ATypes : TResultTypes); inline;
    Class function ConvertNode(Todo: TFPExprNode; ToType: TResultType): TFPExprNode;
    class Function BuiltinsManager : TExprBuiltInManager;
    Function Level1 : TFPExprNode;
    Function Level2 : TFPExprNode;
    Function Level3 : TFPExprNode;
    Function Level4 : TFPExprNode;
    Function Level5 : TFPExprNode;
    Function Level6 : TFPExprNode;
    Function Level7 : TFPExprNode;
    Function Primitive : TFPExprNode;
    function GetToken: TTokenType;
    Function TokenType : TTokenType;
    Function CurrentToken : String;
    Procedure CreateHashList;
    Property Scanner : TFPExpressionScanner Read FScanner;
    Property ExprNode : TFPExprNode Read FExprNode;
    Property Dirty : Boolean Read FDirty;
  public
    Constructor Create(AOwner :TComponent); override;
    Destructor Destroy; override;
    Function IdentifierByName(const AName : ShortString) : TFPExprIdentifierDef; virtual;
    Procedure Clear;
    Procedure EvaluateExpression(Out 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 AsCurrency : Currency Read GetAsCurrency;
    Property AsInteger : Int64 Read GetAsInteger;
    Property AsString : String Read GetAsString;
    Property AsBoolean : Boolean Read GetAsBoolean;
    Property AsDateTime : TDateTime Read GetAsDateTime;
  Published
    // The Expression to parse
    property Expression : String read FExpression write SetExpression;
    Property Identifiers : TFPExprIdentifierDefs Read FIdentifiers Write SetIdentifiers;
    Property BuiltIns : TBuiltInCategories Read FBuiltIns Write SetBuiltIns;
  end;
  TFPExpressionParserClass = Class of TFPExpressionParser;

  { TExprBuiltInManager }

  TExprBuiltInManager = Class(TComponent)
  Private
    FDefs : TFPExprIdentifierDefs;
    function GetCount: Integer;
    function GetI(AIndex : Integer): TFPBuiltInExprIdentifierDef;
  protected
    Property Defs : TFPExprIdentifierDefs Read FDefs;
  Public
    Constructor Create(AOwner : TComponent); override;
    Destructor Destroy; override;
    Function IndexOfIdentifier(Const AName : ShortString) : Integer;
    Function FindIdentifier(Const AName : ShortString) : TFPBuiltinExprIdentifierDef;
    Function IdentifierByName(Const AName : ShortString) : TFPBuiltinExprIdentifierDef;
    Function AddVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AResultType : TResultType; AValue : String) : TFPBuiltInExprIdentifierDef;
    Function AddBooleanVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : Boolean) : TFPBuiltInExprIdentifierDef;
    Function AddIntegerVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : Integer) : TFPBuiltInExprIdentifierDef;
    Function AddFloatVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : TExprFloat) : TFPBuiltInExprIdentifierDef;
    Function AddCurrencyVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : Currency) : TFPBuiltInExprIdentifierDef;
    Function AddStringVariable(Const ACategory : TBuiltInCategory; Const AName : ShortString; AValue : String) : 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 : TFPExprFunctionEvent) : TFPBuiltInExprIdentifierDef;
    Function AddFunction(Const ACategory : TBuiltInCategory; Const AName : ShortString; Const AResultType : Char; Const AParamTypes : String; ANodeClass : TFPExprFunctionClass) : TFPBuiltInExprIdentifierDef;
    Procedure Delete(AIndex: Integer);
    Function Remove(aIdentifier : String) : Integer;
    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; Categories : TBuiltInCategories = AllBuiltIns);
function ArgToFloat(Arg: TFPExpressionResult): TExprFloat;



implementation

uses typinfo;

{ TFPExpressionParser }

const
  cNull=#0;
  cSingleQuote = '''';
  cHexIdentifier = '$';
  cOctalIdentifier = '&';
  cBinaryIdentifier = '%';

  Digits        = ['0'..'9','.'];
  HexDigits     = ['0'..'9', 'A'..'F', 'a'..'f'];
  OctalDigits   = ['0'..'7'];
  BinaryDigits  = ['0', '1'];
  WhiteSpace    = [' ',#13,#10,#9];
  Operators     = ['+','-','<','>','=','/','*','^'];
  Delimiters    = Operators+[',','(',')'];
  Symbols       = ['%']+Delimiters;
  WordDelimiters = WhiteSpace + Symbols;

var
  FileFormatSettings: TFormatSettings;

Resourcestring
  SBadQuotes        = 'Unterminated string';
  SUnknownDelimiter = 'Unknown delimiter character: "%s"';
  SErrUnknownCharacter = 'Unknown character at pos %d: "%s"';
  SErrUnexpectedEndOfExpression = 'Unexpected end of expression';
  SErrUnknownComparison = 'Internal error: Unknown comparison';
  SErrUnknownBooleanOp = 'Internal error: Unknown boolean operation';
  SErrBracketExpected = 'Expected ) bracket at position %d, but got %s';
  SerrUnknownTokenAtPos = 'Unknown token at pos %d : %s';
  SErrLeftBracketExpected = 'Expected ( bracket at position %d, but got %s';
  SErrInvalidFloat = '%s is not a valid floating-point value';
  SErrUnknownIdentifier = 'Unknown identifier: %s';
  SErrInExpression = 'Cannot evaluate: error in expression';
  SErrInExpressionEmpty = 'Cannot evaluate: empty expression';
  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';
  SErrNoNegation = 'Cannot negate expression of type %s : %s';
  SErrNoNOTOperation = 'Cannot perform "not" on expression of type %s: %s';
  SErrTypesDoNotMatch = 'Type mismatch: %s<>%s for expressions "%s" and "%s".';
  SErrTypesIncompatible = 'Incompatible types: %s<>%s for expressions "%s" and "%s".';
  SErrNoNodeToCheck = 'Internal error: No node to check !';
  SInvalidNodeType = 'Node type (%s) not in allowed types (%s) for expression: %s';
  SErrUnterminatedExpression = 'Badly terminated expression. Found token at position %d : %s';
  SErrDuplicateIdentifier = 'An identifier with name "%s" already exists.';
  SErrInvalidResultCharacter = '"%s" is not a valid return type indicator';
  ErrInvalidArgumentCount = 'Invalid argument count for function %s';
  SErrInvalidArgumentType = 'Invalid type for argument %d: Expected %s, got %s';
  SErrInvalidResultType = 'Invalid result type: %s';
  SErrNotVariable = 'Identifier %s is not a variable';
  SErrInactive = 'Operation not allowed while an expression is active';
  SErrIFNeedsBoolean = 'First argument to IF must be of type boolean: %s';
  SErrCaseNeeds3 = 'Case statement needs to have at least 4 arguments';
  SErrCaseEvenCount = 'Case statement needs to have an even number of arguments';
  SErrCaseLabelNotAConst = 'Case label %d "%s" is not a constant expression';
  SErrCaseLabelType = 'Case label %d "%s" needs type %s, but has type %s';
  SErrCaseValueType = 'Case value %d "%s" needs type %s, but has type %s';
  SErrDivisionByZero = '%s division by zero';

{ ---------------------------------------------------------------------
  Auxiliary functions
  ---------------------------------------------------------------------}

Procedure RaiseParserError(Msg : String);
begin
  Raise EExprParser.Create(Msg);
end;

Procedure RaiseParserError(Fmt : String; Args : Array of const);
begin
  Raise EExprParser.CreateFmt(Fmt,Args);
end;

function TokenName(AToken: TTokenType): String;

begin
  Result:=GetEnumName(TypeInfo(TTokenType),Ord(AToken));
end;

function ResultTypeName(AResult: TResultType): String;

begin
  Result:=GetEnumName(TypeInfo(TResultType),Ord(AResult));
end;

function CharToResultType(C: Char): TResultType;
begin
  Case Upcase(C) of
    'S' : Result:=rtString;
    'D' : Result:=rtDateTime;
    'B' : Result:=rtBoolean;
    'I' : Result:=rtInteger;
    'F' : Result:=rtFloat;
    'C' : Result:=rtCurrency;
  else
    RaiseParserError(SErrInvalidResultCharacter,[C]);
  end;
end;

Var
  BuiltIns : TExprBuiltInManager;

function BuiltinIdentifiers: TExprBuiltInManager;

begin
  If (BuiltIns=Nil) then
    BuiltIns:=TExprBuiltInManager.Create(Nil);
  Result:=BuiltIns;
end;

Procedure FreeBuiltIns;

begin
  FreeAndNil(Builtins);
end;

{ TFloatToCurrencyNode }

procedure TFloatToCurrencyNode.Check;
begin
  CheckNodeType(Operand,[rtFloat]);
end;

function TFloatToCurrencyNode.NodeType: TResultType;
begin
  Result:=rtCurrency;
end;

procedure TFloatToCurrencyNode.GetNodeValue(var Result: TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Result.ResultType:=rtCurrency;
  Result.ResCurrency:=Result.ResFloat;
end;

{ TIntToCurrencyNode }

function TIntToCurrencyNode.NodeType: TResultType;
begin
  Result:=rtCurrency;
end;

procedure TIntToCurrencyNode.GetNodeValue(var Result: TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Result.ResCurrency:=Result.ResInteger;
  Result.ResultType:=rtCurrency;
end;

{ TFPModuloOperation }

procedure TFPModuloOperation.Check;
begin
  CheckNodeType(Left,[rtInteger]);
  CheckNodeType(Right,[rtInteger]);
  inherited Check;
end;

function TFPModuloOperation.AsString: string;
begin
  Result:=Left.AsString+' mod '+Right.asString;
end;

function TFPModuloOperation.NodeType: TResultType;
begin
  Result:=rtInteger;
end;

procedure TFPModuloOperation.GetNodeValue(var Result: TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Result.ResInteger:=Result.ResInteger mod RRes.ResInteger;
  Result.ResultType:=rtInteger;
end;

{ TAggregateMax }

procedure TAggregateMax.InitAggregate;
begin
  inherited InitAggregate;
  FFirst:=True;
  FResult.ResultType:=FArgumentNodes[0].NodeType;
  Case FResult.ResultType of
    rtFloat : FResult.resFloat:=0.0;
    rtCurrency : FResult.resCurrency:=0.0;
    rtInteger : FResult.resInteger:=0;
  end;
end;

procedure TAggregateMax.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;
      rtCurrency: OK:=N.ResCurrency>FResult.ResCurrency;
      rtinteger: OK:=N.ResInteger>FResult.ResFloat;
    end;
  if OK then
    Case N.ResultType of
      rtFloat: FResult.ResFloat:=N.ResFloat;
      rtinteger: FResult.ResFloat:=N.ResInteger;
      rtCurrency: FResult.ResCurrency:=N.ResCurrency;
    end;
end;

{ TAggregateMin }

procedure TAggregateMin.InitAggregate;
begin
  inherited InitAggregate;
  FFirst:=True;
  FResult.ResultType:=FArgumentNodes[0].NodeType;
  Case FResult.ResultType of
    rtFloat : FResult.resFloat:=0.0;
    rtCurrency : FResult.resCurrency:=0.0;
    rtInteger : FResult.resInteger:=0;
  end;
end;

procedure TAggregateMin.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;
      rtCurrency: OK:=N.ResCurrency<FResult.ResCurrency;
      rtinteger: OK:=N.ResInteger<FResult.ResFloat;
    end;
  if OK then
    Case FResult.ResultType of
      rtFloat: FResult.ResFloat:=N.ResFloat;
      rtCurrency: FResult.ResCurrency:=N.ResCurrency;
      rtinteger: FResult.ResFloat:=N.ResInteger;
    end;
  inherited UpdateAggregate;
end;

{ TAggregateAvg }

procedure TAggregateAvg.InitAggregate;
begin
  inherited InitAggregate;
end;

procedure TAggregateAvg.UpdateAggregate;
begin
  inherited UpdateAggregate;
  Inc(FCount);
end;

procedure TAggregateAvg.GetNodeValue(var Result: TFPExpressionResult);
begin
  inherited GetNodeValue(Result);
  Result.ResultType:=FResult.ResultType;
  if FCount=0 then
    Case FResult.ResultType of
      rtInteger:
        begin
        Result.ResultType:=rtFloat;
        Result.ResFloat:=0.0;
        end;
      rtFloat:
        Result.ResFloat:=0.0;
      rtCurrency:
        Result.ResCurrency:=0.0;
    end
  else
    Case FResult.ResultType of
      rtInteger:
        begin
        Result.ResultType:=rtFloat;
        Result.ResFloat:=FResult.ResInteger/FCount;
        end;
      rtFloat:
        Result.ResFloat:=FResult.ResFloat/FCount;
      rtCurrency:
        Result.ResCurrency:=FResult.ResCurrency/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 }

function TAggregateSum.ConvertArgument(aIndex: Integer; aNode: TFPExprNode; aType: TResultType): TFPExprNode;
begin
  if not (aNode.NodeType in [rtFloat,rtInteger,rtCurrency]) then
    RaiseParserError(SErrInvalidArgumentType,[aIndex,ResultTypeName(aType),ResultTypeName(aNode.NodeType)]);
  Result:=aNode;
end;

procedure TAggregateSum.InitAggregate;

begin
  FResult.ResultType:=FArgumentNodes[0].NodeType;
  Case FResult.ResultType of
    rtFloat: FResult.ResFloat:=0.0;
    rtCurrency : FResult.ResCurrency:=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;
    rtCurrency: FResult.ResCurrency:=FResult.ResCurrency+R.ResCurrency;
    rtinteger: FResult.ResInteger:=FResult.ResInteger+R.ResInteger;
  end;
end;

{ ---------------------------------------------------------------------
  TFPExpressionScanner
  ---------------------------------------------------------------------}

function TFPExpressionScanner.IsAlpha(C: Char): Boolean;
begin
  Result := C in ['A'..'Z', 'a'..'z'];
end;

constructor TFPExpressionScanner.Create;
begin
  Source:='';
end;


procedure TFPExpressionScanner.SetSource(const AValue: String);
begin
  FSource:=AValue;
  LSource:=Length(FSource);
  FTokenType:=ttEOF;
  If LSource=0 then
    FPos:=0
  else
    FPos:=1;
  FChar:=Pchar(FSource);
  FToken:='';
end;

function TFPExpressionScanner.NextPos: Char;
begin
  Inc(FPos);
  Inc(FChar);
  Result:=FChar^;
end;


function TFPExpressionScanner.IsWordDelim(C: Char): Boolean;
begin
  Result:=C in WordDelimiters;
end;

function TFPExpressionScanner.IsDelim(C: Char): Boolean;
begin
  Result:=C in Delimiters;
end;

function TFPExpressionScanner.IsDigit(C: Char; AKind: TNumberKind): Boolean;
begin
  case AKind of
    nkDecimal: Result := C in Digits;
    nkHex    : Result := C in HexDigits;
    nkOctal  : Result := C in OctalDigits;
    nkBinary : Result := C in BinaryDigits;
  end;
end;

Procedure TFPExpressionScanner.SkipWhiteSpace;

begin
  While (FChar^ in WhiteSpace) and (FPos<=LSource) do
    NextPos;
end;

Function TFPExpressionScanner.DoDelimiter : TTokenType;

Var
  B : Boolean;
  C,D : Char;

begin
  C:=FChar^;
  FToken:=C;
  B:=C in ['<','>'];
  D:=C;
  C:=NextPos;

  if B and (C in ['=','>']) then
    begin
    FToken:=FToken+C;
    NextPos;
    If (D='>') then
      Result:=ttLargerThanEqual
    else if (C='>') then
      Result:=ttUnequal
    else
      Result:=ttLessThanEqual;
    end
  else
    Case D of
      '+' : Result := ttPlus;
      '-' : Result := ttMinus;
      '<' : Result := ttLessThan;
      '>' : Result := ttLargerThan;
      '=' : Result := ttEqual;
      '/' : Result := ttDiv;
      '*' : Result := ttMul;
      '(' : Result := ttLeft;
      ')' : Result := ttRight;
      ',' : Result := ttComma;
      '^' : Result := ttPower;
    else
      ScanError(Format(SUnknownDelimiter,[D]));
    end;

end;

Procedure TFPExpressionScanner.ScanError(Msg : String);

begin
  Raise EExprScanner.Create(Msg)
end;

Function TFPExpressionScanner.DoString : TTokenType;

  Function TerminatingChar(C : Char) : boolean;

  begin
    Result:=(C=cNull) or
            ((C=cSingleQuote) and
              Not ((FPos<LSource) and (FSource[FPos+1]=cSingleQuote)));
  end;


Var
  C : Char;

begin
  FToken := '';
  C:=NextPos;
  while not TerminatingChar(C) do
    begin
    FToken:=FToken+C;
    If C=cSingleQuote then
      NextPos;
    C:=NextPos;
    end;
  if (C=cNull) then
    ScanError(SBadQuotes);
  Result := ttString;
  FTokenType:=Result;
  NextPos;
end;

function TFPExpressionScanner.GetCurrentChar: Char;
begin
  If FChar<>Nil then
    Result:=FChar^
  else
    Result:=#0;
end;

procedure Val(const S: string; out V: TExprFloat; out Code: Integer);
var
  L64: Int64;
begin
  if (S <> '') and (S[1] in ['&', '$', '%']) then
  begin
    System.Val(S, L64, Code);
    if Code = 0 then
      V := L64
  end
  else
    System.Val(S, V, Code);
end;

Function TFPExpressionScanner.DoNumber(AKind: TNumberKind) : TTokenType;

Var
  C : Char;
  X : TExprFloat;
  I : Integer;
  prevC: Char;

  function ValidDigit(C: Char; AKind: TNumberKind): Boolean;
  begin
    Result := IsDigit(C, AKind);
    if (not Result) then
      case AKind of
        nkDecimal:
          Result := ((FToken <> '') and (UpCase(C)='E')) or
                    ((FToken <> '') and (C in ['+','-']) and (prevC='E'));
        nkHex:
          Result := (C = cHexIdentifier) and (prevC = #0);
        nkOctal:
          Result := (C = cOctalIdentifier) and (prevC = #0);
        nkBinary:
          Result := (C = cBinaryIdentifier) and (prevC = #0);
      end;
  end;

begin
  C:=CurrentChar;
  prevC := #0;
  while (C <> cNull) do
  begin
    if IsWordDelim(C) then
      case AKind of
        nkDecimal:
          if not (prevC in ['E','-','+']) then break;
        nkHex, nkOctal:
          break;
        nkBinary:
          if (prevC <> #0) then break;   // allow '%' as first char
      end;
    if not ValidDigit(C, AKind) then
      ScanError(Format(SErrInvalidNumberChar,[C]));
    FToken := FToken+C;
    prevC := Upcase(C);
    C:=NextPos;
    end;
  Val(FToken,X,I);
  If (I<>0) then
    ScanError(Format(SErrInvalidNumber,[FToken]));
  Result:=ttNumber;
end;

Function TFPExpressionScanner.DoIdentifier : TTokenType;

Var
  C : Char;
  S : String;
begin
  C:=CurrentChar;
  while (not IsWordDelim(C)) and (C<>cNull) do
    begin
    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);
  If (S='or') then
    Result:=ttOr
  else if (S='xor') then
    Result:=ttXOr
  else if (S='and') then
    Result:=ttAnd
  else if (S='true') then
    Result:=ttTrue
  else if (S='false') then
    Result:=ttFalse
  else if (S='not') then
    Result:=ttnot
  else if (S='if') then
    Result:=ttif
  else if (S='case') then
    Result:=ttcase
  else if (S='mod') then
    Result:=ttMod
  else
    Result:=ttIdentifier;
end;

Function TFPExpressionScanner.GetToken : TTokenType;

Var
  C : Char;

begin
  FToken := '';
  SkipWhiteSpace;
  C:=FChar^;
  if c=cNull then
    Result:=ttEOF
  else if IsDelim(C) then
    Result:=DoDelimiter
  else if (C=cSingleQuote) then
    Result:=DoString
  else if (C=cHexIdentifier) then
    Result := DoNumber(nkHex)
  else if (C=cOctalIdentifier) then
    Result := DoNumber(nkOctal)
  else if (C=cBinaryIdentifier) then
    Result := DoNumber(nkBinary)
  else if IsDigit(C, nkDecimal) then
    Result:=DoNumber(nkDecimal)
  else if IsAlpha(C) or (C='"') then
    Result:=DoIdentifier
  else
    ScanError(Format(SErrUnknownCharacter,[FPos,C]))  ;
  FTokenType:=Result;
end;

{ ---------------------------------------------------------------------
  TFPExpressionParser
  ---------------------------------------------------------------------}

function TFPExpressionParser.TokenType: TTokenType;

begin
  Result:=FScanner.TokenType;
end;

function TFPExpressionParser.CurrentToken: String;
begin
  Result:=FScanner.Token;
end;

procedure TFPExpressionParser.CreateHashList;

Var
  ID : TFPExpridentifierDef;
  BID : TFPBuiltinExpridentifierDef;
  I  : Integer;
  M : TExprBuiltinManager;

begin
  FHashList.Clear;
  // Builtins
  M:=BuiltinsManager;
  If (FBuiltins<>[]) and Assigned(M) then
    For I:=0 to M.IdentifierCount-1 do
      begin
      BID:=M.Identifiers[I];
      If BID.Category in FBuiltins then
        FHashList.Add(LowerCase(BID.Name),BID);
      end;
  // User
  For I:=0 to FIdentifiers.Count-1 do
    begin
    ID:=FIdentifiers[i];
    FHashList.Add(LowerCase(ID.Name),ID);
    end;
  FDirty:=False;
end;

function TFPExpressionParser.IdentifierByName(const AName: ShortString): TFPExprIdentifierDef;
begin
  If FDirty then
    CreateHashList;
  Result:=TFPExprIdentifierDef(FHashList.Find(LowerCase(AName)));
end;

procedure TFPExpressionParser.Clear;
begin
  FExpression:='';
  FHashList.Clear;
  FExprNode.Free;
end;

constructor TFPExpressionParser.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FIdentifiers:=TFPExprIdentifierDefs.Create(TFPExprIdentifierDef);
  FIdentifiers.FParser:=Self;
  FScanner:=TFPExpressionScanner.Create;
  FHashList:=TFPHashObjectList.Create(False);
end;

destructor TFPExpressionParser.Destroy;
begin
  FreeAndNil(FHashList);
  FreeAndNil(FExprNode);
  FreeAndNil(FIdentifiers);
  FreeAndNil(FScanner);
  inherited Destroy;
end;

function TFPExpressionParser.GetToken: TTokenType;

begin
  Result:=FScanner.GetToken;
end;

procedure TFPExpressionParser.CheckEOF;

begin
  If (TokenType=ttEOF) then
    ParserError(SErrUnexpectedEndOfExpression);
end;

procedure TFPExpressionParser.SetIdentifiers(const AValue: TFPExprIdentifierDefs
  );
begin
  FIdentifiers.Assign(AValue)
end;

procedure TFPExpressionParser.EvaluateExpression(Out Result: TFPExpressionResult);
begin
  If (FExpression='') then
    ParserError(SErrInExpressionEmpty);
  if not Assigned(FExprNode) then
    ParserError(SErrInExpression);
  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;

Class function TFPExpressionParser.ConvertNode(Todo : TFPExprNode; ToType : TResultType): TFPExprNode;
begin
  Result:=ToDo;
  Case ToDo.NodeType of
    rtInteger :
      Case ToType of
        rtFloat    : Result:=TIntToFloatNode.Create(Result);
        rtCurrency : Result:=TIntToCurrencyNode.Create(Result);
        rtDateTime : Result:=TIntToDateTimeNode.Create(Result);
      end;
    rtFloat :
      Case ToType of
        rtCurrency : Result:=TFloatToCurrencyNode.Create(Result);
        rtDateTime : Result:=TFloatToDateTimeNode.Create(Result);
      end;
    rtCurrency :
      Case ToType of
        rtFloat    : Result:=TCurrencyToFloatNode.Create(Result);
        rtDateTime : Result:=TCurrencyToDateTimeNode.Create(Result);
      end;
  end;
end;

function TFPExpressionParser.GetAsBoolean: Boolean;

var
  Res: TFPExpressionResult;

begin
  EvaluateExpression(Res);
  CheckResultType(Res,rtBoolean);
  Result:=Res.ResBoolean;
end;

function TFPExpressionParser.GetAsDateTime: TDateTime;
var
  Res: TFPExpressionResult;

begin
  EvaluateExpression(Res);
  CheckResultType(Res,rtDateTime);
  Result:=Res.ResDatetime;
end;

function TFPExpressionParser.GetAsFloat: TExprFloat;

var
  Res: TFPExpressionResult;

begin
  EvaluateExpression(Res);
  CheckResultTypes(Res,[rtFloat,rtCurrency,rtInteger]);
  case Res.ResultType of
    rtInteger : Result:=Res.ResInteger;
    rtFloat : Result:=Res.ResFloat;
    rtCurrency : Result:=res.ResCurrency;
  end;
end;

function TFPExpressionParser.GetAsCurrency: Currency;
var
  Res: TFPExpressionResult;

begin
  EvaluateExpression(Res);
  CheckResultTypes(Res,[rtFloat,rtCurrency,rtInteger]);
  case Res.ResultType of
    rtInteger : Result:=Res.ResInteger;
    rtFloat : Result:=Res.ResFloat;
    rtCurrency : Result:=res.ResCurrency;
  end;
end;

function TFPExpressionParser.GetAsInteger: Int64;

var
  Res: TFPExpressionResult;

begin
  EvaluateExpression(Res);
  CheckResultType(Res,rtInteger);
  Result:=Res.ResInteger;
end;

function TFPExpressionParser.GetAsString: String;

var
  Res: TFPExpressionResult;

begin
  EvaluateExpression(Res);
  CheckResultType(Res,rtString);
  Result:=Res.ResString;
end;

{
  Checks types of todo and match. If ToDO can be converted to it matches
  the type of match, then a node is inserted.
  For binary operations, this function is called for both operands.
}

function TFPExpressionParser.MatchNodes(Todo,Match : TFPExprNode): TFPExprNode;

Var
  FromType,ToType : TResultType;

begin
  Result:=Todo;
  FromType:=Todo.NodeType;
  ToType:=Match.NodeType;
  If (FromType<>ToType) then
    Case FromType of
    rtInteger:
      if (ToType in [rtFloat,rtCurrency,rtDateTime]) then
        Result:=ConvertNode(Todo,toType);
    rtFloat:
      if (ToType in [rtCurrency,rtDateTime]) then
        Result:=ConvertNode(Todo,toType);
    rtCurrency:
      if (ToType in [rtFloat,rtDateTime]) then
        Result:=ConvertNode(Todo,toType);
    end;
end;

{
  if the result types differ, they are converted to a common type if possible.
}

procedure TFPExpressionParser.CheckNodes(var Left, Right: TFPExprNode);

begin
  Left:=MatchNodes(Left,Right);
  Right:=MatchNodes(Right,Left);
end;

procedure TFPExpressionParser.SetBuiltIns(const AValue: TBuiltInCategories);
begin
  if FBuiltIns=AValue then exit;
  FBuiltIns:=AValue;
  FDirty:=True;
end;

function TFPExpressionParser.Level1: TFPExprNode;

var
  tt: TTokenType;
  Right : TFPExprNode;

begin
{$ifdef debugexpr}Writeln('Level 1 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
  if TokenType = ttNot then
    begin
    GetToken;
    CheckEOF;
    Right:=Level2;
    Result:=TFPNotNode.Create(Right);
    end
  else
    Result:=Level2;
  Try
    while (TokenType in [ttAnd,ttOr,ttXor]) do
      begin
      tt:=TokenType;
      GetToken;
      CheckEOF;
      Right:=Level2;
      Case tt of
        ttOr  : Result:=TFPBinaryOrOperation.Create(Result,Right);
        ttAnd : Result:=TFPBinaryAndOperation.Create(Result,Right);
        ttXor : Result:=TFPBinaryXorOperation.Create(Result,Right);
      Else
        ParserError(SErrUnknownBooleanOp)
      end;
      end;
  Except
    Result.Free;
    Raise;
  end;
end;

function TFPExpressionParser.Level2: TFPExprNode;

var
  Right : TFPExprNode;
  tt : TTokenType;
  C : TFPBinaryOperationClass;

begin
{$ifdef debugexpr}  Writeln('Level 2 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
  Result:=Level3;
  try
    if (TokenType in ttComparisons) then
      begin
      tt:=TokenType;
      GetToken;
      CheckEOF;
      Right:=Level3;
      CheckNodes(Result,Right);
      Case tt of
        ttLessthan         : C:=TFPLessThanOperation;
        ttLessthanEqual    : C:=TFPLessThanEqualOperation;
        ttLargerThan       : C:=TFPGreaterThanOperation;
        ttLargerThanEqual  : C:=TFPGreaterThanEqualOperation;
        ttEqual            : C:=TFPEqualOperation;
        ttUnequal          : C:=TFPUnequalOperation;
      Else
        ParserError(SErrUnknownComparison)
      end;
      Result:=C.Create(Result,Right);
      end;
  Except
    Result.Free;
    Raise;
  end;
end;

function TFPExpressionParser.Level3: TFPExprNode;

var
  tt : TTokenType;
  right : TFPExprNode;

begin
{$ifdef debugexpr}  Writeln('Level 3 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
  Result:=Level4;
  try
    while TokenType in [ttPlus,ttMinus] do
      begin
      tt:=TokenType;
      GetToken;
      CheckEOF;
      Right:=Level4;
      CheckNodes(Result,Right);
      Case tt of
        ttPlus  : Result:=TFPAddOperation.Create(Result,Right);
        ttMinus : Result:=TFPSubtractOperation.Create(Result,Right);
      end;
      end;
  Except
    Result.Free;
    Raise;
  end;
end;




function TFPExpressionParser.Level4: TFPExprNode;

var
  tt : TTokenType;
  right : TFPExprNode;

begin
{$ifdef debugexpr}  Writeln('Level 4 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
  Result:=Level5;
  try
    while (TokenType in [ttMul,ttDiv,ttMod]) do
      begin
      tt:=TokenType;
      GetToken;
      Right:=Level5;
      CheckNodes(Result,Right);
      Case tt of
        ttMul : Result:=TFPMultiplyOperation.Create(Result,Right);
        ttDiv : Result:=TFPDivideOperation.Create(Result,Right);
        ttMod : Result:=TFPModuloOperation.Create(Result,Right);
      end;
      end;
  Except
    Result.Free;
    Raise;
  end;
end;

function TFPExpressionParser.Level5: TFPExprNode;

Var
  B : Boolean;

begin
{$ifdef debugexpr}  Writeln('Level 5 ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
  B:=False;
  if (TokenType in [ttPlus,ttMinus]) then
    begin
    B:=TokenType=ttMinus;
    GetToken;
    end;
  Result:=Level6;
  If B then
    Result:=TFPNegateOperation.Create(Result);
end;

function TFPExpressionParser.Level6: TFPExprNode;
var
  right: TFPExprNode;
begin
{$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;
    Result:=Level1;
    try
      if (TokenType<>ttRight) then
        ParserError(Format(SErrBracketExpected,[SCanner.Pos,CurrentToken]));
      GetToken;
    Except
      Result.Free;
      Raise;
    end;
    end
  else
    Result:=Primitive;
end;

function TFPExpressionParser.Primitive: TFPExprNode;

Var
  I : Int64;
  C : Integer;
  X : TExprFloat;
  ACount : Integer;
  IFF : Boolean;
  IFC : Boolean;
  ID : TFPExprIdentifierDef;
  Args : TExprArgumentArray;
  AI : Integer;

begin
{$ifdef debugexpr}  Writeln('Primitive : ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
  SetLength(Args,0);
  if (TokenType=ttNumber) then
    begin
    if TryStrToInt64(CurrentToken,I) then
      Result:=TFPConstExpression.CreateInteger(I)
    else
      begin
      Val(CurrentToken,X,C);
      If (C=0) then
        Result:=TFPConstExpression.CreateFloat(X)
      else
        ParserError(Format(SErrInvalidFloat,[CurrentToken]));
      end;
    end
  else if (TokenType=ttString) then
    Result:=TFPConstExpression.CreateString(CurrentToken)
  else if (TokenType in [ttTrue,ttFalse]) then
    Result:=TFPConstExpression.CreateBoolean(TokenType=ttTrue)
  else if Not (TokenType in [ttIdentifier,ttIf,ttcase]) then
    ParserError(Format(SerrUnknownTokenAtPos,[Scanner.Pos,CurrentToken]))
  else
    begin
    IFF:=TokenType=ttIf;
    IFC:=TokenType=ttCase;
    if Not (IFF or IFC) then
      begin
      ID:=self.IdentifierByName(CurrentToken);
      If (ID=Nil) then
        ParserError(Format(SErrUnknownIdentifier,[CurrentToken]))
      end;
    // Determine number of arguments
    if Iff then
      ACount:=3
    else if IfC then
      ACount:=-4
    else if (ID.IdentifierType in [itFunctionCallBack,itFunctionHandler,itFunctionNode]) then
      ACount:=ID.ArgumentCount
    else
      ACount:=0;
    // Parse arguments.
    // Negative is for variable number of arguments, where Abs(value) is the minimum number of arguments
    If (ACount<>0) then
      begin
      GetToken;
      If (TokenType<>ttLeft) then
         ParserError(Format(SErrLeftBracketExpected,[Scanner.Pos,CurrentToken]));
      SetLength(Args,Abs(ACount));
      AI:=0;
      Try
        Repeat
          GetToken;
          // Check if we must enlarge the argument array
          If (ACount<0) and (AI=Length(Args)) then
            begin
            SetLength(Args,AI+1);
            Args[AI]:=Nil;
            end;
          Args[AI]:=Level1;
          Inc(AI);
          If (TokenType<>ttComma) then
            If (AI<Abs(ACount)) then
              ParserError(Format(SErrCommaExpected,[Scanner.Pos,CurrentToken]))
        Until (AI=ACount) or ((ACount<0) and (TokenType=ttRight));
        If TokenType<>ttRight then
          ParserError(Format(SErrBracketExpected,[Scanner.Pos,CurrentToken]));
      except
        On E : Exception do
          begin
          Dec(AI);
          While (AI>=0) do
            begin
            FreeAndNil(Args[Ai]);
            Dec(AI);
            end;
          Raise;
          end;
      end;
      end;
    If Iff then
      Result:=TIfOperation.Create(Args[0],Args[1],Args[2])
    else If IfC then
      Result:=TCaseOperation.Create(Args)
    else
      Case ID.IdentifierType of
        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;
end;


procedure TFPExpressionParser.SetExpression(const AValue: String);
begin
  if FExpression=AValue then exit;
  FExpression:=AValue;
  FScanner.Source:=AValue;
  If Assigned(FExprNode) then
    FreeAndNil(FExprNode);
  If (FExpression<>'') then
    begin
    GetToken;
    FExprNode:=Level1;
    If (TokenType<>ttEOF) then
      ParserError(Format(SErrUnterminatedExpression,[Scanner.Pos,CurrentToken]));
    FExprNode.Check;
    end
  else
    FExprNode:=Nil;
end;

procedure TFPExpressionParser.CheckResultType(const Res: TFPExpressionResult;
  AType: TResultType); inline;
begin
  If (Res.ResultType<>AType) then
    RaiseParserError(SErrInvalidResultType,[ResultTypeName(Res.ResultType)]);
end;

procedure TFPExpressionParser.CheckResultTypes(const Res: TFPExpressionResult; ATypes: TResultTypes);
begin
  If Not (Res.ResultType in ATypes) then
    RaiseParserError(SErrInvalidResultType,[ResultTypeName(Res.ResultType)]);
end;

class function TFPExpressionParser.BuiltinsManager: TExprBuiltInManager;
begin
  Result:=BuiltinIdentifiers;
end;


function TFPExpressionParser.Evaluate: TFPExpressionResult;
begin
  EvaluateExpression(Result);
end;

function TFPExpressionParser.ResultType: TResultType;
begin
  if not Assigned(FExprNode) then
    ParserError(SErrInExpression);
  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;

{ ---------------------------------------------------------------------
  TFPExprIdentifierDefs
  ---------------------------------------------------------------------}

function TFPExprIdentifierDefs.GetI(AIndex : Integer): TFPExprIdentifierDef;
begin
  Result:=TFPExprIdentifierDef(Items[AIndex]);
end;

procedure TFPExprIdentifierDefs.SetI(AIndex : Integer;
  const AValue: TFPExprIdentifierDef);
begin
  Items[AIndex]:=AValue;
end;

procedure TFPExprIdentifierDefs.Update(Item: TCollectionItem);
begin
  If Assigned(FParser) then
    FParser.FDirty:=True;
end;

function TFPExprIdentifierDefs.IndexOfIdentifier(const AName: ShortString
  ): Integer;
begin
  Result:=Count-1;
  While (Result>=0) And (CompareText(GetI(Result).Name,AName)<>0) do
    Dec(Result);
end;

function TFPExprIdentifierDefs.FindIdentifier(const AName: ShortString
  ): TFPExprIdentifierDef;

Var
  I : Integer;

begin
  I:=IndexOfIdentifier(AName);
  If (I=-1) then
    Result:=Nil
  else
    Result:=GetI(I);
end;

function TFPExprIdentifierDefs.IdentifierByName(const AName: ShortString
  ): TFPExprIdentifierDef;
begin
  Result:=FindIdentifier(AName);
  if (Result=Nil) then
    RaiseParserError(SErrUnknownIdentifier,[AName]);
end;

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;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=AResultType;
  Result.Value:=AValue;
end;

function TFPExprIdentifierDefs.AddBooleanVariable(const AName: ShortString;
  AValue: Boolean): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=rtBoolean;
  Result.FValue.ResBoolean:=AValue;
end;

function TFPExprIdentifierDefs.AddIntegerVariable(const AName: ShortString;
  AValue: Integer): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=rtInteger;
  Result.FValue.ResInteger:=AValue;
end;

function TFPExprIdentifierDefs.AddFloatVariable(const AName: ShortString;
  AValue: TExprFloat): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=rtFloat;
  Result.FValue.ResFloat:=AValue;
end;

function TFPExprIdentifierDefs.AddCurrencyVariable(const AName: ShortString; AValue: Currency): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=rtCurrency;
  Result.FValue.ResCurrency:=AValue;
end;

function TFPExprIdentifierDefs.AddStringVariable(const AName: ShortString;
  AValue: String): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=rtString;
  Result.FValue.ResString:=AValue;
end;

function TFPExprIdentifierDefs.AddDateTimeVariable(const AName: ShortString;
  AValue: TDateTime): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.IdentifierType:=itVariable;
  Result.Name:=AName;
  Result.ResultType:=rtDateTime;
  Result.FValue.ResDateTime:=AValue;
end;

function TFPExprIdentifierDefs.AddFunction(const AName: ShortString;
  const AResultType: Char; const AParamTypes: String;
  ACallBack: TFPExprFunctionCallBack): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.Name:=Aname;
  Result.IdentifierType:=itFunctionCallBack;
  if (AParamTypes <> '') and (AParamTypes[Length(AParamTypes)] = '+') then begin
    Result.ParameterTypes := Copy(AParamTypes, 1, Length(AParamTypes)-1);
    Result.FVariableArgumentCount := true;
  end else
    Result.ParameterTypes := AParamTypes;
  Result.ResultType:=CharToResultType(AResultType);
  Result.FOnGetValueCB:=ACallBack;
end;

function TFPExprIdentifierDefs.AddFunction(const AName: ShortString;
  const AResultType: Char; const AParamTypes: String;
  ACallBack: TFPExprFunctionEvent): TFPExprIdentifierDef;
begin
  Result:=Add as TFPExprIdentifierDef;
  Result.Name:=Aname;
  Result.IdentifierType:=itFunctionHandler;
  if (AParamTypes <> '') and (AParamTypes[Length(AParamTypes)] = '+') then begin
    Result.ParameterTypes := Copy(AParamTypes, 1, Length(AParamTypes)-1);
    Result.FVariableArgumentCount := true;
  end else
    Result.ParameterTypes := AParamTypes;
  Result.ResultType:=CharToResultType(AResultType);
  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;
  if (AParamTypes <> '') and (AParamTypes[Length(AParamTypes)] = '+') then begin
    Result.ParameterTypes := Copy(AParamTypes, 1, Length(AParamTypes)-1);
    Result.FVariableArgumentCount := true;
  end else
    Result.ParameterTypes := AParamTypes;
  Result.ResultType:=CharToResultType(AResultType);
  Result.FNodeType:=ANodeClass;
end;


{ ---------------------------------------------------------------------
  TFPExprIdentifierDef
  ---------------------------------------------------------------------}

procedure TFPExprIdentifierDef.SetName(const AValue: ShortString);
begin
  if FName=AValue then exit;
  If (AValue<>'') then
    If Assigned(Collection) and (TFPExprIdentifierDefs(Collection).IndexOfIdentifier(AValue)<>-1) then
      RaiseParserError(SErrDuplicateIdentifier,[AValue]);
  FName:=AValue;
end;

procedure TFPExprIdentifierDef.SetResultType(const AValue: TResultType);

begin
  If AValue<>FValue.ResultType then
    begin
    FValue.ResultType:=AValue;
    SetValue(FStringValue);
    end;
end;

procedure TFPExprIdentifierDef.SetValue(const AValue: String);
begin
  FStringValue:=AValue;
  If (AValue<>'') then
    Case FValue.ResultType of
      rtBoolean  : FValue.ResBoolean:=FStringValue='True';
      rtInteger  : FValue.ResInteger:=StrToInt(AValue);
      rtFloat    : FValue.ResFloat:=StrToFloat(AValue, FileFormatSettings);
      rtCurrency : FValue.ResFloat:=StrToCurr(AValue, FileFormatSettings);
      rtDateTime : FValue.ResDateTime:=StrToDateTime(AValue, FileFormatSettings);
      rtString   : FValue.ResString:=AValue;
    end
  else
    Case FValue.ResultType of
      rtBoolean  : FValue.ResBoolean:=False;
      rtInteger  : FValue.ResInteger:=0;
      rtFloat    : FValue.ResFloat:=0.0;
      rtCurrency : FValue.ResCurrency:=0.0;
      rtDateTime : FValue.ResDateTime:=0;
      rtString   : FValue.ResString:='';
    end
end;

procedure TFPExprIdentifierDef.CheckResultType(const AType: TResultType);
begin
  If FValue.ResultType<>AType then
    RaiseParserError(SErrInvalidResultType,[ResultTypeName(AType)])
end;

procedure TFPExprIdentifierDef.CheckVariable;
begin
  If Identifiertype<>itvariable then
    RaiseParserError(SErrNotVariable,[Name]);
  if EventBasedVariable then
    FetchValue;
end;

function TFPExprIdentifierDef.ArgumentCount: Integer;
begin
  if FVariableArgumentCount then
    Result := -Length(FArgumentTypes)
  else
    Result:=Length(FArgumentTypes);
end;

procedure TFPExprIdentifierDef.Assign(Source: TPersistent);

Var
  EID : TFPExprIdentifierDef;

begin
  if (Source is TFPExprIdentifierDef) then
    begin
    EID:=Source as TFPExprIdentifierDef;
    FStringValue:=EID.FStringValue;
    FValue:=EID.FValue;
    FArgumentTypes:=EID.FArgumentTypes;
    FVariableArgumentCount := EID.FVariableArgumentCount;
    FIDType:=EID.FIDType;
    FName:=EID.FName;
    FOnGetValue:=EID.FOnGetValue;
    FOnGetValueCB:=EID.FOnGetValueCB;
    FOnGetVarValue:=EID.FOnGetVarValue;
    FOnGetVarValueCB:=EID.FOnGetVarValueCB;
    end
  else
    inherited Assign(Source);
end;

procedure TFPExprIdentifierDef.SetArgumentTypes(const AValue: String);

Var
  I : integer;

begin
  if FArgumentTypes=AValue then exit;
  For I:=1 to Length(AValue) do
    CharToResultType(AValue[i]);
  FArgumentTypes:=AValue;
end;

procedure TFPExprIdentifierDef.SetAsBoolean(const AValue: Boolean);
begin
  CheckVariable;
  CheckResultType(rtBoolean);
  FValue.ResBoolean:=AValue;
end;

procedure TFPExprIdentifierDef.SetAsDateTime(const AValue: TDateTime);
begin
  CheckVariable;
  CheckResultType(rtDateTime);
  FValue.ResDateTime:=AValue;
end;

procedure TFPExprIdentifierDef.SetAsFloat(const AValue: TExprFloat);
begin
  CheckVariable;
  CheckResultType(rtFloat);
  FValue.ResFloat:=AValue;
end;

procedure TFPExprIdentifierDef.SetAsCurrency(const AValue: Currency);
begin
  CheckVariable;
  CheckResultType(rtCurrency);
  FValue.ResCurrency:=AValue;
end;

procedure TFPExprIdentifierDef.SetAsInteger(const AValue: Int64);
begin
  CheckVariable;
  CheckResultType(rtInteger);
  FValue.ResInteger:=AValue;
end;

procedure TFPExprIdentifierDef.SetAsString(const AValue: String);
begin
  CheckVariable;
  CheckResultType(rtString);
  FValue.resString:=AValue;
end;

function TFPExprIdentifierDef.GetValue: String;
begin
  Case FValue.ResultType of
    rtBoolean  : If FValue.ResBoolean then
                   Result:='True'
                 else
                   Result:='False';
    rtInteger  : Result:=IntToStr(FValue.ResInteger);
    rtFloat    : Result:=FloatToStr(FValue.ResFloat, FileFormatSettings);
    rtCurrency : Result:=CurrToStr(FValue.ResCurrency, FileFormatSettings);
    rtDateTime : Result:=FormatDateTime('cccc',FValue.ResDateTime, FileFormatSettings);
    rtString   : Result:=FValue.ResString;
  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;
end;

function TFPExprIdentifierDef.GetAsFloat: TExprFloat;
begin
  CheckResultType(rtFloat);
  CheckVariable;
  Result:=FValue.ResFloat;
end;

function TFPExprIdentifierDef.GetAsCurrency: Currency;
begin
  CheckResultType(rtCurrency);
  CheckVariable;
  Result:=FValue.ResCurrency;
end;

function TFPExprIdentifierDef.GetAsBoolean: Boolean;
begin
  CheckResultType(rtBoolean);
  CheckVariable;
  Result:=FValue.ResBoolean;
end;

function TFPExprIdentifierDef.GetAsDateTime: TDateTime;
begin
  CheckResultType(rtDateTime);
  CheckVariable;
  Result:=FValue.ResDateTime;
end;

function TFPExprIdentifierDef.GetAsInteger: Int64;
begin
  CheckResultType(rtInteger);
  CheckVariable;
  Result:=FValue.ResInteger;
end;

function TFPExprIdentifierDef.GetAsString: String;
begin
  CheckResultType(rtString);
  CheckVariable;
  Result:=FValue.ResString;
end;

{ ---------------------------------------------------------------------
  TExprBuiltInManager
  ---------------------------------------------------------------------}

function TExprBuiltInManager.GetCount: Integer;
begin
  Result:=FDefs.Count;
end;

function TExprBuiltInManager.GetI(AIndex : Integer
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs[Aindex])
end;

constructor TExprBuiltInManager.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FDefs:=TFPExprIdentifierDefs.Create(TFPBuiltInExprIdentifierDef)
end;

destructor TExprBuiltInManager.Destroy;
begin
  FreeAndNil(FDefs);
  inherited Destroy;
end;

function TExprBuiltInManager.IndexOfIdentifier(const AName: ShortString
  ): Integer;
begin
  Result:=FDefs.IndexOfIdentifier(AName);
end;

function TExprBuiltInManager.FindIdentifier(const AName: ShortString
  ): TFPBuiltinExprIdentifierDef;
begin
  Result:=TFPBuiltinExprIdentifierDef(FDefs.FindIdentifier(AName));
end;

function TExprBuiltInManager.IdentifierByName(const AName: ShortString
  ): TFPBuiltinExprIdentifierDef;
begin
  Result:=TFPBuiltinExprIdentifierDef(FDefs.IdentifierByName(AName));
end;

function TExprBuiltInManager.AddVariable(const ACategory: TBuiltInCategory;
  const AName: ShortString; AResultType: TResultType; AValue: String
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.Addvariable(AName,AResultType,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddBooleanVariable(
  const ACategory: TBuiltInCategory; const AName: ShortString; AValue: Boolean
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddBooleanvariable(AName,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddIntegerVariable(
  const ACategory: TBuiltInCategory; const AName: ShortString; AValue: Integer
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddIntegerVariable(AName,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddFloatVariable(
  const ACategory: TBuiltInCategory; const AName: ShortString;
  AValue: TExprFloat): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddFloatVariable(AName,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddCurrencyVariable(const ACategory: TBuiltInCategory; const AName: ShortString; AValue: Currency
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddCurrencyVariable(AName,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddStringVariable(
  const ACategory: TBuiltInCategory; const AName: ShortString; AValue: String
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddStringVariable(AName,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddDateTimeVariable(
  const ACategory: TBuiltInCategory; const AName: ShortString; AValue: TDateTime
  ): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddDateTimeVariable(AName,AValue));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddFunction(const ACategory: TBuiltInCategory;
  const AName: ShortString; const AResultType: Char; const AParamTypes: String;
  ACallBack: TFPExprFunctionCallBack): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddFunction(AName,AResultType,AParamTypes,ACallBack));
  Result.Category:=ACategory;
end;

function TExprBuiltInManager.AddFunction(const ACategory: TBuiltInCategory;
  const AName: ShortString; const AResultType: Char; const AParamTypes: String;
  ACallBack: TFPExprFunctionEvent): TFPBuiltInExprIdentifierDef;
begin
  Result:=TFPBuiltInExprIdentifierDef(FDefs.AddFunction(AName,AResultType,AParamTypes,ACallBack));
  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;

procedure TExprBuiltInManager.Delete(AIndex: Integer);
begin
  FDefs.Delete(AIndex);
end;

function TExprBuiltInManager.Remove(aIdentifier: String): Integer;
begin
  Result:=IndexOfIdentifier(aIdentifier);
  if Result<>-1 then
    Delete(Result);
end;


{ ---------------------------------------------------------------------
  Various Nodes
  ---------------------------------------------------------------------}

{ TFPBinaryOperation }

procedure TFPBinaryOperation.CheckSameNodeTypes;

Var
  LT,RT : TResultType;


begin
  LT:=Left.NodeType;
  RT:=Right.NodeType;
  if (RT<>LT) then
    RaiseParserError(SErrTypesDoNotMatch,[ResultTypeName(LT),ResultTypeName(RT),Left.AsString,Right.AsString])
end;

constructor TFPBinaryOperation.Create(ALeft, ARight: TFPExprNode);
begin
  FLeft:=ALeft;
  FRight:=ARight;
end;

destructor TFPBinaryOperation.Destroy;
begin
  FreeAndNil(FLeft);
  FreeAndNil(FRight);
  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
    RaiseParserError(SErrNoLeftOperand,[classname]);
  If Not Assigned(Right) then
    RaiseParserError(SErrNoRightOperand,[classname]);
end;

{ TFPUnaryOperator }

constructor TFPUnaryOperator.Create(AOperand: TFPExprNode);
begin
  FOperand:=AOperand;
end;

destructor TFPUnaryOperator.Destroy;
begin
  FreeAndNil(FOperand);
  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
    RaiseParserError(SErrNoOperand,[Self.className]);
end;

{ TFPConstExpression }

constructor TFPConstExpression.CreateString(AValue: String);
begin
  FValue.ResultType:=rtString;
  FValue.ResString:=AValue;
end;

constructor TFPConstExpression.CreateInteger(AValue: Int64);
begin
  FValue.ResultType:=rtInteger;
  FValue.ResInteger:=AValue;
end;

constructor TFPConstExpression.CreateDateTime(AValue: TDateTime);
begin
  FValue.ResultType:=rtDateTime;
  FValue.ResDateTime:=AValue;
end;

constructor TFPConstExpression.CreateFloat(AValue: TExprFloat);
begin
  Inherited create;
  FValue.ResultType:=rtFloat;
  FValue.ResFloat:=AValue;
end;

constructor TFPConstExpression.CreateCurrency(AValue: Currency);
begin
  Inherited create;
  FValue.ResultType:=rtCurrency;
  FValue.ResCurrency:=AValue;
end;

constructor TFPConstExpression.CreateBoolean(AValue: Boolean);
begin
  FValue.ResultType:=rtBoolean;
  FValue.ResBoolean:=AValue;
end;

procedure TFPConstExpression.Check;
begin
  // Nothing to check;
end;

function TFPConstExpression.NodeType: TResultType;
begin
  Result:=FValue.ResultType;
end;

Procedure TFPConstExpression.GetNodeValue(var Result : TFPExpressionResult);
begin
  Result:=FValue;
end;

function TFPConstExpression.AsString: string ;
begin
  Case NodeType of
    rtString  : Result:=''''+FValue.resString+'''';
    rtInteger : Result:=IntToStr(FValue.resInteger);
    rtDateTime : Result:=''''+FormatDateTime('cccc',FValue.resDateTime)+'''';
    rtBoolean : If FValue.ResBoolean then Result:='True' else Result:='False';
    rtFloat : Str(FValue.ResFloat,Result);
    rtCurrency : Str(FValue.ResCurrency,Result);
  end;
end;


{ TFPNegateOperation }

procedure TFPNegateOperation.Check;
begin
  Inherited;
  If Not (Operand.NodeType in [rtInteger,rtFloat,rtCurrency]) then
    RaiseParserError(SErrNoNegation,[ResultTypeName(Operand.NodeType),Operand.AsString])
end;

function TFPNegateOperation.NodeType: TResultType;
begin
  Result:=Operand.NodeType;
end;

Procedure TFPNegateOperation.GetNodeValue(var Result : TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Case Result.ResultType of
    rtInteger : Result.resInteger:=-Result.ResInteger;
    rtFloat : Result.resFloat:=-Result.ResFloat;
    rtCurrency : Result.resCurrency:=-Result.ResCurrency;
  end;
end;

function TFPNegateOperation.AsString: String;
begin
  Result:='-'+TrimLeft(Operand.AsString);
end;

{ TFPBinaryAndOperation }

procedure TFPBooleanOperation.Check;
begin
  inherited Check;
  CheckNodeType(Left,[rtInteger,rtBoolean]);
  CheckNodeType(Right,[rtInteger,rtBoolean]);
  CheckSameNodeTypes;
end;

function TFPBooleanOperation.NodeType: TResultType;
begin
  Result:=Left.NodeType;
end;

Procedure TFPBinaryAndOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Case Result.ResultType of
    rtBoolean : Result.resBoolean:=Result.ResBoolean and RRes.ResBoolean;
    rtInteger : Result.resInteger:=Result.ResInteger and RRes.ResInteger;
  end;
end;

function TFPBinaryAndOperation.AsString: string;
begin
  Result:=Left.AsString+' and '+Right.AsString;
end;

{ TFPExprNode }

procedure TFPExprNode.CheckNodeType(Anode: TFPExprNode; Allowed: TResultTypes);

Var
  S : String;
  A : TResultType;

begin
  If (Anode=Nil) then
    RaiseParserError(SErrNoNodeToCheck);
  If Not (ANode.NodeType in Allowed) then
    begin
    S:='';
    For A:=Low(TResultType) to High(TResultType) do
      If A in Allowed then
        begin
        If S<>'' then
          S:=S+',';
        S:=S+ResultTypeName(A);
        end;
    RaiseParserError(SInvalidNodeType,[ResultTypeName(ANode.NodeType),S,ANode.AsString]);
    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);
end;

{ TFPBinaryOrOperation }

function TFPBinaryOrOperation.AsString: string;
begin
  Result:=Left.AsString+' or '+Right.AsString;
end;

Procedure TFPBinaryOrOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Case Result.ResultType of
    rtBoolean : Result.resBoolean:=Result.ResBoolean or RRes.ResBoolean;
    rtInteger : Result.resInteger:=Result.ResInteger or RRes.ResInteger;
  end;
end;

{ TFPBinaryXOrOperation }

function TFPBinaryXOrOperation.AsString: string;
begin
  Result:=Left.AsString+' xor '+Right.AsString;
end;

Procedure TFPBinaryXOrOperation.GetNodeValue(var Result : TFPExpressionResult);
Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Case Result.ResultType of
    rtBoolean : Result.resBoolean:=Result.ResBoolean xor RRes.ResBoolean;
    rtInteger : Result.resInteger:=Result.ResInteger xor RRes.ResInteger;
  end;
end;

{ TFPNotNode }

procedure TFPNotNode.Check;
begin
  If Not (Operand.NodeType in [rtInteger,rtBoolean]) then
    RaiseParserError(SErrNoNotOperation,[ResultTypeName(Operand.NodeType),Operand.AsString])
end;

function TFPNotNode.NodeType: TResultType;
begin
  Result:=Operand.NodeType;
end;

procedure TFPNotNode.GetNodeValue(var Result: TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Case result.ResultType of
    rtInteger : Result.resInteger:=Not Result.resInteger;
    rtBoolean : Result.resBoolean:=Not Result.resBoolean;
  end
end;

function TFPNotNode.AsString: String;
begin
  Result:='not '+Operand.AsString;
end;

{ TIfOperation }

constructor TIfOperation.Create(ACondition, ALeft, ARight: TFPExprNode);
begin
  Inherited Create(ALeft,ARight);
  FCondition:=ACondition;
end;

destructor TIfOperation.destroy;
begin
  FreeAndNil(FCondition);
  inherited destroy;
end;

procedure TIfOperation.GetNodeValue(var Result: TFPExpressionResult);

begin
  FCondition.GetNodeValue(Result);
  If Result.ResBoolean then
    Left.GetNodeValue(Result)
  else
    Right.GetNodeValue(Result)
end;

procedure TIfOperation.Check;
begin
  inherited Check;
  if (Condition.NodeType<>rtBoolean) then
    RaiseParserError(SErrIFNeedsBoolean,[Condition.AsString]);
  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;
end;

function TIfOperation.AsString: string;
begin
  Result:=Format('if(%s , %s , %s)',[Condition.AsString,Left.AsString,Right.AsString]);
end;

{ TCaseOperation }

procedure TCaseOperation.GetNodeValue(var Result: TFPExpressionResult);

Var
  I,L : Integer;
  B : Boolean;
  RT,RV : TFPExpressionResult;

begin
  FArgs[0].GetNodeValue(RT);
  L:=Length(FArgs);
  I:=2;
  B:=False;
  While (Not B) and (I<L) do
    begin
    FArgs[i].GetNodeValue(RV);
    Case RT.ResultType of
      rtBoolean  : B:=RT.ResBoolean=RV.ResBoolean;
      rtInteger  : B:=RT.ResInteger=RV.ResInteger;
      rtFloat    : B:=RT.ResFloat=RV.ResFloat;
      rtCurrency : B:=RT.resCurrency=RV.resCurrency;
      rtDateTime : B:=RT.ResDateTime=RV.ResDateTime;
      rtString   : B:=RT.ResString=RV.ResString;
    end;
    If Not B then
      Inc(I,2);
    end;
  // Set result type.
  Result.ResultType:=FArgs[1].NodeType;
  If B then
    FArgs[I+1].GetNodeValue(Result)
  else if ((L mod 2)=0) then
    FArgs[1].GetNodeValue(Result);
end;

procedure TCaseOperation.Check;

Var
  T,V : TResultType;
  I : Integer;
  N : TFPExprNode;

begin
  If (Length(FArgs)<3) then
    RaiseParserError(SErrCaseNeeds3);
  If ((Length(FArgs) mod 2)=1) then
    RaiseParserError(SErrCaseEvenCount);
  T:=FArgs[0].NodeType;
  V:=FArgs[1].NodeType;
  For I:=2 to Length(Fargs)-1 do
    begin
    N:=FArgs[I];
    // Even argument types (labels) must equal tag.
    If ((I mod 2)=0) then
      begin
      If Not (N is TFPConstExpression) then
        RaiseParserError(SErrCaseLabelNotAConst,[I div 2,N.AsString]);
      If (N.NodeType<>T) then
        RaiseParserError(SErrCaseLabelType,[I div 2,N.AsString,ResultTypeName(T),ResultTypeName(N.NodeType)]);
      end
    else // Odd argument types (values) must match first.
      begin
      If (N.NodeType<>V) then
        RaiseParserError(SErrCaseValueType,[(I-1)div 2,N.AsString,ResultTypeName(V),ResultTypeName(N.NodeType)]);
      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;
begin
  Result:=FArgs[1].NodeType;
end;

constructor TCaseOperation.Create(Args: TExprArgumentArray);
begin
  Fargs:=Args;
end;

destructor TCaseOperation.destroy;

Var
  I : Integer;

begin
  For I:=0 to Length(FArgs)-1 do
    FreeAndNil(Fargs[I]);
  inherited destroy;
end;

function TCaseOperation.AsString: string;

Var
  I : integer;

begin
  Result:='';
  For I:=0 to Length(FArgs)-1 do
    begin
    If (Result<>'') then
      Result:=Result+', ';
    Result:=Result+FArgs[i].AsString;
    end;
  Result:='Case('+Result+')';
end;

{ TFPBooleanResultOperation }

procedure TFPBooleanResultOperation.Check;
begin
  inherited Check;
  CheckSameNodeTypes;
end;

function TFPBooleanResultOperation.NodeType: TResultType;
begin
  Result:=rtBoolean;
end;

{ TFPEqualOperation }

function TFPEqualOperation.AsString: string;
begin
  Result:=Left.AsString+' = '+Right.AsString;
end;

Procedure TFPEqualOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Case Result.ResultType of
    rtBoolean  : Result.resBoolean:=Result.ResBoolean=RRes.ResBoolean;
    rtInteger  : Result.resBoolean:=Result.ResInteger=RRes.ResInteger;
    rtFloat    : Result.resBoolean:=Result.ResFloat=RRes.ResFloat;
    rtCurrency : Result.resBoolean:=Result.resCurrency=RRes.resCurrency;
    rtDateTime : Result.resBoolean:=Result.ResDateTime=RRes.ResDateTime;
    rtString   : Result.resBoolean:=Result.ResString=RRes.ResString;
  end;
  Result.ResultType:=rtBoolean;
end;

{ TFPUnequalOperation }

function TFPUnequalOperation.AsString: string;
begin
  Result:=Left.AsString+' <> '+Right.AsString;
end;

Procedure TFPUnequalOperation.GetNodeValue(var Result : TFPExpressionResult);
begin
  Inherited GetNodeValue(Result);
  Result.ResBoolean:=Not Result.ResBoolean;
end;


{ TFPLessThanOperation }

function TFPLessThanOperation.AsString: string;
begin
  Result:=Left.AsString+' < '+Right.AsString;
end;

procedure TFPLessThanOperation.GetNodeValue(var Result : TFPExpressionResult);
Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Case Result.ResultType of
    rtInteger  : Result.resBoolean:=Result.ResInteger<RRes.ResInteger;
    rtFloat    : Result.resBoolean:=Result.ResFloat<RRes.ResFloat;
    rtCurrency : Result.resBoolean:=Result.resCurrency<RRes.resCurrency;
    rtDateTime : Result.resBoolean:=Result.ResDateTime<RRes.ResDateTime;
    rtString   : Result.resBoolean:=Result.ResString<RRes.ResString;
  end;
  Result.ResultType:=rtBoolean;
end;

{ TFPGreaterThanOperation }

function TFPGreaterThanOperation.AsString: string;
begin
  Result:=Left.AsString+' > '+Right.AsString;
end;

Procedure TFPGreaterThanOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  Case Result.ResultType of
    rtInteger : case Right.NodeType of
                  rtInteger : Result.resBoolean:=Result.ResInteger>RRes.ResInteger;
                  rtFloat : Result.resBoolean:=Result.ResInteger>RRes.ResFloat;
                  rtCurrency : Result.resBoolean:=Result.ResInteger>RRes.resCurrency;
                end;
    rtFloat   : case Right.NodeType of
                  rtInteger : Result.resBoolean:=Result.ResFloat>RRes.ResInteger;
                  rtFloat : Result.resBoolean:=Result.ResFloat>RRes.ResFloat;
                  rtCurrency : Result.resBoolean:=Result.ResFloat>RRes.ResCurrency;
                end;
    rtCurrency   : case Right.NodeType of
                  rtInteger : Result.resBoolean:=Result.ResCurrency>RRes.ResInteger;
                  rtFloat : Result.resBoolean:=Result.ResCurrency>RRes.ResFloat;
                  rtCurrency : Result.resBoolean:=Result.ResCurrency>RRes.ResCurrency;
                end;
    rtDateTime : Result.resBoolean:=Result.ResDateTime>RRes.ResDateTime;
    rtString   : Result.resBoolean:=Result.ResString>RRes.ResString;
  end;
  Result.ResultType:=rtBoolean;
end;

{ TFPGreaterThanEqualOperation }

function TFPGreaterThanEqualOperation.AsString: string;
begin
  Result:=Left.AsString+' >= '+Right.AsString;
end;

Procedure TFPGreaterThanEqualOperation.GetNodeValue(var Result : TFPExpressionResult);
begin
  Inherited GetNodeValue(Result);
  Result.ResBoolean:=Not Result.ResBoolean;
end;

{ TFPLessThanEqualOperation }

function TFPLessThanEqualOperation.AsString: string;
begin
  Result:=Left.AsString+' <= '+Right.AsString;
end;

Procedure TFPLessThanEqualOperation.GetNodeValue(var Result : TFPExpressionResult);
begin
  Inherited GetNodeValue(Result);
  Result.ResBoolean:=Not Result.ResBoolean;
end;

{ TFPOrderingOperation }

procedure TFPOrderingOperation.Check;

Const
  AllowedTypes =[rtInteger,rtfloat,rtCurrency,rtDateTime,rtString];

begin
  CheckNodeType(Left,AllowedTypes);
  CheckNodeType(Right,AllowedTypes);
  inherited Check;
end;

{ TMathOperation }

procedure TMathOperation.Check;

Const
  AllowedTypes =[rtInteger,rtfloat,rtCurrency,rtDateTime,rtString];

begin
  inherited Check;
  CheckNodeType(Left,AllowedTypes);
  CheckNodeType(Right,AllowedTypes);
  CheckSameNodeTypes;
end;

function TMathOperation.NodeType: TResultType;
begin
  Result:=Left.NodeType;
end;

{ TFPAddOperation }

function TFPAddOperation.AsString: string;
begin
  Result:=Left.AsString+' + '+Right.asString;
end;

Procedure TFPAddOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  case Result.ResultType of
    rtInteger  : Result.ResInteger:=Result.ResInteger+RRes.ResInteger;
    rtString   : Result.ResString:=Result.ResString+RRes.ResString;
    rtDateTime : Result.ResDateTime:=Result.ResDateTime+RRes.ResDateTime;
    rtFloat    : Result.ResFloat:=Result.ResFloat+RRes.ResFloat;
    rtCurrency : Result.ResCurrency:=Result.ResCurrency+RRes.ResCurrency;
  end;
  Result.ResultType:=NodeType;
end;

{ TFPSubtractOperation }

procedure TFPSubtractOperation.check;

Const
  AllowedTypes =[rtInteger,rtfloat,rtCurrency,rtDateTime];

begin
  CheckNodeType(Left,AllowedTypes);
  CheckNodeType(Right,AllowedTypes);
  inherited check;
end;

function TFPSubtractOperation.AsString: string;
begin
  Result:=Left.AsString+' - '+Right.asString;
end;

Procedure TFPSubtractOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  case Result.ResultType of
    rtInteger  : Result.ResInteger:=Result.ResInteger-RRes.ResInteger;
    rtDateTime : Result.ResDateTime:=Result.ResDateTime-RRes.ResDateTime;
    rtFloat    : Result.ResFloat:=Result.ResFloat-RRes.ResFloat;
    rtCurrency : Result.resCurrency:=Result.resCurrency-RRes.ResCurrency;
  end;
end;

{ TFPMultiplyOperation }

procedure TFPMultiplyOperation.check;

Const
  AllowedTypes =[rtInteger,rtCurrency,rtfloat];

begin
  CheckNodeType(Left,AllowedTypes);
  CheckNodeType(Right,AllowedTypes);
  Inherited;
end;

function TFPMultiplyOperation.AsString: string;
begin
  Result:=Left.AsString+' * '+Right.asString;
end;

Procedure TFPMultiplyOperation.GetNodeValue(var Result : TFPExpressionResult);
Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  case Result.ResultType of
    rtInteger  : Result.ResInteger:=Result.ResInteger*RRes.ResInteger;
    rtFloat    : Result.ResFloat:=Result.ResFloat*RRes.ResFloat;
    rtCurrency : Result.ResCurrency:=Result.ResCurrency*RRes.ResCurrency;
  end;
end;

{ TFPDivideOperation }

procedure TFPDivideOperation.check;
Const
  AllowedTypes =[rtInteger,rtCurrency,rtfloat];

begin
  CheckNodeType(Left,AllowedTypes);
  CheckNodeType(Right,AllowedTypes);
  inherited check;
end;

function TFPDivideOperation.AsString: string;
begin
  Result:=Left.AsString+' / '+Right.asString;
end;

function TFPDivideOperation.NodeType: TResultType;

begin
  if (Left.NodeType=rtCurrency) and (Right.NodeType=rtCurrency) then
    Result:=rtCurrency
  else
    Result:=rtFloat;
end;

Procedure TFPDivideOperation.GetNodeValue(var Result : TFPExpressionResult);

Var
  RRes : TFPExpressionResult;

begin
  Left.GetNodeValue(Result);
  Right.GetNodeValue(RRes);
  case Result.ResultType of
    rtInteger  :
      if RRes.ResInteger<>0 then
        Result.ResFloat:=Result.ResInteger/RRes.ResInteger
      else
        RaiseParserError(SErrDivisionByZero, [ResultTypeName(rtInteger)]);
    rtFloat    :
      if RRes.ResFloat<>0 then
        Result.ResFloat:=Result.ResFloat/RRes.ResFloat
      else
        RaiseParserError(SErrDivisionByZero, [ResultTypeName(rtInteger)]);
    rtCurrency :
        if NodeType=rtCurrency then
           if RRes.ResCurrency <> 0 then
             Result.ResCurrency:=Result.ResCurrency/RRes.ResCurrency
           else
             RaiseParserError(SErrDivisionByZero, [ResultTypeName(rtCurrency)])
        else
          if RRes.ResFloat<> 0 then
            Result.ResFloat:=Result.ResFloat/RRes.ResFloat
          else
            RaiseParserError(SErrDivisionByZero, [ResultTypeName(rtFloat)]);
  end;
  Result.ResultType:=NodeType;
end;

{ TFPPowerOperation }

procedure TFPPowerOperation.Check;
const
  AllowedTypes = [rtInteger, rtCurrency, 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;
begin
  Result:=Operand.AsString;
end;

{ TIntToFloatNode }

procedure TIntConvertNode.Check;
begin
  inherited Check;
  CheckNodeType(Operand,[rtInteger])
end;

function TIntToFloatNode.NodeType: TResultType;
begin
  Result:=rtFloat;
end;

Procedure TIntToFloatNode.GetNodeValue(var Result : TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Result.ResFloat:=Result.ResInteger;
  Result.ResultType:=rtFloat;
end;


{ TIntToDateTimeNode }

function TIntToDateTimeNode.NodeType: TResultType;
begin
  Result:=rtDatetime;
end;

procedure TIntToDateTimeNode.GetNodeValue(var Result : TFPExpressionResult);
begin
  Operand.GetnodeValue(Result);
  Result.ResDateTime:=Result.ResInteger;
  Result.ResultType:=rtDateTime;
end;

{ TFloatToDateTimeNode }

procedure TFloatToDateTimeNode.Check;
begin
  inherited Check;
  CheckNodeType(Operand,[rtFloat]);
end;

function TFloatToDateTimeNode.NodeType: TResultType;
begin
  Result:=rtDateTime;
end;

Procedure TFloatToDateTimeNode.GetNodeValue(var Result : TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Result.ResDateTime:=Result.ResFloat;
  Result.ResultType:=rtDateTime;
end;

{ TCurrencyToDateTimeNode }

procedure TCurrencyToDateTimeNode.Check;
begin
  inherited Check;
  CheckNodeType(Operand,[rtCurrency]);
end;

function TCurrencyToDateTimeNode.NodeType: TResultType;
begin
  Result:=rtDateTime;
end;

Procedure TCurrencyToDateTimeNode.GetNodeValue(var Result : TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Result.ResDateTime:=Result.ResCurrency;
  Result.ResultType:=rtDateTime;
end;

{ TCurrencyToFloatNode }

procedure TCurrencyToFloatNode.Check;
begin
  inherited Check;
  CheckNodeType(Operand,[rtCurrency]);
end;

function TCurrencyToFloatNode.NodeType: TResultType;
begin
  Result:=rtFloat;
end;

Procedure TCurrencyToFloatNode.GetNodeValue(var Result : TFPExpressionResult);
begin
  Operand.GetNodeValue(Result);
  Result.ResFloat:=Result.ResCurrency;
  Result.ResultType:=rtFloat;
end;

{ TFPExprIdentifierNode }

constructor TFPExprIdentifierNode.CreateIdentifier(AID: TFPExprIdentifierDef);
begin
  Inherited Create;
  FID:=AID;
  PResult:=@FID.FValue;
  FResultType:=FID.ResultType;
end;

function TFPExprIdentifierNode.NodeType: TResultType;
begin
  Result:=FResultType;
end;

Procedure TFPExprIdentifierNode.GetNodeValue(var Result : TFPExpressionResult);
begin
  if Identifier.EventBasedVariable then
    Identifier.FetchValue;
  Result:=PResult^;
  Result.ResultType:=FResultType;
end;

{ TFPExprVariable }

procedure TFPExprVariable.Check;
begin
  // Do nothing;
end;

function TFPExprVariable.AsString: string;
begin
  Result:=FID.Name;
end;

{ TFPExprFunction }

procedure TFPExprFunction.CalcParams;

Var
  I : Integer;

begin
  For I:=0 to Length(FArgumentParams)-1 do
    begin
    FArgumentNodes[i].GetNodeValue(FArgumentParams[i]);
    end;
end;

Function TFPExprFunction.ConvertArgument(aIndex : Integer; aNode : TFPExprNode; aType : TResultType) : TFPExprNode;

Var
  N : TFPExprNode;

begin
  // Automatically convert integers to floats for float/currency parameters
  N:=TFPExpressionParser.ConvertNode(aNode,aType);
  if (aNode=N) then
    // No conversion was performed, raise error
    RaiseParserError(SErrInvalidArgumentType,[aIndex,ResultTypeName(aType),ResultTypeName(aNode.NodeType)]);
  Result:=N;
end;

function TFPExprFunction.HasAggregate: Boolean;
var
  I: Integer;
begin
  Result := true;
  if IsAggregate then
    exit;
  For I:=0 to Length(FArgumentNodes)-1 do
    if FArgumentNodes[I].HasAggregate then
      exit;
  Result := false;
end;

procedure TFPExprFunction.Check;

Var
  I : Integer;
  rtp,rta : TResultType;

begin
  If (Length(FArgumentNodes)<>FID.ArgumentCount) and not FID.VariableArgumentCount then
    RaiseParserError(ErrInvalidArgumentCount,[FID.Name]);
  For I:=0 to Length(FArgumentNodes)-1 do
    begin
    if (i < Length(FID.ParameterTypes)) then
      rtp := CharToResultType(FID.ParameterTypes[i+1])
    else if FID.VariableArgumentCount then
      rtp := CharToResultType(FID.ParameterTypes[Length(FID.ParameterTypes)]);
    rta:=FArgumentNodes[i].NodeType;
    If (rtp<>rta) then
      FArgumentNodes[i]:=ConvertArgument(I+1,FArgumentNodes[i],rtp);
    end;
end;

constructor TFPExprFunction.CreateFunction(AID: TFPExprIdentifierDef; const Args: TExprArgumentArray);

begin
  Inherited CreateIdentifier(AID);
  FArgumentNodes:=Args;
  SetLength(FArgumentParams,Length(Args));
end;

destructor TFPExprFunction.Destroy;

Var
  I : Integer;

begin
  For I:=0 to Length(FArgumentNodes)-1 do
    FreeAndNil(FArgumentNodes[I]);
  inherited Destroy;
end;

procedure TFPExprFunction.InitAggregate;
var
  I: Integer;
begin
  For I:=0 to Length(FArgumentNodes)-1 do
    FArgumentNodes[i].InitAggregate;
end;

procedure TFPExprFunction.UpdateAggregate;
var
  I: Integer;
begin
  For I:=0 to Length(FArgumentNodes)-1 do
    FArgumentNodes[i].UpdateAggregate;
end;

function TFPExprFunction.AsString: String;

Var
  S : String;
  I : Integer;

begin
  S:='';
  For I:=0 to length(FArgumentNodes)-1 do
    begin
    If (S<>'') then
      S:=S+',';
    S:=S+FArgumentNodes[I].AsString;
    end;
  If (S<>'') then
    S:='('+S+')';
  Result:=FID.Name+S;
end;

{ TFPFunctionCallBack }

constructor TFPFunctionCallBack.CreateFunction(AID: TFPExprIdentifierDef;
  Const Args : TExprArgumentArray);
begin
  Inherited;
  FCallBack:=AID.OnGetFunctionValueCallBack;
end;

Procedure TFPFunctionCallBack.GetNodeValue(var Result : TFPExpressionResult);
begin
  If Length(FArgumentParams)>0 then
    CalcParams;

  FCallBack(Result,FArgumentParams);
  Result.ResultType:=NodeType;
end;

{ TFPFunctionEventHandler }

constructor TFPFunctionEventHandler.CreateFunction(AID: TFPExprIdentifierDef;
  Const Args : TExprArgumentArray);
begin
  Inherited;
  FCallBack:=AID.OnGetFunctionValue;
end;

Procedure TFPFunctionEventHandler.GetNodeValue(var Result : TFPExpressionResult);
begin
  If Length(FArgumentParams)>0 then
    CalcParams;
  FCallBack(Result,FArgumentParams);
  Result.ResultType:=NodeType;
end;

{ ---------------------------------------------------------------------
  Standard Builtins support
  ---------------------------------------------------------------------}

{ Template for builtin.

Procedure MyCallback (Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
end;

}

function ArgToFloat(Arg: TFPExpressionResult): TExprFloat;
// Utility function for the built-in math functions. Accepts also integers
// in place of the floating point arguments. To be called in builtins or
// user-defined callbacks having float results.
begin
  if Arg.ResultType = rtInteger then
    result := Arg.resInteger
  else if Arg.ResultType = rtCurrency then
    result := Arg.resCurrency
  else
    result := Arg.resFloat;
end;

// Math builtins

Procedure BuiltInCos(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Cos(ArgToFloat(Args[0]));
end;

Procedure BuiltInSin(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Sin(ArgToFloat(Args[0]));
end;

Procedure BuiltInArcTan(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Arctan(ArgToFloat(Args[0]));
end;

Procedure BuiltInAbs(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Abs(ArgToFloat(Args[0]));
end;

Procedure BuiltInSqr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Sqr(ArgToFloat(Args[0]));
end;

Procedure BuiltInSqrt(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Sqrt(ArgToFloat(Args[0]));
end;

Procedure BuiltInExp(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Exp(ArgToFloat(Args[0]));
end;

Procedure BuiltInLn(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Ln(ArgToFloat(Args[0]));
end;

Const
  L10 = ln(10);

Procedure BuiltInLog(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Ln(ArgToFloat(Args[0]))/L10;
end;

Procedure BuiltInRound(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resInteger:=Round(ArgToFloat(Args[0]));
end;

Procedure BuiltInTrunc(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resInteger:=Trunc(ArgToFloat(Args[0]));
end;

Procedure BuiltInInt(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=Int(ArgToFloat(Args[0]));
end;

Procedure BuiltInFrac(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resFloat:=frac(ArgToFloat(Args[0]));
end;

// String builtins

Procedure BuiltInLength(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resInteger:=Length(Args[0].resString);
end;

Procedure BuiltInCopy(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resString:=Copy(Args[0].resString,Args[1].resInteger,Args[2].resInteger);
end;

Procedure BuiltInDelete(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resString:=Args[0].resString;
  Delete(Result.resString,Args[1].resInteger,Args[2].resInteger);
end;

Procedure BuiltInPos(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resInteger:=Pos(Args[0].resString,Args[1].resString);
end;

Procedure BuiltInUppercase(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resString:=Uppercase(Args[0].resString);
end;

Procedure BuiltInLowercase(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resString:=Lowercase(Args[0].resString);
end;

Procedure BuiltInStringReplace(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  F : TReplaceFlags;

begin
  F:=[];
  If Args[3].resBoolean then
    Include(F,rfReplaceAll);
  If Args[4].resBoolean then
    Include(F,rfIgnoreCase);
  Result.resString:=StringReplace(Args[0].resString,Args[1].resString,Args[2].resString,f);
end;

Procedure BuiltInCompareText(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resInteger:=CompareText(Args[0].resString,Args[1].resString);
end;

// Date/Time builtins

Procedure BuiltInDate(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resDateTime:=Date;
end;

Procedure BuiltInTime(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resDateTime:=Time;
end;

Procedure BuiltInNow(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resDateTime:=Now;
end;

Procedure BuiltInDayofWeek(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  Result.resInteger:=DayOfWeek(Args[0].resDateTime);
end;

Procedure BuiltInExtractYear(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  Y,M,D : Word;

begin
  DecodeDate(Args[0].resDateTime,Y,M,D);
  Result.resInteger:=Y;
end;

Procedure BuiltInExtractMonth(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  Y,M,D : Word;

begin
  DecodeDate(Args[0].resDateTime,Y,M,D);
  Result.resInteger:=M;
end;

Procedure BuiltInExtractDay(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  Y,M,D : Word;

begin
  DecodeDate(Args[0].resDateTime,Y,M,D);
  Result.resInteger:=D;
end;

Procedure BuiltInExtractHour(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  H,M,S,MS : Word;

begin
  DecodeTime(Args[0].resDateTime,H,M,S,MS);
  Result.resInteger:=H;
end;

Procedure BuiltInExtractMin(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  H,M,S,MS : Word;

begin
  DecodeTime(Args[0].resDateTime,H,M,S,MS);
  Result.resInteger:=M;
end;

Procedure BuiltInExtractSec(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  H,M,S,MS : Word;

begin
  DecodeTime(Args[0].resDateTime,H,M,S,MS);
  Result.resInteger:=S;
end;

Procedure BuiltInExtractMSec(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

Var
  H,M,S,MS : Word;

begin
  DecodeTime(Args[0].resDateTime,H,M,S,MS);
  Result.resInteger:=MS;
end;

Procedure BuiltInEncodedate(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=Encodedate(Args[0].resInteger,Args[1].resInteger,Args[2].resInteger);
end;

Procedure BuiltInEncodeTime(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=EncodeTime(Args[0].resInteger,Args[1].resInteger,Args[2].resInteger,Args[3].resInteger);
end;

Procedure BuiltInEncodeDateTime(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=EncodeDate(Args[0].resInteger,Args[1].resInteger,Args[2].resInteger)
                     +EncodeTime(Args[3].resInteger,Args[4].resInteger,Args[5].resInteger,Args[6].resInteger);
end;

Procedure BuiltInShortDayName(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=DefaultFormatSettings.ShortDayNames[Args[0].resInteger];
end;

Procedure BuiltInShortMonthName(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=DefaultFormatSettings.ShortMonthNames[Args[0].resInteger];
end;
Procedure BuiltInLongDayName(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=DefaultFormatSettings.LongDayNames[Args[0].resInteger];
end;

Procedure BuiltInLongMonthName(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=DefaultFormatSettings.LongMonthNames[Args[0].resInteger];
end;

Procedure BuiltInFormatDateTime(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=FormatDateTime(Args[0].resString,Args[1].resDateTime);
end;


// Conversion
Procedure BuiltInIntToStr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=IntToStr(Args[0].resinteger);
end;

Procedure BuiltInStrToInt(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resInteger:=StrToInt(Args[0].resString);
end;

Procedure BuiltInStrToIntDef(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resInteger:=StrToIntDef(Args[0].resString,Args[1].resInteger);
end;

Procedure BuiltInFloatToStr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=FloatToStr(Args[0].resFloat);
end;

Procedure BuiltInStrToFloat(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resFloat:=StrToFloat(Args[0].resString);
end;

Procedure BuiltInStrToFloatDef(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resFloat:=StrToFloatDef(Args[0].resString,Args[1].resFloat);
end;

Procedure BuiltInDateToStr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=DateToStr(Args[0].resDateTime);
end;

Procedure BuiltInTimeToStr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=TimeToStr(Args[0].resDateTime);
end;

Procedure BuiltInStrToDate(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=StrToDate(Args[0].resString);
end;

Procedure BuiltInStrToDateDef(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=StrToDateDef(Args[0].resString,Args[1].resDateTime);
end;

Procedure BuiltInStrToTime(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=StrToTime(Args[0].resString);
end;

Procedure BuiltInStrToTimeDef(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=StrToTimeDef(Args[0].resString,Args[1].resDateTime);
end;

Procedure BuiltInStrToDateTime(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=StrToDateTime(Args[0].resString);
end;

Procedure BuiltInStrToDateTimeDef(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resDateTime:=StrToDateTimeDef(Args[0].resString,Args[1].resDateTime);
end;

procedure BuiltInFormatFloat(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);
begin
  result.ResString := FormatFloat(Args[0].resString, Args[1].ResFloat);
end;

Procedure BuiltInBoolToStr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resString:=BoolToStr(Args[0].resBoolean);
end;

Procedure BuiltInStrToBool(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resBoolean:=StrToBool(Args[0].resString);
end;

Procedure BuiltInStrToBoolDef(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resBoolean:=StrToBoolDef(Args[0].resString,Args[1].resBoolean);
end;

// Boolean
Procedure BuiltInShl(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resInteger:=Args[0].resInteger shl Args[1].resInteger
end;

Procedure BuiltInShr(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  Result.resInteger:=Args[0].resInteger shr Args[1].resInteger
end;

Procedure BuiltinIFS(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  If Args[0].resBoolean then
    Result.resString:=Args[1].resString
  else
    Result.resString:=Args[2].resString
end;

Procedure BuiltinIFI(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  If Args[0].resBoolean then
    Result.resinteger:=Args[1].resinteger
  else
    Result.resinteger:=Args[2].resinteger
end;

Procedure BuiltinIFF(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  If Args[0].resBoolean then
    Result.resfloat:=Args[1].resfloat
  else
    Result.resfloat:=Args[2].resfloat
end;

Procedure BuiltinIFD(Var Result : TFPExpressionResult; Const Args : TExprParameterArray);

begin
  If Args[0].resBoolean then
    Result.resDateTime:=Args[1].resDateTime
  else
    Result.resDateTime:=Args[2].resDateTime
end;

procedure RegisterStdBuiltins(AManager: TExprBuiltInManager;  Categories: TBuiltInCategories = AllBuiltIns);

begin
  With AManager do
    begin
    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);
      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);
      AddFunction(bcConversion,'formatfloat','S','SF',@BuiltInFormatFloat);
      AddFunction(bcConversion,'formatdatetime','S','SD',@BuiltinFormatDateTime);
      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;

{ TFPBuiltInExprIdentifierDef }

procedure TFPBuiltInExprIdentifierDef.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  If Source is TFPBuiltInExprIdentifierDef then
    FCategory:=(Source as TFPBuiltInExprIdentifierDef).Category;
end;

procedure InitFileFormatSettings;
begin
  FileFormatSettings := DefaultFormatSettings;
  FileFormatSettings.DecimalSeparator := '.';
  FileFormatSettings.DateSeparator := '-';
  FileFormatSettings.TimeSeparator := ':';
  FileFormatsettings.ShortDateFormat := 'yyyy-mm-dd';
  FileFormatSettings.LongTimeFormat := 'hh:nn:ss';
end;

initialization
  RegisterStdBuiltins(BuiltinIdentifiers);
  InitFileFormatSettings;

finalization
  FreeBuiltins;
end.

