Porting a Delphi/Win32 Code Parser to Delphi Prism

For an internal QA/Docs project, we’re working on a tool that, given a .NET library, Delphi .pas file or Objective-C .h file emits an XML file with all public types and members of those types. This tool will be used as part of the process to make sure we have all publicly accessible members documented in our wiki. There are few parsers available that understand all Delphi code (our own Prism parser does not support all Delphi/Win32 constructs, so it wasn’t an option for this little project), however there is one out there, that has always worked very well: mwdelpar, which Jacob Thurman took over as part of his work on Castalia: Castalia Delphi Parser. This is a free parser for Delphi licensed under the MPL.

Since I needed this to work in Delphi Prism, I spend an hour porting the code to Prism, and in this blog post I’ll cover some of the things I had to change to get it working.

Unit names and uses

For the 4 units involved, I had to change the unit header, as I wanted them all in the same namespace. The original looked like

unit CastaliaPasLex
I changed it to:
{$IFDEF PRISM} Castalia.Pascal.Parser{$ELSE} CastaliaPasLex {$ENDIF}
;

The second thing needed was to add a {$IFDEF PRISM} {$ENDIF} around the uses clauses.

After that I set the Allow Globals, Delphi Compatibility and the Allow Create keyword option and added *{$IFDEF PRISM} {$ENDIF}*around the destructors found in the file.

Making types public

Another difference in Prism is that the types are internal (assembly) by default and cannot be used outside the assembly. What I did is add {$IFDEF PRISM}public {$ENDIF} before the enums and classes I needed to expose.

Converting pointer to record to classes

The parser code had types like:

PDefineRec =^TDefineRec; TDefineRec =record Defined: Boolean; StartCount: Integer; Next: PDefineRec;end;
This linked list could be converted to a generic LinkedList supported by .NET, but in this case it was much easier to just turn it into a class:
{$IFDEF PRISM} PDefineRec = TDefineRec;{$ELSE} PDefineRec =^TDefineRec;{$ENDIF} TDefineRec ={$IFNDEF PRISM}record{$ELSE}classpublic{$ENDIF} Defined: Boolean; StartCount: Integer; Next: PDefineRec;end;
Most of the code that used this worked right away after that, except for the places it used a ^ to dereference the Next pointer.

Function pointers

The Prism compiler is a bit more strict when it comes to function pointers, especially when assigning a function to a variable of a function pointer type. A simple fix was to replace:

fIdentFuncTable[I]:= Func15;
with
fIdentFuncTable[I]:={$IFDEF PRISM}@{$ENDIF} Func15;
## Enum Members

The next part was slightly harder to do: Delphi Prism requires enums to be prefixed by the type that they’re defined in. This could be solved with a simple (interactive) search/replace in the file, since all keywords started with pt, I did a search/replace on ‘ pt’ and ‘[pt’ with ‘ {$IFDEF PRISM}TptTokenKind{$ENDIF}.pt’ or ‘[{$IFDEF PRISM}TptTokenKind{$ENDIF}.pt‘.

The Rest

After that, there were a only few small tweaks left to do, like changing calls to GetEnumValue to value.ToString, replace a few PChars with strings and a few RTL string functions to their .NET counter parts. All in all, it only took about an hour to convert the entire parser – and with the ifdefs I used it still compiles in Delphi/Win32, too.