ZetCode

Algol 68 listings and pretty printing

last modified November 14, 2026

A compiler that only reports errors is hard to work with, because the source text and the structure that the compiler sees are two different things. Algol 68 Genie can write both: a pretty-printed copy of the source in a regular layout, and a listing file that shows the parsed program, its syntax tree, the mode table, and the prelude that the program is compiled against.

This article reformats a crowded program, reads the sections of the listing file, follows the tree of one declaration, looks up a mode number, dumps the standard prelude, and explains which switches produce each section.

A program to inspect

The examples use a four-line program with two variables and a print call. It is short enough to compare the source with the listing, and large enough to contain declarations, a formula, and a predeclared procedure.

calc.a68
BEGIN
  INT a := 3;
  INT b := a * a;
  print((b, newline))
END

The program prints 9 in the default field of an integer, which is eleven characters wide and includes the sign. That output appears at the end of every run below, because the switches add reports rather than change the result.

$ a68g calc.a68
         +9

Pretty printing

The --pretty-print option reads the source and writes a reformatted copy to a file whose name is the source name with the extension replaced by .f. The program is not run, and the original file is not changed. The copy puts one unit per line, aligns the body of a clause, and spaces the assignments and operators.

ugly.a68
BEGIN INT a:=3;INT b:=a*a;
IF b>5 THEN print(("big",newline)) ELSE print(("small",newline)) FI
END

The result is a valid program that can be compiled or run directly. The printer puts THEN, ELSE, and FI on lines of their own, writes a space before the argument list of print, and leaves a blank line between the declarations. The file does not end with a newline, and the blank lines contain the spaces that the indentation would use.

$ a68g --pretty-print ugly.a68
$ cat ugly.f
BEGIN INT a := 3;
      
      INT b := a * a;
      
      IF b > 5
      THEN print (("big", newline))
      ELSE print (("small", newline))
      FI
END
$ a68g ugly.f
big

The listing file

The --listing option writes a file with the extension .l. Its default sections are a source listing, a syntax tree listing, and a pragmat listing that records the switches and the source file name. For the four-line program the file has 59 lines.

calc.a68
BEGIN
  INT a := 3;
  INT b := a * a;
  print((b, newline))
END

The source listing interleaves the source lines with the tags that the block declares. Each tag has a number, the mode, the name, and the node it was built from, so tag 1298 REF INT a, node 59, line 2 ties the variable to its declaration. The pragmat listing at the end is the record of how the run was started.

$ a68g --listing calc.a68
         +9
$ head -39 calc.l
Algol 68 Genie 2.8.4
Copyright 2016 Marcel van der Veer <algol68g@xs4all.nl>.

This is free software covered by the GNU General Public License.
There is ABSOLUTELY NO WARRANTY for Algol 68 Genie;
not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

Please report bugs to Marcel van der Veer <algol68g@xs4all.nl>.

Compilation is supported.
Parallel-clause is supported.
Curses is not supported.
Regular expressions are supported.
TCP/IP is supported.
GNU libplot is not supported.
GNU Scientific Library is not supported.
PostgreSQL is not supported.
GNU libc version glibc 2.35.
GNU libpthread version NPTL 2.35.

File "calc.a68

Source listing
------ -------

1     BEGIN

[level 5, in level 4, 64 increment]
     tag 1299 REF INT b, node 73, line 3
     tag 1298 REF INT a, node 59, line 2
     tag 1301 generator INT, node 73, line 3
     tag 1300 generator INT, node 59, line 2

2       INT a := 3;
3       INT b := a * a;
4       print((b, newline))
5     END

The first block of the file is the banner that the interpreter prints for every listing, including the features that the build supports. The opening quote of the file name is not closed in the output, which is a quirk of the printer rather than a sign of a broken listing.

$ tail -6 calc.l

Pragmat listing
------- -------

1: --listing
2: calc.a68

Syntax trees

The --tree option adds the parse tree that each source line produced. Every node has an address, a parser phase, a mode number, and a name such as serial-clause or int-denotation, and the indentation shows the nesting. The same program grows from 59 to 156 lines.

calc.a68
BEGIN
  INT a := 3;
  INT b := a * a;
  print((b, newline))
END

The excerpt below is the tree of the first declaration. Reading from the outside in, the line is a declaration list, the list holds a variable declaration, the declaration has a REF INT declarer, and the declarer defines the identifier a with the denotation 3. Node numbers increase with the position in the file, and the mode numbers point into the mode table that --moids prints.

$ a68g --listing --tree calc.a68
         +9
$ sed -n '62,81p' calc.l
2       INT a := 3;

