Componentes.Terceros.jvcl/official/3.32/examples/JvDiagramShape/3. DependencyWalker/JclParseUses.pas

959 lines
23 KiB
ObjectPascal

{******************************************************************
JEDI-VCL Demo
Copyright (C) 2002 Project JEDI
Original author:
Contributor(s):
You may retrieve the latest version of this file at the JEDI-JVCL
home page, located at http://jvcl.sourceforge.net
The contents of this file are used with permission, subject to
the Mozilla Public License Version 1.1 (the "License"); you may
not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1_1Final.html
Software distributed under the License is distributed on an
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
******************************************************************}
unit JclParseUses;
{$I jvcl.inc}
interface
uses
Classes, SysUtils;
type
EUsesListError = class(Exception);
TUsesList = class
private
FText: string;
function GetCount: Integer;
function GetItems(Index: Integer): string;
public
constructor Create(const AText: PChar);
function Add(const UnitName: string): Integer;
function IndexOf(const UnitName: string): Integer;
procedure Insert(Index: Integer; const UnitName: string);
procedure Remove(Index: Integer);
property Text: string read FText;
property Count: Integer read GetCount;
property Items[Index: Integer]: string read GetItems; default;
end;
TCustomGoal = class
public
constructor Create(Text: PChar); virtual; abstract;
end;
TProgramGoal = class(TCustomGoal)
private
FTextAfterUses: string;
FTextBeforeUses: string;
FUsesList: TUsesList;
public
constructor Create(Text: PChar); override;
destructor Destroy; override;
property TextAfterUses: string read FTextAfterUses;
property TextBeforeUses: string read FTextBeforeUses;
property UsesList: TUsesList read FUsesList;
end;
TLibraryGoal = class(TCustomGoal)
private
FTextAfterUses: string;
FTextBeforeUses: string;
FUsesList: TUsesList;
public
constructor Create(Text: PChar); override;
destructor Destroy; override;
property TextAfterUses: string read FTextAfterUses;
property TextBeforeUses: string read FTextBeforeUses;
property UsesList: TUsesList read FUsesList;
end;
TUnitGoal = class(TCustomGoal)
private
FTextAfterImpl: string;
FTextAfterIntf: string;
FTextBeforeIntf: string;
FUsesImpl: TUsesList;
FUsesIntf: TUsesList;
public
constructor Create(Text: PChar); override;
destructor Destroy; override;
property TextAfterImpl: string read FTextAfterImpl;
property TextAfterIntf: string read FTextAfterIntf;
property TextBeforeIntf: string read FTextBeforeIntf;
property UsesImpl: TUsesList read FUsesImpl;
property UsesIntf: TUsesList read FUsesIntf;
end;
function CreateGoal(Text: PChar): TCustomGoal;
implementation
uses
{$IFDEF COMPILER6_UP}
RtlConsts;
{$ELSE}
Consts;
{$ENDIF}
const
Blanks: TSysCharSet = [#9, #10, #13, ' '];
SLibrary = 'library';
SProgram = 'program';
SUnit = 'unit';
SUses = 'uses';
resourcestring
SDuplicateUnit = 'Duplicate unit ''%s''';
SInvalidLibrary = 'Invalid library';
SInvalidProgram = 'Invalid program';
SInvalidUnit = 'Invalid unit';
SInvalidUses = 'Invalid uses clause';
function PeekIdentifier(var P:PChar):boolean;forward;
function PeekKeyword(var P: PChar; Keyword: PChar): Boolean; forward;
function ReadIdentifier(var P: PChar): string; forward;
procedure SkipCommentsAndBlanks(var P: PChar); forward;
//----------------------------------------------------------------------------
function CheckIdentifier(var P: PChar): Boolean;
begin
Result := P^ in ['A'..'Z', '_', 'a'..'z'];
if Result then
begin
Inc(P);
while P^ in ['0'..'9', 'A'..'Z', '_', 'a'..'z'] do
Inc(P);
end;
end;
//----------------------------------------------------------------------------
function CheckKeyword(var P: PChar; Keyword: PChar): Boolean;
var
KeywordLen: Integer;
begin
KeywordLen := StrLen(Keyword);
Result := StrLIComp(P, Keyword, KeywordLen) = 0;
if Result then
Inc(P, KeywordLen);
end;
//----------------------------------------------------------------------------
function CreateGoal(Text: PChar): TCustomGoal;
var
P: PChar;
begin
Result := nil;
P := Text;
SkipCommentsAndBlanks(P);
if PeekKeyword(P, SProgram) then
Result := TProgramGoal.Create(Text)
else
if PeekKeyword(P, SLibrary) then
Result := TLibraryGoal.Create(Text)
else
if PeekKeyword(P, SUnit) then
Result := TUnitGoal.Create(Text);
end;
//----------------------------------------------------------------------------
function PeekKeyword(var P: PChar; Keyword: PChar): Boolean;
var
KeywordLen: Integer;
begin
KeywordLen := StrLen(Keyword);
Result := StrLIComp(P, KeyWord, KeywordLen) = 0;
end;
//----------------------------------------------------------------------------
function PeekIdentifier(var P: PChar):boolean;
var Q:PChar;
begin
Q := P;
Result := CheckIdentifier(P);
P := Q;
end;
function ReadIdentifier(var P: PChar): string;
var
PStart: PChar;
begin
Result := '';
if P^ in ['A'..'Z', '_', 'a'..'z'] then
begin
PStart := P;
Inc(P);
while P^ in ['0'..'9', 'A'..'Z', '_', 'a'..'z'] do
Inc(P);
SetString(Result, PStart, P - PStart);
end;
end;
//----------------------------------------------------------------------------
procedure SkipChars(var P: PChar; Chars: TSysCharSet);
begin
while P^ in Chars do
Inc(P);
end;
//----------------------------------------------------------------------------
procedure SkipComments(var P: PChar);
var
Test: PChar;
begin
if P^ = '{' then
begin
Test := StrScan(P, '}');
if Test <> nil then
P := Test + 1;
end
else
if StrLComp(P, '(*', 2) = 0 then
begin
Test := StrPos(P, '*)');
if Test <> nil then
P := Test + 2;
end
else
if StrLComp(P, '//', 2) = 0 then
begin
Test := StrPos(P, #13#10);
if Test <> nil then
P := Test + 2;
end;
end;
//----------------------------------------------------------------------------
procedure SkipCommentsAndBlanks(var P: PChar);
var
Test: PChar;
begin
repeat
Test := P;
SkipChars(P, Blanks);
SkipComments(P);
until Test = P;
end;
//----------------------------------------------------------------------------
{ TUsesList private }
//----------------------------------------------------------------------------
function TUsesList.GetCount: Integer;
var
P: PChar;
begin
Result := 0;
if FText = '' then
Exit;
P := PChar(FText);
// an empty uses clause consisting of only blanks and comments
// (resulting from removal of the last unit) is valid too
SkipCommentsAndBlanks(P);
if P^ = #0 then
Exit;
if not CheckKeyword(P, SUses) then
raise EUsesListError.Create(SInvalidUses);
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
Inc(Result);
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',':
Inc(P);
';':
Break;
else
if not PeekIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
end;
end;
end;
//----------------------------------------------------------------------------
function TUsesList.GetItems(Index: Integer): string;
var
P, PIdentifier: PChar;
I: Integer;
begin
Result := '';
if (Index < 0) or (Index > Count - 1) then
raise EUsesListError.CreateFmt(SListIndexError, [Index]);
P := PChar(FText);
if not CheckKeyword(P, SUses) then
raise EUsesListError.Create(SInvalidUses);
I := -1;
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
PIdentifier := P;
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
Inc(I);
if I = Index then
begin
while PIdentifier^ in ['0'..'9', 'A'..'Z', '_', 'a'..'z'] do
begin
Result := Result + PIdentifier^;
Inc(PIdentifier);
end;
Exit;
end;
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',':
Inc(P);
';':
Break;
else
if not PeekIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
end;
end;
end;
//----------------------------------------------------------------------------
{ TUsesList public }
//----------------------------------------------------------------------------
constructor TUsesList.Create(const AText: PChar);
var
P, PStart: PChar;
begin
inherited Create;
FText := '';
if AText = nil then
Exit;
PStart := PChar(AText);
P := PStart;
if CheckKeyword(P, SUses) then
begin
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',':
Inc(P);
';':
begin
Inc(P);
Break;
end;
else
if not PeekIdentifier(P) then
raise EUsesListError.Create(SInvalidUses)
end;
end;
SetString(FText, PStart, P - PStart);
end;
end;
//----------------------------------------------------------------------------
function TUsesList.Add(const UnitName: string): Integer;
var
I: Integer;
P: PChar;
begin
Result := -1;
I := IndexOf(UnitName);
if I <> -1 then
raise EUsesListError.CreateFmt(SDuplicateUnit, [UnitName]);
if FText = '' then
begin
FText := Format('%s'#13#10' %s;'#13#10#13#10, [SUses, UnitName]);
try
Result := IndexOf(UnitName);
except
FText := '';
raise;
end;
end
else
begin
P := PChar(FText);
if not CheckKeyword(P, SUses) then
raise EUsesListError.Create(SInvalidUses);
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',':
Inc(P);
';':
begin
System.Insert(Format(', %s', [UnitName]), FText, P - PChar(FText) + 1);
Result := IndexOf(UnitName);
Break;
end;
else
raise EUsesListError.Create(SInvalidUses);
end;
end;
end;
end;
//----------------------------------------------------------------------------
function TUsesList.IndexOf(const UnitName: string): Integer;
var
P, PIdentifier: PChar;
Identifier: string;
I: Integer;
begin
Result := -1;
if FText = '' then
Exit;
P := PChar(FText);
if not CheckKeyword(P, SUses) then
raise EUsesListError.Create(SInvalidUses);
I := -1;
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
PIdentifier := P;
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
SetString(Identifier, PIdentifier, P - PIdentifier);
Inc(I);
if AnsiCompareText(UnitName, Identifier) = 0 then
begin
Result := I;
Exit;
end;
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',':
Inc(P);
';':
Break;
else
raise EUsesListError.Create(SInvalidUses);
end;
end;
end;
//----------------------------------------------------------------------------
procedure TUsesList.Insert(Index: Integer; const UnitName: string);
var
I: Integer;
P: PChar;
begin
if (Index < 0) or (Index > Count - 1) then
raise EUsesListError.CreateFmt(SListIndexError, [Index]);
I := IndexOf(UnitName);
if I <> -1 then
raise EUsesListError.CreateFmt(SDuplicateUnit, [UnitName]);
if FText = '' then
begin
FText := Format('%s'#13#10' %s;'#13#10#13#10, [SUses, UnitName]);
try
if Index <> IndexOf(UnitName) then
Exit;
except
FText := '';
raise;
end;
end
else
begin
P := PChar(FText);
if not CheckKeyword(P, SUses) then
raise EUsesListError.Create(SInvalidUses);
I := -1;
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
Inc(I);
if I = Index then
begin
System.Insert(Format('%s, ', [UnitName]), FText, P - PChar(FText) + 1);
Exit;
end;
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',':
Inc(P);
else
raise EUsesListError.Create(SInvalidUses);
end;
end;
end;
end;
//----------------------------------------------------------------------------
procedure TUsesList.Remove(Index: Integer);
var
Count, I, DelPos: Integer;
P, PIdentifier: PChar;
begin
Count := GetCount;
if (Index < 0) or (Index > Count - 1) then
raise EUsesListError.CreateFmt(SListIndexError, [Index]);
P := PChar(FText);
if not CheckKeyword(P, SUses) then
raise EUsesListError.Create(SInvalidUses);
if (Count = 1) and (Index = 0) then
begin
Delete(FText, 1, Length(SUses));
P := PChar(FText);
end;
I := -1;
while P^ <> #0 do
begin
SkipCommentsAndBlanks(P);
Inc(I);
if I = Index then
begin
// remove unit
PIdentifier := P;
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
DelPos := PIdentifier - PChar(FText) + 1;
Delete(FText, DelPos, P - PIdentifier);
// skip comments and blanks
P := PChar(FText) + DelPos - 1;
PIdentifier := P;
SkipCommentsAndBlanks(P);
// check <unitname> in <filename> syntax
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
DelPos := PIdentifier - PChar(FText) + 1;
Delete(FText, DelPos, P - PIdentifier);
P := PChar(FText) + DelPos - 1;
end;
// remove separator
case P^ of
',', ';':
begin
DelPos := P - PChar(FText) + 1;
Delete(FText, DelPos, 1);
end;
else
raise EUsesListError.Create(SInvalidUses);
end;
// remove trailing spaces, if any
PIdentifier := PChar(FText) + DelPos - 1;
P := PIdentifier;
SkipChars(P, Blanks);
DelPos := PIdentifier - PChar(FText) + 1;
Delete(FText, DelPos, P - PIdentifier);
// skip further comments and blanks
P := PChar(FText) + DelPos - 1;
SkipCommentsAndBlanks(P);
Exit;
end;
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUses);
SkipCommentsAndBlanks(P);
if PeekKeyword(P, 'in') then
begin
Inc(P, 2);
SkipCommentsAndBlanks(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
while not (P^ in [#0, '''']) do
Inc(P);
if P^ <> '''' then
raise EUsesListError.Create(SInvalidUses);
Inc(P);
SkipCommentsAndBlanks(P);
end;
case P^ of
',', ';':
begin
// make sure semicolon is the last separator in case the last unit is going to be removed
if (Index = Count - 1) and (I = Index - 1) then
P^ := ';';
Inc(P);
end;
else
raise EUsesListError.Create(SInvalidUses);
end;
end;
end;
//----------------------------------------------------------------------------
{ TProgramGoal public }
//----------------------------------------------------------------------------
constructor TProgramGoal.Create(Text: PChar);
var
P, PStart: PChar;
begin
FTextBeforeUses := '';
FTextAfterUses := '';
PStart := Text;
P := PStart;
// check 'program' label
SkipCommentsAndBlanks(P);
if not CheckKeyword(P, SProgram) then
raise EUsesListError.Create(SInvalidProgram);
SkipCommentsAndBlanks(P);
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidProgram);
SkipCommentsAndBlanks(P);
if P^ <> ';' then
raise EUsesListError.Create(SInvalidProgram);
Inc(P);
SkipCommentsAndBlanks(P);
// remember text before uses
SetString(FTextBeforeUses, PStart, P - PStart);
if PeekKeyword(P, SUses) then
begin
FUsesList := TUsesList.Create(P);
PStart := P + Length(FUsesList.Text);
end
else // empty uses list
begin
FUsesList := TUsesList.Create(nil);
PStart := P;
end;
// remember text after uses
P := StrEnd(PStart);
SetString(FTextAfterUses, PStart, P - PStart);
end;
//----------------------------------------------------------------------------
destructor TProgramGoal.Destroy;
begin
FUsesList.Free;
inherited Destroy;
end;
//----------------------------------------------------------------------------
{ TLibraryGoal public }
//----------------------------------------------------------------------------
constructor TLibraryGoal.Create(Text: PChar);
var
P, PStart: PChar;
begin
FTextBeforeUses := '';
FTextAfterUses := '';
PStart := Text;
P := PStart;
// check 'library' label
SkipCommentsAndBlanks(P);
if not CheckKeyword(P, SLibrary) then
raise EUsesListError.Create(SInvalidLibrary);
SkipCommentsAndBlanks(P);
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidLibrary);
SkipCommentsAndBlanks(P);
if P^ <> ';' then
raise EUsesListError.Create(SInvalidLibrary);
Inc(P);
SkipCommentsAndBlanks(P);
// remember text before uses
SetString(FTextBeforeUses, PStart, P - PStart);
if PeekKeyword(P, SUses) then
begin
FUsesList := TUsesList.Create(P);
PStart := P + Length(FUsesList.Text);
end
else // empty uses list
begin
FUsesList := TUsesList.Create(nil);
PStart := P;
end;
// remember text after uses
P := StrEnd(PStart);
SetString(FTextAfterUses, PStart, P - PStart);
end;
//----------------------------------------------------------------------------
destructor TLibraryGoal.Destroy;
begin
FUsesList.Free;
inherited Destroy;
end;
//----------------------------------------------------------------------------
{ TUnitGoal public }
//----------------------------------------------------------------------------
constructor TUnitGoal.Create(Text: PChar);
var
P, PStart: PChar;
begin
FTextBeforeIntf := '';
FTextAfterIntf := '';
FTextAfterImpl := '';
PStart := Text;
P := PStart;
// check 'unit' label
SkipCommentsAndBlanks(P);
while (P^ <> #0) and not PeekKeyword(P, 'unit') do
begin
SkipChars(P, [#1..#255] - Blanks);
SkipCommentsAndBlanks(P);
end;
if not CheckKeyword(P, SUnit) then
raise EUsesListError.Create(SInvalidUnit);
SkipCommentsAndBlanks(P);
if not CheckIdentifier(P) then
raise EUsesListError.Create(SInvalidUnit);
SkipCommentsAndBlanks(P);
if P^ <> ';' then
raise EUsesListError.Create(SInvalidUnit);
Inc(P);
// check 'interface' label
// SkipCommentsAndBlanks(P);
while (P^ <> #0) and not PeekKeyword(P, 'interface') do
begin
SkipChars(P, [#1..#255] - Blanks);
SkipCommentsAndBlanks(P);
end;
if not CheckKeyword(P, 'interface') then
raise EUsesListError.Create(SInvalidUnit);
SkipCommentsAndBlanks(P);
// remember text before interface uses
SetString(FTextBeforeIntf, PStart, P - PStart);
if PeekKeyword(P, SUses) then
begin
FUsesIntf := TUsesList.Create(P);
PStart := P + Length(FUsesIntf.Text);
end
else
begin
FUsesIntf := TUsesList.Create(nil);
PStart := P;
end;
// locate implementation
while (P^ <> #0) and not PeekKeyword(P, 'implementation') do
begin
SkipChars(P, [#1..#255] - Blanks);
SkipCommentsAndBlanks(P);
end;
if not CheckKeyword(P, 'implementation') then
raise EUsesListError.Create(SInvalidUnit);
SkipCommentsAndBlanks(P);
// remember text after interface uses
SetString(FTextAfterIntf, PStart, P - PStart);
if PeekKeyword(P, SUses) then
begin
FUsesImpl := TUsesList.Create(P);
PStart := P + Length(FUsesImpl.Text);
end
else
begin
FUsesImpl := TUsesList.Create(nil);
PStart := P;
end;
// remember text after implementation uses
P := StrEnd(PStart);
SetString(FTextAfterImpl, PStart, P - PStart);
end;
//----------------------------------------------------------------------------
destructor TUnitGoal.Destroy;
begin
FUsesIntf.Free;
FUsesImpl.Free;
inherited Destroy;
end;
end.