Advertisement
Dennis07

Automatically detect circular references in units *fixed*

Jun 12th, 2018
184
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. unit uMain;
  2.  
  3. interface
  4.  
  5. uses
  6.   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  7.   System.Classes, System.Generics.Collections, System.Math, Vcl.Graphics,
  8.   Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, System.IOUtils,
  9.   Vcl.Direct2D, Vcl.Grids;
  10.  
  11. type
  12.   TForm1 = class(TForm)
  13.     FileOpenDialog1: TFileOpenDialog;
  14.     StringGrid1: TStringGrid;
  15.     procedure FormCreate(Sender: TObject);
  16.     procedure FormDestroy(Sender: TObject);
  17.     procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  18.       Rect: TRect; State: TGridDrawState);
  19.   private
  20.   { Private-Deklarationen }
  21.     type
  22.  
  23.     TUnit = class
  24.     public
  25.       LineCount: Integer;
  26.       Dependencies: TStringList;
  27.       constructor Create(const AFileName: String);
  28.       destructor Destroy; override;
  29.     end;
  30.  
  31.   var
  32.     Units: TObjectDictionary<String, TUnit>;
  33.   public
  34.     { Public-Deklarationen }
  35.   end;
  36.  
  37. var
  38.   Form1: TForm1;
  39.  
  40. implementation
  41.  
  42. {$R *.dfm}
  43.  
  44. procedure TForm1.FormCreate(Sender: TObject);
  45. var
  46.   Current: String;
  47.   Index: Integer;
  48. begin
  49.   Units := TObjectDictionary<String, TUnit>.Create([doOwnsValues]);
  50.   if not FileOpenDialog1.Execute then
  51.   begin
  52.     Application.Terminate;
  53.     Exit;
  54.   end;
  55.   for Current in TDirectory.GetFiles(FileOpenDialog1.FileName, '*.pas') do
  56.   begin
  57.     Units.Add(TPath.GetFileNameWithoutExtension(Current),
  58.       TUnit.Create(Current));
  59.   end;
  60.   StringGrid1.ColCount := Units.Count;
  61.   if Units.Count <> 0 then
  62.   begin
  63.     StringGrid1.Rows[0].AddStrings(Units.Keys.ToArray);
  64.     for Index := 0 to Pred(StringGrid1.ColCount) do
  65.     begin
  66.       if StringGrid1.RowCount <= Units[StringGrid1.Cols[Index][0]].Dependencies.Count
  67.       then
  68.       begin
  69.         StringGrid1.RowCount :=
  70.           Succ(Units[StringGrid1.Cols[Index][0]].Dependencies.Count);
  71.       end;
  72.       StringGrid1.Cols[Index].AddStrings(Units[StringGrid1.Cols[Index][0]]
  73.         .Dependencies);
  74.     end;
  75.   end;
  76. end;
  77.  
  78. { TForm1.TUnit }
  79.  
  80. constructor TForm1.TUnit.Create(const AFileName: String);
  81.  
  82.   function IsTerm(const AStr: PChar; const ATerm: String): Boolean;
  83.   var
  84.     Index: Integer;
  85.   begin
  86.     for Index := 0 to Pred(ATerm.Length) do
  87.     begin
  88.       if (AStr[Index] = #0) or
  89.         (UpCase(AStr[Index]) <> UpCase(ATerm.Chars[Index])) then
  90.       begin
  91.         Exit(False);
  92.       end;
  93.     end;
  94.     Result := AStr[Index] in [' ', #9, #10, #13];
  95.   end;
  96.  
  97.   function GetTerm(var AStr: PChar): String;
  98.   begin
  99.     Initialize(Result);
  100.     while AStr[0] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_', '.', ' ', #9,
  101.       #10, #13] do
  102.     begin
  103.       if not(AStr[0] in [' ', #9, #10, #13]) then
  104.       begin
  105.         Result := Concat(Result, AStr[0]);
  106.       end;
  107.       Inc(AStr);
  108.     end;
  109.   end;
  110.  
  111. var
  112.   Lines: TStringList;
  113.   Current: PChar;
  114.   Section: (sectUnknown, sectApplication, sectApplicationUses, sectUnit,
  115.     sectUnitInterface, sectUnitInterfaceUses, sectUnitImplementation,
  116.     sectUnitImplementationUses);
  117. begin
  118.   inherited Create;
  119.   Dependencies := TStringList.Create;
  120.   Lines := TStringList.Create;
  121.   try
  122.     Lines.LoadFromFile(AFileName);
  123.     LineCount := Lines.Count;
  124.     Current := PChar(Lines.Text);
  125.     Section := sectUnknown;
  126.     while Current[0] <> #0 do
  127.     begin
  128.       case Current[0] of
  129.         '/':
  130.           if Current[1] = '/' then
  131.           begin
  132.             Inc(Current, 2);
  133.             while not((Current[0] = #13) and (Current[1] = #10)) do
  134.             begin
  135.               Inc(Current);
  136.             end;
  137.             Inc(Current, 2);
  138.           end
  139.           else
  140.           begin
  141.             Inc(Current);
  142.           end;
  143.         '(':
  144.           if Current[1] = '*' then
  145.           begin
  146.             Inc(Current, 2);
  147.             while not((Current[0] = '*') and (Current[1] = ')')) do
  148.             begin
  149.               Inc(Current);
  150.             end;
  151.             Inc(Current, 2);
  152.           end
  153.           else
  154.           begin
  155.             Inc(Current);
  156.           end;
  157.         '{':
  158.           begin
  159.             Inc(Current);
  160.             while Current[0] <> '}' do
  161.             begin
  162.               Inc(Current);
  163.             end;
  164.             Inc(Current);
  165.           end;
  166.         '''':
  167.           begin
  168.             Inc(Current);
  169.             while Current[0] <> '''' do
  170.             begin
  171.               Inc(Current);
  172.             end;
  173.             Inc(Current);
  174.           end;
  175.         'A' .. 'Z', 'a' .. 'z', '_':
  176.           begin
  177.             case Section of
  178.               sectUnknown:
  179.                 if IsTerm(Current, 'application') then
  180.                 begin
  181.                   Section := sectApplication;
  182.                 end
  183.                 else
  184.                 begin
  185.                   if IsTerm(Current, 'unit') then
  186.                   begin
  187.                     Section := sectUnit;
  188.                   end;
  189.                 end;
  190.               sectApplication:
  191.                 if IsTerm(Current, 'uses') then
  192.                 begin
  193.                   Section := sectApplicationUses;
  194.                 end;
  195.               sectUnitInterface:
  196.                 if IsTerm(Current, 'uses') then
  197.                 begin
  198.                   Section := sectUnitInterfaceUses;
  199.                 end
  200.                 else
  201.                 begin
  202.                   if IsTerm(Current, 'implementation') then
  203.                   begin
  204.                     Section := sectUnitImplementation;
  205.                   end;
  206.                 end;
  207.               sectUnit:
  208.                 if IsTerm(Current, 'interface') then
  209.                 begin
  210.                   Section := sectUnitInterface;
  211.                 end;
  212.               sectUnitImplementation:
  213.                 if IsTerm(Current, 'uses') then
  214.                 begin
  215.                   Section := sectUnitImplementationUses;
  216.                 end;
  217.               // Here we only need unit interface uses clauses
  218.               { sectApplicationUses, } sectUnitInterfaceUses { , }
  219.               { sectUnitImplementationUses } :
  220.                 begin
  221.                   Dependencies.Add(GetTerm(Current));
  222.                   Continue;
  223.                 end;
  224.             end;
  225.             Inc(Current);
  226.             while Current[0] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'] do
  227.             begin
  228.               Inc(Current);
  229.             end;
  230.           end;
  231.         '&':
  232.           begin
  233.             Inc(Current);
  234.             while Current[0] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'] do
  235.             begin
  236.               Inc(Current);
  237.             end;
  238.           end;
  239.         '0' .. '9':
  240.           begin
  241.             Inc(Current);
  242.             while Current[0] in ['E', 'e', '0' .. '9', '.'] do
  243.             begin
  244.               Inc(Current);
  245.             end;
  246.           end;
  247.         '$':
  248.           begin
  249.             Inc(Current);
  250.             while Current[0] in ['A' .. 'F', 'a' .. 'f', '0' .. '9'] do
  251.             begin
  252.               Inc(Current);
  253.             end;
  254.           end;
  255.         '#':
  256.           begin
  257.             Inc(Current);
  258.             while Current[0] in ['0' .. '9'] do
  259.             begin
  260.               Inc(Current);
  261.             end;
  262.           end;
  263.         ' ', ')', '[', ']', '.', ',', ':', '=', '<', '>', '+', '-', '*', '@',
  264.           '^', #9, #10, #13:
  265.           begin
  266.             Inc(Current);
  267.           end;
  268.         ';':
  269.           begin
  270.             case Section of
  271.               sectApplicationUses:
  272.                 Section := sectApplication;
  273.               sectUnitInterfaceUses:
  274.                 Section := sectUnitInterface;
  275.               sectUnitImplementationUses:
  276.                 Section := sectUnitImplementation;
  277.             end;
  278.             Inc(Current);
  279.           end;
  280.       else
  281.         begin
  282.           raise Exception.CreateFmt('Invalid character: %s',
  283.             [QuotedStr(Current[0])]);
  284.         end;
  285.       end;
  286.     end;
  287.   finally
  288.     Lines.Free;
  289.   end;
  290. end;
  291.  
  292. destructor TForm1.TUnit.Destroy;
  293. begin
  294.   Dependencies.Free;
  295.   inherited;
  296. end;
  297.  
  298. procedure TForm1.FormDestroy(Sender: TObject);
  299. begin
  300.   Units.Free;
  301. end;
  302.  
  303. procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  304.   Rect: TRect; State: TGridDrawState);
  305.  
  306.   function FindDependency(const AUnit, ADependency: String;
  307.     const ARecDepth: Integer): Boolean;
  308.   var
  309.     Current: String;
  310.   begin
  311.     if (ARecDepth <> 0) and Units.ContainsKey(AUnit) then
  312.     begin
  313.       Result := Units[AUnit].Dependencies.IndexOf(ADependency) <> -1;
  314.       if not Result then
  315.       begin
  316.         for Current in Units[AUnit].Dependencies do
  317.         begin
  318.           if FindDependency(Current, ADependency, Pred(ARecDepth)) then
  319.           begin
  320.             Exit(True);
  321.           end;
  322.         end;
  323.       end;
  324.     end
  325.     else
  326.     begin
  327.       Result := False;
  328.     end;
  329.   end;
  330.  
  331. begin
  332.   StringGrid1.Canvas.Brush.Color := StringGrid1.Color;
  333.   StringGrid1.Canvas.FillRect(Rect);
  334.   StringGrid1.Canvas.Font := StringGrid1.Font;
  335.   if ARow = 0 then
  336.   begin
  337.     StringGrid1.Canvas.Font.Style := [fsBold];
  338.   end
  339.   else
  340.   begin
  341.     if Units.ContainsKey(StringGrid1.Cells[ACol, ARow]) then
  342.     begin
  343.       if FindDependency(StringGrid1.Cells[ACol, ARow],
  344.         StringGrid1.Cols[ACol][0], 1) then
  345.       begin
  346.         StringGrid1.Canvas.Brush.Color := clRed;
  347.       end
  348.       else
  349.       begin
  350.         StringGrid1.Canvas.Brush.Color := clGreen;
  351.       end;
  352.     end;
  353.   end;
  354.   StringGrid1.Canvas.TextRect(Rect, Rect.Left + 2, Rect.Top + 2,
  355.     StringGrid1.Cells[ACol, ARow]);
  356.   if StringGrid1.ColWidths[ACol] < StringGrid1.Canvas.TextWidth
  357.     (StringGrid1.Cells[ACol, ARow]) + 8 then
  358.   begin
  359.     StringGrid1.ColWidths[ACol] := StringGrid1.Canvas.TextWidth
  360.       (StringGrid1.Cells[ACol, ARow]) + 8;
  361.   end;
  362. end;
  363.  
  364. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement