10

I have a large codebase which I am working with which has units like this:

unit myformunit;

interface
type
  TMyForm = class(Form)
  end;

  procedure not_a_method1;

  procedure not_a_method2;

  var
     global1,global2,global3:Integer;
  ...

In short, the authors of the code did not write methods, they wrote global procedures. There are tens of thousands of them. Inside these procedures, they reference a single instance of MyForm:TMyForm.

I am considering writing a parser/rewriter utility that will turn this code into "at least minimally object oriented code". The strategy is to move the interface and implementation section globals into the form, as a start. I realize that's hardly elegant OOP. But it's a step forward from globals.

If I could do this on one unit at a time, I might be able to repair the breakage in the rest of the project, if I only did it on one form at a time. But I'd like to reduce the amount of time it takes to rewrite the units, instead of doing it by hand. Some forms have 500+ procedures and 500+ interface and implementation global variables which are in fact, specific to the state of a single instance of the form that they are in the same unit with.

Basically, what I will do if nothing like this exists is write a parser based on the Castalia Delphi parser. I'm hoping that perhaps ModelMakerCodeExplorer, or castalia, or some other similar tool has something that would at least do part of what I need for me, so I don't have to build this utility myself. Even if I do have to build it myself, I estimate it might automate about a thousand to two thousand hours of grunt-work for me. I can at least run it, and then see how much breaks, and then revert or commit after I've decided on a level of effort to refactor this code.

Alternative strategies that accomplish the same goal (go from zero encapsulation and zero OOP, to more encapsulation, and slightly more than zero OOP, in an incremental way, on a large, unstructured delphi codebase that only used objects when it was unavoidable, and never had any idea about real OOP) are welcomed.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • 5
    Having been through something similar, all I can say is, good luck, you'll need it. :( – Mason Wheeler Jun 19 '12 at 22:50
  • Great question and scenario, just I think 1-2K hours (about 40 days non-stop) is probably a bit more than it could probably take. I've done *some* pascal/dfm parsing before and with the right tools, it's actually not that difficult. I'm trying to find what I used for that right now, but I believe it was along the lines of "Gold Parser" which can have an add-on explicitly for Pascal... http://www.goldparser.org/engine/1/delphi/index.htm – Jerry Dodge Jun 19 '12 at 23:52
  • Actually, their "Grammars" include one for Delphi 7 in particular – Jerry Dodge Jun 19 '12 at 23:58
  • I cannot find my original project :( but I had it to where it could load an entire unit and display a hierarchy view of all the types/classes, their members, uses clauses, constants, and in your case, procedures. You could recognize, for example, if the procedure name starts with `Svr` or `Cli` then separate them into objects called `TMySvr.DoSomething` or `TMyCli.DoSomething`. – Jerry Dodge Jun 20 '12 at 00:04
  • 1
    Anyway my point is, if I (of all people) could do such parsing, then I'm more than confident you can accomplish this hurdle through your own parsing too. – Jerry Dodge Jun 20 '12 at 00:06
  • I have some experience using the Castalia parser. I am thinking I'll use it to build an "XML DOM" model of the code, and I will then uses a rules engine to transform it. the 2K hours is "doing it all without any tool help". I think writing such a parser and engine is about 80 hours work. – Warren P Jun 20 '12 at 02:34
  • I would also suggest writing your own. Do one or two units manually, then use that as an example to write a program that processes all your pas files. Maybe you can do it on the level of just detecting and processing ASCII strings instead of trying to parse logically. – Jan Doggen Jun 20 '12 at 06:43
  • The lexer level of Castalia delphi parser plus some regular expressions might be enough in my case. The regexes would just be so I can make "proceed" and "block" rules, which would allow me to make rules for when a procedure is not to be changed. – Warren P Jun 20 '12 at 12:30

2 Answers2

1

Changing the globals to form fields seems like just cut and paste them. You might consider moving them in a dummy procedure and use MMX to normalize the declarations first.

Then use ModelMaker Code Explorer to move the procedures and functions into the form, which is only just cut and paste in the Member View.

Not necessary, but as a next step remove the references to the form instance from the method bodies. This can be achieved by find and replace.

Or did I miss something?

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
  • It's not the cut and paste I need help with so much as the refactoring that would have to occur. Imagine that 10,000 globals moved from unitnamespace.globalname to unitnamespace.GlobalInstance.globalname. Now do a sync-edit through the entire project and update all the references that used to compile just fine. If modelmaker MMX will do a global fixup on those global references then it's my saviour. – Warren P Jun 20 '12 at 12:27
0

The Delphi Sonar plugin (open source) does not fix the code but can be used and configured to search for 'bad code':

The Delphi Sonar plugin enables analysis of projects written using Delphi or Pascal. It was tested with projects written in Delphi 6, 7, 2006 and XE. This plugin is a donation of Sabre Airline Solutions. Its tests include: Counting of lines of code, statements, number of files, classes, packages, methods, accessors, public API (methods, classes and fields), comments ratio, CPD (code duplication, how many lines, block and in how many files), Code Complexity (per method, class, file; complexity distribution over methods, classes and files), LCOM4 and RFC, Unit tests reports, Rules, Code coverage reports, Source code highlight for unit tests, “Dead” code recognition, Unused files recognition.

mjn
  • 36,362
  • 28
  • 176
  • 378
  • 2
    I can simply report length of file, and say "175,000 lines in .pas file, 175,000 lines of bad code", in this case. :-) – Warren P Jun 20 '12 at 12:29