ALGOL 68 FOR OS/2


Dave Lloyd and Peter Craven
Oxford & Cambridge Compilers Ltd
55 Brampton Rd, Cambridge, CB1 3HJ, UK
Email: enquiries@occl-cam.demon.co.uk
Tel: +44 (0)1223 572074

ALGOL 68 is an exceptionally powerful general purpose programming language. Its design promotes clarity of thought and robust programming. ALGOL 68 was revolutionary when it was introduced and it still is now. The requirements of large systems and the capabilities of personal computers (particularly with the arrival of OS/2) have finally caught up with the foresight of Algol 68.

We present an overview of Algol 68, the OCCL compiler and the programming environment.

Introduction

The very first programming languages (Autocode, FORTRAN, LISP and COBOL) were designed to map directly to the basic machine operations while providing a human intelligible description of what was going on and relieving the programmer of the burden of much of the incidental detail. This purpose of programming languages is still as true today as it ever was; our desire for abstraction is simply greater and our machines more capable.

The next step up in abstraction was ALGOL a few years later which learnt from the lessons of FORTRAN. It was the first language to have a formal definition of its syntax (the now widely used Backus-Naur Format or BNF) and it was the first language to provide a block structure and insist on the declaration of types of all variables; and insist on type consistency when used. It also unified the notions of "function" and "subroutine" with the notion of "procedure".

The names of these early languages give away much of the intention of their design: FORmula TRANslator, ALGOrithmic Language and COmmon Business Orientated Language. By "algorithmic language", the designers of ALGOL meant a language to communicate algorithms between programmers as much as a language to execute algorithms on computers: it was thus also the first standard pseudo-code.

ALGOL 60 (as it became standardised) was widely used throughout the 1960s and continued to be used well into the 1970s. (One of us cut his programming teeth on ALGOL 60 on a PDP 8/e). However programming languages continued to develop and ALGOL 60 spawned many variants and extended forms such as ALGOL-W, CORAL and Pascal (Nicklaus Wirth was on the ALGOL committee), and indeed almost all block structured languages ultimately owe their origin to ALGOL (rather than FORTRAN or COBOL!). These new languages usually provided new features but no language had all the features: you used the language that was available on your system and closest to your needs, and bad luck if that meant three different languages for different parts of the job.

