-2

The title might be a bit confusing, but basically, I want a function that can minify Delphi code.

Something like this:

function MinifyDelphiCode(delphi: String): String;
begin
// Returns the minified Delphi (.pas) code String
end;

Is there by any chance a built-in function or a library for this? or does anyone have a function that already does this?


Here's an example of what I would expect in terms of input and output:

Input:

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, System.Skia,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Skia;

type
  TForm2 = class(TForm)
    SkSvg1: TSkSvg;
    Button1: TButton;
    RadioButton1: TRadioButton;
    CheckBox1: TCheckBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

{
   gas

        dgsgdsdgs
     gdsdgs
  dgssdgdg

}

procedure TForm2.Button1Click(Sender: TObject);
  // Some comment
begin
  ShowMessage('Hello World');  // Random comment that I put here
end;

end.

Output:

unit Unit2;  interface  uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, System.Skia, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Skia;  type TForm2 = class(TForm) SkSvg1: TSkSvg; Button1: TButton; RadioButton1: TRadioButton; CheckBox1: TCheckBox; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end;  var Form2: TForm2;  implementation  {$R *.fmx}  { gas  dgsgdsdgs gdsdgs dgssdgdg  }  procedure TForm2.Button1Click(Sender: TObject); begin ShowMessage('Hello World');   end;  end. 

It doesn't need to be exactly like my output. My output is just an example.

Shaun Roselt
  • 1,650
  • 5
  • 18
  • 44
  • `Result := delphi.Replace(#13#10, #32);`. If you need to understand comments etc., you need a true Delphi parser. – Andreas Rejbrand Jul 26 '23 at 17:54
  • Anyhow, your definition of "minify" seems very arbitrary to me. You appear to want #13#10 -> #32, but you don't need to collapse multiple #32's into a single one, and you only want some kinds of comments to be removed... – Andreas Rejbrand Jul 26 '23 at 18:03
  • Yes. A Parser will almost definitely be needed as there could be comments and other things in there too. My simple input/output example is just something I quickly typed to show what I mean. – Shaun Roselt Jul 26 '23 at 18:06
  • So you want to remove `// comments` but not ``{ comments }``? – Andreas Rejbrand Jul 26 '23 at 18:07
  • I don't really care if there are comments or not. So both // and {} can be removed or they can stay. – Shaun Roselt Jul 26 '23 at 20:41
  • Minifying code that has `//` comments in it, without removing those comments, would render subsequent code unusable. `{ }` comments are safe to leave in – Remy Lebeau Jul 26 '23 at 22:09
  • 1
    Removing newlines and comments isn't particularly interesting, especially when you can load the file up into Delphi and other tools and click a button and it will reformat it right back to its origional structure but without comments. In javascript, it renames variables, rewrites certain expressions, and makes it basically unreadable no matter what you might do to reconstitute it. This can be done with Pascal as well, but ... why? It would be unmaintainable. What would be the purpose of distributing source if it's impossible to fix bugs or update? – David Schwartz Jul 27 '23 at 00:15

1 Answers1

-1

I decided to write my own function for this. Here's the code:

