Componentes.Terceros.RemObj.../internal/5.0.23.613/1/Data Abstract for Delphi/Source/uDAExpressionEvaluator.pas
david 2824855ea7 - Modificación del paquete RemObjects_Core_D10 para que sea un paquete de runtime/designtime (antes era designtime sólo)
- Recompilación en Delphi10 de todos los paquetes de RO para generar las DCU's en Lib\D10
- Recompilación en Delphi10 de todos los paquetes de DA para generar las DCU's en Lib\D10

git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.RemObjects@9 b6239004-a887-0f4b-9937-50029ccdca16
2007-09-10 14:06:19 +00:00

602 lines
18 KiB
ObjectPascal

unit uDAExpressionEvaluator;
{----------------------------------------------------------------------------}
{ Data Abstract Library - Core Library }
{ }
{ compiler: Delphi 6 and up, Kylix 3 and up }
{ platform: Win32, Linux }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Using this code requires a valid license of the Data Abstract }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I DataAbstract.inc}
interface
uses
SysUtils, Classes, uDAMacros;
type
EDAEvaluationException = class(Exception);
TDAExpressionEvaluator = class;
TDAEEGetValue = procedure (Sender: TDAExpressionEvaluator; const aIdentifier: string; out aValue: Variant) of object;
TDAEEFunctionCall = procedure (Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant) of object;
TDAEEFunction = class(TObject)
private
fName: string;
fOnCall: TDAEEFunctionCall;
fNameHash: Integer;
procedure SetName(const Value: string);
public
property Name: string read fName write SetName;
property NameHash: Integer read fNameHash;
property OnCall: TDAEEFunctionCall read fOnCall write fOnCall;
end;
TDAEEFunctionList = class(TObject)
private
fItems: TList;
function GetCount: Integer;
function GetItem(i: Integer): TDAEEFunction;
public
constructor Create;
destructor Destroy; override;
property Count: Integer read GetCount;
property Items[I: Integer]: TDAEEFunction read GetItem; default;
function Add(const aName: string): TDAEEFunction;
procedure Delete(I: Integer);
procedure Clear;
end;
TDAExpressionEvaluator = class(TObject)
private
fOnUnknownFunctionCall: TDAEEFunctionCall;
fOnGetValue: TDAEEGetValue;
fFunctionList: TDAEEFunctionList;
fParser: TROPascalParser;
function EvaluateProcCall(const aIdentifier: string; const Args: Variant): Variant;
function EvaluateValue(const aIdentifier: string): Variant;
function Op_In(const V1, V2: Variant): Variant;
function Op_Like(const V1, V2: Variant): Variant;
function ParseBooleanExpression: Variant;
function ParseComparisonExpression: Variant;
function ParseFactor: Variant;
function ParseSimpleExpression: Variant;
function ParseTerm: Variant;
public
constructor Create;
destructor Destroy; override;
property OnGetValue: TDAEEGetValue read fOnGetValue write fOnGetValue;
property OnUnknownFunctionCall: TDAEEFunctionCall read fOnUnknownFunctionCall write fOnUnknownFunctionCall;
property FunctionList: TDAEEFunctionList read fFunctionList;
function Evaluate(const aString: string): Variant;
end;
TDAStdExpressionEvaluator = class(TDAExpressionEvaluator)
private
procedure Convert(Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant);
procedure Len(Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant);
procedure IsNull(Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant);
procedure IIF(Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant);
procedure Trim(Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant);
procedure Substring(Sender: TDAExpressionEvaluator; const aIdentifier: string; const Args: array of Variant; out aValue: Variant);
public
constructor Create;
end;
implementation
uses Variants;
{ TDAExpressionEvaluator }
constructor TDAExpressionEvaluator.Create;
begin
inherited Create;
fFunctionList := TDAEEFunctionList.Create;
end;
destructor TDAExpressionEvaluator.Destroy;
begin
fFunctionList.Free;
inherited Destroy;
end;
function TDAExpressionEvaluator.Evaluate(const aString: string): Variant;
begin
fParser := TROPascalParser.Create;
fParser.OpenBlockEscape := true;
try
fParser.SetText(aString);
Result := ParseBooleanExpression;
if fParser.CurrtokenId <> CSTI_EOF then
raise EDAEvaluationException.Create('End of expression expected');
finally
fParser.Free;
end;
end;
function TDAExpressionEvaluator.EvaluateProcCall(const aIdentifier: string;
const Args: Variant): Variant;
var
s: string;
h, i: Integer;
lFunction: TDAEEFunction;
lRealArgs: array of Variant;
begin
if not VarIsArray(Args) then begin
SetLength(lRealArgs, 1);
lRealArgs[0] := args;
end else begin
SetLength(lRealArgs, VarArrayHighBound(Args, 1)+1);
for i := 0 to Length(lRealArgs) -1 do begin
lRealArgs[i] := Args[i];
end;
end;
s := FastUppercase(aIdentifier);
h := MakeHash(s);
for i := 0 to fFunctionList.Count -1 do begin
lFunction := fFunctionList[i];
if (lFunction.NameHash = h) and (lFunction.Name = s) and (assigned(lFunction.OnCall)) then begin
lFunction.OnCall(self, aIdentifier, lRealArgs, Result);
exit;
end;
end;
if assigned(fOnUnknownFunctionCall) then
fOnUnknownFunctionCall(Self, aIdentifier, Args, Result)
else
raise EDAEvaluationException('Unknown function: '+aIdentifier);
end;
function TDAExpressionEvaluator.EvaluateValue(const aIdentifier: string): Variant;
begin
if assigned(fOnGetValue) then
fOnGetValue(Self, aIdentifier, Result)
else
raise EDAEvaluationException('Unknown identifier: '+aIdentifier);
end;
function TDAExpressionEvaluator.ParseComparisonExpression: Variant;
var
V1, V2: Variant;
lTok: TROPasToken;
begin
V1 := ParseSimpleExpression;
lTok := fParser.CurrTokenId;
while (lTok = CSTII_In) or
(lTok = CSTII_Like) or
(lTok = CSTI_Greater ) or
(lTok = CSTI_GreaterEqual) or
(lTok = CSTI_Smaller ) or
(lTok = CSTI_SmallerEqual) or
(lTok = CSTI_Equal) or
(lTok =CSTI_NotEqual) do
begin
FParser.Next;
V2 := ParseSimpleExpression;
case lTok of
CSTII_In: V1 := Op_In(v1, v2);
CSTII_Like: V1 := Op_Like(v1, v2);
CSTI_Greater: v1 := v1 > v2;
CSTI_GreaterEqual: v1 := v1 >= v2;
CSTI_Smaller: v1 := v1 <= v2;
CSTI_SmallerEqual: v1 := v1 <= v2;
CSTI_Equal: v1 := v1 = v2;
//CSTI_notEqual:
else v1 := v1 <> v2;
end;
lTok := fParser.CurrTokenId;
end;
Result := V1;
end;
function TDAExpressionEvaluator.ParseSimpleExpression: Variant;
var
V1, V2: Variant;
lTok: TROPasToken;
begin
V1 := ParseTerm;
lTok := fParser.CurrTokenId;
while (lTok = CSTI_Plus) or
(lTok = CSTI_Minus) do
begin
FParser.Next;
V2 := ParseTerm;
case lTok of
CSTI_Minus: V1 := V1 - V2;
else v1 := v1 + v2; // CSTI_plus
end;
lTok := fParser.CurrTokenId;
end;
Result := V1;
end;
function ParseDate(s: string): TDateTime;
begin
s := copy(S,2,Length(s)-2);
if length(s) = 10 then begin
if (s[3] <> '/') or (s[6] <> '/') then raise EDAEvaluationException.Create('Invalid date format');
Result := EncodeDate(StrToInt(copy(s, 7, 4)), StrToInt(copy(S, 1, 2)), StrToInt(copy(s, 4, 2)));
end else
raise EDAEvaluationException.Create('Invalid date format');
end;
function TDAExpressionEvaluator.ParseFactor: Variant;
var
tmp: TList;
pv: PVariant;
i: Integer;
e: Extended;
s: string;
begin
case fParser.CurrTokenId of
CSTII_Not: begin
FParser.Next;
Result := not ParseFactor;
end;
CSTI_Plus: begin
FParser.Next;
Result := + ParseFactor;
end;
CSTI_Minus: begin
FParser.Next;
Result := - ParseFactor;
end;
CSTI_OpenRound: begin
FParser.Next;
Result := ParseBooleanExpression;
if fParser.CurrTokenId = CSTI_Comma then begin
tmp := TList.Create;
New(pv);
pv^ := Result;
tmp.Add(pv);
while FPArser.CurrTokenId = CSTI_Comma do begin
FParser.Next;
try
result := ParseBooleanExpression;
New(pv);
pv^ := Result;
tmp.Add(pv);
except
for i := 0 to tmp.Count -1 do begin
pv := tmp[i];
dispose(pv);
end;
tmp.Free;
raise;
end;
end;
Result := VarArrayCreate([0, tmp.Count -1], varVariant);
for i := 0 to tmp.Count -1 do begin
pv := tmp[i];
Result[i] := pv^;
dispose(pv);
end;
tmp.Free;
end;
if fParser.CurrTokenId <> CSTI_CloseRound then
raise EDAEvaluationException.Create('Closing parenthesis expected');
FParser.Next;
end;
CSTI_HexInt, CSTI_Integer: begin
Result := StrToInt64(FParser.OriginalToken);
FParser.Next;
end;
CSTI_Real: begin
Val(FParser.OriginalToken, e, i); Result := e;
FParser.Next;
end;
CSTI_String: begin
Result := StringReplace(FParser.OriginalToken, #39#39, #39, [rfReplaceAll]);
Result := Copy(Result, 2, Length(Result) -2);
Fparser.Next;
end;
CSTI_Date: begin
Result := ParseDate(FParser.OriginalToken);
FParser.Next;
end;
CSTI_Identifier: begin
s := FPArser.OriginalToken;
FParser.Next;
if FParser.CurrTokenID = CSTI_OpenRound then
Result := EvaluateProcCall(s, ParseFactor)
else
Result := EvaluateValue(s)
end;
else
raise EDAEvaluationException.Create('Syntax error');
end;
end;
function TDAExpressionEvaluator.ParseBooleanExpression: Variant;
var
V1, V2: Variant;
lTok: TROPasToken;
begin
V1 := ParseComparisonExpression;
lTok := fParser.CurrTokenId;
while (lTok = CSTII_and) or
(lTok = CSTII_or) or
(lTok = CSTII_xor) do
begin
FParser.Next;
V2 := ParseComparisonExpression;
case lTok of
CSTII_or: V1 := V1 or V2;
CSTII_xor: V1 := V1 xor V2;
else v1 := v1 and v2; // CSTII_and
end;
lTok := fParser.CurrTokenId;
end;
Result := V1;
end;
function TDAExpressionEvaluator.ParseTerm: Variant;
var
V1, V2: Variant;
lTok: TROPasToken;
begin
V1 := ParseFactor;
lTok := fParser.CurrTokenId;
while (lTok = CSTI_multiply) or
(lTok = CSTI_divide) or
(lTok = CSTI_Modulus) do
begin
FParser.Next;
V2 := ParseFactor;
case lTok of
CSTI_Multiply: V1 := V1 * V2;
CSTI_Divide: V1 := V1 / V2;
else v1 := v1 mod v2; // CSTI_modulus
end;
lTok := fParser.CurrTokenId;
end;
Result := V1;
end;
function TDAExpressionEvaluator.Op_In(const V1, V2: Variant): Variant;
var
i: Integer;
begin
if VarIsArray(V2) then begin
for i := VarArrayLowBound(V2, 1) to VarArrayHighBound(V2, 1) do begin
if v2[i] = v1 then begin Result := true; exit; end;
end;
Result := False;
end else
raise EDAEvaluationException.Create('Array expected for IN expression');
end;
function WidePos(const iSubString, iString:WideString): Integer; overload;
var i,j:integer;
LenS,LenS1:integer;
b: Boolean;
begin
LenS1 := Length(iSubString);
LenS := Length(iString);
i := 1;
while i <= LenS-LenS1+1 do begin
{ IsCandidate }
b := true;
for j := 1 to LenS1 do begin
if iString[i+j-1] <> iSubString[j] then begin
b := false;
break;
end;
end;
if b then begin
result := i;
exit;
end;
inc(i);
end;
result := 0;
end;
function TDAExpressionEvaluator.Op_Like(const V1, V2: Variant): Variant;
var
w1, w2: WideString;
begin
w1 := WideUppercase(V1);
W2 := WideUppercase(V2);
if Length(W2) = 0 then begin Result := False; exit; end;
if (Length(W2) > 0) and ((W2[1] = '%') or (W2[1] = '*')) then begin
if ((W2[Length(W2)] = '%') or (W2[Length(W2)] = '*')) then begin
W2 := Copy(W2, 2, Length(W2) -2);
Result := WidePos(W2, W1) > 0;
end else begin
W2 := Copy(W2, 2, Length(W2) -1);
Result := Copy(W1, LEngth(W1) - Length(W2)+1, Length(W2)) = W2;
end;
end else if (Length(W2)> 0) and ((W2[Length(W2)] = '%') or (W2[Length(W2)] = '*')) then begin
W2 := copy(W2, 1, Length(W2)-1);
result := Copy(W1, 1, Length(W2)) = W2;
end else
result := w1 = w2;
end;
{ TDAEEFunction }
procedure TDAEEFunction.SetName(const Value: string);
begin
fName := FastUppercase(Value);
fNameHash := MakeHash(fName);
end;
{ TDAEEFunctionList }
function TDAEEFunctionList.Add(const aName: string): TDAEEFunction;
begin
result := TDAEEFunction.Create;
Result.Name := aName;
fItems.Add(Result);
end;
procedure TDAEEFunctionList.Clear;
var
i: Integer;
begin
for i := fItems.Count -1 downto 0 do begin
TDAEEFunction(fItems[i]).Free;
end;
fItems.Clear;
end;
constructor TDAEEFunctionList.Create;
begin
inherited Create;
fItems := TList.Create;
end;
procedure TDAEEFunctionList.Delete(I: Integer);
begin
TDAEEFunction(fItems[i]).Free;
fItems.Delete(i);
end;
destructor TDAEEFunctionList.Destroy;
begin
Clear;
fItems.Free;
inherited Destroy;
end;
function TDAEEFunctionList.GetCount: Integer;
begin
result := fItems.Count;
end;
function TDAEEFunctionList.GetItem(i: Integer): TDAEEFunction;
begin
result := TDAEEFunction(fItems[i]);
end;
{ TDAStdExpressionEvaluator }
procedure TDAStdExpressionEvaluator.Convert(Sender: TDAExpressionEvaluator;
const aIdentifier: string; const Args: array of Variant;
out aValue: Variant);
var
aType: TVarType;
s: string;
begin
if Length(Args) <> 2 then raise EDAEvaluationException.Create('2 arguments expected for "Convert"');
s := FastUppercase(args[1]);
if (s = 'SYSTEM.BYTE') or (s = 'BYTE') then aType := varByte else
if (s = 'SYSTEM.SBYTE') or (s = 'SBYTE') then aType := varShortInt else
if (s = 'SYSTEM.INT16') or (s = 'INT16') then aType := varSmallint else
if (s = 'SYSTEM.UINT16') or (s = 'UINT16') then aType := varWord else
if (s = 'SYSTEM.INT32') or (s = 'INT32') then aType := varInteger else
if (s = 'SYSTEM.UINT32') or (s = 'UINT32') then aType := varLongWord else
if (s = 'SYSTEM.INT64') or (s = 'INT64') then aType := varInt64 else
if (s = 'SYSTEM.UINT64') or (s = 'UINT64') then aType := VarInt64 else
if (s = 'SYSTEM.STRING') or (s = 'STRING') then aType := varOleStr else
if (s = 'SYSTEM.SINGLE') or (s = 'SINGLE') then aType := varSIngle else
if (s = 'SYSTEM.DOUBLE') or (s = 'DOUBLE') then aType := varDouble else
if (s = 'SYSTEM.CHAR') or (s = 'CHAR') then aType := varOleStr else
if (s = 'SYSTEM.DATETIME') or (s = 'DATETIME') then aType := varDate else
raise EDAEvaluationException.Create('Unknown type used for conversion: "'+args[1]+'"');
VarCast(aValue, Args[0], aType);
end;
constructor TDAStdExpressionEvaluator.Create;
begin
inherited Create;
FunctionList.Add('Convert').OnCall := Convert;
FunctionList.Add('Len').OnCall := Len;
FunctionList.Add('IsNull').OnCall := IsNull;
FunctionList.Add('IIF').OnCall := IIF;
FunctionList.Add('Trim').OnCall := Trim;
FunctionList.Add('SubString').OnCall := SubString;
end;
procedure TDAStdExpressionEvaluator.IIF(Sender: TDAExpressionEvaluator;
const aIdentifier: string; const Args: array of Variant;
out aValue: Variant);
begin
if Length(Args) <> 3 then raise EDAEvaluationException.Create('3 arguments expected for "IIF"');
if Args[0] then
aValue := Args[1]
else
aValue := Args[2];
end;
procedure TDAStdExpressionEvaluator.IsNull(Sender: TDAExpressionEvaluator;
const aIdentifier: string; const Args: array of Variant;
out aValue: Variant);
begin
if Length(Args) <> 2 then raise EDAEvaluationException.Create('2 arguments expected for "IsNull"');
if VarIsError(Args[0]) or VarIsNull(Args[0]) then
aValue := Args[1]
else
aValue := Args[0];
end;
procedure TDAStdExpressionEvaluator.Len(Sender: TDAExpressionEvaluator;
const aIdentifier: string; const Args: array of Variant;
out aValue: Variant);
begin
if Length(Args) <> 1 then raise EDAEvaluationException.Create('1 argument expected for "Len"');
aValue := Length(WideString(Args[0]));
end;
procedure TDAStdExpressionEvaluator.Substring(
Sender: TDAExpressionEvaluator; const aIdentifier: string;
const Args: array of Variant; out aValue: Variant);
var
w: WideString;
s: string;
begin
if Length(Args) <> 3 then raise EDAEvaluationException.Create('3 arguments expected for "Substring"');
if VarType(Args[0]) = varOleStr then begin
w := Args[0];
aValue := Copy(w, Integer(args[1])-1, Integer(Args[2])); // zero based
end else begin
s := Args[0];
aValue := Copy(s, Integer(args[1])-1, Integer(Args[2])); // zero based
end;end;
procedure TDAStdExpressionEvaluator.Trim(Sender: TDAExpressionEvaluator;
const aIdentifier: string; const Args: array of Variant;
out aValue: Variant);
var
w: WideString;
s: string;
begin
if Length(Args) <> 1 then raise EDAEvaluationException.Create('1 argument expected for "Trim"');
if VarType(Args[0]) = varOleStr then begin
w := Args[0];
while (length(w) > 0) and ((w[1] = #13) or (w[1] = #10) or (w[1] = #9) or (w[1] = #32)) do
Delete(w, 1, 1);
while (length(w) > 0) and ((w[Length(w)] = #13) or (w[Length(w)] = #10) or (w[Length(w)] = #9) or (w[Length(w)] = #32)) do
Delete(w, Length(w), 1);
aValue := w;
end else begin
s := Args[0];
while (length(s) > 0) and ((s[1] = #13) or (s[1] = #10) or (s[1] = #9) or (s[1] = #32)) do
Delete(s, 1, 1);
while (length(s) > 0) and ((s[Length(s)] = #13) or (s[Length(s)] = #10) or (s[Length(s)] = #9) or (s[Length(s)] = #32)) do
Delete(s, Length(s), 1);
aValue := s;
end;
end;
end.