Syntax tree
     20 000051 p01 05-04-00 #0361 (VOID) serial-clause, genie_unit
     21 000052 p01 05-04-00        initialiser-series, genie_unit
     22 000053 p01 05-04-00         initialiser-series, genie_unit
     23 000054 p01 05-04-00         |declaration-list, genie_unit
     24 000055 p01 05-04-00         | variable-declaration, genie_unit
     25 000056 p01 05-04-00 #0035   |  REF INT declarer, genie_unit
     26 000057 p01 05-04-00 #0008   |  |INT indicant, genie_unit
     27 000058 p01 05-04-00         |  | int-symbol "INT", genie_unit
     25 000059 p01 05-04-00 #0035   |  | defining-identifier "a", tag 001298, mode 000035, genie_unit
     25 000060 p01 05-04-00         |  assign-symbol ":="
     25 000061 p01 05-04-00 #0008   |  INT unit, genie_constant
     26 000062 p01 05-04-00 #0008   |   INT tertiary, genie_constant
     27 000063 p01 05-04-00 #0008   |    INT secondary, genie_constant
     28 000064 p01 05-04-00 #0008   |     INT primary, genie_constant
     29 000065 p01 05-04-00 #0008   |      INT denotation, genie_constant
     30 000066 p01 05-04-00 #0008   |       INT int-denotation "3", genie_unit
     22 000067 p01 05-04-00         semi-symbol ";"

The first two columns identify the node and the node it hangs from, so they are listing addresses rather than source line numbers. The source line is printed above each tree, and the phase column stays at p01 for a program that was parsed in one pass.

$ wc -l calc.l
156 calc.l

Modes

Every mode that the program uses receives a number, and the --moids option adds a table that lists them from the highest number down. The entries show the mode's structure, its size in bytes, and whether it was used; the prelude occupies the low numbers, and modes built for the program come first. The file grows to 425 lines for the same four-line program.

calc.a68
BEGIN
  INT a := 3;
  INT b := a * a;
  print((b, newline))
END

The top of the table names the clause modes, and the last entries are the aliases that the prelude declares, such as STRING for FLEX [] CHAR. The mode number in a tree node can be looked up here: #0035 in the declaration above is the REF INT entry with the same number.

$ a68g --listing --moids calc.a68
         +9
$ sed -n '/Mode listing/,+6p' calc.l
Mode listing
---- -------

     #363 "SERIES" (#0) used
     #362 "SERIES" (#361) used
     #361 "SERIES" (VOID) used
     #360 "STOWED" (#359) size 64
     #359 "STOWED" (REF INT, #90) used size 64
$ grep -n "#35 REF INT" calc.l
387:     #35 REF INT deflex #35 used size 24

The prelude

A program is compiled together with the standard prelude, which declares print, whole, the file modes, and the rest of the environment. The --extensive option turns on the tree and the mode table and adds a listing of that prelude, so the whole environment can be read in the same format as the program.

calc.a68
BEGIN
  INT a := 3;
  INT b := a * a;
  print((b, newline))
END

The prelude listing begins with the declarations that the interpreter adds itself and continues with the library. For this program the file reaches 1814 lines, of which the prelude is the largest part. The option is meant for reading, not for regular use.

$ a68g --listing --extensive calc.a68
         +9
$ grep -n "^[A-Z][a-zA-Z ]*$" calc.l
24:Source listing
40:Syntax tree listing
52:Syntax tree
64:Syntax tree
85:Syntax tree
109:Syntax tree
148:Mode listing
517:Standard prelude listing
1810:Pragmat listing
$ wc -l calc.l
1814 calc.l

Cross references and statistics

The option list also names --xref for a cross reference, --statistics for run statistics, and --preludelisting for a prelude section in the listing. In version 2.8.4 the first two are accepted and recorded in the pragmat listing, but they add no section to the file, with or without -O2. --preludelisting works only together with --listing; it then writes the prelude section alone, without the tree or the mode table, and the file reaches 1353 lines for this program.

calc.a68
BEGIN
  INT a := 3;
  INT b := a * a;
  print((b, newline))
END

The listing produced with both switches has the same three sections as a plain --listing run, and the pragmat listing is the only place where the switches appear. A cross reference therefore has to be produced by other means, such as a search through the source or the tag lines of the source listing.

$ a68g --listing --xref --statistics calc.a68
         +9
$ grep "^[A-Z][a-zA-Z ]*$" calc.l
Source listing
Syntax tree listing
Pragmat listing
$ tail -7 calc.l

Pragmat listing
------- -------

1: --listing
2: --xref
3: --statistics
4: calc.a68

Which switch writes what

The reports are cumulative and each one has a cost in file size. A plain listing shows the source with its tags; --tree adds the parse tree of every line; --moids adds the mode table; --extensive adds both and the prelude. The pretty printer is independent of all of them and writes a different file.

The listing is produced while the program is parsed, so it is also written when the program is only checked. The debugging article uses the tree to explain a runtime report, and the style guide uses the pretty printer as a first pass over an untidy file.

Source

The Algol 68 Genie project

Revised Report on the Algorithmic Language Algol 68

In this article we pretty-printed a program, read the source and pragmat sections of a listing, followed the syntax tree of a declaration, looked up a mode number, dumped the standard prelude, and checked which switches add which section.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Algol 68 tutorials.