unit XData.Query.Expression;

interface

uses
  Generics.Collections,
  XData.Model.Classes,
  XData.Query.Method,
  XData.Query.Tokenizer;

type
  TQueryVisitor = class;

  TOperatorType = (opNot, opMul, opDiv, opMod, opAdd, opSub, opLt, opGt, opLe, opGe,
    opEq, opNe, opAnd, opOr, opLike, opILike);

  TOperatorPrecedence = (ConditionalOr, ConditionalAnd, Equality, Relational, Additive, Multiplicative, Unary, Maximum {do not use});

  TQueryOperator = class
  strict private
    FName: string;
    FPrecedence: TOperatorPrecedence;
    FOperatorType: TOperatorType;
    FIsBinary: boolean;
    FIsRelational: boolean;
    FIsLogical: boolean;
    FIsArithmetic: boolean;
  private
    function GetIsUnary: boolean;
  public
    constructor Create(const AName: string; AType: TOperatorType; APrecedence: TOperatorPrecedence);
    property OperatorType: TOperatorType read FOperatorType;
    property Name: string read FName;
    property Precedence: TOperatorPrecedence read FPrecedence;
    property IsBinary: boolean read FIsBinary;
    property IsUnary: boolean read GetIsUnary;
    property IsLogical: boolean read FIsLogical;
    property IsArithmetic: boolean read FIsArithmetic;
    property IsRelational: boolean read FIsRelational;
  end;

  TQueryExpression = class
  public
    procedure Accept(const Visitor: TQueryVisitor); virtual; abstract;
  end;

  TBinaryExpression = class(TQueryExpression)
  strict private
    FOper: TQueryOperator;
    FLeft: TQueryExpression;
    FRight: TQueryExpression;
  public
    constructor Create(const AOper: TQueryOperator; const ALeft, ARight: TQueryExpression);
    destructor Destroy; override;
    property Oper: TQueryOperator read FOper;
    property Left: TQueryExpression read FLeft;
    property Right: TQueryExpression read FRight;
  end;

  TUnaryExpression = class(TQueryExpression)
  public
    FOper: TQueryOperator;
    FExpression: TQueryExpression;
  public
    constructor Create(const AOper: TQueryOperator; const AExpression: TQueryExpression);
    destructor Destroy; override;
    property Oper: TQueryOperator read FOper;
    property Expression: TQueryExpression read FExpression;
  end;

  TLogicalExpression = class(TBinaryExpression)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TComparisonExpression = class(TBinaryExpression)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TArithmeticExpression = class(TBinaryExpression)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TArithmeticUnaryExpression = class(TUnaryExpression)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TBooleanUnaryExpression = class(TUnaryExpression)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TParenthesisExpression = class(TQueryExpression)
  strict private
    FExpression: TQueryExpression;
  public
    constructor Create(const AExpression: TQueryExpression);
    destructor Destroy; override;
    property Expression: TQueryExpression read FExpression;
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TMemberExpression = class(TQueryExpression)
  strict private
    FNames: TList<string>;
  public
    constructor Create(const AFirstName: string);
    destructor Destroy; override;
    property Names: TList<string> read FNames;
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TResolvedMemberExpression = class(TQueryExpression)
  strict private
    FAlias: string;
    FMember: string;
    FProp: TXDataProperty;
    function GetFullName: string;
  public
    constructor Create(const AAlias, AMember: string; AProp: TXDataProperty);
    property Alias: string read FAlias;
    property Member: string read FMember;
    property FullName: string read GetFullName;
    property Prop: TXDataProperty read FProp;
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TMethodCallExpression = class(TQueryExpression)
  strict private
    FMethod: TQueryMethod;
    FArguments: TList<TQueryExpression>;
  public
    constructor Create(const AMethod: TQueryMethod);
    destructor Destroy; override;
    property Method: TQueryMethod read FMethod;
    property Arguments: TList<TQueryExpression> read FArguments;
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TLiteralExpression = class(TQueryExpression)
  end;

  TLiteralExpression<T> = class(TLiteralExpression)
  strict private
    FValue: T;
  public
    constructor Create(const AValue: T);
    property Value: T read FValue;
  end;

  TNullLiteralExpression = class(TLiteralExpression)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TStringLiteralExpression = class(TLiteralExpression<string>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TIntegerLiteralExpression = class(TLiteralExpression<integer>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TInt64LiteralExpression = class(TLiteralExpression<Int64>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TBooleanLiteralExpression = class(TLiteralExpression<Boolean>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TDoubleLiteralExpression = class(TLiteralExpression<Double>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TDateTimeLiteralExpression = class(TLiteralExpression<TDateTime>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TGuidLiteralExpression = class(TLiteralExpression<TGuid>)
  public
    procedure Accept(const Visitor: TQueryVisitor); override;
  end;

  TQueryOrderInfo = class
  strict private
    FExpression: TQueryExpression;
    FAscending: boolean;
  public
    constructor Create(const AExpression: TQueryExpression; AAscending: boolean);
    destructor Destroy; override;
    property Expression: TQueryExpression read FExpression;
    property Ascending: boolean read FAscending;
  end;

  TQueryExpandInfo = class
  strict private
    FExpression: TQueryExpression;
  public
    constructor Create(const AExpression: TQueryExpression);
    destructor Destroy; override;
    property Expression: TQueryExpression read FExpression;
  end;

  TQueryVisitor = class abstract
  public
    procedure NullLiteral(const Expr: TNullLiteralExpression); virtual; abstract;
    procedure StringLiteral(const Expr: TStringLiteralExpression); virtual; abstract;
    procedure DateTimeLiteral(const Expr: TDateTimeLiteralExpression); virtual; abstract;
    procedure GuidLiteral(const Expr: TGuidLiteralExpression); virtual; abstract;
    procedure DoubleLiteral(const Expr: TDoubleLiteralExpression); virtual; abstract;
    procedure IntegerLiteral(const Expr: TIntegerLiteralExpression); virtual; abstract;
    procedure Int64Literal(const Expr: TInt64LiteralExpression); virtual; abstract;
    procedure BooleanLiteral(const Expr: TBooleanLiteralExpression); virtual; abstract;
    procedure Logical(const Expr: TLogicalExpression); virtual; abstract;
    procedure Comparison(const Expr: TComparisonExpression); virtual; abstract;
    procedure Arithmetic(const Expr: TArithmeticExpression); virtual; abstract;
    procedure ArithmeticUnary(const Expr: TArithmeticUnaryExpression); virtual; abstract;
    procedure Parenthesis(const Expr: TParenthesisExpression); virtual; abstract;
    procedure Member(const Expr: TMemberExpression); virtual; abstract;
    procedure MethodCall(const Expr: TMethodCallExpression); virtual; abstract;
    procedure BooleanUnary(const Expr: TBooleanUnaryExpression); virtual; abstract;
    procedure ResolvedMember(const Expr: TResolvedMemberExpression); virtual; abstract;
  end;

  TStackQueryVisitor<T: class> = class(TQueryVisitor)
  strict private
    FStack: TList<T>;
  strict protected
    procedure Push(const Value: T);
  public
    constructor Create;
    destructor Destroy; override;
    function Pop: T;
    function Peek: T;
    function PeekPrevious: T;
  end;

implementation

{ TQueryOperator }

constructor TQueryOperator.Create(const AName: string; AType: TOperatorType;
  APrecedence: TOperatorPrecedence);
begin
  FName := AName;
  FOperatorType := AType;
  FPrecedence := APrecedence;
  FIsBinary := (AType <> TOperatorType.opNot);
  FIsLogical := AType in [TOperatorType.opAnd, TOperatorType.opOr, TOperatorType.opNot];
  FIsArithmetic := AType in [TOperatorType.opAdd, TOperatorType.opSub, TOperatorType.
    opMul, TOperatorType.opDiv, TOperatorType.opMod];
  FIsRelational := AType in [TOperatorType.opEq, TOperatorType.opGe, TOperatorType.opGt, TOperatorType.opLe,
    TOperatorType.opLt, TOperatorType.opNe, TOperatorType.opLike, TOperatorType.opILike];
end;

function TQueryOperator.GetIsUnary: boolean;
begin
  Result := not IsBinary;
end;

{ TBinaryExpression }

constructor TBinaryExpression.Create(const AOper: TQueryOperator; const ALeft,
  ARight: TQueryExpression);
begin
  FOper := AOper;
  FLeft := ALeft;
  FRight := ARight;
end;

destructor TBinaryExpression.Destroy;
begin
  FLeft.Free;
  FRight.Free;
  inherited;
end;

{ TUnaryExpression }

constructor TUnaryExpression.Create(const AOper: TQueryOperator;
  const AExpression: TQueryExpression);
begin
  FOper := AOper;
  FExpression := AExpression;
end;

destructor TUnaryExpression.Destroy;
begin
  FExpression.Free;
  inherited;
end;

{ TParenthesisExpression }

procedure TParenthesisExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.Parenthesis(Self);
end;

constructor TParenthesisExpression.Create(const AExpression: TQueryExpression);
begin
  FExpression := AExpression;
end;

destructor TParenthesisExpression.Destroy;
begin
  FExpression.Free;
  inherited;
end;

{ TMemberExpression }

procedure TMemberExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.Member(Self);
end;

constructor TMemberExpression.Create(const AFirstName: string);
begin
  FNames := TList<string>.Create;
  FNames.Add(AFirstName);
end;

destructor TMemberExpression.Destroy;
begin
  FNames.Free;
  inherited;
end;

{ TLiteralExpression<T> }

constructor TLiteralExpression<T>.Create(const AValue: T);
begin
  FValue := AValue;
end;

{ TMethodCallExpression }

procedure TMethodCallExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.MethodCall(Self);
end;

constructor TMethodCallExpression.Create(const AMethod: TQueryMethod);
begin
  FMethod := AMethod;
  FArguments := TObjectList<TQueryExpression>.Create(true);
end;

destructor TMethodCallExpression.Destroy;
begin
  FArguments.Free;
  inherited;
end;

{ TLogicalExpression }

procedure TLogicalExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.Logical(Self);
end;

{ TComparisonExpression }

procedure TComparisonExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.Comparison(Self);
end;

{ TArithmeticExpression }

procedure TArithmeticExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.Arithmetic(Self);
end;

{ TArithmeticUnaryExpression }

procedure TArithmeticUnaryExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.ArithmeticUnary(Self);
end;

{ TBooleanUnaryExpression }

procedure TBooleanUnaryExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.BooleanUnary(Self);
end;

{ TStackQueryVisitor<T> }

constructor TStackQueryVisitor<T>.Create;
begin
  FStack := TObjectList<T>.Create(true);
end;

destructor TStackQueryVisitor<T>.Destroy;
begin
  FStack.Free;
  inherited;
end;

function TStackQueryVisitor<T>.Peek: T;
begin
  Assert(FStack.Count > 0);
  Result := FStack.Last;
end;

function TStackQueryVisitor<T>.PeekPrevious: T;
begin
  Assert(FStack.Count > 1);
  Result := FStack[FStack.Count - 2];
end;

function TStackQueryVisitor<T>.Pop: T;
begin
  Assert(FStack.Count > 0);
  Result := FStack.Extract(FStack.Last);
end;

procedure TStackQueryVisitor<T>.Push(const Value: T);
begin
  FStack.Add(Value);
end;

{ TNullLiteralExpression }

procedure TNullLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.NullLiteral(Self);
end;

{ TStringLiteralExpression }

procedure TStringLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.StringLiteral(Self);
end;

{ TIntegerLiteralExpression }

procedure TIntegerLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.IntegerLiteral(Self);
end;

{ TInt64LiteralExpression }

procedure TInt64LiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.Int64Literal(Self);
end;

{ TBooleanLiteralExpression }

procedure TBooleanLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.BooleanLiteral(Self);
end;

{ TDoubleLiteralExpression }

procedure TDoubleLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.DoubleLiteral(Self);
end;

{ TDateTimeLiteralExpression }

procedure TDateTimeLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.DateTimeLiteral(Self);
end;

{ TGuidLiteralExpression }

procedure TGuidLiteralExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.GuidLiteral(Self);
end;

{ TResolvedMemberExpression }

procedure TResolvedMemberExpression.Accept(const Visitor: TQueryVisitor);
begin
  Visitor.ResolvedMember(Self);
end;

constructor TResolvedMemberExpression.Create(const AAlias, AMember: string;
  AProp: TXDataProperty);
begin
  FAlias := AAlias;
  FMember := AMember;
  FProp := AProp;
end;

function TResolvedMemberExpression.GetFullName: string;
begin
  if Alias <> '' then
    Result := Alias + '.' + Member
  else
    Result := Member;
end;

{ TQueryOrderInfo }

constructor TQueryOrderInfo.Create(const AExpression: TQueryExpression; AAscending: boolean);
begin
  FExpression := AExpression;
  FAscending := AAscending;
end;

destructor TQueryOrderInfo.Destroy;
begin
  FExpression.Free;
  inherited;
end;

{ TQueryExpandInfo }

constructor TQueryExpandInfo.Create(const AExpression: TQueryExpression);
begin
  FExpression := AExpression;
end;

destructor TQueryExpandInfo.Destroy;
begin
  FExpression.Free;
  inherited;
end;

end.