In 1966, the ALGOL committee started work on a revision of ALGOL which turned into a radical redesign resulting in ALGOL 68. The first standard was published in 1968 as the name suggests, but a revised standard was published in 1973 (it is a measure of the authors' success in 1968, that the 1973 language is very similar to the 1968 standard). It is the Revised Standard of 1973 that is now normally meant by "ALGOL 68".

ALGOL 68 was, if anything, a greater leap up than its predecessor had been. Its goal was to be the general purpose programming language; but not by throwing every desired feature into the pot - in direct antithesis to PL/I which appeared at about the same time. The authors of the language included the following as their aims:

Purpose
"ALGOL 68 is designed to communicate algorithms, to execute them efficiently on a variety of different computers, and to aid in teaching them to students."
Orthogonal design
"The number of independent primitive concepts has been minimized in order that the language be easy to describe, to learn and to implement. On the other hand these concepts have been applied "orthogonally" in order to maximize the expressive power of the language while trying to avoid deleterious superfluities."
Security
"ALGOL 68 has been designed in such a way that most syntactical and many other errors can be detected easily before they lead to calamitous results. Furthermore, the opportunities for making such errors are greatly restricted."
Efficiency
"ALGOL 68 allows the programmer to specify programs which can be run efficiently on present-day computers and yet do not require sophisticated and time-consuming optimization features of a compiler."

A language must be easy to describe, otherwise the authors of the language are in danger of making mistakes in its definition. A language must be easy to learn, or else a gap will open up between the programmer's understanding and what the program means to the compiler. And a language must be easy to implement, or else the programmer will find conflicts between particular compilers' treatment of a program and the defined meaning of the program. Ockham's principle of avoiding multiplication of entities (orthogonality) is a principle that, sadly, has been neglected by the designers of many languages since ALGOL 68, with perhaps the worst offender being Fortran 90.

Algol 68 presented the following innovations (among many):


Since the definition of Algol 68 many languages have appeared both for program specification and for execution. The Wirth family continues from Pascal to Modula-2 to Oberon and even the recent languages have not caught up completely with Algol 68. FORTRAN has progressed to FORTRAN77 (which only really added the block IF), and more recently to Fortran 90 which finally adds the sophistication in array handling that Algol 68 provided (though recent proposals for Fortran 95 are finally progressing beyond Algol 68 - although not beyond the visions of proposals for Algol 68 in the mid 1970s!). Ada borrowed extensively from Algol 68 (although it chose to use a more Pascal syntax) and provided some innovation but rejected much that we consider important (such as procedures as data object). There have also been many experimental or academic languages that have tried out notions such as functional programming or data-flow languages but these have not yet developed to usable languages for real-world applications.

And, of course, C. C is a language that was never properly designed. It evolved from a hodge-podge of BCPL with some ALGOL-W ideas thrown in, intended as a high-level assembler for PDP-11s and Unix. For all of the ANSI overhaul of C which tidied up many details and included many ideas from Algol 68 (such as typing the parameters of a function), it is still little more than a high-level assembler. We find it alarming that so much software is written with a language as untrustworthy as C.

A final strand of language development has been the recent "object-orientated programming languages". There is nothing new under the sun of course. This all goes back to Simula 67 which came out of the same melting pot as Algol 68 sharing many of the same concepts. Simula was a language designed for simulation purposes (as its name suggests) and provided a good model of co-routines with classes to manage the multiple stack threads. We have a lot of respect for Simula, but the same cannot be said for the takeup of C++. Stroustrup, the author of C++, has commented that his goal was to teach Simula to his students but he didn't have a Simula system available on the lab machines; so he designed a simple enough language to demonstrate the concepts of Simula on student problems. Stroustup did this as a preprocessor for C, hence C++ (which actually means "take C, improve it, and then throw the result away"!). C++ is nevertheless an imposing superstructure built on a foundation of quicksand.

It will be interesting to see how Algol 68 develops in the future in response to recent developments in class-structuring, polymorphism, parallel programming and persistent storage.

It would be tempting to say of Algol 68, "the right tool for the job; now what's the job?". And this is more true of Algol 68 than any other language. While it is hard to see what is so special from a simple "feature list", in a subsequent section, we shall give you a quick tour of the language. But the real win is the clean and consistent whole and this may not be so immediately apparent. It does however result in:


First Example: Animal

In the next section we'll explain some of the elements of Algol 68's grammar, but first here is a taste of what Algol 68 looks like. Our first example is a simple guessing game, where the computer will try to guess the name of an animal that you're thinking of by asking you some questions that characterise the animal. Despite the unfamiliar syntax, we hope you find its meaning apparent.

[A quick note on Algol 68's textual representation. Two cases are used for tokens: upper case (or bold) is used for reserved words, mode names and operator names; lower case is used for identifiers. Whitespace is not significant in identifiers. Comments are surrounded by #-marks.]


#
        Example program: Animal 

    A program is built from a module which is nominated as 
    the 'program root'.
    So we kick off with a module header calling our module "animal"
    and using module "transput" so we can do reads and writes.
#
MODULE animal
USE    transput
BEGIN

#
    "reply" takes a string which it prints and reads the response
    from the user:
#
PROC reply = (STRING prompt) STRING: 
    ( STRING s ; print ((prompt, newline)) ; read ((s, newline)) ; s );

#
    "ask" takes a string which it prints as a question and
    gets a "yes" or "no" answer, prompting the user if anything else
    is typed and delivering TRUE if "yes" was typed. 
    Note the use of LC to force the response to lower-case.
#
PROC ask = (STRING question) BOOL:
BEGIN
    print ((question, " ?", newline));

    STRING s;

    WHILE 
        read ((s, newline)) ; s:= LC s; 
        s /= "yes" AND s /= "no"
    DO  
        print (("Please answer ""yes"" or ""no"" !", newline))
    OD;

    s = "yes"
END;

#
    We now define our decision tree, which has a general NODE
    which can either be a STRING meaning there are no further questions
    to ask or a DECISION meaning that we have a property of the
    animal which we must ask to decide which way to go.

    Note the mention of NODE - this is an OCCL deviation currently 
    required to cope with recursive mode declarations.
#
MODE NODE,

MODE DECISION = STRUCT (STRING property, REF NODE yes, no),

MODE NODE = UNION (STRING, DECISION);

#
    "do better" is called when the decision tree has been followed
    to a leaf but the user's guess was something else. In which case
    we must replace that leaf with a further decision node and ask the
    user for an extra feature which distinguishes the new animal from
    the old.

    Note that we have written the cast to DECISION around the whole 
    IF-clause rather than casting each display.
#
PROC do better = (STRING guess) NODE:
BEGIN
    STRING new animal = reply ("What was the correct answer ?");

    STRING property   = reply ("Please give a distinguishing feature: ");

    REF NODE guess node = HEAP NODE := guess,

             new   node = HEAP NODE := new animal;

    DECISION IF ask ("Is this a property of a " + new animal)

                THEN (property, new node, guess node)

                ELSE (property, guess node, new node)
             FI
END;

#
    "guess" is the main decision tree traversal procedure.
    At each decision node it asks the user whether the animal has
    a particular property and follows the yes or no branches
    accordingly. When we get to a leaf STRING, we ask outright whether 
    that is the guess. If not we call "do better".
#
PROC guess = (REF NODE node) VOID:
CASE node

  IN (STRING s): ( NOT ask ("Is it a "+ s) | node := do better (s) )

   , (DECISION d): guess ( (ask ("Does it have "+ property OF d) | yes OF d | no OF d) )
ESAC;

#
    Kick off by building the root database with one animal supplied
    by the user.
#
NODE database := reply ("Please tell me the name of an animal: ");

#
    We now enter a loop which keeps asking the user to think of an animal
    and let the computer guess what it is. It stops when the user is 
    bored and declines the challenge!

    Note that the following might be thought of as constituting the
    'main' routine in languages like C.
#
print (("Now please think of an animal.", newline));

WHILE print (("Press ""enter"" when you"re ready for me to guess what it was.", newline));

      read (newline);

      guess (database);

      ask ("Do you want to think of another animal")
DOD
    # the DOD is an OCCL extension: a contraction of DO SKIP OD #

END
 

A Quick Tour of Algol 68

Algol 68 is a seductive notation for readily expressing the essence of an algorithm - what you write as pseudo-code is the program itself!

For all of its power, Algol 68 is a surprisingly simple language. This section is a lightning sketch of its construction which should give you a flavour of the language in conjunction with the examples.

The syntax of Algol 68 tells us what programs are valid, but the semantics tells us what such a program means, i.e., what will happen when it is run. The Algol 68 syntax can be factored into three simple categories:


The basic rule of Algol 68 is that everything delivers a value and the set of possible values that a clause or unit can deliver is defined by a mode. Mode is akin to "type" in other languages, and there is a mode void to describe those cases where no tangible value is delivered. Algol 68 is very strict about its typing, and there exists no mechanism to subvert the type security (as ther is, by contrast, in C). This leads to more reliable and robust programs.

One of the great strengths of Algol 68 is its mode system. As well as a standard set of modes such as integers and reals, it provides the means to build infinitely more new modes allowing arbitrarily complicated data-structures to be simply represented.


A useful extension of the union is anymode which is formally a union of all modes known to the compiler (this can be written within Algol 68 but it would be tedious in the extreme). anymode is useful as a completely secure unconstrained type: it can hold a value of any mode, but you must know what you want when you use it. It has particular application to GUI programming where it serves as the holder of the instance data of a window. You can, of course, compound anymode into large modes still - such as a list of anything. Contrast anymode with the C void * !

Declarations

Algol 68 is a block structured language, and declarations have a scope or visibility defined by the enclosing range - and clauses define ranges.


Units

A unit delivers a value when "elaborated" (i.e., executed or evaluated) and that value will always conform to the same mode. All of the following are units and can be used anywhere that a unit can be used:


Clauses

Clauses provide the framework for the program off which everything else hangs.


For examples of clauses please look back at the animal example.

Invisible syntax: Coercions

Algol 68 provides some automatic glue to save the programmer the burden of specifying small operations such as dereferencing (i.e., indirection) or integer to real conversions. These are the coercions; they are essentially syntactic constructs with a null textual representation. It is the compiler's responsibility to sort this out, and you will find that you rarely need to even remember their existence.


What does this all mean?

The summary of the language is rather dry and it may not be obvious what advantages there are to writing in Algol 68. We present an assortment of comments from programmers on the subject...

"Algol 68 has an intelligible workable syntax for modes - it is easy to compound from smaller modes to larger modes particularly involving references and procedures. Contrast the following procedure type from C:
void ( *signal(int sig, void (*func)(int)) )( int );
with
REF PROC (INT, PROC (INT) VOID) INT
(although I must confess I could not decode the C type - I had to deduce it from the examples. So I may have got it wrong!)"

"The compiler provides protection against accidental corruption of data by the use of nil or dangling pointers or array indices outside their bounds. These can produce very insidious bugs that are the very devil to trace."

"There is a good chance if it compiles it works! It is difficult to write something meaningful to the compiler but not intended (think of the damage that an extra semicolon or a dangling else can make in C)."

"User-defined operators and anonymous routine texts together almost form a user-defined programming paradigm allowing complicated abstract operations to be written in a natural fashion with a well-defined meaning instead of having to resort to a special purpose language with the problems of integration with the rest of the program that usually brings. With suitable libraries, one can write code with the feel of SNOBOL, Prolog, Lisp, or even shell scripts."

"In contrast to many languages, the semantics of Algol 68 is orthogonal to its syntax which means that as long as we do so consistently, we can change the modes of units and the program will still be meaningful. This means that throughout the development cycle changes to one part of the source require fewer changes in other parts of the source."

"You can interleave declarations and statements allowing declarations to appear right after the value has been computed. You can then use identities (constants) rather than having to use a variable and a single assignment. The ability to hold on to values without creating a variable is in fact very powerful, and permits a "variable-less programming" paradigm (almost functional programming) which is much more secure than that offered by traditional languages."

"Procedures can contain procedures within them and the inner procedures can reference identifiers from the outer procedures. This allows a much more flexible approach to structuring the program particularly in recursive algorithms such as tree-walkers."

"There are no restrictions for compile-time constants. Many languages (notably C and FORTRAN77) only allow compile-time computable expressions in many contexts (such as array bounds or initialisers) but the programmer shouldn't need to be aware of the difference between compile-time and run-time. And in Algol 68 there is no difference. The OCCL compiler does of course perform plenty of constant propagation so that the code is indistinguishable from a a compile-time restricted language."

"The bounds of arrays are a dynamic property of the array and not part of the type. This allows a procedure to adapt to the length of the array given to it by simple enquiries. And of course there is no problem allocating local or global arrays with run-time computed bounds."

"Procedures can deliver whole arrays - the programmer is freed from worrying about where the storage for elements has come from and when (or if) it should be freed. Even better - this holds for arrays inside structures or arrays of arrays just as well"

"Assignment copies the whole object including elements of arrays and elements of arrays inside structures or arrays of arrays."

"Unlike C, in Algol 68 unions know the mode of the value they are carrying and this can be interrogated with the conformity clause. There is no opportunity to write something as one type and accidentally read it as another."

"Algol 68 has a much better mechanism than the C "varargs": just use a [ ]UNION(...). This is much more secure because not only is the number of arguments known, but their individual types. For example the standard procedure print is defined as taking just one parameter, but this is actually a [ ]UNION(STRING, INT, REAL, ...)."

"The brief forms of the choice clauses are very powerful. Particularly because they are just different representations of the same construct rather than the very different conditional statements and conditional expressions of other languages."

"The support that Algol 68 provides for complex arithmetic, functions as data objects, and array slices makes it perfect for scientific and engineering purposes. Ada which is equally capable in many respects does not provide functions as data objects which ALGOL 60 and FORTRAN both provided and indeed Countess Ada devised (Jensen's device)! The only other language to provide these facilities in combination is Fortran 90."

An efficient implementation of ALGOL 68 for OS/2

When Algol 68 first appeared it was widely criticised for being an inefficient language: it appeared to impose too much burden on the compiler. However the early implementations of Algol 68 disproved this myth quite quickly, and implementors found that, on the contrary, the sleek design allowed them to focus their efforts on where it really mattered and many optimisations could be wrought for far less effort than in other languages where the intent of the programmer was obscured by the constraints of the language.

We wish to promote a programming environment where you can write abstract programs with the same efficiency as programs with the details spelt out. So the OCCL Algol 68 compiler combines efficient high-level strategies with good low-level tactics to provide well balanced code generation. These include:


Garbage-collection

The comment earlier about HEAP generators delivering storage that will only be reclaimed when it is no longer in use, begs the question of how that storage is reclaimed. Algol 68 provides no construct to free storage explicitly (which can lead to dangling pointers if one use of a pointer is freed while another use of the same pointer is blissfully unaware of this fact). Instead the compiler arranges that the program will periodically reclaim all storage that cannot be used again - and this is known as garbage collection.

Not having to worry about when storage can safely be returned to the free pool is a great boon to the programmer: as with many things in Algol 68, the system just gets it right for you, saving many mistakes. We've done the work of getting garbage collection correct and you don't need to repeat it! This is particularly important in large systems where 40% of development time can be taken up just organising storage allocation and recovery and ensuring policy on storage ownership is observed by all members of the project.

The heap is central to much of the freedom that Algol 68 gives the programmer and we believe we have a particularly efficient implementation of both the heap allocator and the garbage collector. The performance of the heap (including occasional garbage collection) exceeds many popular C implementations of "malloc" (which requires explicit freeing): even in a highly heap intensive program with frequent gabage collection, only 10-20% of the execution time is spent in heap allocation and garbage collection (figures of 80% are not uncommon with poor mallocs or with poor uses of malloc).

The heap allocation bins free blocks according to their size, and speculatively breaks larger blocks into smaller blocks when a bin runs empty. This greatly reduces fragmentation of the heap.

The garbage collector uses a mark and sweep algorithm which traces all pointers out from the stack: anything not accessible from the stack (i.e. identifiers) can be reclaimed. The compiler generates a marker routine for each mode in the system whose values might need to be traced. This results in very fast garbage collection: typically 10-100ms for large programs, rarely more than a second.

Parallel-clauses and OS/2 threads

OS/2 provides multitasking threads and Algol 68 has parallel-clauses. Rather than mapping the one directly onto the other, we treat an OS/2 thread as an execution resource on which parallel actions can run. Most programs will have many parallel actions but only few threads (typically a thread per I/O request and one for computation). This provides a dynamic balancing between large numbers of parallel actions required for a parallel algorithm (such as a graph transformer) and the few parallel actions needed to carry on working whilst waiting for user input. Proper Dijkstra semaphores are provided for synchronisation between parallel actions as expected for Algol 68; these have an integer level which can be UP'd or DOWN'd - but a parallel action is blocked if it tries to DOWN a zero level semaphore. OS/2 system semaphores are only used to synchronise threads when there are fewer parallel actions than threads, or when garbage collection occurs. Synchronisation over threads can take milliseconds as the system scheduler is involved, while synchronisation between parallel actions using the same thread can take microseconds as it is not much more than a call.

Special note: the OS/2 Presentation Manager requires that the thread that created a window message queue continues to serve it. The round robin allocation of parallel actions to threads has an additional mechanism to ensure that certain parallel actions (such as the message queue) always run on the same thread.

Modular Programming

Early compilers for Algol 68 (most notably those from RSRE) were the first to take the step up from the independent object files of FORTRAN - which are still enshrined in ANSI C - and provide for modular compilation. Since then languages such as Modula-2 and Ada (as "packages") and more recently Fortran 90 have embraced the idea.

Strictly speaking, modules are not a part of the IFIP standard language Algol 68: the authors took the attitude that further research was needed and it was best left to implementors. But there were several proposals that were nearly accepted for the standard subsequently. We have adopted a variation of the RSRE module system, and users of that system will have no trouble moving to the OCCL system.

What is a module?

A module is an encapsulation of a series of declarations and units. The declarations can be made visible in subsequent modules, and the units are elaborated as part of the main program.

As part of the module head, we mention the other modules we wish to USE, i.e., import declarations from. And as part of the module tail we mention all declarations that we wish to KEEP i.e., be exported. Indeed declarations from other modules are only visible by such means - in radical contrast to FORTRAN and C which leaves the system linker to resolve external references (and which is rarely able to check type consistency). This protects the programmer from inconsistent definitions for types and procedure interfaces in different compilation units: bugs which can be very hard to trace. It is as if a C compiler automatically maintained the .h header files to go with the .c source file.

However modules are more than simple type paranoia: tight module interfaces, with information about inner details hidden, provide a good decoupling between functionality and implementation that can be enforced by the compiler; further the dependencies of the module on other aspects of the system are explicit and not left lurking until link time. This can save a lot of wasted effort during maintenance of a program and during substantial reworkings of a program. Agreed module interfaces are even more important in the development of a large system where the user of a module is often not the author.

Here is a simple example of a module for 2d vectors and a module which uses it:


MODULE vectors
BEGIN
    MODE VECTOR = STRUCT (REAL x, y);

    # Operations between planar vectors #

    OP + = (VECTOR a, b) VECTOR: (x OF a + x OF a, y OF a + y OF b),
       - = (VECTOR a, b) VECTOR: (x OF a - x OF a, y OF a - y OF b),
       * = (VECTOR a, b) REAL: x OF a * x OF b + y OF a * y OF b;

    OP MOD = (VECTOR a) REAL: SQRT (a * a) # the modulus or length #,

    OP PERP = (VECTOR a) VECTOR: (y OF a, -x OF a); # a perpendicular vector #

    # Operations between vectors and scalars #

    OP * = (VECTOR a, REAL b) VECTOR: (x OF a * b, y OF a * b),
       / = (VECTOR a, REAL b) VECTOR: (x OF a / b, y OF a / b),
       * = (REAL a, VECTOR b) VECTOR: b * a;

    # Identifiers for the cartesian basis which should be used to
      construct vectors (allowing a different representation of vectors
      to be used transparently at a later date).
    #
    VECTOR unit x = (1, 0), unit y = (0, 1)
END
KEEP VECTOR, +, -, *, unit x, unit y

MODULE lines
USE vectors
BEGIN
    # This manipulates a line segment represented as a vector from 
      origin to one end and a vector from one end to the other end.
    #
    MODE LINE = STRUCT (VECTOR oa, ab); 

    # "intersection" takes two lines and delivers a vector to the 
      intersection if it exists, and otherwise delivers nothing. "eps" 
      is used as the threshold on the determinant below which lines are
      considered parallel.
    #
    PROC intersection = (LINE p, q, REAL eps) UNION (VOID, VECTOR): 
    IF
        REAL mod p = MOD ab OF p, mod q = MOD ab OF q;
        VECTOR unit p = ab OF p / mod p, unit q = ab OF q / mod q;
        REAL det = unit p * PERP unit q;

        ABS det > eps 
    THEN
        # we solve for the intersection as a scaling on the unit vector
          for either line.
        #
        VECTOR pq = oa OF q - oa OF p;
        REAL u = pq * PERP unit p / det, 
             v = pq * PERP unit q / det;

        IF  0 <= u AND u <= mod p
        AND 0 <= v AND v <= mod q
        THEN
            oa OF p + u * unit p
        ELSE
            # point of intersection is outside the segments #
            EMPTY
        FI
    ELSE
        # the lines don't intersect - they're parallel #
        EMPTY
    FI
END
KEEP LINE, intersection

 

Modules as units of compilation

As well as providing an abstraction of interface, the OCCL compiler treats modules as units of compilation. Modules are compiled separately but not independently - any modules used must have previously been compiled so that all symbols explicitly kept can be imported. One might think this would mean that when a module is recompiled all modules which used it would also need to be recompiled. Fear not, as the OCCL system will only recompile dependent modules if symbols actually applied by the later module have changed. This can mean that some modules which use a changed module get recompiled but others do not. And it is best left to the system to track it all!

Instead of linking our object files into a program, we talk about composing our modules into a program (with the "compositor"). The OCCL system invokes the system linker only as the final stage in building a binary (to resolve references to OS/2 API functions or to external C or FORTRAN functions).

The compositor is more than a simple link-editor and is responsible for branch optimisation and dead code elimination that is only really possible globally. In particular this means that a program which uses only a few functions from a large library does not contain the whole image of the library: only those declarations and units actually needed are composed into the program.

The Compilation Tool

This is the unifying tool behind the OCCL Algol 68 programming environment. It is responsible for determining which modules need rebuilding; either because source files have changed or because used modules have changed. It is used instead of make for tracking Algol 68 modules and uses information recorded during compilation (i.e., no makefile). It is also responsible for project management, providing services such as:


ALIEN calls and the OS/2 API

So how do you call routines outside the module system? We refer to these routines as alien. The OCCL Algol 68 compiler provides a special unit for introducing alien routines. Currently only the OS/2 System calling convention is supported. This is appropriate for the OS/2 system API such as file system or Presentation Manager functions. The IBM C-Set compiler (and probably most others) also supports this convention, allowing routines so compiled to be called directly from Algol 68. The compiler has to rely on the programmer specifying the procedure type correctly and there are some restrictions on the modes that can be passed across an alien procedure interface; this is to ensure that both sides agree over representation and storage ownership. There are additional mechanisms available for reading and writing raw storage directly, allowing conversion between representations. To assist in this exercise, a tool is provided which converts C header files into an equivalent module of identity and alien declarations - albeit limited in scope.
Example:

PROC (INT {hwnd}, STRING {pszText}) INT win set window text = ALIEN "WinSetWindowText"; 

The OCCL system in use

We finish with a quick illustration of how the OCCL programming environment works in practice.

First of all we need to initialise our project:

compile init 

This creates an album directory which will be the repository for precompiled modules and additional compiler generated modules. As part of initialisation, the album will be chained to a system album holding the Standard Prelude (but you can change this).

The general approach to building up a project is to compile the source file for each module either directly with the Algol 68 compiler:

> algol animal 
ALGOL 68 Compiler, MK3.1a (OS/2 2+, 32-bit x86) 
(C) Oxford & Cambridge Compilers Ltd 1993. 
Compiled 128 lines: D:\projects\examples\animal.a68 
Ok - 129 lines compiled 

or via the Compilation Tool:
> compile source animal 
Local source path for compiling animal ? <enter> 
Local options for compiling animal ? <enter> 
Compiling animal ... 
ALGOL 68 Compiler, MK3.1a (OS/2 2+, 32-bit x86) 
(C) Oxford & Cambridge Compilers Ltd 1993. 
Compiled 128 lines: D:\projects\examples\animal.a68 
Ok - 129 lines compiled 
No active programs to build! 
- specify a program directive to describe a new program. 
- (see help for details) 
Successfully compiled: animal 

We will rarely use the compiler directly except perhaps for single module programs or occasional fine control.

We now have the module "animal" compiled, and so our next step is build a program from this module. This is what the last few lines of information from the Compilation Tool was about - it wants to build programs and we haven't told it about any yet and it suggests using the program directive (let us call the resulting program animal as well):

> compile program animal 
What is the name of the program module [animal] ? 
Compositor options for animal ? 
Linker options for animal ? 
Program animal.exe is out of date due to changes in module animal 
Composing animal ... 
Program Compositor v2.2a (OS/2 2+, 32-bit x86). 
(c) Oxford & Cambridge Compilers Ltd 1994, All rights reserved. 
Linking animal ... 
Successfully built: animal 

The Compilation Tool asked us the name of the module from which to build a program - it offered as default the same name as the program itself, this was just right so we just hit <enter>. It also asked us for any options to the compositor - the defaults are usually fine, and it also asked us for any options to the system linker, LINK386, which we will rarely need to supply.

So we now have our program "animal", an ordinary OS/2 executable that can be run just by typing animal. Below is a transcript of a session just to show you what happens - you might like to walk-through the source text and convince yourself of why it does what it does.

> animal 
Please tell me the name of an animal: 
fish 
Now please think of an animal. 
Press "enter" when you're ready for me to guess what it was. 
Is it a fish ? 
no 
What was the correct answer ? 
cat 
Please give a distinguishing feature: 
gills 
Is this a property of a cat ? 
no 
Do you want to think of another animal ? 
yes 
Press "enter" when you're ready for me to guess what it was. 
Does it have gills ? 
no 
Is it a cat ? 
no 
What was the correct answer ? 
dog 
Please give a distinguishing feature: 
9 lives 
Is this a property of a dog ? 
no 
Do you want to think of another animal ? 
no 

We can find out what the status of modules and programs in our project is by:

> compile status 
ALBUM album chained to \libs\album 

MODULES: 

A68 Module animal (Complete) 

PROGRAMS: 

Program animal (active) 

which tells us firstly what the album in use is and that it has been chained to the standard supplied album, and lists the modules and programs in our album with their status: Module animal is Complete which means that it is fully compiled; Program animal is active which means that the Compilation Tool will try and keep it up to date if any source files change.

Suppose we modify animal.a68 to include a small initial database: Replace

NODE database := reply ("Please tell me the name of an animal: "); 

with
NODE database := DECISION ("gills", HEAP NODE := "fish", HEAP NODE := "cat"); 

and save the file to disk. Now when the Compilation Tool is run with no directives at all:
Example:
> compile 
Module animal is out of date due to changes in file D:\projects\examples\animal.a68 
Compiling animal ... 
ALGOL 68 Compiler, MK3.1a (OS/2 2+, 32-bit x86) 
(C) Oxford & Cambridge Compilers Ltd 1993. 
Compiled 132 lines: D:\projects\examples\animal.a68 
Ok - 133 lines compiled 
Program animal.exe is out of date due to changes in module animal 
Composing animal ... 
Program Compositor v2.2a (OS/2 2+, 32-bit x86). 
(c) Oxford & Cambridge Compilers Ltd 1994, All rights reserved. 
Linking animal ... 
Successfully compiled: animal 
Successfully built: animal 

it discovers that the source file for module animal has changed and so recompiles it, and then, of course, the program binary is out of date and needs to be rebuilt.

And there's not much more to it than that! Once you are familiar with the different approach that modular programming brings, this sort of thing will be completely natural.

Future Development

The OCCL compiler system is in continuing development and here are some of the things planned for future releases:


The OCCL compiler system is designed for portability. A Windows/NT version is in beta-release, and Linux and other PC operating systems are planned.

But using OCCL Algol 68 does not confine you to Intel architecture machines: we already have Transputer and SPARC back-ends in alpha-release and a PowerPC back-end is planned. If you have any requirements or interests in versions other than OS/2, please contact OCCL.

Appendix: Differences between OCCL Algol 68 and the 1973 IFIP Standard

This appendix is mainly intended for programmers already familiar with Algol 68. There are still a few important differences between the language implemented and the 1973 standard. However most Algol 68 programs should compile without complaint. Please inform OCCL of any difficulty you find and we will try to help.

Restrictions


Deviations


Extensions



ALGOL 68 FOR OS/2 / OCCL / enquiries@occl-cam.demon.co.uk