function MinifyDelphi(delphi: String): String;
  function RemoveCommentsFromLine(MyLine: String): String;
  begin
    var MultiLineComment := '';
    if (MyLine.Contains('//')) then
    begin
      if (MyLine[1] = '/') AND (MyLine[2] = '/') then
      begin
        MyLine := ''; // This whole line is a comment that can't be minified, so remove it.
      end else
      begin
        var StringCharCount := MyLine.CountChar('''');
        var StringCharFirst := MyLine.IndexOf('''');
        var StringCharSecond := MyLine.Substring(StringCharFirst+1).IndexOf('''') + StringCharFirst + 1;
        var CommentChar := MyLine.IndexOf('//');
        if (MyLine.CountChar('{') > 0) AND (MyLine.CountChar('}') > 0) then
            if (MyLine.IndexOf('}') > MyLine.IndexOf('{')) then
            begin
                MultiLineComment := MyLine.Remove(MyLine.IndexOf('}')+1);
                MyLine := MyLine.Substring(MultiLineComment.Length);
                StringCharCount := MyLine.CountChar('''');
                StringCharFirst := MyLine.IndexOf('''');
                StringCharSecond := MyLine.Substring(StringCharFirst+1).IndexOf('''') + StringCharFirst + 1;
                CommentChar := MyLine.IndexOf('//');
            end;

        if (StringCharCount > 1) AND (MyLine.Contains('//')) then
        begin
          if (StringCharFirst > -1) AND (StringCharSecond > -1) AND (StringCharFirst <> StringCharSecond) then
            if (StringCharFirst < CommentChar) AND (StringCharSecond > CommentChar) AND (StringCharCount mod 2 = 0) then
            begin
               // We found //, but it is within a string (quotes ' '), let's leave it.
              var CommentCharAnother := MyLine.LastIndexOf('//');
              if (CommentChar <> CommentCharAnother) then
                MyLine := MyLine.Remove(CommentCharAnother); // Somewhere else is a comment, remove it.
            end else
            begin
              if (StringCharFirst < CommentChar) AND (StringCharSecond > CommentChar) then
                CommentChar := MyLine.Substring(CommentChar+2).IndexOf('//') + CommentChar + 1;
              MyLine := MyLine.Remove(CommentChar); // Somewhere else is a comment, remove it.
            end;
        end else
          MyLine := MyLine.Remove(CommentChar); // There's no strings, remove this comment.
      end;
    end;
    Result := MultiLineComment + MyLine;
  end;
begin
  var sLine := '';
  for var I in delphi.Split([sLineBreak]) do
  begin
    var TrimmedLine := I.Trim([' ', #09, #10, #13]);
    TrimmedLine := RemoveCommentsFromLine(TrimmedLine);

    if ((sLine.Length + TrimmedLine.Length + 1) >= 1023) then
    begin
      Result := Result + sLine + sLineBreak;
      sLine := TrimmedLine + ' ';
    end else
      sLine := sLine + TrimmedLine + ' ';
  end;

  if (sLine.Length > 0) then Result := Result + sLine;
end;

This function minifies the Delphi code, but note that it removes single-line comments (//) and keeps multiline comments ({}). I remove single-line comments (//) because they can't be minified.


I tested it on various Delphi codes and it seems to work fine for the minify needs that I have. If there are any issues, then feel free to comment and I'll try to fix them or improve this function to minify better

Shaun Roselt
  • 1,650
  • 5
  • 18
  • 44
  • 1
    Test it with the line `s:= 'https://w3.org'; // havoc` enclosed by other lines. Then test the line `s:= ''; // 'good' code`. Want more test cases? How about `s:= '//'; // it's fun`? – AmigoJack Jul 26 '23 at 22:13
  • You didn't say what the goal is that minifying wants to achieve. Spaces between identifiers and operators, and similar, can be safely removed. For example, all the spaces in the uses clause except for the one after `uses` can be safely removed. And your line `var TrimmedLine := I.Trim([' ', #09, #10, #13]);` can be safely reduced to `var TrimmedLine:=I.Trim([' ',#09,#10,#13]);`. – Philip J. Rayment Jul 26 '23 at 22:26
  • Thanks for the test cases Philip and Amigo. I will test these and modify the answer accordingly! – Shaun Roselt Jul 26 '23 at 23:40
  • Thanks. I updated the code in the answer. It now works with your test cases in the comments. – Shaun Roselt Jul 27 '23 at 00:42
  • My example was bogus, I've now realised, so I've deleted it. – Philip J. Rayment Jul 27 '23 at 02:14
  • 2
    You still don't think this throughly - consider the line `{ see https://w3.org }`. Also `*)` overrides `//`, so the line `// *) more` still ends a comment at that point, not at the end of the line. Thus, your initial solution and its current version are both improper, if not even sloppy (think about also deleting unnecessary spaces inbetween). – AmigoJack Jul 27 '23 at 09:36
  • @AmigoJack I updated my answer to take `{ see https://w3.org }` into account, but I'm not sure what you mean for `// *) more` – Shaun Roselt Jul 27 '23 at 17:50
  • Have a look at [types of comments](https://docwiki.embarcadero.com/RADStudio/Sydney/en/Delphi_Comments) if you don't know the `(* comment *)` syntax. – AmigoJack Jul 27 '23 at 21:36