Skip to content

Commit

Permalink
Enh: Parse class interfaces and very basic analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveBlakeman64 committed Nov 23, 2020
1 parent be22b36 commit 09d4ecc
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ Tests/Tests.dproj.local
*.res
*.local
*.stat
*.identcache
*.dsk
40 changes: 40 additions & 0 deletions DelphiClass.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
unit DelphiClass;

interface

uses
Classes;

type
TDelphiClass = class
private
fName : String;
fRoutines: TStringList;
public
constructor Create(Name: String);
destructor Destroy; override;

property Name : String read fName;
property Routines: TStringList read fRoutines;
end;

implementation

uses
SysUtils;

{ TDelphiClass }

constructor TDelphiClass.Create(Name: String);
begin
fName:=Name;
fRoutines:=TStringList.Create;
end;

destructor TDelphiClass.Destroy;
begin
FreeAndNil(fRoutines);
inherited;
end;

end.
21 changes: 19 additions & 2 deletions DelphiProject.pas
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ implementation
uses
System.Generics.Defaults,
System.IOUtils,
SysUtils;
SysUtils, DelphiClass;

{ TDelphiProject }

Expand Down Expand Up @@ -255,6 +255,8 @@ procedure TDelphiProject.ParseUnit(U: TDelphiUnit; FileName: String; Depth: Inte
end;
end;

var
CurrentClass: TDelphiClass;
begin
if U.Parsed then
Exit;
Expand All @@ -277,9 +279,24 @@ procedure TDelphiProject.ParseUnit(U: TDelphiUnit; FileName: String; Depth: Inte
ParseUsesClause(U.InterfaceUses);

//Lex.SkipTo('implementation');

while not Lex.OptionalSym('implementation') do
begin
if Lex.SymbolIs('class') then
begin
CurrentClass:=TDelphiClass.Create(Lex.PreviousSym(1));
Lex.GetSym;
U.InterfaceClasses.Add(CurrentClass);
while not Lex.OptionalSym('end') do
begin
if Lex.OptionalSym('procedure') and not Lex.OptionalSym('(') then
CurrentClass.Routines.Add('procedure ' + GetQualifiedName)
else if Lex.OptionalSym('function') and not Lex.OptionalSym('(') then
CurrentClass.Routines.Add('function ' + GetQualifiedName)
else
Lex.GetSym;
end;
end;

if Lex.OptionalSym('procedure') and not Lex.OptionalSym('(') then
U.InterfaceRoutines.Add('procedure ' + GetQualifiedName)
else if Lex.OptionalSym('function') and not Lex.OptionalSym('(') then
Expand Down
43 changes: 27 additions & 16 deletions DelphiUnit.pas
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ interface
uses
System.Generics.Defaults,
System.Generics.Collections,
System.Classes;
System.Classes,
DelphiClass;

type

Expand All @@ -20,6 +21,7 @@ interface
duWeighting,
duDepth,
duDepthDiff,
duClasses,
duRoutines,
duIntfDependency,
duImplDependency,
Expand All @@ -43,6 +45,7 @@ TDelphiUnit = class
fContainsCycles : TLazyBool;
fInterfaceUses : TObjectList<TDelphiUnit>;
fInterfaceRoutines : TStringList;
fInterfaceClasses : TObjectList<TDelphiClass>;
fImplementationUses : TObjectList<TDelphiUnit>;

fRefsFromInterfaces : TObjectList<TDelphiUnit>; // other units referring to me form their Interface
Expand Down Expand Up @@ -76,19 +79,20 @@ TDelphiUnit = class

function DepthDifferential: Integer;

property Name : String read fName write fName;
property FileName : String read fFileName write fFileName;
property Depth : Integer read fDepth write fDepth;
property LineCount : Integer read fLineCount write fLineCount;
property Parsed : Boolean read fParsed write fParsed;
property ContainsCycles : Boolean read GetContainsCycles;
property MaxDependency : Integer read GetMaxDependency;
property MaxInterfaceDependency : Integer read GetMaxInterfaceDependency;
property InterfaceUses : TObjectList<TDelphiUnit> read fInterfaceUses;
property InterfaceRoutines : TStringList read fInterfaceRoutines;
property ImplementationUses : TObjectList<TDelphiUnit> read fImplementationUses;
property RefsFromInterfaces : TObjectList<TDelphiUnit> read fRefsFromInterfaces ;
property RefsFromImplementations: TObjectList<TDelphiUnit> read fRefsFromImplementations;
property Name : String read fName write fName;
property FileName : String read fFileName write fFileName;
property Depth : Integer read fDepth write fDepth;
property LineCount : Integer read fLineCount write fLineCount;
property Parsed : Boolean read fParsed write fParsed;
property ContainsCycles : Boolean read GetContainsCycles;
property MaxDependency : Integer read GetMaxDependency;
property MaxInterfaceDependency : Integer read GetMaxInterfaceDependency;
property InterfaceUses : TObjectList<TDelphiUnit> read fInterfaceUses;
property InterfaceClasses : TObjectList<TDelphiClass> read fInterfaceClasses;
property InterfaceRoutines : TStringList read fInterfaceRoutines;
property ImplementationUses : TObjectList<TDelphiUnit> read fImplementationUses;
property RefsFromInterfaces : TObjectList<TDelphiUnit> read fRefsFromInterfaces ;
property RefsFromImplementations: TObjectList<TDelphiUnit> read fRefsFromImplementations;
end;

TDelphiUnitComparer = class(TComparer<TDelphiUnit>)
Expand Down Expand Up @@ -121,6 +125,7 @@ procedure TDelphiUnit.Clear;
fMaxDependency:=-1;
fContainsCycles:=lbNotCalculated;
fInterfaceUses.Clear;
fInterfaceClasses.Clear;
fInterfaceRoutines.Clear;
fImplementationUses.Clear;
fRefsFromInterfaces.Clear;
Expand All @@ -136,9 +141,10 @@ constructor TDelphiUnit.Create;
fLineCount:=0;
fMaxInterfaceDependency:=-1;
fMaxDependency:=-1;
fContainsCycles :=lbNotCalculated;
fContainsCycles := lbNotCalculated;
fInterfaceUses := TObjectList<TDelphiUnit>.Create(False);
fInterfaceRoutines :=TStringList.Create;
fInterfaceClasses := TObjectList<TDelphiClass>.Create(True);
fInterfaceRoutines := TStringList.Create;
fImplementationUses := TObjectList<TDelphiUnit>.Create(False);
fRefsFromInterfaces := TObjectList<TDelphiUnit>.Create(False);
fRefsFromImplementations := TObjectList<TDelphiUnit>.Create(False);
Expand All @@ -160,6 +166,7 @@ function TDelphiUnit.DepthDifferential: Integer;
destructor TDelphiUnit.Destroy;
begin
FreeAndNil(fInterfaceUses);
FreeAndNil(fInterfaceClasses);
FreeAndNil(fInterfaceRoutines);
FreeAndNil(fImplementationUses);
FreeAndNil(fRefsFromInterfaces);
Expand Down Expand Up @@ -357,6 +364,7 @@ class function TDelphiUnit.UnitContainsCycles(UnitToProcess: TDelphiUnit): Boole
procedure TDelphiUnit.GetStatDetails(StatType: TDelphiUnitStatType; Strings: TStrings);
var
U: TDelphiUnit;
C: TDelphiClass;
begin
Strings.Clear;
case StatType of
Expand All @@ -374,6 +382,8 @@ procedure TDelphiUnit.GetStatDetails(StatType: TDelphiUnitStatType; Strings: TSt
duWeighting : Strings.AddObject(IntToStr(Weighting), Self);
duDepth : Strings.AddObject(IntToStr(Depth), Self);
duDepthDiff : Strings.AddObject(IntToStr(DepthDifferential), Self);
duClasses : for C in fInterfaceClasses do
Strings.AddObject(C.Name + ': ' + IntToStr(C.Routines.Count) + ' routines', C);
duRoutines : Strings.Assign(InterfaceRoutines);
duIntfDependency: GetDependencyTree(Self, Strings, dufInterfacesOnly, 0, 10);
duImplDependency: GetDependencyTree(Self, Strings, dufAll, 0, 10);
Expand Down Expand Up @@ -454,6 +464,7 @@ function TDelphiUnitComparer.Compare(const Left, Right: TDelphiUnit): Integer;
duWeighting : Result := CompareIntegers(Left.Weighting, Right.Weighting);
duDepth : Result := CompareIntegers(Left.Depth, Right.Depth);
duDepthDiff : Result := CompareIntegers(Left.DepthDifferential, Right.DepthDifferential);
duClasses : Result := CompareIntegers(Left.InterfaceClasses.Count, Right.InterfaceClasses.Count);
duRoutines : Result := CompareIntegers(Left.InterfaceRoutines.Count, Right.InterfaceRoutines.Count);
duIntfDependency: Result := CompareIntegers(Left.MaxInterfaceDependency, Right.MaxInterfaceDependency);
duImplDependency: Result := CompareIntegers(Left.MaxDependency, Right.MaxDependency);
Expand Down
22 changes: 17 additions & 5 deletions LexicalAnalyser.pas
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ TLexicalAnalyser = class
private
fText : String;
fPos : Integer;
fPrev : String;
fPrevPrev : String; // 2 symbols back
fPrev : String; // 1 symbols back
fSym : string;
fCh : Char;
fLineNo : Integer;
Expand All @@ -22,7 +23,6 @@ TLexicalAnalyser = class
procedure GetCh;
procedure GetASym;
procedure ShowError(S: String);
function SymbolIs(S: String): Boolean;
function TextBetween(Start: Integer; Finish: Integer): String;
function LineStart: Integer;
function LineEnd: Integer;
Expand All @@ -31,11 +31,13 @@ TLexicalAnalyser = class
constructor CreateFromFile(FileName: String);
constructor CreateFromString(S: String);

function CurrentSym: String;
function GetSym: String;
function CurrentSym: String;
function PreviousSym(SymbolsAgo: Integer): String;
function GetSym: String;

function AtEnd: Boolean;
function AtEnd: Boolean;

function SymbolIs(S: String): Boolean;
procedure RequiredSym(S: String);
function OptionalSym(S: String): Boolean;
procedure SkipToEof;
Expand Down Expand Up @@ -298,6 +300,7 @@ function TLexicalAnalyser.GetSym: String;
begin
if fSym = #0 then
raise Exception.Create('GetSym: EOF!!! previous was: ' + fSym);
fPrevPrev:=fPrev;
fPrev:=fSym;
GetASym;
Result:=fSym;
Expand Down Expand Up @@ -372,4 +375,13 @@ function TLexicalAnalyser.OptionalSym(S: String): Boolean;
end
end;

function TLexicalAnalyser.PreviousSym(SymbolsAgo: Integer): String;begin
if SymbolsAgo = 0 then
Result:=fPrev
else if SymbolsAgo = 1 then
Result:=fPrevPrev
else
raise Exception.Create('PreviousSym: too far back!');
end;

end.
Expand Down
20 changes: 20 additions & 0 deletions Main.dfm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ object FormMain: TFormMain
OnChange = PageControl1Change
object TabSheetSettings: TTabSheet
Caption = 'Project Settings'
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object PanelDPIFudge: TPanel
Left = 0
Top = 0
Expand Down Expand Up @@ -138,6 +142,10 @@ object FormMain: TFormMain
object TabSheetStatistics: TTabSheet
Caption = 'Statistics'
ImageIndex = 2
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object PanelDPIFudgeStats: TPanel
Left = 0
Top = 0
Expand Down Expand Up @@ -221,6 +229,10 @@ object FormMain: TFormMain
object TabSheetGEXF: TTabSheet
Caption = 'GEXF'
ImageIndex = 3
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object PanelFudgeGEXF: TPanel
Left = 0
Top = 0
Expand Down Expand Up @@ -278,6 +290,10 @@ object FormMain: TFormMain
object TabSheetIgnoredFiles: TTabSheet
Caption = 'Ignored Files'
ImageIndex = 4
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object PanelFudgeIgnore: TPanel
Left = 0
Top = 0
Expand Down Expand Up @@ -317,6 +333,10 @@ object FormMain: TFormMain
object TabSheetLog: TTabSheet
Caption = 'Log'
ImageIndex = 1
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object MemoLog: TMemo
Left = 0
Top = 0
Expand Down
14 changes: 9 additions & 5 deletions Main.pas
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,12 @@ implementation
cStatsColWeighting = 6;
cStatsColDepth = 7;
cStatsColDepthDiff = 8;
cStatsColIntfProcs = 9;
cStatsColIntfDependency = 10;
cStatsColImplDependency = 11;
cStatsColCyclic = 12;
cStatsColFileName = 13;
cStatsColIntfClasses = 9;
cStatsColIntfProcs = 10;
cStatsColIntfDependency = 11;
cStatsColImplDependency = 12;
cStatsColCyclic = 13;
cStatsColFileName = 14;

procedure TFormMain.ButtonAddSearchPathClick(Sender: TObject);
begin
Expand Down Expand Up @@ -237,6 +238,7 @@ procedure TFormMain.InitGrid;
AddHeading( cStatsColWeighting, 'Weighting', 100);
AddHeading( cStatsColDepth, 'Depth', 100);
AddHeading( cStatsColDepthDiff, 'Depth Diff', 100);
AddHeading( cStatsColIntfClasses, 'Intf Classes', 100);
AddHeading( cStatsColIntfProcs, 'Intf Routines', 100);
AddHeading( cStatsColIntfDependency, 'Intf Dependency', 100);
AddHeading( cStatsColImplDependency, 'Impl Dependency', 100);
Expand Down Expand Up @@ -264,6 +266,7 @@ procedure TFormMain.LoadGrid(SortCol: TDelphiUnitStatType; Ascending: Boolean; S
StringGridStats.Cells[cStatsColWeighting, Row] := IntToStr(U.Weighting);
StringGridStats.Cells[cStatsColDepth , Row] := IntToStr(U.Depth);
StringGridStats.Cells[cStatsColDepthDiff, Row] := IntToStr(U.DepthDifferential);
StringGridStats.Cells[cStatsColIntfClasses,Row] := IntToStr(U.InterfaceClasses.Count);
StringGridStats.Cells[cStatsColIntfProcs, Row] := IntToStr(U.InterfaceRoutines.Count);
StringGridStats.Cells[cStatsColIntfDependency, Row] := IntToStr(U.MaxInterfaceDependency);
StringGridStats.Cells[cStatsColImplDependency, Row] := IntToStr(U.MaxDependency);
Expand Down Expand Up @@ -371,6 +374,7 @@ function TFormMain.StatTypeForCol(Col: Integer): TDelphiUnitStatType;
cStatsColWeighting : Result:= duWeighting;
cStatsColDepth : Result:= duDepth;
cStatsColDepthDiff : Result:= duDepthDiff;
cStatsColIntfClasses : Result:= duClasses;
cStatsColIntfProcs : Result:= duRoutines;
cStatsColFileName : Result:= duFileName;
cStatsColIntfDependency : Result:= duIntfDependency;
Expand Down
23 changes: 23 additions & 0 deletions Tests/LexicalAnalyserTests.pas
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ TLexicalAnalyserTests = class
[TestCase('Parenthesis: array', '[A],[,A,]')]
procedure TestParenthesis(const InputString : String; const Open, Content, Close: String);

[Test]
[TestCase('Previous', 'A = B,=,A')]
procedure TestPrevious(const InputString : String; const Previous0, Previous1: String);

end;

implementation
Expand Down Expand Up @@ -206,6 +210,25 @@ procedure TLexicalAnalyserTests.TestParenthesis(const InputString, Open, Content
end;
end;

procedure TLexicalAnalyserTests.TestPrevious(const InputString, Previous0, Previous1: String);
var
Lex: TLexicalAnalyser;
FirstSym: String;
SecondSym: String;
ThirdSym: String;
begin
Lex:=TLexicalAnalyser.CreateFromString(InputString);
try
FirstSym := Lex.CurrentSym;
SecondSym := Lex.GetSym;
ThirdSym := Lex.GetSym;
Assert.AreEqual(Lex.PreviousSym(0), Previous0);
Assert.AreEqual(Lex.PreviousSym(1), Previous1);
finally
FreeAndNil(Lex);
end;
end;

procedure TLexicalAnalyserTests.TestUnary(const InputString, Op, Operand: String);
var
Lex: TLexicalAnalyser;
Expand Down
Loading

0 comments on commit 09d4ecc

Please sign in to comment.