Sign in Join
FPDoc Onlineexperiment

ptop - Pascal source beautifier

ptop program

ptop is a source beautifier written by Peter Grogono based on the ancient pretty-printer by Ledgard, Hueras, and Singer, modernized by the Free Pascalteam (objects, streams, configurability etc).

This configurability, and the thorough bottom-up design are the advantages of this program over the diverse Turbo Pascal source beautifiers on e.g. SIMTEL.

The program is quite simple to operate:

ptop "[-v] [-i indent] [-b bufsize ][-c optsfile] infile outfile"

The infile parameter is the Pascal file to be processed, and will be written to outfile, overwriting an existing outfile if it exists.

Some options modify the behavior of ptop:

-h

Write an overview of the possible parameters and command line syntax.

-c ptop.cfg

Read some configuration data from configuration file instead of using the internal defaults then. A config file is not required, the program can operate without one. See also -g.

-i ident

Set the number of indent spaces used for BEGIN END; and other blocks.

-b bufsize

Set the streaming buffersize to bufsize. The default is 255; 0 is considered non-valid and ignored.

-v

Be verbose. Currently only outputs the number of lines read/written and some error messages.

-g ptop.cfg

Write ptop configuration defaults to the file "ptop.cfg". The contents of this file can be changed to your liking, and it can be used with the -c option.

The ptop configuration file

Creating and distributing a configuration file for ptop is not necessary, unless you want to modify the standard behavior of ptop. The configuration file is never preloaded, so if you want to use it you should always specify it with a -c ptop.cfg parameter.

The structure of a ptop configuration file is a simple building block repeated several (20-30) times, for each Pascal keyword known to the ptop program. (See the default configuration file or ptopu.pp source to find out which keywords are known).

The basic building block of the configuration file consists of one or two lines, describing how ptop should react on a certain keyword. First comes a line without square brackets with the following format:

keyword=option1,option2,option3,...

If one of the options is "dindonkey" (see further below), a second line - with square brackets - is needed:

[keyword]=otherkeyword1,otherkeyword2,otherkeyword3,...

As you can see the block contains two types of identifiers: keywords (keyword and otherkeyword1..3 in above example) and options, (option1..3 above).

Keywords are the built-in valid Pascal structure-identifiers like BEGIN, END, CASE, IF, THEN, ELSE, IMPLEMENTATION. The default configuration file lists most of these.

Besides the real Pascal keywords, some other codewords are used for operators and comment expressions as in table Utilities that come with Free Pascal.

Keywords for operators
Name of codeword Operator
casevar : in a case label ( unequal ’colon’)
becomes :=
delphicomment //
opencomment { or (*
closecomment } or *)
semicolon ;
colon :
equals =
openparen [
closeparen ]
period .

The options codewords define actions to be taken when the keyword before the equal sign is found, as listed in table Utilities that come with Free Pascal.

Possible options
Option does what
crsupp Suppress CR before the keyword.
crbefore Force CR before keyword.
(do not use with crsupp.)
blinbefore Blank line before keyword.
dindonkey De-indent on associated keywords.
(see below)
dindent Deindent (always)
spbef Space before
spaft Space after
gobsym Print symbols which follow a
keyword but which do not
affect layout. prints until
terminators occur.
(terminators are hard-coded in pptop,
still needs changing)
inbytab Indent by tab.
crafter Force CR after keyword.
upper Prints keyword all uppercase
lower Prints keyword all lowercase
capital Capitalizes keyword: 1st letter
uppercase, rest lowercase.

The option "dindonkey" given in table table Utilities that come with Free Pascal requires some further explanation. "dindonkey" is a contraction of "DeINDent ON associated KEYword". When it is present as an option in the first line, then a second, square-bracketed, line is required. A de-indent will be performed when any of the other keywords listed in the second line are encountered in the source.

Example: The lines

else=crbefore,dindonkey,inbytab,upper
[else]=if,then,else

mean the following:

  • The keyword this block is about is else because it’s on the LEFT side of both equal signs.

  • The option crbefore signals not to allow other code (so just spaces) before the ELSE keyword on the same line.

  • The option dindonkey de-indents if the parser finds any of the keywords in the square brackets line (if,then,else).

  • The option inbytab means indent by a tab.

  • The option upper uppercase the keyword (else or Else becomes ELSE)

Try to play with the configfile step by step until you find the effect you desire. The configurability and possibilities of ptop are quite large. E.g. I like all keywords uppercased instead of capitalized, so I replaced all capital keywords in the default file by upper.

ptop is still development software. So it is wise to visually check the generated source and try to compile it, to see if ptop hasn’t introduced any errors.

ptopu unit

The source of the PtoP program is conveniently split in two files: one is a unit containing an object that does the actual beautifying of the source, the other is a shell built around this object so it can be used from the command line. This design makes it possible to include the object in a program (e.g. an IDE) and use its features to format code.

The object resides in the PtoPU unit, and is declared as follows

  TPrettyPrinter=Object(TObject)
      Indent : Integer;    { How many characters to indent ? }
      InS    : PStream;
      OutS   : PStream;
      DiagS  : PStream;
      CfgS : PStream;
      Constructor Create;
      Function PrettyPrint : Boolean;
    end;

Using this object is very simple. The procedure is as follows:

  1. Create the object, using its constructor.

  2. Set the InS stream. This is an open stream, from which Pascal source will be read. This is a mandatory step.

  3. Set the OutS stream. This is an open stream, to which the beautified Pascal source will be written. This is a mandatory step.

  4. Set the DiagS stream. Any diagnostics will be written to this stream. This step is optional. If you don’t set this, no diagnostics are written.

  5. Set the CfgS stream. A configuration is read from this stream. (see the previous section for more information about configuration). This step is optional. If you don’t set this, a default configuration is used.

  6. Set the Indent variable. This is the number of spaces to use when indenting. Tab characters are not used in the program. This step is optional. The indent variable is initialized to 2.

  7. Call PrettyPrint. This will pretty-print the source in InS and write the result to OutS. The function returns True if no errors occurred, False otherwise.

So, a minimal procedure would be:

Procedure CleanUpCode;

var
  Ins,OutS : PBufStream;
  PPRinter : TPrettyPrinter;

begin
  Ins:=New(PBufStream,Init('ugly.pp',StopenRead,TheBufSize));
  OutS:=New(PBufStream,Init('beauty.pp',StCreate,TheBufSize));
  PPrinter.Create;
  PPrinter.Ins:=Ins;
  PPrinter.outS:=OutS;
  PPrinter.PrettyPrint;
end;

Using memory streams allows very fast formatting of code, and is particularly suitable for editors.

An experiment, built from development snapshots. Not the official Free Pascal or Lazarus documentation.