Advertisement
Dennis07

Automatically detect circular references in units *fixed*

Jun 12th, 2018
176
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.   Lines.LoadFromFile(AFileName);
  122.   LineCount := Lines.Count;
  123.   Current := PChar(Lines.Text);
  124.   Section := sectUnknown;
  125.   while Current[0] <> #0 do
  126.   begin
  127.     case Current[0] of
  128.       '/':
  129.         if Current[1] = '/' then
  130.         begin
  131.           Inc(Current, 2);
  132.           while not((Current[0] = #13) and (Current[1] = #10)) do
  133.           begin
  134.             Inc(Current);
  135.           end;
  136.           Inc(Current, 2);
  137.         end
  138.         else
  139.         begin
  140.           Inc(Current);
  141.         end;
  142.       '(':
  143.         if Current[1] = '*' then
  144.         begin
  145.           Inc(Current, 2);
  146.           while not((Current[0] = '*') and (Current[1] = ')')) do
  147.           begin
  148.             Inc(Current);
  149.           end;
  150.           Inc(Current, 2);
  151.         end
  152.         else
  153.         begin
  154.           Inc(Current);
  155.         end;
  156.       '{':
  157.         begin
  158.           Inc(Current);
  159.           while Current[0] <> '}' do
  160.           begin
  161.             Inc(Current);
  162.           end;
  163.           Inc(Current);
  164.         end;
  165.       '''':
  166.         begin
  167.           Inc(Current);
  168.           while Current[0] <> '''' do
  169.           begin
  170.             Inc(Current);
  171.           end;
  172.           Inc(Current);
  173.         end;
  174.       'A' .. 'Z', 'a' .. 'z', '_':
  175.         begin
  176.           case Section of
  177.             sectUnknown:
  178.               if IsTerm(Current, 'application') then
  179.               begin
  180.                 Section := sectApplication;
  181.               end
  182.               else
  183.               begin
  184.                 if IsTerm(Current, 'unit') then
  185.                 begin
  186.                   Section := sectUnit;
  187.                 end;
  188.               end;
  189.             sectApplication:
  190.               if IsTerm(Current, 'uses') then
  191.               begin
  192.                 Section := sectApplicationUses;
  193.               end;
  194.             sectUnitInterface:
  195.               if IsTerm(Current, 'uses') then
  196.               begin
  197.                 Section := sectUnitInterfaceUses;
  198.               end
  199.               else
  200.               begin
  201.                 if IsTerm(Current, 'implementation') then
  202.                 begin
  203.                   Section := sectUnitImplementation;
  204.                 end;
  205.               end;
  206.             sectUnit:
  207.               if IsTerm(Current, 'interface') then
  208.               begin
  209.                 Section := sectUnitInterface;
  210.               end;
  211.             sectUnitImplementation:
  212.               if IsTerm(Current, 'uses') then
  213.               begin
  214.                 Section := sectUnitImplementationUses;
  215.               end;
  216.             // Here we only need unit interface uses clauses
  217.             { sectApplicationUses, } sectUnitInterfaceUses { , }
  218.             { sectUnitImplementationUses } :
  219.               begin
  220.                 Dependencies.Add(GetTerm(Current));
  221.                 Continue;
  222.               end;
  223.           end;
  224.           Inc(Current);
  225.           while Current[0] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'] do
  226.           begin
  227.             Inc(Current);
  228.           end;
  229.         end;
  230.       '&':
  231.         begin
  232.           Inc(Current);
  233.           while Current[0] in ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'] do
  234.           begin
  235.             Inc(Current);
  236.           end;
  237.         end;
  238.       '0' .. '9':
  239.         begin
  240.           Inc(Current);
  241.           while Current[0] in ['E', 'e', '0' .. '9', '.'] do
  242.           begin
  243.             Inc(Current);
  244.           end;
  245.         end;
  246.       '$':
  247.         begin
  248.           Inc(Current);
  249.           while Current[0] in ['A' .. 'F', 'a' .. 'f', '0' .. '9'] do
  250.           begin
  251.             Inc(Current);
  252.           end;
  253.         end;
  254.       '#':
  255.         begin
  256.           Inc(Current);
  257.           while Current[0] in ['0' .. '9'] do
  258.           begin
  259.             Inc(Current);
  260.           end;
  261.         end;
  262.       ' ', ')', '[', ']', '.', ',', ':', '=', '<', '>', '+', '-', '*', '@', '^',
  263.         #9, #10, #13:
  264.         begin
  265.           Inc(Current);
  266.         end;
  267.       ';':
  268.         begin
  269.           case Section of
  270.             sectApplicationUses:
  271.               Section := sectApplication;
  272.             sectUnitInterfaceUses:
  273.               Section := sectUnitInterface;
  274.             sectUnitImplementationUses:
  275.               Section := sectUnitImplementation;
  276.           end;
  277.           Inc(Current);
  278.         end;
  279.     else
  280.       begin
  281.         raise Exception.CreateFmt('Invalid character: %s',
  282.           [QuotedStr(Current[0])]);
  283.       end;
  284.     end;
  285.   end;
  286. end;
  287.  
  288. destructor TForm1.TUnit.Destroy;
  289. begin
  290.   Dependencies.Free;
  291.   inherited;
  292. end;
  293.  
  294. procedure TForm1.FormDestroy(Sender: TObject);
  295. begin
  296.   Units.Free;
  297. end;
  298.  
  299. procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  300.   Rect: TRect; State: TGridDrawState);
  301.  
  302.   function FindDependency(const AUnit, ADependency: String;
  303.     const ARecDepth: Integer): Boolean;
  304.   var
  305.     Current: String;
  306.   begin
  307.     if (ARecDepth <> 0) and Units.ContainsKey(AUnit) then
  308.     begin
  309.       Result := Units[AUnit].Dependencies.IndexOf(ADependency) <> -1;
  310.       if not Result then
  311.       begin
  312.         for Current in Units[AUnit].Dependencies do
  313.         begin
  314.           if FindDependency(Current, ADependency, Pred(ARecDepth)) then
  315.           begin
  316.             Exit(True);
  317.           end;
  318.         end;
  319.       end;
  320.     end
  321.     else
  322.     begin
  323.       Result := False;
  324.     end;
  325.   end;
  326.  
  327. begin
  328.   StringGrid1.Canvas.Brush.Color := StringGrid1.Color;
  329.   StringGrid1.Canvas.FillRect(Rect);
  330.   StringGrid1.Canvas.Font := StringGrid1.Font;
  331.   if ARow = 0 then
  332.   begin
  333.     StringGrid1.Canvas.Font.Style := [fsBold];
  334.   end
  335.   else
  336.   begin
  337.     if Units.ContainsKey(StringGrid1.Cells[ACol, ARow]) then
  338.     begin
  339.       if FindDependency(StringGrid1.Cells[ACol, ARow],
  340.         StringGrid1.Cols[ACol][0], 1) then
  341.       begin
  342.         StringGrid1.Canvas.Brush.Color := clRed;
  343.       end
  344.       else
  345.       begin
  346.         StringGrid1.Canvas.Brush.Color := clGreen;
  347.       end;
  348.     end;
  349.   end;
  350.   StringGrid1.Canvas.TextRect(Rect, Rect.Left + 2, Rect.Top + 2,
  351.     StringGrid1.Cells[ACol, ARow]);
  352.   if StringGrid1.ColWidths[ACol] < StringGrid1.Canvas.TextWidth
  353.     (StringGrid1.Cells[ACol, ARow]) + 8 then
  354.   begin
  355.     StringGrid1.ColWidths[ACol] := StringGrid1.Canvas.TextWidth
  356.       (StringGrid1.Cells[ACol, ARow]) + 8;
  357.   end;
  358. end;
  359.  
  360. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement