Files
maszyna/QueryParserComp.pas
2015-04-16 11:50:14 +02:00

734 lines
18 KiB
ObjectPascal

unit QueryParserComp;
(*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*)
interface
uses
Classes, Sysutils;
resourcestring
sTextNotSet = 'You must set the TextToParse property first.';
sIllegalSpecialChar = 'Illegal special character.';
sIllegalStringChar = 'Illegal string delimiter.';
type
TTokenType = (ttString, ttSymbol, ttComment, ttDelimiter, ttSpecialChar,
ttStatementDelimiter, ttCommentedSymbol, ttCommentDelimiter);
TSetOfChar = set of Char;
TComment = array[0..1] of string;
TCharacterType = (ctSymbol, ctBeginComment, ctEndComment, ctDelimiter,
ctString, ctSpecialChar);
TCommentType = set of (cmt1, cmt2, cmt3);
const
CR = #13;
LF = #10;
TAB = #9;
CRLF = CR + LF;
Delimiters: TSetOfChar = [' ', ',', ';', CR, LF, TAB];
Comments: array[0..2] of TComment = (('/*', '*/'), ('#', LF), ('//', LF));
type
TEndOfStatement = procedure(Sender: TObject; SQLStatement: String) of object;
TQueryParserComp = class(TComponent)
private
FStream: TStringStream;
FEOF: Boolean;
FToken: String;
FTokenType: TTokenType;
FComment: Boolean;
FString: Boolean;
FWasString: Boolean;
FCommentType: TCommentType;
FStringDelimiters: String;
FLastStringDelimiterFound: String;
FSymbolsCount: Integer;
FSpecialCharacters: String;
FRemoveStrDelimiter: Boolean;
FStringToParse: String;
FGoPosition: Integer;
FOnStatementDelimiter: TEndOfStatement;
FCountFromStatement: Boolean;
FStatementDelimiters: TStringList;
FStringDelimiter: Char;
FGenerateOnStmtDelimiter: Boolean;
procedure Init;
procedure SetStringToParse(AStringToParse: String);
function StatementDelimiter: Boolean;
function CheckForBeginComment: Boolean;
function CheckForEndComment(Character: Char): Boolean;
function CharacterType(Character: Char): TCharacterType;
function CheckCharcterType(Character: Char): Boolean;
function StringDelimiter(Character: Char): Boolean;
function SpecialCharacter(Character: Char): Boolean;
procedure RemoveStringDelimiter(var Source: String);
procedure SetDelimiterType(Source: String);
procedure SetToken;
procedure SetSD(ASD: TStringList);
procedure SetSpecialCharacters(ASpecialCharacters: String);
procedure SetStringDelimiters(AStringDelimiters: String);
protected
procedure DoStatementDelimiter; dynamic;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure LoadStringToParse(FileName: string);
procedure First;
procedure FirstToken;
procedure NextToken;
function GetNextSymbol : string;
property EndOfFile: Boolean read FEOF;
property Comment: Boolean read FComment;
property Token: String read FToken;
property TokenType: TTokenType read FTokenType;
property CurrentStringDelimiter: Char read FStringDelimiter;
property SymbolsCount: Integer read FSymbolsCount default 0;
property StringStream: TStringStream read FStream;
published
property IsEOFStmtDelimiter: Boolean read FGenerateOnStmtDelimiter
write FGenerateOnStmtDelimiter;
property StringDelimiters: String read FStringDelimiters
write SetStringDelimiters;
property SpecialCharacters: String read FSpecialCharacters
write SetSpecialCharacters;
property RemoveStrDelimiter: Boolean read FRemoveStrDelimiter
write FRemoveStrDelimiter;
property CountFromStatement: Boolean read FCountFromStatement
write FCountFromStatement;
property TextToParse: String read FStringToParse
write SetStringToParse;
property StatementDelimiters: TStringList read FStatementDelimiters write SetSD;
property OnStatementDelimiter: TEndOfStatement read FOnStatementDelimiter
write FOnStatementDelimiter;
end;
procedure Register;
implementation
{ TSQLParser }
procedure Register;
begin
RegisterComponents('Samples', [TQueryParserComp]);
end;
constructor TQueryParserComp.Create(AOwner: TComponent);
begin
inherited;
FStatementDelimiters := TStringList.Create;
FStatementDelimiters.Add('GO');
FStatementDelimiters.Add(';');
FCountFromStatement := True;
end;
procedure TQueryParserComp.LoadStringToParse(FileName: string);
var
fs:TFileStream;
size:integer;
begin
fs:= TFileStream.Create(FileName, fmOpenRead);
size:= fs.Size;
if Assigned(FStream) then
FStream.Free;
FStream := TStringStream.Create('');
FStream.CopyFrom(fs,size);
fs.Free;
Init;
First;
end;
procedure TQueryParserComp.FirstToken;
begin
Init;
NextToken;
end;
function TQueryParserComp.CheckForBeginComment: Boolean;
var
Buffer: String;
begin
Result := False;
if not FEOF and not FString then
begin
Buffer := FStream.ReadString(1);
if cmt1 in FCommentType then
if Buffer[1] = '*' then
begin
FCommentType := [cmt1];
Result := True;
end;
if cmt2 in FCommentType then
if Buffer[1] = '/' then
begin
FCommentType := [cmt2];
Result := True;
end;
if cmt3 in FCommentType then
if Buffer[1] = '-' then
begin
FCommentType := [cmt3];
Result := True;
end;
FStream.Seek(-1, soFromCurrent);
end;
end;
procedure TQueryParserComp.SetDelimiterType(Source: String);
begin
FToken := Source[1];
if ((cmt2 in FCommentType) or (cmt3 in FCommentType)) and FComment
and ((Source[1] = CR) or (Source[1] = LF)) then
begin
FComment := False;
FTokenType := ttCommentDelimiter;
end
else
FTokenType := ttDelimiter;
end;
procedure TQueryParserComp.NextToken;
var
Buffer: String;
ETextNotSet: Exception;
begin
if FEOF then
Exit;
if not Assigned(FStream) then
begin
ETextNotSet := Exception.Create(sTextNotSet);
raise ETextNotSet;
end;
if not FString then
FStringDelimiter := ' ';
FToken := '';
if not FEOF then
begin
Buffer := FStream.ReadString(1);
if Length(Buffer) > 0 then
begin
if (Buffer[1] in Delimiters) and not (FString or FComment)then
SetDelimiterType(Buffer)
else
begin
if FStream.Position > 0 then
FStream.Seek(-1, soFromCurrent);
SetToken;
end;
end
else
FEOF := True;
end;
case FTokenType of
ttSymbol: Inc(FSymbolsCount);
ttString:
begin
FStringDelimiter := FToken[1];
if FRemoveStrDelimiter then
RemoveStringDelimiter(FToken);
end;
end;
if StatementDelimiter then
FTokenType := ttStatementDelimiter;
if FEOF then
begin
FLastStringDelimiterFound := '';
FWasString := False;
FString := False;
if FGenerateOnStmtDelimiter then
DoStatementDelimiter;
end;
end;
function TQueryParserComp.GetNextSymbol : string;
begin
GetNextSymbol:= '';
while ( not EndOfFile) do
begin
NextToken;
if (TokenType=ttSymbol) then
begin
GetNextSymbol:= Token;
break;
end
end
end;
function TQueryParserComp.StatementDelimiter: Boolean;
var
i: Integer;
begin
Result := False;
if not FString then
for i := 0 to FStatementDelimiters.Count - 1 do
begin
Result := (UpperCase(FToken) = UpperCase(FStatementDelimiters.Strings[i]));
if Result then
begin
if FCountFromStatement then
FSymbolsCount := 0;
DoStatementDelimiter;
Break;
end;
end;
end;
function TQueryParserComp.CharacterType(Character: Char): TCharacterType;
begin
Result := ctSymbol;
case Character of
'/':
begin
if not FComment then
begin
FCommentType := [cmt1, cmt2];
if CheckForBeginComment then
Result := ctBeginComment;
end;
end;
'-':
begin
if not FComment then
begin
FCommentType := [cmt3];
if CheckForBeginComment then
Result := ctBeginComment;
end;
end;
'*':
begin
if CheckForEndComment(Character) then
Result := ctEndComment;
end;
CR, LF, ' ', ',', TAB:
begin
if CheckForEndComment(Character) then
Result := ctEndComment
else
Result := ctDelimiter;
if FString and ((Character = CR) or (Character = LF)) then
begin
FString := False;
FWasString := False;
Result := ctDelimiter;
end;
end;
end;
if not FString then
if SpecialCharacter(Character) then
begin
if not FComment then
Result := ctSpecialChar;
end;
if not FComment then
if StringDelimiter(Character) then
begin
Result := ctSymbol;
if FString then
begin
FLastStringDelimiterFound := '';
FWasString := True;
FString := False;
end
else
begin
FWasString := False;
FString := True;
end;
end;
end;
function TQueryParserComp.CheckForEndComment(Character: Char): Boolean;
var
Buffer: String;
begin
Result := False;
if not FComment or FString then
Exit;
if not FEOF then
begin
if cmt1 in FCommentType then
begin
Buffer := FStream.ReadString(1);
if Buffer[1] = '/' then
Result := True;
FStream.Seek(-1, soFromCurrent);
end;
if (cmt2 in FCommentType) or (cmt3 in FCommentType) then
begin
if (Character = CR) or (Character = LF) then
Result := True;
end;
end;
end;
function TQueryParserComp.CheckCharcterType(Character: Char): Boolean;
var
Buffer: String;
begin
Result := False;
case CharacterType(Character) of
ctBeginComment:
begin
if not FComment then
begin
if FToken <> '' then
begin
FStream.Seek(-1, soFromCurrent);
FTokenType := ttSymbol;
end
else
begin
FComment := True;
FToken := FToken + Character;
Buffer := FStream.ReadString(1);
FToken := FToken + Buffer;
FTokenType := ttComment;
end;
Result := True;
end;
end;
ctEndComment:
begin
if FComment then
begin
Result := True;
if FToken <> '' then
begin
FStream.Seek(-1, soFromCurrent);
FTokenType := ttCommentedSymbol;
end
else
begin
FComment := False;
FToken := FToken + Character;
if FCommentType = [cmt1] then
begin
Buffer := FStream.ReadString(1);
FToken := FToken + Buffer;
end;
FTokenType := ttComment;
end;
end;
end;
ctSymbol:
begin
FToken := FToken + Character;
if FComment then
FTokenType := ttCommentedSymbol
else
begin
if FString or FWasString then
begin
if FWasString then
begin
FTokenType := ttString;
FWasString := False;
Result := True;
end
else
FTokenType := ttString;
end
else
FTokenType := ttSymbol;
end;
end;
ctSpecialChar:
begin
if FToken <> '' then
begin
FStream.Seek(-1, soFromCurrent);
FTokenType := ttSymbol;
end
else
begin
FToken := FToken + Character;
FTokenType := ttSpecialChar;
end;
Result := True;
end;
ctDelimiter:
begin
FTokenType := ttDelimiter;
Result := True;
end;
end
end;
procedure TQueryParserComp.RemoveStringDelimiter(var Source: String);
var
EndOfString: Integer;
i: Integer;
begin
for i := 1 to Length(FStringDelimiters) do
begin
EndOfString := 1;
while not (EndOfString = 0) do
begin
EndOfString := Pos(FStringDelimiters[i], Source);
if EndOfString <> 0 then
begin
FLastStringDelimiterFound := Copy(FStringDelimiters, i, 1);
Delete(Source, EndOfString, 1);
end;
end;
end;
end;
function TQueryParserComp.StringDelimiter(Character: Char): Boolean;
var
i: Integer;
Buffer: String;
begin
Result := False;
for i := 1 to Length(FStringDelimiters) do
if (Character = FStringDelimiters[i]) then
begin
if (FLastStringDelimiterFound = '') then
FLastStringDelimiterFound := FStringDelimiters[i];
if (FLastStringDelimiterFound = FStringDelimiters[i]) then
begin
if not FEOF then
Buffer := FStream.ReadString(1);
if Length(Buffer) > 0 then
begin
if Buffer[1] <> Character then
begin
FStream.Seek(-1, soFromCurrent);
Result := True;
end
else
FToken := FToken + Buffer;
end
else
Result := True;
end;
end;
end;
function TQueryParserComp.SpecialCharacter(Character: Char): Boolean;
var
i: Integer;
begin
Result := False;
for i := 1 to Length(FSpecialCharacters) do
if Character = FSpecialCharacters[i] then
Result := True;
end;
procedure TQueryParserComp.SetToken;
var
EndToken: Boolean;
Buffer: String;
begin
EndToken := False;
while not (EndToken or FEOF) do
begin
FEOF := FStream.Position >= FStream.Size-1;
Buffer := FStream.ReadString(1);
if not (Buffer[1] in Delimiters) then
EndToken := CheckCharcterType(Buffer[1])
else
begin
if not (FString or FComment) then
begin
EndToken := True;
FStream.Seek(-1, soFromCurrent);
end
else
begin
if FString and ((Buffer[1] = CR) or (Buffer[1] = LF)) then
begin
FString := False;
FWasString := False;
EndToken := True;
end
else
if FComment and ((cmt2 in FCommentType) or
(cmt3 in FCommentType)) and ((Buffer[1] = CR) or
(Buffer[1] = LF)) then
begin
EndToken := True;
FStream.Seek(-1, soFromCurrent);
FComment := False;
FCommentType := [];
end
else
FToken := FToken + Buffer;
end;
end;
end; //while
end;
destructor TQueryParserComp.Destroy;
begin
if Assigned(FStream) then
FStream.Free;
inherited Destroy;
end;
procedure TQueryParserComp.DoStatementDelimiter;
var
Buffer: String;
CurrentPosition: Integer;
begin
if Assigned(FOnStatementDelimiter) then
begin
CurrentPosition := FStream.Position;
FStream.Seek(FGoPosition, soFromBeginning);
Buffer := FStream.ReadString(CurrentPosition-FGoPosition-Length(FToken));
FStream.Seek(Length(FToken), soFromCurrent);
if FStream.Position >= FStream.Size then
FEOF := True;
FGoPosition := FStream.Position;
if FCountFromStatement then
FSymbolsCount := 0;
FOnStatementDelimiter(Self, Trim(Buffer));
end;
end;
procedure TQueryParserComp.SetStringToParse(AStringToParse: String);
begin
if AStringToParse = '' then
Exit;
if Assigned(FStream) then
FStream.Free;
TrimRight(AStringToParse);
FStream := TStringStream.Create(AStringToParse);
FStringToParse := AStringToParse;
Init;
end;
procedure TQueryParserComp.SetSD(ASD: TStringList);
begin
FStatementDelimiters.Assign(ASD);
end;
procedure TQueryParserComp.First;
begin
Init;
end;
procedure TQueryParserComp.Init;
var
ETextNotSet: Exception;
begin
if not Assigned(FStream) then
begin
ETextNotSet := Exception.Create(sTextNotSet);
raise ETextNotSet;
end;
FStream.Seek(0, soFromBeginning);
FToken := '';
FTokenType := ttString;
FComment := False;
FCommentType := [];
FEOF := False;
FSymbolsCount := 0;
FGoPosition := 0;
FLastStringDelimiterFound := '';
FWasString := False;
FString := False;
end;
procedure TQueryParserComp.SetSpecialCharacters(ASpecialCharacters: String);
var
i: Integer;
k: Integer;
IllegalSpecialChar: Exception;
begin
for i := 1 to Length(ASpecialCharacters) do
begin
for k := 0 to FStatementDelimiters.Count - 1 do
begin
if (Pos(ASpecialCharacters[i], FStatementDelimiters.Strings[k]) <> 0) then
begin
IllegalSpecialChar := Exception.Create(sIllegalSpecialChar);
raise IllegalSpecialChar;
end;
end;
if (ASpecialCharacters[i] in Delimiters) or
(Pos(ASpecialCharacters[i], FStringDelimiters) <> 0) then
begin
IllegalSpecialChar := Exception.Create(sIllegalSpecialChar);
raise IllegalSpecialChar;
end;
end;
FSpecialCharacters := ASpecialCharacters;
end;
procedure TQueryParserComp.SetStringDelimiters(AStringDelimiters: String);
var
i: Integer;
k: Integer;
IllegalStringChar: Exception;
begin
for i := 1 to Length(AStringDelimiters) do
begin
for k := 0 to FStatementDelimiters.Count - 1 do
begin
if (Pos(AStringDelimiters[i], FStatementDelimiters.Strings[k]) <> 0) then
begin
IllegalStringChar := Exception.Create(sIllegalStringChar);
raise IllegalStringChar;
end;
end;
if (AStringDelimiters[i] in Delimiters) or
(Pos(AStringDelimiters[i], FSpecialCharacters) <> 0) then
begin
IllegalStringChar := Exception.Create(sIllegalStringChar);
raise IllegalStringChar;
end;
end;
FStringDelimiters := AStringDelimiters;
end;
end.