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.
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.
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.
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.
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.
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.
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.
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
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
List all Algol 68 tutorials.