WAITS: Programming in SAIL

Today we'll look at SAIL, which was derived from Algol 60 and was developed and used on WAITS for both system programs and research projects.

There was a compiler named SAIL,

Assembled and coded in FAIL.

Its authors, they say

(one glorious day)

Were run out of town on a rail.

Comment in the source code for the SAIL compiler, SAIL[S,AIL]

History

SAIL was a combination of work done on two languages. The first was Gogol, which was a simplified version of Algol 60 for the PDP-1. Not much information on this remains apart from a list of its error messages.

The other source - primarily for its associative arrays - was the LEAP language, which was written by Jerome Feldman and Paul Rovner for the Lincoln Labs Tx-2 machine; there's a paper describing this at the ACM.

The compiler was written by SAIL members Don Swinehart and R. Sproull, with the first version being available in November 1969. As the manual puts it

SAIL in a sense has something for everyone. For those who think in ALGOL, SAIL has ALGOL. For those who want the most from the PDP-10 and the time-sharing system, SAIL allows flexible linking to hand-coded machine language programs. For those who have complex input/output requirements, the language provides complete access to the I/O facilities of the PDP-10 system. For those who aspire to speed, SAIL generates fairly good code.

It achieved its goal, with wide use at Stanford and it spread to other PDP-10 sites.

In 1980, there was a commercial spin off of SAIL called MAINSAIL. This was cross-platform, with initial versions on other PDP-10 and -11 operating systems, and possibly VAX and System/360 also. The language had some success (one user was VLSI Technologies for electronic design tools) and was available for at least HP-UX, AIX and IRIX up to the early 2000s. Bitsavers has some documentation.

SAIL features

Like every other Algol 60 implementation, program format and I/O is slightly different, so to set the scene, here's a simple hello world program.

BEGIN
    INTEGER I;
    COMMENT Print Hello World 5 times;
    FOR I ← 1 STEP 1 UNTIL 5 DO
        OUTSTR("HELLO, WORLD" & '15 & '12)
END PROGRAM

This follows the typical Algol 60 block structure, using BEGINEND and having statements separated rather than terminated by semicolons, so the last statement in a block should not have a semicolon at the end. Comments are statements as well so need to be terminated depending on their position.

The assignment operator is an arrow, which was typeable using the Stanford keyboard; on a teletype you could use _ instead. A ↔ B would swap the values of A and B.

For I/O there is an extensive set of routines that can handles files and different devices, but for console I/O you can use OUTSTR to print strings (adding a CR/LF line terminator in the above example) and read a line of input to a string with INCHWL.

Real, integer and boolean types are provided, along with arrays. Strings are internally represented as a two word value, the first being a character count and the second a pointer to an array of 7 bit characters. The body of the OUTSTR shows concatenation of a string literal with two characters, CR/LF, to produce a new string.

Procedures take value or reference parameters, can be called recursively (with the keyword RECURSIVE PROCEDURE). Procedures can also be passed into other procedures.

The LEAP facilities allow sets (PUT item IN set) and associative arrays (MAKE key ⊗ object ≡ value). You can then do search operations on these using FOREACH. An example adapted from the manual:

FOREACH x,y,z SUCH THAT father ⊗ x ≡ y AND father ⊗ y ≡ z DO
    PUT z IN grandfathers

Macros are similar to C's macro processor: you can define constants and then they get expanded in the program when used:

    DEFINE ARRAY_SIZE="11";
    ...
    INTEGER ARRAY DATA[1:ARRAY_SIZE];

or to take parameters.

    DEFINE APPEND(x, y)="x ← x & y";

TPK in SAIL

Let's use the compiler to run the TPK algorithm. The source code can be found here. This can be loaded onto the disk using the techniques described in this post.

The full program looks like this:

BEGIN
    COMMENT TPK algorithm in SAIL for WAITS;

    REQUIRE "{}{}" DELIMITERS;
    COMMENT Size of array to use for TPK;
    DEFINE N = {11};
    COMMENT Macro definitions to print a string with CR/LF at the end;
    DEFINE CRLF={('15 & '12)};
    DEFINE TYPE(MSG)={OUTSTR(MSG & CRLF)};

    REAL PROCEDURE FN(REAL X);
    BEGIN
        RETURN(SQRT(ABS X) + 5*X↑3)
    END;

    COMMENT Main program;
    INTEGER J;
    REAL ARRAY A[1:N];
    TYPE("Please enter 11 numbers");
    FOR J ← 1 STEP 1 UNTIL N DO
    BEGIN
        STRING REPLY;
        BOOLEAN BRCHAR;
        REPLY ← INCHWL;
        A[J] ← REALSCAN(REPLY, BRCHAR)
    END;
    TYPE("Results are");
    FOR J ← N STEP -1 UNTIL 1 DO
    BEGIN
        REAL RESULT;
        RESULT ← FN(A[J]);
        IF RESULT > 400.0 THEN
            TYPE("Too large")
        ELSE
            TYPE(CVF(RESULT))
    END;
END

One additional trick is used for macros:

    REQUIRE "{}{}" DELIMITERS;
    DEFINE TYPE(MSG)={OUTSTR(MSG & CRLF)};

The first line redefines the start and end delimiters for macros to be curly brackets rather than double quotes; if this is not done, any double quotes in the text being expanded, for example a string constant TYPE("Hello") would not work.

The procedure FN shows use of ABS as a unary function, and as the exponentiation operator.

We use several type conversion functions here. REALSCAN takes a string and a boolean and returns a real value if found, otherwise it will set the boolean to true (we ignore this in the program). CVF conversed a real to a string.

To compile and run, assuming the program is stored in a file tpk.sai, use exec:

.exec tpk

SAIL: TPK    1
LOADING

LOADER 2K CORE
EXECUTION
Please enter 11 numbers

Errors found in compilation are handled interactively. If for some reason I had put UNREAL PROCEDURE it would halt with

.comp tpk

SAIL: TPK    1
UNDECLARED IDENTIFIER: UNREAL
TPK, PAGE 1
01100           UNREAL
                       PROCEDURE FN(REAL X);
^

At this prompt, you can press Enter to continue: in some cases it may be able to correct the situation but here it would ignore the statement. You could press E and it would open the file at that line in the SOS editor; T would do the same in TV. X would exit. Pressing ? shows a full list of what options are available

Further information

The main manual for SAIL can be found in SAIL.DCS[S,DOC] or on Bitsavers as a PDF.

Dan Swinehart gave a talk on the history of SAIL (and some background on its name) at a 2009 reunion - the video is at the Stanford Archives.

Questions, corrections, comments

I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com and I will add it here and update the main text.