Intermediate

I/O Operations in ALGOL 60

Console output, reading input, formatted reports, and file I/O through shell redirection in ALGOL 60 - all runnable with GNU MARST and Docker

Introduction

Here is the surprising truth about input and output in ALGOL 60: the language never defined any. The 1960 Report deliberately left I/O out so the committee could concentrate on algorithmic expression, and they assumed each implementation would supply whatever its host machine needed. The practical cost was that no ALGOL 60 program doing I/O was ever truly portable between compilers.

The gap was eventually filled by the Modified Report (1976), which standardised a set of I/O procedures. GNU MARST - the implementation used throughout this series - follows the Modified Report and gives us outstring, outinteger, outreal for output and ininteger, inreal for input. Every procedure takes a channel number as its first argument; channel 1 is the standard console, which is why every program in this series writes to channel 1. The input procedures read from that same standard channel.

This tutorial goes well beyond the single outstring call from Hello World. You will print mixed text and numbers, build a small formatted report and a table, read numbers typed at the terminal, and finally do real “file I/O” the way ALGOL 60 programs actually do it - by letting the shell redirect files into and out of the standard channel. Because ALGOL 60 is an imperative, procedural language, all of this reads as a straightforward sequence of procedure calls.

Output in Depth

You met outstring in Hello World. The other two core procedures are outinteger and outreal. One quirk is worth committing to memory: outinteger and outreal always print the number followed by a single trailing space, while outstring prints its text exactly as written. That trailing space conveniently separates values, so you rarely add one yourself.

Create a file named io_output.alg:

begin
  comment ALGOL 60 defined no I/O of its own; these procedures come
  from the Modified Report that GNU MARST implements. Channel 1 is the
  standard console. This program shows the three core output verbs;
  integer items;
  real price, total;

  items := 3;
  price := 4.50;
  total := items * price;

  comment outstring prints text exactly as written, including any \n;
  outstring(1, "=== Order Summary ===\n");

  comment outinteger and outreal each print their number followed by a
  single space, so you normally do not add one yourself;
  outstring(1, "items = "); outinteger(1, items); outstring(1, "\n");
  outstring(1, "price = "); outreal(1, price);    outstring(1, "\n");
  outstring(1, "total = "); outreal(1, total);    outstring(1, "\n");

  comment A whole multi-line block can live in one string literal;
  outstring(1, "\nThanks for your order!\nCome again.\n")
end

Notice how a real value carries no fixed number of decimal places: 4.50 prints as 4.5 and 13.5 prints as 13.5. MARST formats reals in their shortest faithful form rather than padding them, which is something to plan around when you want neat columns.

Building a Formatted Table

Combining outstring for labels and spacing with outinteger/outreal for values lets you assemble reports row by row inside a loop. This example prints a Celsius-to-Fahrenheit conversion table using a for loop with a step of 10.

Create a file named io_table.alg:

begin
  comment A formatted table assembled from the output procedures. Each
  row prints a Celsius value and its Fahrenheit equivalent. Recall that
  outinteger and outreal print their number followed by a space;
  integer c;
  real f;

  outstring(1, "Celsius  Fahrenheit\n");
  outstring(1, "-------  ----------\n");
  for c := 0 step 10 until 40 do
  begin
    f := c * 9.0 / 5.0 + 32.0;
    outstring(1, "  ");
    outinteger(1, c);
    outstring(1, "    ");
    outreal(1, f);
    outstring(1, "\n")
  end
end

Because the standard output procedures give you no field-width control, perfectly aligned columns are genuinely hard in ALGOL 60 - you nudge things into place with literal spaces in outstring. This limitation is a direct consequence of the language leaving I/O to implementations: there was never a standard printf-style format string.

Reading Input

Input mirrors output. ininteger(1, var) reads one integer from the standard channel into a variable, and inreal(1, var) reads one real. Both skip any leading spaces and newlines before the number, so values may be separated by spaces or spread across several lines.

Create a file named io_input.alg:

begin
  comment ininteger reads an integer from the standard channel (1),
  skipping any leading spaces or newlines. We read two integers and
  report a few results computed from them;
  integer a, b;

  outstring(1, "Reading two integers from standard input...\n");
  ininteger(1, a);
  ininteger(1, b);

  outstring(1, "a       = "); outinteger(1, a);      outstring(1, "\n");
  outstring(1, "b       = "); outinteger(1, b);      outstring(1, "\n");
  outstring(1, "a + b   = "); outinteger(1, a + b);  outstring(1, "\n");
  outstring(1, "a * b   = "); outinteger(1, a * b);  outstring(1, "\n")
end

