Componentes.Terceros.RemObj.../internal/5.0.23.613/1/Data Abstract for Delphi/Source/uDAMacros.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

862 lines
24 KiB
ObjectPascal

unit uDAMacros;
{----------------------------------------------------------------------------}
{ 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;
type
TROPasToken = (
CSTI_EOF,
{Items that are used internally}
CSTIINT_Comment,
CSTIINT_WhiteSpace,
{Tokens}
CSTI_Identifier,
CSTI_SemiColon,
CSTI_Comma,
CSTI_Period,
CSTI_Colon,
CSTI_OpenRound,
CSTI_CloseRound,
CSTI_OpenBlock,
CSTI_CloseBlock,
CSTI_Assignment,
CSTI_Equal,
CSTI_NotEqual,
CSTI_Smaller,
CSTI_SmallerEqual,
CSTI_Greater,
CSTI_GreaterEqual,
CSTI_Plus,
CSTI_Minus,
CSTI_Divide,
CSTI_Multiply,
CSTI_Integer,
CSTI_Real,
CSTI_String,
CSTI_Date,
CSTI_HexInt,
CSTI_Modulus,
CSTII_In,
CSTII_like,
CSTII_And,
CSTII_Or,
CSTII_Xor,
CSTII_Not
);
{TROParserErrorKind is used to store the parser error}
TROParserErrorKind = (iNoError, iCommentError, iStringError, iCharError, iSyntaxError);
TROParserErrorEvent = procedure(Parser: TObject; Kind: TROParserErrorKind) of object;
{TROPacalParser is the parser used to parse the current script}
TROPascalParser = class(TObject)
private
FData: string;
FRow,
FCol: Cardinal;
FLastEnterPos: Longint;
FText: PChar;
FRealPosition, FTokenLength: Cardinal;
FTokenId: TROPasToken;
FToken: string;
FOriginalToken: string;
FParserError: TROParserErrorEvent;
fOpenBlockEscape: Boolean;
// only applicable when Token in [CSTI_Identifier, CSTI_Integer, CSTI_Real, CSTI_String, CSTI_Char, CSTI_HexInt]
public
{ means you can use [something] to escape identifiers}
property OpenBlockEscape: Boolean read fOpenBlockEscape write fOpenBlockEscape;
{Go to the next token}
procedure Next;
{Return the token in case it is a string, char, integer, number or idenTROier}
property GetToken: string read FToken;
{Return the token but do not uppercase it}
property OriginalToken: string read FOriginalToken;
{The current token position}
property CurrTokenPos: Cardinal read FRealPosition;
{The current token ID}
property CurrTokenID: TROPasToken read FTokenId;
{Row}
property Row: Cardinal read FRow;
{Column}
property Col: Cardinal read FCol;
{Load a script}
procedure SetText(const Data: string);
{Parser error event will be called on (syntax) errors in the script}
property OnParserError: TROParserErrorEvent read FParserError write FParserError;
end;
TExternalProc = function(Sender: TObject; const Parameters: array of string): string of object;
TROMacroProc = class
private
FExternalProc: TExternalProc;
FExternalName: string;
FExternalNameHash: Longint;
fParamCount: integer;
public
property ExternalName: string read FExternalName write FExternalName;
property ExternalNameHash: Integer read FExternalNameHash write FExternalNameHash;
property ExternalProc: TExternalProc read FExternalProc write FExternalProc;
property ParamCount: integer read fParamCount write fParamCount;
end;
TROMacroVar = class
private
FName: string;
FNameHash: Longint;
FValue: string;
public
constructor Create(const aName: string);
property Name: string read FName;
property NameHash: Longint read FNameHash;
property Value: string read FValue write FValue;
end;
TOnUnknownIdentifier = function(Sender: TObject; const Name, OrgName: string; var Value: string): Boolean of object;
TROMacroParser = class(TInterfacedObject)
private
FProcs: TList;
FVars: TList;
FOnUnknownIdentifier: TOnUnknownIdentifier;
FParser: TROPascalParser;
procedure ParserError(Parser: TObject; Kind: TROParserErrorKind);
function GetVarCount: Longint;
function GetVarNo(I: Integer): TROMacroVar;
public
property OnUnknownIdentifier: TOnUnknownIdentifier read FOnUnknownIdentifier write FOnUnknownIdentifier;
procedure ClearProcs;
procedure RegisterProc(const Name: string; ExProc: TExternalProc; aParamCount: integer);
property VariableCount: Longint read GetVarCount;
property Variable[I: Longint]: TROMacroVar read GetVarNo;
procedure DeleteVariable(I: Longint);
procedure ClearVariables;
function AddVariable(const Name: string): TROMacroVar;
function IndexOfName(const aName: string): integer;
constructor Create;
function EvalToken(const Text: string): string;
function Eval(const Text: string; TextDelimiter: char = ''''): string;
destructor Destroy; override;
end;
function MakeHash(const s: string): Longint;
function FastUppercase(const s: string): string;
implementation
function MakeHash(const s: string): Longint;
{small hash maker}
var
I: Integer;
begin
Result := 0;
for I := 1 to Length(s) do
Result := ((Result shl 7) or (Result shr 25)) + Ord(s[I]);
end;
function FastUppercase(const s: string): string;
begin
Result := Uppercase(s);
end;
procedure TROPascalParser.Next;
var
Err: TROParserErrorKind;
function _GetToken(CurrTokenPos, CurrTokenLen: Cardinal): string;
var
s: string;
begin
SetLength(s, CurrTokenLen);
Move(FText[CurrTokenPos], S[1], CurrtokenLen);
Result := s;
end;
function ParseToken(var CurrTokenPos, CurrTokenLen: Cardinal; var CurrTokenId: TROPasToken): TROParserErrorKind;
{Parse the token}
var
ct, ci: Cardinal;
s: string;
hs: Boolean;
begin
ParseToken := iNoError;
ct := CurrTokenPos;
case FText[ct] of
#0: begin
CurrTokenId := CSTI_EOF;
CurrTokenLen := 0;
end;
'A'..'Z', 'a'..'z', '_': begin
ci := ct + 1;
while (FText[ci] in ['_', '0'..'9', 'a'..'z', 'A'..'Z']) do begin
Inc(ci);
end;
CurrTokenLen := ci - ct;
CurrTokenId := CSTI_Identifier;
s := FastUppercase(_GetToken(CurrTokenPos, CurrTokenLen));
case s[1] of
'I': if s = 'IN' then CurrTokenId := CSTII_In;
'L': if s = 'LIKE' then CurrTokenId := CSTII_like;
'A': if s = 'AND' then CurrTokenId := CSTII_And;
'O': if s = 'OR' then CurrTokenId := CSTII_Or;
'X': if s = 'XOR' then CurrTokenId := CSTII_Xor;
'N': if s = 'NOT' then CurrTokenId := CSTII_Not;
end;
end;
'$': begin
ci := ct + 1;
while (FText[ci] in ['0'..'9', 'a'..'f', 'A'..'F']) do
Inc(ci);
CurrTokenId := CSTI_HexInt;
CurrTokenLen := ci - ct;
end;
'0'..'9': begin
hs := False;
ci := ct;
while (FText[ci] in ['0'..'9']) do begin
Inc(ci);
if (FText[ci] = '.') and (not hs) then begin
if FText[ci + 1] = '.' then break;
hs := True;
Inc(ci);
end;
end;
if hs then
CurrTokenId := CSTI_Real
else
CurrTokenId := CSTI_Integer;
CurrTokenLen := ci - ct;
end;
#39: begin
ci := ct + 1;
while (FText[ci] <> #0) and (FText[ci] <> #13) and
(FText[ci] <> #10) and (FText[ci] <> #39) do begin
Inc(ci);
while (FText[ci] = #39) and (FText[ci + 1] = #39) do
Inc(ci, 2);
end;
if FText[ci] = #39 then
CurrTokenId := CSTI_String
else begin
CurrTokenId := CSTI_String;
ParseToken := iStringError;
end;
CurrTokenLen := ci - ct + 1;
end;
'#': begin
ci := ct + 1;
while (FText[ci] in ['0'..'9', '/']) do begin
Inc(ci);
end;
if FText[ci] <> '#' then begin
ParseToken := iCharError;
CurrTokenId := CSTI_Date;
end
else begin
inc(ci);
CurrTokenId := CSTI_Date;
end;
CurrTokenLen := ci - ct;
end;
'=': begin
CurrTokenId := CSTI_Equal;
CurrTokenLen := 1;
end;
'>': begin
if FText[ct + 1] = '=' then begin
CurrTokenid := CSTI_GreaterEqual;
CurrTokenLen := 2;
end
else begin
CurrTokenid := CSTI_Greater;
CurrTokenLen := 1;
end;
end;
'<': begin
if FText[ct + 1] = '=' then begin
CurrTokenId := CSTI_SmallerEqual;
CurrTokenLen := 2;
end
else if FText[ct + 1] = '>' then begin
CurrTokenId := CSTI_NotEqual;
CurrTokenLen := 2;
end
else begin
CurrTokenId := CSTI_Smaller;
CurrTokenLen := 1;
end;
end;
')': begin
CurrTokenId := CSTI_CloseRound;
CurrTokenLen := 1;
end;
'(': begin
if FText[ct + 1] = '*' then begin
ci := ct + 1;
while (FText[ci] <> #0) do begin
if (FText[ci] = #13) then begin
if FText[ci + 1] = #10 then
Inc(ci);
FLastEnterPos := ci;
Inc(FRow);
end
else if FText[ci] = #10 then begin
FLastEnterPos := ci;
Inc(FRow);
end;
if (FText[ci] = '*') and (FText[ci + 1] = ')') then
Break;
Inc(ci);
end;
if (FText[ci] = #0) then begin
CurrTokenId := CSTIINT_Comment;
ParseToken := iCommentError;
end
else begin
CurrTokenId := CSTIINT_Comment;
Inc(ci, 2);
end;
CurrTokenLen := ci - ct;
end
else begin
CurrTokenId := CSTI_OpenRound;
CurrTokenLen := 1;
end;
end;
'[': begin
if fOpenBlockEscape then begin
ci := ct + 1;
while not (FText[ci] in [']', #0]) do begin
if FText[ci] = '\' then
inc(Ci);
Inc(ci);
end;
if FText[ci] = ']' then Inc(ci);
CurrTokenLen := ci - ct;
CurrTokenId := CSTI_Identifier;
end else begin
CurrTokenId := CSTI_OpenBlock;
CurrTokenLen := 1;
end;
end;
']': begin
CurrTokenId := CSTI_CloseBlock;
CurrTokenLen := 1;
end;
',': begin
CurrTokenId := CSTI_Comma;
CurrTokenLen := 1;
end;
'.': begin
CurrTokenId := CSTI_Period;
CurrTokenLen := 1;
end;
';': begin
CurrTokenId := CSTI_Semicolon;
CurrTokenLen := 1;
end;
':': begin
if FText[ct + 1] = '=' then begin
CurrTokenId := CSTI_Assignment;
CurrTokenLen := 2;
end
else begin
CurrTokenId := CSTI_Colon;
CurrTokenLen := 1;
end;
end;
'+': begin
CurrTokenId := CSTI_Plus;
CurrTokenLen := 1;
end;
'-': begin
CurrTokenId := CSTI_Minus;
CurrTokenLen := 1;
end;
'%': begin
CurrTokenId := CSTI_Modulus;
CurrTokenLen := 1;
end;
'*': begin
CurrTokenId := CSTI_Multiply;
CurrTokenLen := 1;
end;
'/': begin
if FText[ct + 1] = '/' then begin
ci := ct + 1;
while (FText[ci] <> #0) and (FText[ci] <> #13) and (FText[ci] <> #10) do begin
Inc(ci);
end;
if (FText[ci] = #0) then begin
CurrTokenId := CSTIINT_Comment;
ParseToken := iCommentError;
end
else begin
if (FText[ci] = #13) and (FText[ci + 1] = #10) then
Inc(ci);
Inc(FRow);
FLastEnterPos := ci;
CurrTokenId := CSTIINT_Comment;
end;
CurrTokenLen := ci - ct + 1;
end
else begin
CurrTokenId := CSTI_Divide;
CurrTokenLen := 1;
end;
end;
#32, #9, #13, #10: begin
ci := ct + 1;
while (FText[ci] in [#32, #9, #13, #10]) do begin
if (FText[ci] = #13) then begin
if FText[ci + 1] = #10 then
Inc(ci);
FLastEnterPos := ci;
Inc(FRow);
end
else if FText[ci] = #10 then begin
FLastEnterPos := ci;
Inc(FRow);
end;
Inc(ci);
end;
CurrTokenId := CSTIINT_WhiteSpace;
CurrTokenLen := ci - ct;
end;
'{': begin
ci := ct + 1;
while (FText[ci] <> #0) and (FText[ci] <> '}') do begin
if (FText[ci] = #13) then begin
if FText[ci + 1] = #10 then
Inc(ci);
FLastEnterPos := ci;
Inc(FRow);
end
else if FText[ci] = #10 then begin
FLastEnterPos := ci;
Inc(FRow);
end;
Inc(ci);
end;
if (FText[ci] = #0) then begin
CurrTokenId := CSTIINT_Comment;
ParseToken := iCommentError;
end
else
CurrTokenId := CSTIINT_Comment;
CurrTokenLen := ci - ct + 1;
end;
else begin
ParseToken := iSyntaxError;
CurrTokenId := CSTIINT_Comment;
CurrTokenLen := 1;
end;
end;
end;
//-------------------------------------------------------------------
begin
if FText = nil then begin
FTokenLength := 0;
FRealPosition := 0;
FRow := 1;
FLastEnterPos := 0;
FTokenId := CSTI_EOF;
Exit;
end;
repeat
FRealPosition := FRealPosition + FTokenLength;
Err := ParseToken(FRealPosition, FTokenLength, FTokenID);
if Err <> iNoError then begin
FTokenLength := 0;
FTokenId := CSTI_EOF;
FToken := '';
FOriginalToken := '';
FCol := Longint(FRealPosition) - FLastEnterPos;
if @FParserError <> nil then FParserError(Self, Err);
exit;
end;
case FTokenID of
CSTIINT_Comment, CSTIINT_WhiteSpace: Continue;
CSTI_Integer, CSTI_Real, CSTI_String, CSTI_Date, CSTI_HexInt: begin
FOriginalToken := _GetToken(FRealPosition, FTokenLength);
FToken := FOriginalToken;
end;
CSTI_Identifier: begin
FOriginalToken := _GetToken(FRealPosition, FTokenLength);
FToken := FastUppercase(FOriginalToken);
end;
else begin
FOriginalToken := '';
FToken := '';
end;
end;
Break;
until False;
FCol := Longint(FRealPosition) - FLastEnterPos;
end;
procedure TROPascalParser.SetText(const Data: string);
begin
FData := Data;
FText := Pointer(FData);
FTokenLength := 0;
FRealPosition := 0;
FRow := 1;
FLastEnterPos := -1;
FTokenId := CSTI_EOF;
Next;
end;
{ TROMacroParser }
function TROMacroParser.AddVariable(const Name: string): TROMacroVar;
begin
Result := TROMacroVar.Create(Uppercase(Name));
FVars.Add(Result);
end;
procedure TROMacroParser.ClearProcs;
var
i: Longint;
begin
for i := FProcs.Count - 1 downto 0 do begin
TROMacroProc(FPRocs[i]).Free;
end;
FProcs.Clear;
end;
procedure TROMacroParser.ClearVariables;
var
i: Longint;
begin
for i := FVars.Count - 1 downto 0 do begin
TROMacroVar(FVars[i]).Free;
end;
FVars.Clear;
end;
constructor TROMacroParser.Create;
begin
inherited Create;
FProcs := TList.Create;
FVars := TList.Create;
FParser := TROPascalParser.Create;
FParser.OnParserError := ParserError;
end;
procedure TROMacroParser.DeleteVariable(I: Integer);
var
f: TROMacroVar;
begin
f := FVars[i];
FVars.Delete(i);
f.Free;
end;
destructor TROMacroParser.Destroy;
begin
FParser.Free;
ClearProcs;
ClearVariables;
FProcs.Free;
FVars.Free;
inherited Destroy;
end;
function MKString(const s: string): string;
begin
Result := StringReplace(s, #39#39, #39, [rfReplaceAll]);
Delete(Result, 1, 1);
Delete(Result, Length(Result), 1);
end;
function SQLEscapeStr(const s: string): string;
var
i, l: Longint;
c: char;
begin
SetLength(Result, Length(s) + 10);
Result[1] := '''';
i := 1;
l := 1;
while i <= length(s) do begin
case s[i] of
#13: c := 'r';
#10: c := 'n';
#9: c := 't';
'\': c := '\';
#39: c := #39;
'"': c := '"';
else
c := #0;
end;
if c = #0 then begin
inc(l);
if L > Length(Result) then
SetLength(Result, l + 10);
Result[l] := s[i];
end
else begin
inc(l, 2);
if L > Length(Result) then
SetLength(Result, l + 10);
Result[l - 1] := '\';
Result[l] := c;
end;
Inc(i);
end;
SetLength(Result, l + 1);
Result[l + 1] := '''';
end;
type
TOperator = (opFirst, opAdd);
function TROMacroParser.EvalToken(const Text: string): string;
function Evaluate: string;
var
h, i: Longint;
b: Boolean;
resval: string;
procedure CheckParamCount(P: TROMacroProc; Params: array of string);
begin
if (Length(Params) < P.ParamCount) then
raise Exception.CreateFmt('Invalid number of parameters. %d given, %d expected.',
[Length(Params), P.ParamCount])
end;
function CallProc(P: TROMacroProc): string;
var
Params: array of string;
s: string;
begin
FParser.Next;
if FParser.CurrTokenId <> CSTI_OpenRound then begin
CheckParamCount(P, Params);
Result := P.ExternalProc(Self, Params);
exit;
end;
FParser.Next;
while FParser.CurrTokenID <> CSTI_CloseRound do begin
s := Evaluate;
SetLength(Params, Length(Params) + 1);
Params[Length(Params) - 1] := s;
if FParser.CurrTokenId = CSTI_CloseRound then begin
Break;
end;
if FParser.CurrTokenId <> CSTI_Comma then
raise Exception.Create('[' + IntToStr(FParser.Row) + ':' + IntToStr(FParser.Col) + ']: Closing parenthesis expected');
FParser.Next;
end;
FParser.Next;
CheckParamCount(P, Params);
Result := P.ExternalProc(Self, Params);
end;
begin
Result := '';
while true do begin
case FParser.CurrTokenID of
CSTI_Identifier: begin
h := MakeHash(FParser.GetToken);
b := False;
for i := 0 to FProcs.Count - 1 do begin
with TROMacroProc(FProcs[i]) do begin
if (ExternalNameHash = h) and SameText(ExternalName, FParser.GetToken) then begin
resval := CallProc(TROMacroProc(FProcs[i]));
b := True;
end;
end;
end;
if (not b) then begin
for i := 0 to FVars.Count - 1 do begin
with TROMacroVar(FVars[i]) do begin
if (NameHash = h) and (Name = FParser.GetToken) then begin
FParser.Next;
resval := Value;
b := True;
break;
end;
end;
end;
end;
if (not b) and (@FOnUnknownIdentifier <> nil) then begin
b := FOnUnknownIdentifier(Self, FParser.GetToken, FParser.OriginalToken, resval);
if b then FParser.Next;
end;
if not b then
raise Exception.Create('[' + IntToStr(FParser.Row) + ':' + IntToStr(FParser.Col) + ']: Unknown identifier ' + FParser.GetToken);
end;
CSTI_Real, CSTI_Integer: begin
Resval := FParser.GetToken;
FParser.Next;
end;
CSTI_HexInt: begin
Resval := IntToStr(StrToInt(FParser.GetToken));
FParser.Next;
end;
CSTI_String, CSTI_Date: begin
Resval := '';
while (FParser.CurrTokenId = CSTI_String) or (FParser.CurrTokenId = CSTI_Date) do begin
if FParser.CurrTokenId = CSTI_String then
Resval := resval + MKString(FParser.GetToken)
else
Resval := resval + FParser.GetToken;
FParser.Next;
end;
ResVal := SQLEscapeStr(ResVal);
end;
end;
Result := Result + ResVal;
case FParser.CurrTokenId of
CSTI_Minus: Result := Result + ' - ';
CSTI_Plus: Result := Result + ' + ';
else
Break;
end;
FParser.Next;
end;
end;
begin
FParser.SetText(Text);
Result := Evaluate();
if FParser.CurrTokenID <> CSTI_EOF then
// Raise Exception.Create('['+IntToStr(FParser.Row)+':'+IntToStr(FParser.Col)+']: End of expression expected');
end;
const
LenInc = 32;
function TROMacroParser.Eval(const Text: string; TextDelimiter: char = ''''): string;
var
start, i, l: Longint;
InStr: Boolean;
s: string;
begin
SetLength(Result, Length(Text) + LenInc);
i := 1;
l := 0;
InStr := False;
while i <= length(Text) do begin
if Text[i] = '''' then
InStr := not InStr;
if (Text[i] = '{') and not (InStr) then begin
start := i;
inc(i);
InStr := False;
while i <= Length(Text) do begin
case Text[i] of
#39: InStr := not InStr;
'}': if not InStr then Break;
end;
Inc(i);
end;
s := EvalToken(Copy(Text, Start + 1, I - Start - 1));
if L + Length(s) > Length(Result) then
SetLength(Result, l + Length(s) + LenInc);
Start := 1;
while Start <= Length(s) do begin
Inc(l);
Result[l] := s[Start];
Inc(Start);
end;
Inc(i);
end
else begin
inc(l);
if L > Length(Result) then
SetLength(Result, l + LenInc);
Result[l] := Text[i];
Inc(i);
end;
end;
SetLength(Result, l);
end;
function TROMacroParser.GetVarCount: Longint;
begin
Result := FVars.Count;
end;
function TROMacroParser.GetVarNo(I: Integer): TROMacroVar;
begin
Result := FVars[i];
end;
procedure TROMacroParser.ParserError(Parser: TObject;
Kind: TROParserErrorKind);
var
err: string;
begin
case Kind of
iCommentError: err := 'Comment Error';
iCharError, iStringError: err := 'String error';
else
err := 'Syntax Error';
end;
raise Exception.Create('[' + IntToStr(FParser.Row) + ':' + IntToStr(FParser.Col) + ']: ' + err);
end;
procedure TROMacroParser.RegisterProc(const Name: string; ExProc: TExternalProc; aParamCount: integer);
var
R: TROMacroProc;
begin
R := TROMacroProc.Create;
r.ExternalName := UpperCase(Name);
r.ExternalNameHash := MakeHash(r.ExternalName);
r.ExternalProc := ExProc;
r.ParamCount := aParamCount;
FProcs.Add(r);
end;
function TROMacroParser.IndexOfName(const aName: string): integer;
begin
For Result:= 0 to FVars.Count -1 do
if SameText(aName, TROMacroVar(FVars[Result]).Name) then Exit;
Result:=-1;
end;
{ TROMacroVar }
constructor TROMacroVar.Create(const aName: string);
begin
inherited Create;
FName := aName;
FNameHash := MakeHash(aName);
end;
end.