Since the program reads from standard input, you feed it values by piping them in (shown in the Docker section below). The numbers you supply are not echoed back automatically - only what the program explicitly prints appears in the output.

File I/O Through Redirection

The Modified Report procedures read and write the standard channel; ALGOL 60 has no portable “open this named file” statement. In practice you do file I/O exactly the way Unix tools do: let the shell connect a file to standard input or standard output with < and >. The program below reads a count n, then n real values, and reports their average - perfect for driving from a data file.

Create a file named io_average.alg:

begin
  comment Read a count, then that many real numbers, and report the
  average. Because we read and write the standard channel, the shell
  can redirect a file in and capture the result out - the practical
  way to do file I/O with ALGOL 60;
  integer n, i;
  real x, total;

  ininteger(1, n);
  total := 0.0;
  for i := 1 step 1 until n do
  begin
    inreal(1, x);
    total := total + x
  end;

  outstring(1, "count   = "); outinteger(1, n);       outstring(1, "\n");
  outstring(1, "total   = "); outreal(1, total);      outstring(1, "\n");
  outstring(1, "average = "); outreal(1, total / n);  outstring(1, "\n")
end

This program needs a data file to read. Create a file named numbers.txt:

1
2
4
10.0 20.0 30.0 40.0

The first line gives the count; the second line holds the four values. Note that total is declared real, so dividing it by the integer count n performs real division and yields a fractional average when the numbers do not divide evenly.

Running with Docker

Use the same GNU MARST image as the rest of this series. Pull it once:

1
docker pull codearchaeology/algol60:latest

Output-only programs run exactly like Hello World:

1
2
3
4
5
# Output in depth
docker run --rm -v $(pwd):/app -w /app codearchaeology/algol60:latest algol60-run io_output.alg

# Formatted table
docker run --rm -v $(pwd):/app -w /app codearchaeology/algol60:latest algol60-run io_table.alg

Programs that read input need the -i flag so Docker keeps standard input connected. Pipe values in for the interactive example, and redirect the data file for the averaging example:

1
2
3
4
5
6
7
8
# Reading input: pipe two integers into standard input
echo "5 7" | docker run --rm -i -v $(pwd):/app -w /app codearchaeology/algol60:latest algol60-run io_input.alg

# File input: redirect numbers.txt into standard input
docker run --rm -i -v $(pwd):/app -w /app codearchaeology/algol60:latest algol60-run io_average.alg < numbers.txt

# File output: also capture the report into results.txt
docker run --rm -i -v $(pwd):/app -w /app codearchaeology/algol60:latest algol60-run io_average.alg < numbers.txt > results.txt

Expected Output

io_output.alg:

=== Order Summary ===
items = 3
price = 4.5
total = 13.5

Thanks for your order!
Come again.

io_table.alg:

Celsius  Fahrenheit
-------  ----------
  0     32
  10     50
  20     68
  30     86
  40     104

io_input.alg, given the input 5 7:

Reading two integers from standard input...
a       = 5
b       = 7
a + b   = 12
a * b   = 35

io_average.alg, reading numbers.txt:

count   = 4
total   = 100
average = 25

When you run the last command with > results.txt, nothing prints to the terminal; the same report is written to results.txt instead.

Key Concepts

  • ALGOL 60 defined no I/O. The original 1960 Report left input and output to each implementation; the procedures used here come from the Modified Report (1976) that GNU MARST follows. This is why I/O code is not portable across historical ALGOL 60 compilers.
  • Channels identify streams. Every I/O procedure takes a channel number first; channel 1 is the standard console, used for both out* (writing) and in* (reading) in MARST.
  • Three output procedures cover the basics: outstring(1, s) prints text verbatim, outinteger(1, n) prints an integer, and outreal(1, x) prints a real.
  • Numbers print with a trailing space. Both outinteger and outreal append a single space after the value, which separates consecutive numbers without extra effort.
  • Reals use shortest-form output. outreal prints the minimal faithful representation (4.50 becomes 4.5), so there is no built-in control over decimal places or field width.
  • Reading skips whitespace. ininteger(1, var) and inreal(1, var) skip leading spaces and newlines, so input values can be space-separated or split across lines.
  • File I/O is shell redirection. Lacking a portable file-open statement, ALGOL 60 programs read and write the standard channel and rely on the shell’s < and > to attach files - remember to add Docker’s -i flag so standard input stays connected.
  • Declarations precede statements. As with every ALGOL 60 block, all variables are declared at the top of the begin ... end block before any I/O or computation occurs.

Running Today

All examples can be run using Docker:

docker pull codearchaeology/algol60:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining