Px compilers

The Pascal-P compiler was created in 1973, then went through several versions, which so far have not been available. Ch. Jacobi gives an overview of the Pascal-P versions in PUG newsletter #4:

Name Origination Year Source Description
Pascal P1 Zurich 1973 No Either of the early Pascal-P systems (released in March and July 1973 respectively)
Pascal-P2 Zurich 1974 Yes The Pascal-P system released in May 74
Pascal-P3 Zurich 1976 No The new Pascal-P system with the same hypothetical machine as the one underlying the Pascal P2 system
Pascal-P4 Zurich 1976 Yes The new Pascal-P system with a slightly modified hypothetical machine (allowing a more efficient implementation)

The versions of P1 that existed have (so far) not been available. The revised version, P2, is available, and was used as the basis for the UCSD system. P3 was a “step” implementation used to bridge between P2 and P4, and is also not obtainable.

The last major version of Pascal-P was P4.

PL/0

PL/0 is a small educational language dsigned and implemented by Wirth to be used as an example of compiler development.

The language was presented by Niklaus Wirth in his book “Algorithms + Data Structures = Programs” (1975). LAter versions of this book did not contain PL/0.
His later book “Compilerbau” (“Compiler Construction”, 1976) provided a full source code of PL/0 compiler written in Pascal. Later editions (3rd, 1984)  the PL/0 compiler was rewritten to Modula.
After Oberon was conceived the example language was succeeded by Oberon-0.

The capabilities of the language were intentionally limited:

–the only data type are integer numbers. Still all constants and variables used have to be declared explicitly, not deduced at compile-time.
–the only operators provided are arithmetical and comparison ones.
–there is one built-in function odd which checks whether the integer argument is odd.
– there are no input/output routines; instead the compiler prints the new value of each variable whenever it gets changed.
– the flow control structures are represented by if-then and while-do constructs, as well as user-defined procedures (which can’t accept any parameters).

The syntax of PL/0 described in extended Backus-Naur form

program = block "." .

block = [ "const" ident "=" number {"," ident "=" number} ";"]
        [ "var" ident {"," ident} ";"]
        { "procedure" ident ";" block ";" } statement .

statement = [ ident ":=" expression | "call" ident 
              | "?" ident | "!" expression 
              | "begin" statement {";" statement } "end" 
              | "if" condition "then" statement 
              | "while" condition "do" statement ].

condition = "odd" expression |
            expression ("="|"#"|"<"|"<="|">"|">=") expression .

expression = [ "+"|"-"] term { ("+"|"-") term}.

term = factor {("*"|"/") factor}.

factor = ident | number | "(" expression ")".

Elements of syntax

Case-sensitivity yes
Variable assignment :=
Variable declaration var
Block begin … end
Physical (shallow) equality =
Physical (shallow) inequality #
Comparison < >
Function definition procedure <name>; <body>;
Function call call <name>
Sequence ;
If – then if <condition> then <trueBlock>
Loop forever while 1 = 1 do <loopBody>
While condition do while do <loopBody>

To compile with Delphi, Freepascal, or any compiler where object is a reserved name: rename identifier ‘object’

program pl0(input,output);
{pl/0 compiler with code generation}
label 99;
const norw = 11;     {no. of reserved words}
   txmax = 100;      {length of identifier table}
   nmax = 14;        {max. no. of digits in numbers}
   al = 10;          {length of identifiers}
   amax = 2047;      {maximum address}
   levmax = 3;       {maximum depth of block nesting}
   cxmax = 200;      {size of code array}
type symbol =
   (nul,ident,number,plus,minus,times,slash,oddsym,
    eql,neq,lss,leq,gtr,geq,lparen,rparen,comma,semicolon,
    period,becomes,beginsym,endsym,ifsym,thensym,
    whilesym,dosym,callsym,constsym,varsym,procsym);
    alfa = packed array [1..al] of char;
    object = (constant,varible,proc);
    symset = set of symbol;
    fct = (lit,opr,lod,sto,cal,int,jmp,jpc);   {functions}
    instruction = packed record
                     f: fct;           {function code}
                     l: 0..levmax;     {level}
                     a: 0..amax        {displacement address}
                  end;
{   lit 0,a  :  load constant a
    opr 0,a  :  execute operation a
    lod l,a  :  load varible l,a
    sto l,a  :  store varible l,a
    cal l,a  :  call procedure a at level l
    int 0,a  :  increment t-register by a
    jmp 0,a  :  jump to a
    jpc 0,a  :  jump conditional to a   }
var ch: char;         {last character read}
    sym: symbol;      {last symbol read}
    id: alfa;         {last identifier read}
    num: integer;     {last number read}
    cc: integer;      {character count}
    ll: integer;      {line length}
    kk, err: integer;
    cx: integer;      {code allocation index}
    line: array [1..81] of char;
    a: alfa;
    code: array [0..cxmax] of instruction;
    word: array [1..norw] of alfa;
    wsym: array [1..norw] of symbol;
    ssym: array [char] of symbol;
    mnemonic: array [fct] of
                 packed array [1..5] of char;
    declbegsys, statbegsys, facbegsys: symset;
    table: array [0..txmax] of
           record name: alfa;
              case kind: object of
              constant: (val: integer);
              varible, proc: (level, adr: integer)
           end;
procedure error(n: integer);
begin writeln(' ****',' ': cc-1, '^',n: 2); err := err+1
end {error};
 
procedure getsym;
   var i,j,k: integer;
 
   procedure getch;
   begin if cc = ll then
      begin if eof(input) then
                 begin write(' program incomplete'); goto 99
                 end;
         ll := 0; cc := 0; write(cx: 5,' ');
         while not eoln(input) do
            begin ll := ll+1; read(ch); write(ch); line[ll]:=ch
            end;
         writeln; readln; ll := ll + 1; line[ll] := ' ';
      end;
      cc := cc+1; ch := line[cc]
   end {getch};
 
begin {getsym}
   while ch  = ' ' do getch;
   if ch in ['a'..'z'] then
   begin {identifier or reserved word} k := 0;
      repeat if k < al then begin k := k+1; a[k] := ch end; getch; until not(ch in ['a'..'z','0'..'9']); if k >= kk then kk := k else
         repeat a[kk] := ' '; kk := kk-1
         until kk = k;
      id := a; i := 1; j := norw;
      repeat k := (i+j) div 2;
         if id <= word[k] then j := k-1; if id >= word[k] then i := k+1
      until i > j;
      if i-1 > j then sym := wsym[k] else sym := ident
   end else
   if ch in ['0'..'9'] then
   begin {number} k := 0; num := 0; sym := number;
      repeat num := 10*num + (ord(ch)-ord('0'));
         k := k+1; getch
      until not(ch in ['0'..'9']);
      if k > nmax then error(30)
   end else
   if ch = ':' then
   begin getch;
      if ch = '=' then
      begin sym := becomes; getch
      end else sym := nul;
   end else
   begin sym := ssym[ch]; getch
   end
end {getsym};
 
procedure gen(x: fct; y,z: integer);
begin if cx > cxmax then
           begin write(' program too long'); goto 99
           end;
   with code[cx] do
      begin f := x; l := y; a := z
      end;
   cx := cx + 1
end {gen};
 
procedure test(s1,s2: symset; n: integer);
begin if not(sym in s1) then
        begin error(n); s1 := s1 + s2;
           while not(sym in s1) do getsym
        end
end {test};
 
procedure block(lev,tx: integer; fsys: symset);
   var dx: integer;     {data allocation index}
      tx0: integer;     {initial table index}
      cx0: integer;     {initial code index}
   procedure enter(k: object);
   begin {enter object into table}
      tx := tx + 1;
      with table[tx] do
      begin name := id; kind := k;
         case k of
         constant: begin if num > amax then
                              begin error(30); num :=0 end;
                      val := num
                   end;
         varible: begin level := lev; adr := dx; dx := dx + 1;
                  end;
         proc: level := lev
         end
      end
   end {enter};
 
   function position(id: alfa): integer;
      var i: integer;
   begin {find indentifier id in table}
      table[0].name := id; i := tx;
      while table[i].name <> id do i := i-1;
      position := i
   end {position};
 
   procedure constdeclaration;
   begin if sym = ident then
      begin getsym;
         if sym in [eql, becomes] then
         begin if sym = becomes then error(1);
            getsym;
            if sym = number then
               begin enter(constant); getsym
               end
            else error(2)
         end else error(3)
      end else error(4)
   end {constdeclaration};
 
   procedure vardeclaration;
   begin if sym = ident then
           begin enter(varible); getsym
           end else error(4)
   end {vardeclaration};
 
   procedure listcode;
      var i: integer;
   begin {list code generated for this block}
      for i := cx0 to cx-1 do
         with code[i] do
            writeln(i:5, mnemonic[f]:5, 1:3, a:5)
   end {listcode};
 
   procedure statement(fsys: symset);
      var i, cx1, cx2: integer;
      procedure expression(fsys: symset);
         var addop: symbol;
         procedure term(fsys: symset);
            var mulop: symbol;
            procedure factor(fsys: symset);
               var i: integer;
            begin test(facbegsys, fsys, 24);
               while sym in facbegsys do
               begin
                  if sym = ident then
                  begin i:= position(id);
                     if i = 0 then error(11) else
                     with table[i] do
                     case kind of
                        constant: gen(lit, 0, val);
                        varible: gen(lod, lev-level, adr);
                        proc: error(21)
                     end;
                     getsym
                  end else
                  if sym = number then
                  begin if num >  amax then
                           begin error(30); num := 0
                           end;
                     gen(lit, 0, num); getsym
                  end else
                  if sym = lparen then
                  begin getsym; expression([rparen]+fsys);
                     if sym = rparen then getsym else error(22)
                  end;
                  test(fsys, [lparen], 23)
               end
            end {factor};
 
         begin {term} factor(fsys+[times, slash]);
            while sym in [times, slash] do
             begin mulop:=sym;getsym;factor(fsys+[times,slash]);
              if mulop=times then gen(opr,0,4) else gen(opr,0,5)
             end
         end {term};
      begin {expression}
         if sym in [plus, minus] then
            begin addop := sym; getsym; term(fsys+[plus,minus]);
               if addop = minus then gen(opr, 0,1)
            end else term(fsys+[plus, minus]);
         while sym in [plus, minus] do
            begin addop := sym; getsym; term(fsys+[plus,minus]);
               if addop=plus then gen(opr,0,2) else gen(opr,0,3)
            end
      end {expression};
 
      procedure condition(fsys: symset);
         var relop: symbol;
      begin
         if sym  = oddsym then
         begin getsym; expression(fsys); gen(opr, 0, 6)
         end else
         begin expression([eql, neq, lss, gtr, leq, geq]+fsys);
            if not(sym in [eql, neq, lss, leq, gtr, geq]) then
               error(20) else
            begin relop := sym; getsym; expression(fsys);
               case relop of
                  eql: gen(opr, 0, 8);
                  neq: gen(opr, 0, 9);
                  lss: gen(opr, 0, 10);
                  geq: gen(opr, 0, 11);
                  gtr: gen(opr, 0, 12);
                  leq: gen(opr, 0, 13);
               end
            end
         end
      end {condition};
 
   begin {statement}
      if sym = ident then
      begin i := position(id);
         if i = 0 then error(11) else
         if table[i].kind <> varible then
            begin {assignment to non-varible} error(12); i := 0
            end;
         getsym; if sym = becomes then getsym else error(13);
         expression(fsys);
         if i <> 0 then
            with table[i] do gen(sto, lev-level, adr)
      end else
      if sym = callsym then
      begin getsym;
         if sym <> ident then error(14) else
            begin i := position(id);
               if i = 0 then error(11) else
               with table[i] do
                  if kind=proc then gen(cal, lev-level, adr)
                  else error(15);
               getsym
            end
      end else
      if sym = ifsym then
      begin getsym; condition([thensym, dosym]+fsys);
         if sym = thensym then getsym else error(16);
         cx1 := cx; gen(jpc, 0, 0);
         statement(fsys); code[cx1].a := cx
      end else
      if sym = beginsym then
      begin getsym; statement([semicolon, endsym]+fsys);
         while sym in [semicolon]+statbegsys do
         begin
            if sym = semicolon then getsym else error(10);
            statement([semicolon, endsym]+fsys)
         end;
         if sym = endsym then getsym else error(17)
      end else
      if sym = whilesym then
      begin cx1 := cx; getsym; condition([dosym]+fsys);
         cx2 := cx; gen(jpc, 0, 0);
         if sym = dosym then getsym else error(18);
         statement(fsys); gen(jmp, 0, cx1); code[cx2].a := cx
      end;
      test(fsys, [], 19)
   end {statement};
 
begin {block} dx:=3; tx0:=tx; table[tx].adr:=cx; gen(jmp,0,0);
   if lev > levmax then error(32);
   repeat
      if sym = constsym then
      begin getsym;
         repeat constdeclaration;
            while sym = comma do
               begin getsym; constdeclaration
               end;
            if sym = semicolon then getsym else error(5)
         until sym <> ident
      end;
      if sym = varsym then
      begin getsym;
         repeat vardeclaration;
            while sym = comma do
               begin getsym; vardeclaration
               end;
            if sym = semicolon then getsym else error(5)
         until sym <> ident;
      end;
      while sym = procsym do
      begin getsym;
         if sym = ident then
            begin enter(proc); getsym
            end
         else error(4);
         if sym = semicolon then getsym else error(5);
         block(lev+1, tx, [semicolon]+fsys);
         if sym = semicolon then
            begin getsym;test(statbegsys+[ident,procsym],fsys,6)
            end
         else error(5)
      end;
      test(statbegsys+[ident], declbegsys, 7)
   until not(sym in declbegsys);
   code[table[tx0].adr].a := cx;
   with table[tx0] do
      begin adr := cx; {start adr of code}
      end;
   cx0 := 0{cx}; gen(int, 0, dx);
   statement([semicolon, endsym]+fsys);
   gen(opr, 0, 0); {return}
   test(fsys, [], 8);
   listcode;
end {block};
 
procedure interpret;
   const stacksize = 500;
   var p,b,t: integer; {program-, base-, topstack-registers}
      i: instruction; {instruction register}
      s: array [1..stacksize] of integer; {datastore}
   function base(l: integer): integer;
      var b1: integer;
   begin b1 := b; {find base l levels down}
      while l > 0 do
         begin b1 := s[b1]; l := l - 1
         end;
      base := b1
   end {base};
 
begin writeln(' start pl/0');
   t := 0; b := 1; p := 0;
   s[1] := 0; s[2] := 0; s[3] := 0;
   repeat i := code[p]; p := p + 1;
      with i do
      case f of
      lit: begin t := t + 1; s[t] := a
           end;
      opr: case a of {operator}
           0: begin {return}
                 t := b - 1; p := s[t + 3]; b := s[t + 2];
              end;
           1: s[t] := -s[t];
           2: begin t := t - 1; s[t] := s[t] + s[t + 1]
              end;
           3: begin t := t - 1; s[t] := s[t] - s[t + 1]
              end;
           4: begin t := t - 1; s[t] := s[t] * s[t + 1]
              end;
           5: begin t := t - 1; s[t] := s[t] div s[t + 1]
              end;
           6: s[t] := ord(odd(s[t]));
           8: begin t := t - 1; s[t] := ord(s[t] = s[t + 1])
              end;
           9: begin t := t - 1; s[t] := ord(s[t] <> s[t + 1])
              end;
          10: begin t := t - 1; s[t] := ord(s[t] < s[t + 1]) end; 11: begin t := t - 1; s[t] := ord(s[t] >= s[t + 1])
              end;
          12: begin t := t - 1; s[t] := ord(s[t] > s[t + 1])
              end;
          13: begin t := t - 1; s[t] := ord(s[t] <= s[t + 1])
              end;
          end;
      lod: begin t := t + 1; s[t] := s[base(l) + a]
           end;
      sto: begin s[base(l)+a] := s[t]; writeln(s[t]); t := t - 1
           end;
      cal: begin {generate new block mark}
              s[t + 1] := base(l); s[t + 2] := b; s[t + 3] := p;
              b := t + 1; p := a
           end;
      int: t := t + a;
      jmp: p := a;
      jpc: begin if s[t] = 0 then p := a; t := t - 1
           end
      end {with, case}
   until p = 0;
   write(' end pl/0');
end {interpret};

begin {main program}
   for ch := chr(0) to chr(255) do ssym[ch] := nul;
   word[ 1] := 'begin     ';      word[ 2] := 'call      ';
   word[ 3] := 'const     ';      word[ 4] := 'do        ';
   word[ 5] := 'end       ';      word[ 6] := 'if        ';
   word[ 7] := 'odd       ';      word[ 8] := 'procedure ';
   word[ 9] := 'then      ';      word[10] := 'var       ';
   word[11] := 'while     ';
   wsym[ 1] := beginsym;     wsym[ 2] := callsym;
   wsym[ 3] := constsym;     wsym[ 4] := dosym;
   wsym[ 5] := endsym;       wsym[ 6] := ifsym;
   wsym[ 7] := oddsym;       wsym[ 8] := procsym;
   wsym[ 9] := thensym;      wsym[10] := varsym;
   wsym[11] := whilesym;
   ssym[ '+'] := plus;       ssym[ '-'] := minus;
   ssym[ '*'] := times;      ssym[ '/'] := slash;
   ssym[ '('] := lparen;     ssym[ ')'] := rparen;
   ssym[ '='] := eql;        ssym[ ','] := comma;
   ssym[ '.'] := period;     ssym[ '#'] := neq;
   ssym[ '<'] := lss; ssym[ '>'] := gtr;
   ssym[ '['] := leq;        ssym[ ']'] := geq;
   ssym[ ';'] := semicolon;
   mnemonic[lit] := '  lit';   mnemonic[opr] := '  opr';
   mnemonic[lod] := '  lod';   mnemonic[sto] := '  sto';
   mnemonic[cal] := '  cal';   mnemonic[int] := '  int';
   mnemonic[jmp] := '  jmp';   mnemonic[jpc] := '  jpc';
   declbegsys := [constsym, varsym, procsym];
   statbegsys := [beginsym, callsym, ifsym, whilesym];
   facbegsys  := [ident, number, lparen];
   page(output); err := 0;
   cc := 0; cx := 0; ll := 0; ch := ' '; kk := al; getsym;
   block(0, 0, [period]+declbegsys+statbegsys);
   if sym <> period then error(9);
  if err=0 then interpret else write(' errors in pl/0 program');
99: writeln
end.
post

Project Oberon

Hardware FPGA implementation document by Niklaus Wirth

Project Oberon emulators

Emulator for the Oberon RISC machine by Peter de Wachter

Oberon RISC Emulator for Pascal Markus Greim

Project Oberon emulator in JavaScript and Java  Michael Schierl

Project Oberon as MacIntosh  application Oberon Workstation

Pipistrella hardware FPGA

OberonStation.  Hardware with FPGA to run Project Oberon.
Since I am sincerely disapppointed in the hardware and support , see my blog entry, I will not describe this here nor link to it.
Search yourself and be warned!

Here is a summary of acronyms and version names jwr robrts net gleaned from
various messages and sources. Please provide feedback and corrections
as appropriate.

ALO ARM Linux Oberon (Oberon in LNO family, for ARM CPU eg Raspberry Pi)
ETHO ETH Oberon (ETH is Eidgen?ssische Technische Hochschule Z?rich)
LEO Linux ETH Oberon [ETHO 2.4.3 for Linux x86]
LNO Linux Native Oberon
NO Native Oberon
OCP Oberon Community Platform
OLR Oberon Linux Revival

Is ETH-Linux-Oberon the same as LEO or LNO? (Probably it is LEO.) Is
Linux-ETH-Oberon the same as LEO? Same as ETH-Linux-Oberon?
See https://lists.inf.ethz.ch/pipermail/oberon/2015/007996.html
and https://lists.inf.ethz.ch/pipermail/oberon/2008/005410.html

BB BlackBox Component Builder, Component Pascal IDE
from Oberon Microsystems, http://www.oberon.ch/blackbox.html
CP Component Pascal
[A dialect in the Oberon family most similar to Oberon-2]

AOS Active Object System (2003)
UnixAOS Unix-based AOS
WinAOS Windows-based AOS
Bluebottle New system based on AOS kernel (2005)
A2 New system after Bluebottle (2008)
See http://www.oberon.ethz.ch/ for AOS/Bluebottle/A2 history
Crazy-Fresh Bluebottle [see http://www.ethoberon.ethz.ch/] Crazy-Fresh
A2 [see http://sourceforge.net/projects/a2oberon/files/]

The following appear to be versions of the language definition itself.
In another message another day I plan to identify documentation for each.

Original Oberon (1987/88/90)
Revised Oberon (1992) [later called Oberon-07]
Oberon-2 is a compatible superset of Revised Oberon (1992)
Oberon-07 is a new language based on Oberon and Oberon-SA
See http://oberon07.com/ and http://oberon07.com/FAQ.xhtml
See https://www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.pdf
See https://www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.Report.pdf
Project Oberon (1992) Ceres-based NS32032 implementation of Revised Oberon
see http://www.ethoberon.ethz.ch/WirthPubl/ProjectOberon.pdf
Project Oberon (2013) FPGA-based RISC5 implementation of Oberon-07
see http://www.projectoberon.com/ and
https://www.inf.ethz.ch/personal/wirth/
Oakwood Guidelines for Oberon-2 Compiler Developers

Other names found for various Oberon implementations and versions include:

Oberon S3 = Oberon System 3 (Became ETH Oberon)
Oberon V4 (Associated with both ETH and University of Linz)
See http://sourceforge.net/projects/oberon/ and
http://www.ssw.uni-linz.ac.at/Research/Projects/Oberon.html
See http://users.cms.caltech.edu/~cs140/140a/Oberon/system_faq.html for
Oberon = V1 ( V2 V4 | System3 )
Oberon V1 [Original Oberon??]
Oberon V2 [??]
Oberon V4 [Started at ETH, more development at University of Linz]
Oberon System3 [Became ETH Oberon]

Native Oberon [Based on ETH Oberon]
(see http://www.oberon.ethz.ch/downloads/index for current versions)
(see http://www.oberon.ethz.ch/archives/systemsarchive/native_new)
PC Native Oberon [for Intel-compatible PCs]
PC Native Oberon for Dummies [for Windows installation]
Linux-based Native Oberon [LNO]
SharkOberon [for DEC Shark Network Computers, ARM-based]
Native Oberon Alpha [http://www.oberon.ethz.ch/faq/faqnativealfabeta]
Native Oberon Beta [see same link as Alpha]

Versions at http://www.oberon.ethz.ch/archives/languagearchive/genealogy
Oberon
Oberon-V
Oberon X
Active Oberon
Oberon-SA
Active Oberon for .NET
Object Oberon
Oberon-2
Concurrent Oberon
Action Oberon
Oberon-D
Component Pascal

Versions at http://www.ethoberon.ethz.ch/genealsys.html not already above
SPARC-Oberon
MacOberon
DEC-Oberon
RISC Oberon
MS-DOS Oberon
Chameleon Oberon
HP-Oberon
Oberon for Windows
Spirit of Oberon
Hybrid Oberon
Oberon for Linux
Oberon Linux PPC
more versions named according to supporting OS?

Oberon-0 a particular Oberon language/compiler
see https://en.wikipedia.org/wiki/PL/0
Oberon0 implementation of an Oberon-0 compiler (?) or minimal Oberon
system (?)
Oberon0.dsk disk image for bootable Oberon0 installer
(see
http://sourceforge.net/projects/nativeoberon/files/nativeoberon/Oberon
0%20boot%20disk/)

Another acronym observed is OP2, which is a Portable Oberon compiler
by R Crelier

School of Niklaus Wirth: The Art of Simplicity

School of Wirth

Got myself an excellent book on the Art of Simplicity. Niklaus Wirth designed programming langauages like Pascal and sequels like Modula-2 and Oberon.  His style and dedication to simplicity in a clear writing and presentation style made a great impression on me.

This book gives unique insights in what has happened and is still happening in the school of Niklaus Wirth. Excellent book!

From the Back Cover

Niklaus Wirth is one of the great pioneers of computer technology and winner of the ACM’s A.M. Turing Award, the most prestigious award in computer science. He has made substantial contributions to the development of programming languages, compiler construction, programming methodology, and hardware design. While working at ERH Zurich, he developed the languages Pascal and Modula-2. He also designed an early high performance workstation, the Personal Computer Lilith, and most recently the language and operating system Oberon.
While Wirth has often been praised for his excellent work as a language designer and engineer, he is also an outstanding educator – something for which he is not as well known. This book brings together prominent computer scientists to describe Wirth’s contributions to education. With the exception of some of his colleagues such as Professors Dijkstra, Hoare, and Rechenberg, all of the contributors to this book are students of Wirth. The essays provide a wide range of contemporary views on modern programming practice and also illuminate the one persistent and pervasive quality found in all his work: his unequivocal demand for simple solutions. The authors and editors hope to pass on their enthusiasm for simple engineering solutions along with their feeling for a man to whom they are all so indebted.

Contents

Editors: László Böszörményi, Jürg Gutknecht, Gustav Pomberger

Part 1: Niklaus Wirth – a Pioneer of Computer Science
Gustav Pomberger, Hanspeter Mossenbock, Peter Rechenberg
Part 2: Niklaus Wirth and Edsger W. Dijkstra From Programming Language Design to Computer Construction
Niklaus Wirth On the transitive closure of a wellfounded relation
Edsger W. Dijkstra
Part 3: The Teachings of a Scholar as Told by his Pupils – Common Work in Retrospect
Oberon – the Overlooked Jewel Michael Franz
Compiler Construction – The Art of Niklaus Wirth Hanspeter Mossenbock
Medos in Retrospect Svend Erik Knudsen
Lean Systems in an Intrinsically Complex World Peter Schulthess
Learning the Value of Simplicity Stephen W. Gehring
Part 4: New Ways in Education and Research
Compiler Construction versus Lotus Notes: A Strange Battle Jurg Gutknecht
Modules and Components – Rivals or Partners Clemens Szyperski
A Compiler for the Java HotSpot Virtual Machine Robert Griesemer, Srdjan Mitrovic
Designing a Cluster Network Hans Eberle
Programming With Functional Nets Martin Odersky
Part 5: Mastering Simplicity – in the Industry
Lilith meets the World of Business Bernhard Wagner
the Chip Company that made $100M with MODULA-2 Robert Burton, Farrell Ostler, Thom Boyer, Fon Brown, Matt Morrise
FFF97 – Oberon in the Real World Dr. Josef Templ
Part 6: The World According to Wirth – Personal, Anecdotal Reviews
Serendipity Kathleen Jensen
Daily Life with N. WirthJirka Hoppe
Third Millennium Culture Ann Dunki Authors and Editors

 

Photos of Lilith

In may 2006 Jos Dreesen send me the following photos of a surviving, but then not functional Lilith. In january 2008 Jos succeeded in having this machine running again, one of the few functional remaining Lilith computers (there may be a functioning one at ETH, about ten are known to exist)!

Lilith

Lilith

Lilith

Lilith

Photos made by Jos Dreesen, 2006

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Lilith

Photos made by Jos Dreesen, 2008

Niklaus Wirth

niklaus_wirthProfessor Niklaus Wirth is an honered and well respected computer scientist. Very influencal with his work on programming, programming languages and operating systems design. Designer of Pascal, Modula, Oberon, the Lilith computer and more. As a professor at the ETH in Zurich Switzerland he advanced our knowledge and capabilities with computers and their programming.

Wirth not only designed languages, he also designed hardware, combining the strengths of his programming languages with a well suited platform. The first computer is called Lilith, of which about 100 were built around 1980. In 1981 he wrote an article about Lilith: “The Personal Computer Lilith”, on this page you find also photo’s recently made of a surviving and working Lilith computer.

Pascal-M

In 1978, via the KIM-1 user club, a Pascal compiler written by Mark Rustad, based on the P2 compiler, with a 6502 interpreter by G.J. v.d. Grinten, was given to me.
It was a complete package, on a KIM-1 cassette tape, and with rudimentary documentation. Quickly Micro-Ade, the invaluable assembler/editor, was enhanced to edit Pascal program source.

PASCAL-M

The idea was great, the result was terrible: load Micro-Ade, edit a Pascal program, load the interpreter (4K), compiler (19K), compile the program, load the object, and run the program. Of course the compiler would find errors and then the editing and compiling cycle on a cassette based system could start again.
I did this several times and then gave up. No way a Pascal compiler was a viable option on a dual cassette player based KIM-1. The lack of a file system in the compiler even made it worse.
At that moment I was finishing my Masters at the VU university in Amsterdam, in Computer Science, and was introduced to Pascal. compilers, the VU Pascal compiler and that was very exciting. My good friend Anton Muller managed to get the sources of the compiler (in Pascal) and the interpreter (assembler), all on paper photocopies and it was exciting to study those.
Alas, I could not do anything usefull with all this, and so I stored paper and cassette tapes away.

In 1983 I joined Digital Equipment as system programmer and had access to first the PDP-11 RSX-11M Pascal compiler and a bit later the first versions of VAX/VMS Pascal. And now things were possible that made me return to the Pascal-M compiler: cross-compile on VMS and run on the KIM-1! I had sources, so I spent all my lunch hours typing in the sources, compiling, testing, all possible on those excellent Digital machines! /br; In fact, it took me two years of lunch breaks to have the first working version of the PASCAL-M compiler on VMS, an interpreter/debugger in Pascal on VMS and an interpreter on the KIM-1. And with some VMS tools I could compile on VMS and load the program from cassette on the KIM-1. This is version 1.x of the PASCAL-M compiler, still mostly original as received form Mark Rustad en Willem v d Grinten, but completely embedded in the VMS – KIM-1 toolbench.
Now the plans were made to enhance the KIM-1 with floppy disk drives and he compiler with a file system, already in place in the VMS version od compiler/interpreter.
Nothing like that happened though. CP/M (on the Spectravideo SVI-738) and and Turbo Pascal came along and I left the PASCAL-M compiler, together with all KIM-1 related work, alone to collect dust. Luckily most source files, living on the VAX/VMS system, made it to floppies on CP/M and later survived many PC changes. All paper of course was of course kept, I seldom throw something away!
In 2003 Ruud Baltissen (HCC Commdodore GG) asked on Usenet about Pascal and 6502 and I remembered the PASCAL-M compiler. So I digged up the paper, and loaded the sources in what I had available then: Borland Pascal 7. And found out I could get the package working on that platform! Still fun. I was too busy to fire up the KIM-1. It still runs, but I have my doubts about the reliability of the memory IC’s. So it is not tested again on the real hardware.
That will change, as the micro-KIM is coming along! The perfect excuse to finally get this package working again and make it better.

Now here you will find the version 1 package. All is here to get a working PASCAL-M compiler/interpreter again. Either by cross-compiling (it is Borland Pascal syntax now, so FreePascal will compile on many platforms, MS-DOS executables are included). 6502 assembler sources are TASM (Squeek Vally) assember sources.
The compiler is still very close to the P2 compiler (see here for P2 source), the original mainframe source is available in scanned format.

Compiler, tools, documentation

Interpreter, assembler, tools, documentation

Orginal source in scanned format, m-code documentation

Recollections about the Development of Pascal

N. Wirth
Institut fur Computersysteme, ETH Zurich CH-8092 Zurich

Abstract

Pascal was defined in 1970 and, after a slow start, became one of the most widely used languages in introductory programming courses. This article first summarises the events leading to Pascal’s design and implementation, and then proceeds with a discussion of some of the language’s merits and deficiencies. In the last part, developments that followed its release are recounted. Its influence chiefly derived from its being a vehicle for structured programming and a basis for further development of languages and for experiments in program verification.

Contents

Early History
The Language
Later Developments
In Retrospect
Acknowledgements
References

Early History

The programming language Pascal was designed in the years 1968/69, and I named it after the French philosopher and mathematician, who in 1642 designed one of the first gadgets that might truly be called a digital calculator. The first compiler for Pascal was operational in early 1970, at which time the language definition also was published [Wirth, 1970]. These facts apparently constitute the anchor points of the history of Pascal. However, its genuine beginnings date much further back. It is perhaps equally interesting to shed some light on the events and trends of the times preceding its birth, as it is to recount the steps that led to its widespread use. I shall therefore start with a – more or less chronological – narrative of the early history of Pascal. In the early 1960s, there existed two principal scientific languages: Fortran and Algol 60 [Naur, 1963). The former was already in wide use and supported by large computer manufacturers. The latter – designed by an international committee of computing experts – lacked such support, but attracted attention by its systematic structure and its concise, formalized definition. It was obvious that Algol deserved more attention and a wider field of applications. In order to achieve it, Algol needed additional constructs making it suitable for purposes other than numerical computation. To this end, IFIP established a Working Group with the charter of defining a successor to Algol. There was hope that the undesirable canyon between scientific and commercial programming, by the mid 1960s epitomized as the Fortran and Cobol worlds, could be bridged. I had the privilege of joining Working Group 2.1. in 1964. Several meetings with seemingly endless discussions about general philosophies of language design, about formal definition methods, about syntactic details, character sets, and the whole spectrum of topics connected with programming revealed a discouraging lack of consensus about the approach to be taken. However, the wealth of ideas and experience presented also provided encouragement to coalesce them into a powerful ensemble. As the number of meetings grew, it became evident that two main factions emerged from the roughly two dozen members of the Working Group. One party consisted of the ambitious members, unwilling to build upon the framework of Algol 60, unafraid of constructing features that were largely untried and whose consequences for implementors remained a matter of speculation, and eager to erect another milestone similar to the one set by Algol 60. The opponents were more pragmatic. They were anxious to retain the body of Algol 60 and to extend it with well-understood features widening the area of applications for the envisaged successor language, but retaining the orderly structure of its ancestor. In this spirit, the addition of basic data types for double precision real numbers and for complex numbers was proposed, as well as the record structure known from COBOL, the replacement of Algol’s call-by-name with a call-by-reference parameter, and the replacement of Algol’s overly general for-statement by a restricted but more efficient version. They hesitated to incorporate novel, untested ideas into an official language, well aware that otherwise a milestone might easily turn into a millstone. In 1965, I was commissioned to submit a proposal to the WG which reflected the views of the pragmatists. In a meeting in October of the same year, however, a majority of the members favoured a competing proposal submitted by A. van Wijngaarden, former member of the Algol 60 team, and decided to select it as the basis for Algol X in a meeting of Warsaw in the fall of 1966 [van Wijngaarden, 1969]. Unfortunately, but as foreseen by many, the complexity of Algol 68 caused many delays, with the consequence that at the time of its implementation in the early 70s, many users of Algol 60 had already adopted other languages [Hoare, 1980]. I proceeded to implement my own proposal in spite of its rejection, and to incorporate the concept of dynamic data structures and pointer binding suggested by CAR. Hoare. The implementation was made at Stanford University for the new IBM 360 computer. The project was supported by a grant of the US National Science Foundation. The outcome was published and became known as Algol W [Wirth, 1966]. The system was adopted at many universities for teaching programming courses, but the language remained confined to IBM 360/370 computers. Essentially, Algol W had extended Algol 60 with new data types representing double precision floating-point and complex numbers, with bit strings and with dynamic data structures linked by pointers. In spite of pragmatic precautions, the implementation turned out to be rather complex, requiring a run-time support package. It failed to be an adequate tool for systems programming, partly because it was burdened with features unnecessary for systems programming tasks, partly because it lacked adequately flexible data structuring facilities. I therefore decided to pursue my original goal to design a general-purpose language without the heavy constraints imposed by the necessity of finding a consensus among two dozen experts about each and every little detail. The past experience had given me a life-long mistrust in the products of committees, where many participate in debating and decision making, and few perform the actual work made difficult by the many. In 1968, I assumed a professorship at the Federal Institute of Technology in Zurich (ETH), where Algol 60 had been the language of choice among researchers in numeric computation. The acquisition of CDC computers in 1965 (and even more so in 1970), made this preference hard to justify, because Algol compilers for these computers were rather poorly designed and could not compete with their Fortran counterparts. Furthermore, the task of teaching programming – in particular systems programming – appeared highly unattractive, given the choice between Fortran and assembler code as the only available tools. After all, it was high time to not only preach the virtues of Structured Programming, but to make them applicable in actual practice by providing a language and compilers offering appropriate constructs. The discipline of Structured Programming had been outlined by E. W. Dijkstra [Dijkstra 1966] and represented a major step forward in the battle against what became known as the Software Crisis. It was felt that the discipline was to be taught at the level of introductory programming courses, rather than as an afterthought while trying to retrain old hands. This insight is still valid today. Structured Programming and Stepwise Refinement [Wirth 1971a] marked the beginnings of a methodology of programming and became a cornerstone in letting program design become a subject of intellectual respectability. Hence, the definition of a new language and the development of its compiler were not a mere research project in language design, but rather a blunt necessity. The situation was to recur several times in the following decades, when the best advice was: Lacking adequate tools, build your own! In 1968, the goals were twofold: The language was to be suitable for expressing the fundamental constructs known at the time in a concise and logical way, and its implementation was to be efficient and competitive with existing Fortran compilers. The latter requirement turned out to be rather demanding, given a computer (the CDC 6000) that was designed very much with Fortran in mind. In particular, dynamic arrays and recursive procedures appeared as formidable obstacles, and were therefore excluded from an initial draft of the language. The prohibition of recursion was a mistake and was soon to be rectified, in due recognition that it is unwise to be influenced severely by an inadequate tool of a transitory nature. The task of writing the compiler was assigned to a single graduate student (E. Marmier) in 1969. As his programming experience was restricted to Fortran, the compiler was to be expressed in Fortran, with its translation into Pascal and subsequent self-compilation planned after its completion. This, as it turned out, was another grave mistake. The inadequacy of Fortran to express the complex data structures of a compiler caused the program to become contorted and its translation amounted to a redesign, because the structures inherent in the problem had become invisible in the Fortran formulation. The compiler relied on syntax analysis based on the table-driven bottom-up (LR) scheme adopted from the Algol-W compiler. Sophistication in syntax analysis was very much in style in the 1960s, allegedly because of the power and flexibility required to process high-level languages. It occurred to me then that a much simpler and more perspicuous method could well be used, if the syntax of a language was chosen with the process of its analysis in mind. The second attempt at building a compiler therefore began with its formulation in the source language itself, which by that time had evolved into what was published as Pascal in 1970 [Wirth, 1970], The compiler was to be a single-pass system based on the proven top-down, recursive-descent principle for syntax analysis. We note here that this method was eligible because the ban against recursion had been lifted: recursivity of procedures was to be the normal case. The team of implementors consisted of U. Ammann, E. Marmier, and R. Schild. After the program was completed – a healthy experience in programming in an unimplemented language! – Schild was banished to his home for two weeks, the time it took him to translate the program into an auxiliary, low-level language available on the CDC computer. Thereafter, the bootstrapping process could begin [Wirth, 1971b]. This compiler was completed by mid 1970, and at this time the language definition was published. With the exception of some slight revisions in 1972, it remained stable thereafter. We began using Pascal in introductory programming courses in late 1971. As ETH did not offer a computer science program until ten years later, the use of Pascal for teaching programming to engineers and physicists caused a certain amount of controversy. My argument was that the request to teach the methods used in industry, combined with the fact that industry uses the methods taught at universities, constitutes a vicious circle barring progress. But it was probably my stubborn persistence rather than any reasoned argument that kept Pascal in use. Ten years later, nobody minded. In order to assist in the teaching effort, Kathy Jensen started to write a tutorial text explaining the primary programming concepts of Pascal by means of many examples. This text was first printed as a Technical Report and thereafter appeared in Springer-Verlag’s Lecture Notes Series [Jensen, 1974].

The Language

The principal role of a language designer is that of a judicious collector of features or concepts. Once these concepts are selected, forms of expressing them must be found, i.e. a syntax must be defined. The forms expressing individual concepts must be carefully molded into a whole. This is most important, as otherwise the language will appear as incoherent, as a skeleton onto which individual constructs were grafted, perhaps as after-thoughts. Sufficient time had elapsed that the main flaws of Algol were known and could be eliminated. For example, the importance of avoiding ambiguities had been recognised. In many instances, a decision had to be taken whether to solve a case in a clearcut fashion, or to remain compatible with Algol. These options sometimes were mutually exclusive. In retrospect, the decisions in favour of compatibility were unfortunate, as they kept inadequacies of the past alive. The importance of compatibility was overestimated, just as the relevance and size of the Algol-60 community had been. Examples of such cases are the syntactic form of structured statements without closing symbol, the way in which the result of a function procedure is specified (assignment to the function identifier), and the incomplete specification of parameters of formal procedures. All these deficiencies were later corrected in Pascal’s successor language Modula-2 [Wirth, 1982], For example, Algol’s ambiguous conditional statement was retained. Consider

IF p THEN IF q THEN A ELSE B

which can, according to the specified syntax, be interpreted in the following two ways:

IF p THEN [IF q THEN A ELSE B]

IF p THEN [IF q THEN A] ELSE B

Pascal retained this syntactic ambiguity, selecting, however, the interpretation that every ELSE be associated with the closest THEN at its left. The remedy, known but rejected at the time, consists of requiring an explicit closing symbol for each structured statement, resulting in the two distinct forms for the two cases as shown below:

IF p THEN IF q THEN A ELSE B END END

IF p THEN
IF q THEN A END
ELSE B
END
Pascal also retained the incomplete specification of parameter types of a formal procedure, leaving open a dangerous loophole for breaching type checks. Consider the declarations

PROCEDURE P (PROCEDURE q);
BEGIN q(x,y) END;

PROCEDURE Q (x: REAL);
BEGIN… END;

and the call P(Q). Then q is called with the wrong number of parameters, which cannot in general be detected at the time of compilation. In contrast to such concessions to tradition stood the elimination of conditional expressions. Thereby the symbol IF clearly becomes a marker of the beginning of a statement and bewildering constructs of the form

IF p THEN x:=IF q THEN y ELSE z ELSE w

are banished from the language.
The baroque for-statement of Algol was replaced by a tamed version which is efficiently implementable, restricting the control variable to be a simple variable and the limit to be evaluated only once instead of before each repetition. For more general cases of repetitions, the while statement was introduced. Thus it became impossible to formulate misleading, non-terminating statements as for example

FOR i:=0 STEP 1 UNTIL i DO S

and the rather obscure formulation

FOR i := n-1, i-1 WHILE i > 0 DO S

could be expressed more clearly by

i :=n;
WHILE i > 0 DO BEGIN i := i-1; S END

The primary innovation of Pascal was to incorporate a variety of data types and data structures, similar to Algol’s introduction of a variety of statement structures. Algol offered only three basic data types, namely integers, real numbers, and truth values, and the array structure; Pascal introduced additional basic types and the possibility to define new basic types (enumerations, subranges), as well as new forms of structuring: record, set, and file (sequence), several of which had been present in COBOL Most important was of course the recursivity of structural definitions, and the consequent possibility to combine and nest structures freely. Along with programmer-defined data types came the clear distinction between type definition and variable declaration, variables being instances of a type. The concept of strong typing – already present in Algol – emerged as an important catalyst for secure programming. A type was to be understood as a template for variables specifying all properties that remain fixed during the time-span of a variable’s existence. Whereas its value changes (through assignments), its range of possible values remains fixed, as well as its structure. This explicitness of static properties allows compilers to verify whether rules governing types are respected. The binding of properties to variables in the program text is called early binding and is the hallmark of high-level languages, because it gives clear expression to the intention of the programmer, unobscured by the dynamics of program execution. However, the strict adherence to the notion of (static) type led to some less fortunate consequences. We here refer to the absence of dynamic arrays. These can be understood as static arrays with the number of elements (or the bounding index values) as a parameter. Pascal did not include parametrised types, primarily for reasons of implementation, although the concept was well understood. Whereas the lack of dynamic array variables may perhaps not have been too serious, the lack of dynamic array parameters is clearly recognised as a defect, if not in the view of the compiler designer, then certainly in the view of the programmer of numerical algorithms. For example, the following declarations do not permit procedure P to be called with x as its actual parameter:

TYPE A0 = ARRAY [1 .. 100] OF REAL; A1 = ARRAY [0 .. 999] OF REAL;
VAR x: A1;
PROCEDURE P(x: AO); BEGIN … END

Another important contribution of Pascal was the clear conceptual and denotational separation of the notions of structure and access method. Whereas in Algol W arrays could only be declared as static variables and hence only be accessed directly, record structured variables could only be accessed via references (pointers), i.e. indirectly. In Pascal, all structures can be either accessed directly or via pointers, indirection being specified by an explicit dereferencing operator. This separation of concerns was known as ‘orthogonal design’, and was pursued (perhaps to extreme) in Algol 68. The introduction of explicit pointers, i.e. variables of pointer type, was the key to a significant widening of the scope of application. Using pointers, dynamic data structures can be built, as in list-processing languages. It is remarkable that the flexibility in data structuring was made possible without sacrificing strict static type checking. This was due to the concept of pointer binding. I.e. of declaring each pointer type as being bound to the type of the referenced objects, as proposed by [Hoare, 1972]. Consider, for Instance, the declarations

TYPE Pt = ^ Rec; Rec = RECORD x,y: REAL END; VAR p, q: Pt;

Then p and q, provided they had been properly initialized, are guaranteed to hold either values referring to a record of type Rec, or the constant NIL A statement of the form p^.x := p.y + q^.x turns out to be as type-safe as the simple x := x +y. Indeed, pointers and dynamic structures were considerably more important than dynamic arrays in all applications except numeric computation. Intricately connected to pointers is the mechanism of storage allocation. As Pascal was to be suitable as a system-building language, it tried not to rely on a built-in run-time garbage collection mechanism as had been necessary for Algol W. The solution adopted was to provide an intrinsic procedure NEW for allocating a variable in a storage area called the heap, and a complementary one for deallocation (DISPOSE). NEW is trivial to implement, and DISPOSE can be ignored, and indeed it turned out to be wise to do so, because system procedures depending on programmer’s information are inherently unsafe. The idea of providing a garbage collection scheme was not considered in view of its complexity. After all, the presence of local variables and of programmer-defined data types and structures require a rather sophisticated and complex scheme crucially depending on system integrity. A collector must be able to rely on information about all variables and their types. This information must be generated by the compiler and, moreover, it must be impossible to invalidate it during program execution. The subject of parameter passing methods had already been a source of endless debates and hassles in the days of the search for a successor to Algol 60. The impracticality of its name parameter had been clearly established, and the indispensability of the value parameter was generally accepted. Yet there were valid arguments for a reference parameter, in particular for structured operands on the one hand, and good reasons for result parameters on the other. In the former case the formal parameter constitutes a hidden pointer to the actual variable, in the latter the formal parameter is a local variable to be assigned to the actual variable upon termination of the procedure. The choice of the reference parameter (in Pascal called VAR-parameter) as the only alternative to the value parameter turned out to be simple, appropriate, and successful.

And last but not least, Pascal included statements for input and output, whose omission from Algol had been a source of continuing criticism. Particularly with regard to Pascal’s role as a language for instruction, a simple form of such statements was chosen. Their first parameter designates a file and, if omitted, causes the data to be read from or written to the default medium, such as keyboard and printer. The reason for including a special statement for this purpose in the language definition, rather than postulating special, standard procedures, was the desire to allow for a variable number and different types of parameters:

Read(x.y);… ;Write(x,y,z)

As mentioned before, a language designer collects frequently used programming constructs from his own experience, from the literature, or from other languages, and molds them into syntactic forms in such a way that they together form an integrated language. Whereas the basic framework of Pascal stems from Algol W, many of the new features emerged from suggestions made by CAR. Hoare, including enumeration, subrange, set, and file types. The form of COBOL-like record types was due to Hoare, as well as the idea to represent a computer’s “logical words” by a well-known abstraction, namely sets (of small integers). These “bits and pieces” were typically presented and discussed during meetings of the IFIP Working Croup 2.1 (Algol), and thereafter appeared as communications in the Algol Bulletin. They were collected in his Notes on Data Structuring [Hoare, 1972]. In Pascal, they were distilled into a coherent and consistent framework of syntax and semantics, such that the structures were freely combinable. Pascal permits the definitions of arrays of records, records of arrays, arrays of sets, and arrays of record with files, just to name a few possibilities. Naturally, implementations would have to impose certain limits to the depth of nesting due to finite resources, and certain combinations, such as a file of files, might not be accepted at all. This case may serve as an example of the distinction between the general concepts defined by the language, and supplementary, restrictive rules governing specific implementations. Although the wealth of data types and structuring forms was the principal asset of Pascal, not all of the components were equally successful. We keep in mind that success is a subjective quality, and opinions may differ widely. I therefore concentrate on an ‘evaluation’ of a few constructs where history has given a reasonably clear verdict. The most vociferous criticism came from Habermann, who correctly pointed out that Pascal was not the last word on language design. Apart from taking issue with types and structures being merged into a single concept, and with the lack of constructs like conditional expressions, the exponentiation operator, and local blocks, which were all present in Algol 60, he reproached Pascal for retaining the much-cursed go to statement [Habermann, 1973]. In hindsight, one cannot but agree; at the time, its absence would have deterred too many people from trying to use Pascal. The bold step of proposing a goto-less language was taken ten years later by Pascal’s successor Modula-2, which remedied many shortcomings and eliminated several remaining compatibility concessions to Algol 60, particularly with regard to syntax [Wirth, 1982], A detailed and well-judged reply to the critique by Habermann was written by Lecarme, who judged the merits and deficiencies on the basis of his experience with Pascal in both teaching and compiler design [Lecarme, 1975]. Another significant critique [Welsh, 1977] discusses the issue of structural vs. name equivalence of data types, a distinction that had unfortunately been left open in the definition of Pascal. It caused many debates until it was resolved by the Standards committee. Perhaps the single most unfortunate construct was the variant record. It was provided for the purpose of constructing inhomogeneous data structures. Both for array and for a dynamic structures, in Pascal the element types must be fixed by type declarations. The variant record allows variations of the element types. The unfortunateness of the variant record of Pascal stems from the fact that it provides more flexibility than required to achieve this legitimate goal. In a dynamic structure, typically every element remains of the same type as defined by its creation. The variant record, however, allows more than the construction of heterogeneous structures, i.e. of structures with elements of different, although related types. It allows the type of elements themselves to change at any time. This additional flexibility has the regrettable property to require type checking at run-time for each access to such a variable or to one of its components. Most implemented of Pascal decided that this checking would be too expensive, enlarging code and deteriorating program efficiency. As a consequence, the variant record became a favourite feature to breach the type system by all programmers in love with tricks, which usually turn into pitfalls and calamities. Variant records also became a major hindrance to the notion of portabiliy of programs. Consider, for example, the declaration

VAR R: RECORD maxspeed: INTEGER; CASE v. Vehicle OF truck: (nofwheels: INTEGER); vessel: (homeport: String) END

Here, the designator R.nofwheels is applicable only if R.v has the value truck, and R.homeport only if R.v = vessel. No compiler checks can safeguard against erroneous use of designators, which, in the case of assignment, may be disastrous, because the variant facility is used by Implementations to save storage by overlaying the fields nofwheels and homeport. With regard to input and output operations, Pascal separated the notions of data transfer (to or from an external medium) and of representation conversion (binary to decimal and vice-versa). External, legible media were to be represented as files (sequences) of characters. Representation conversion was expressed by special read and write statements that have the appearance of procedures but allowed a variable number of parameters. Whereas the latter was essentially a concession to programmers used to Fortran’s I/O statements, the notion of sequence as a structural form was fundamental. Perhaps also in this case, providing sequences of any (even programmer-defined) element types was more than what was genuinely needed in practice. The consequence was that, in contrast to all other data types, files require a certain amount of support from built-in run-time routines, mechanisms not explicitly visible from the program text. The successors of Pascal -Modula-2 and Oberon – later retreated from the notion of the file as a structural form at the same level as array and record. This became possible, because implementations of sequencing mechanisms could be provided through modules (library packages). In Pascal, however, the notion of modules was not yet present; Pascal programs were to be regarded as single, monolithic texts. This view may be acceptable for teaching purposes where exercises are reasonably compact, but it Is not tenable for the construction of large systems. Nevertheless and surprisingly, Pascal compilers could be written as single Pascal programs.

Later Developments

As Pascal appeared to fulfil our expectations with regard to teaching, the compiler still failed to satisfy the stated goals with regard to efficiency in two aspects: First, the compiler as a relatively large stand-alone program resulted in fairly long ‘turn-around times’ for students. In order to alleviate the problem, I designed a subset of the language containing those features that we believed were to be covered in introductory courses, and a compiler/interpreter package that fitted into a 16K-word block of store, which fell under the most-favoured program status of the computation center. The Pascal-S package was published in a report, and was one of the early comprehensive systems made widely available in source form [Wirth, 1981 ]. Comparable Fortran programs were still substantially faster, an undeniable argument in the hands of the Pascal adversaries. As we were of the opinion that structured programming supported by a structured language and efficiency of compilation and of produced code were not necessarily mutually exclusive, a project for a third compiler was launched, which on the one hand was to demonstrate the advantage of structured top-down design with step-wise refinement [Ammann, 1974], and on the other hand was to pay attention to generating high-quality code. This compiler was written by U. Ammann and achieved both goals quite admirably. It was completed in 1976 [Ammann, 1977]. Although the result was a sophisticated compiler of high quality and reliability, in hindsight we must honestly confess that the effort invested was incommensurate with its effect It did not win over many engineers and even fewer physicists. The argument that Fortran programs “ran faster* was simply replaced by “our programs are written in Fortran”. And what authorises us to teach structured, “better” programming to experts of ten years standing? Also, the code was generated for the CDC 6000 computer which -with its 60-bit word and super-RISC structure – was simply not well suited for the task. Much of Ammann s efforts went into implementing the attribute packed of records. Although semantically irrelevant, it was requested by considerations of storage economy on a computer with very long words. Having had the freedom to design not only the language but also the computer would have simplified the project considerably. In the event, the spread of Pascal came from another front. Not long after the publication of the Pascal definition we received correspondence indicating interest in that language and requesting assistance in compiler construction, mainly from people who were not users of CDC computers. It was this stimulus which encouraged me to design a suitable computer architecture. A version of Ammann’s compiler – easily derived from an early stage of the sequence of refinements – would generate code for this “ideal” architecture, which was described in the form of a Pascal program representing an interpreter. In 1973, the architecture became known as the P-machine, the code as P-code, and the compiler as the P-compiler. The P—kit consisted of the compiler in P-code and the interpreter as a Pascal source program [Nori, 1981]. Recipients could restrict their labour to coding the interpreter in their favourite assembler code, or proceed to modify the source of the P-compiler and replace its code generating routines. This P-system turned out to be the key to Pascal’s spread onto many computers, but the reluctance of many to proceed beyond the interpretive scheme also gave rise to Pascal’s classification as a “slow language, restricted to use in teaching. Among the recipients of the P-kit was the team of K. Bowles at the University of California at San Diego (UCSD) around 1975. He had the foresight to see that a Pascal compiler for an interpretive system might well fit into the memory of a microcomputer, and he mustered the courage to try. Moreover, the idea of P-code made it easy to port Pascal on a whole family of micros and to provide a common basis on all of them for teaching. Microcomputers had just started to emerge, using early microprocessors like Intel’s 8080, DEC’S LSI—11, and Rockwell’s 6502; in Europe, they were hardly known at the time. Bowles not only ported our compiler. His team built an entire system around the compiler, including a program editor, a file system, and a debugger, thereby reducing the time needed for an edit-compile-test step dramatically over any other system in educational use. Starting in 1978, this UCSD-Pascal system spread Pascal ve”ry rapidly to a growing number of users [Bowles, 1980], [Clark, 1982]. It won more “Pascal friends” in a year than the systems used on large “main frames” had won in the previous decade. This phenomenal success had three sources: (1) A high-level language was available on microcomputers which would pervade educational institutions; (2) Pascal became supported by an integrated system instead of a “stand-alone” compiler; and (3), perhaps most importantly, Pascal was offered to a large number of computer novices, i.e. people who were not burdened by previous programming habits. In order to adopt Pascal, they did not have to give up a large previous investment in learning all the idiosyncracies of assembler or Fortran coding. The microcomputer made programming a public activity, hitherto exclusively reserved to the high-priests of computing centers, and Pascal effectively beat Fortran on microcomputers. By 1978, there existed over 80 distinct Pascal implementations on hosts ranging from the Intel 8080 microprocessor to the Cray-1 supercomputer. But Pascal’s usefulness was not restricted to educational institutions; by 1980 all four major manufacturers of workstations (Three Rivers, HP, Apollo, Tektronix) were using Pascal for system programming. Besides being the major agent for the spread of Pascal implementations, the P-system was significant in demonstrating how comprehensible, portable, and reliable a compiler and system program could be made. Many programmers learned much from the P-system, including implementors who did not base their work on the P-system, and others who had never before been able to study a compiler in detail. The fact that a compiler was available in source form caused the P-system to become an influential vehicle of extracurricular education. Several years earlier, attempts had been made to transport the Pascal compiler to other main-frame computers. In these projects no interpreter or intermediate code was used; instead they required the design of new generators of native code. The first of these projects was also the most successful. It was undertaken by J. Welsh and C. Quinn from the Queen’s University at Belfast [Welsh, 1972]. The target was the ICL 1900 computer. The project deserves special mentioning, because it should be considered as one of the earliest, genuinely successful ventures that were later to be called software engineering efforts. Since no CDC computer was available at Belfast, the goal was to employ a method that required as little work on a CDC machine as possible. What remained unavoidable would be performed during a short visit to ETH in Zurich. Welsh and Quinn modified the CDC-Pascal compiler, written in Pascal. by replacing all statements affecting code generation. In addition, they wrote a loader and an interpreter of the ICL-architecture, allowing some tests to be performed on the CDC-computer. All these components were programmed before the crucial visit, and were completed without any possibility of testing. In Zurich, the programs were compiled and a few minor errors were corrected within a week. Back in Belfast, the generated compiler code was executable directly by the ICL-machine after correction of a single remaining error. This achievement was due to a very careful programming and checking effort, and it substantiated the claimed advantages to be gained by programming in a high-level language like Pascal, which provides full, static type checking. The feat was even more remarkable, because more than half of the week had to be spent on finding a way to read the programs brought from Belfast. Aware of the incompatibilities of character sets and tape formats of the two machines (7- vs. 9-track tapes), Welsh and Quinn decided to use punched cards as data carrier. Yet, the obstacles encountered were probably no less formidable. It turned out to be a tricky, if not downright impossible task, to read cards punched by an ICL machine with the CDC reader. Not only did the machines use different sets of characters and different encodings, but certain hole combinations were interpreted directly by the reader as end of records. The manufacturers had done their utmost best to ensure incompatibility! Apart from these perils, the travellers had failed to reckon with the thoroughness of the Swiss customs officers. The two boxes filled with some four thousand cards surely had to arouse their deep suspicion, particularly because these cards contained empty cubicles irregularly spaced by punched holes. Nevertheless, after assurances that these valuable possessions were to be re-exported anyway, the two might-be smugglers were allowed to proceed to perform their mission. Upon their return, the fact that now the holes were differently positioned luckily went unnoticed. Other efforts to port the compiler followed; among them were those for the IBM 360 computers at Grenoble, the PDP-11 at Twente [Bron, 1976], and the PDP-10 at Hamburg (Grosse-Lindemann, 1976). By 1973, Pascal had started to become more widely known and was being used in classrooms as well as for smaller software projects. An essential prerequisite for such acceptance was the availability of a user manual including tutorial material in addition to the language definition. Kathy Jensen embarked on providing the tutorial part, and by 1973 the booklet was published by Springer-Verlag, first in their Lecture Notes Series, and after selling too fast, as an issue on its own [Jensen. 1974]. It was soon to be accompanied by a growing number of introductory textbooks from authors from many countries. The User Manual itself was later to be translated into many different languages, and it became a bestseller. A dedicated group of Pascal fans was located at the University of Minnesota’s computation center. Under the leadership and with the enthusiasm of Andy Mickel, a Pascal Users’ Group (PUG) was formed, whose vehicle of communication was the Pascal Newsletter at first edited by G.H. Richmond (U. of Colorado) and later by Mlckel. The first issue appeared in January 1974. It served as a bulletin board for new Pascal implementations, of new experiences and – of course – of ideas for improving and extending the language. Its most important contribution consisted in tracking all the emerging implementations. This helped both consumers to find compilers for their computers and implementors to coordinate their efforts. At ETH Zurich, we had decided to move on towards other projects and to discontinue distribution of the compiler, and the Minnesota group was ready to take over its maintenance and distribution. Maintenance here refers to adaptation to continually changing operating system versions, as well as to the advent of the Pascal Standard. Around 1977, a committee had been formed to define a standard. At the Southampton conference on Pascal, A. M. Addyman asked for help in forming a standards committee under the British Standards Institute (BSI). In 1978 representatives from industry met at a conference in San Diego hosted by K. Bowles to define a number of extensions to Pascal. This hastened the formation of a standards committee under the wings of IEEE and ANSI/X3. The formation of a Working Group within ISO followed in late 1979, and finally the IEEE and ANSI/X3 committees were merged into the single Joint Pascal Committee. Significant conflicts arose between the US committee and the British and ISO committees, particularly over the issue of conformant array parameters (dynamic arrays). The latter became the major difference between the original Pascal and the one adopted by ISO, the other being the requirement of complete parameter specifications for parametric procedures and functions. The conflict on the issue of dynamic arrays eventually led to a difference between the standards adopted by ANSI on the one, and BSI and ISO on the other side. The unextended standard was adopted by IEEE in 1981 and by ANSI in 1982 [Cooper, 1983], [Ledgard, 1984]. The differing standard was published by BSI in 1982 and approved by ISO in 1983 [ISO, 1983], [Jensen, 1991]. In the meantime, several companies had implemented Pascal and added their own, supposedly indlspensible extensions. The Standard was to bring them back under a single umbrella. If anything would have had a chance to make this dream come true, it would have been the speedy action of declaring the original language as the standard, perhaps with the addition of a few clarifications about obscure points. Instead, several members of the group had fallen prey to the devil’s temptations: they extended the language with their own favourite features. Most of these features I had already contemplated in the original design, but dropped because either of difficulties in clear definition or efficient implementation, or because of questionable benefit to the programmer [Wirth, 1975). As a result, long debates started, requiring many meetings. When the committee finally submitted a document, the language had almost found its way back to the original Pascal. However, a decade had elapsed since publication of the report, during which individuals and companies had produced and distributed compilers: and they were not keen to modify them in order to comply with the late Standard, and even less keen to give up their own extensions. An implementation of the Standard was later published in [Welsh, 1986]. Even before publication of the Standard, however, a validation suite of programs was established and played a significant role in promoting compatibility across various implementations [Wichmann, 1983]. Its role even increased after the adoption of the Standard, and in the USA it made a Federal Information Processing Standard for Pascal possible. The early 70s were the time when, in the aftermath of spectacular failures of large projects, terms like Structured Programming and Software Engineering were coined. They acted as symbols of hope for the drowning, and too often were believed to be panaceas for all the troubles of the past This trend further raised interest in Pascal which – after all – was exhibiting a lucid structure and had been strongly influenced by E. W. Dijkstra’s teachings on structured design. The 70s were also the years when in the same vein it was believed that formal development of correctness proofs for programs was the ultimate goal. CAR. Hoare had postulated axioms and rules of inference about programming notations (it later became known as Hoare-logic). He and I undertook the task of defining Pascal’s semantics formally using this logic. However, we had to concede that a number of features had to be omitted from the formal definition (e.g. pointers) [Hoare, 1973]. Pascal therafter served as vehicle for the realization of program validators in at least two places, namely Stanford University and ETH Zurich. E. Marmier had augmented the compiler to accept assertions (in the form of marked comments) of relations among a program’s variables holding after (or before) executable statements. The task of the assertion checker was to verify or refute the consistency of assertions and statements according to Hoare-logic [Marmier, 1975]. His was one of the earliest endeavours in this direction. Although it was able to establish correctness for various reasonably simple programs, its main contribution was to dispell the simple-minded belief that everything can be automated. Pascal exerted a strong influence on the field of language design. It acted as a catalyst for new ideas and as a vehicle to experiment with them, and in this capacity gave rise to several successor languages. Perhaps the first was P. Brinch Hansen’s Concurrent Pascal [Brinch Hansen, 1975]. It embedded the concepts of concurrent processes and synchronization primitives within the sequential language Pascal. A similar goal, but with emphasis on simulation of discrete event systems based on (quasi-) concurrent processes, led to Pascal-Plus, developed by J. Welsh and J. Elder at Belfast [Welsh, 1984). A considerably larger language was the result of an ambitious project by Lampson et.al., whose goal was to cover all the needs of modern, large scale software engineering. Although deviating in many details and also in syntax, this language Mesa had Pascal as ancestor [Mitchell, 1978]. It added the revolutionary concept of modules with import and export relationships, i.e. of information hiding. Its compiler introduced the notion of separate – as distinct from independent – compilation of modules or packages. This idea was adopted later in Modula-2 [Wirth, 1982], a language that in contrast to Mesa retained the principles of simplicity, economy of concepts, and compactness which had led Pascal to success. Another derivative of Pascal is the language Euclid [London, 1978]. The definition of its semantics is based on a formalism, just as the syntax of Algol 60 had been defined by the formalism BNF. Euclid carefully omits features that were not formally definable. Object Pascal is an extension of Pascal incorporating the notion of object-oriented programming, i.e. of the abstract data type binding data and operators together [Tesler, 1985]. And last but not least the language Ada [Barnes, 1980] must be mentioned. Its design was started in 1977 and was distinctly influenced by Pascal. It lacked, however, an economy of design without which definitions become cumbersome and implementations monstrous.

In Retrospect

I have been encouraged to state my assessment of the merits and weaknesses of Pascal, of the mistaken decisions in its design, and of its prospects and place in the future. I prefer not to do so explicitly, and instead to refer the reader to my own successive designs, the languages Modula-2 [Wirth. 1982] and Oberon [Wirth, 1988]. Had I named them Pascal-2 and Pascal-3 instead, the questions might not have been asked, because the evolutionary line of these languages would have been evident. It is also fruitless to question and debate early design decisions; better solutions are often quite obvious in hindsight. Perhaps the most important point was that someone did make decisions, in spite of uncertainties. Basically, the principle to include features that were well understood, in particular by implementors, and to leave out those that were still untried and unimplemented, proved to be the most successful single guideline. The second important principle was to publish the language definition after a complete implementation had been established. Publication of work done is always more valuable than publication of work planned. Although Pascal had no support from industry, professional societies, or governmental agencies, it became widely used. The important reason for this success was that many people capable of recognizing its potential actively engaged themselves in its promotion. As crucial as the existence of good implementations is the availability of documentation. The conciseness of the original report made it attractive for many teachers to expand it into valuable textbooks. Innumerable books appeared in many languages between 1977 and 1985, effectively promoting Pascal to become the most widespread language used in introductory programming courses. Good course material and implementations are the indispensible prerequisites for such an evolution. Pascal is still heavily used in teaching at the time of this writing. It may appear that it undergoes the same fate as Fortran, standing In the way of progress. A more benevolent view assigns Pascal the role of paving the way for successors.

Acknowledgements

I heartily thank the many contributors whose work played an indispensible role in making the Pascal effort a success, and who thereby directly or indirectly helped to advance the discipline of program design. Particular thanks go to CAR. Hoare for providing many enlightening ideas that flowed into Pascal’s design, to U. Ammann, E. Marmier, and R. Schild for their valiant efforts to create an effective and robust compiler, to A. Mickel and his crew for their enthusiasm and untiring engagement in making Pascal widely known by establishing a User Croup and a Newsletter, and to K. Bowles for recognizing that our Pascal compiler was also suitable for microcomputers and for acting on this insight. I also thank the innumerable authors of textbooks, without whose introductory texts Pascal could not have received the acceptance which it did.

References

[Ammann, 1974]
Ammann, U., The method of structured programming applied to the development of a compiler. Int’l Computing Symposium 1973. 93 – 99, North – Holland, 1974.

[Ammann, 1977]
Ammann, U., On Code Generation in a Pascal Compiler. Software – Practice and Experience, 7, 391 -423 (1977).

[Barnes, 1980]
An Overwiew of Ada. Software – Practice and Experience, 70, 851 – 887 (1980).

[Bowles, 1980]
Bowles, K, L. Problem solving using Pascal. Springer-Verlag, 1977.

[Brinch Hansen, 1975]
Brinch Hansen, P., The Programming Language Concurrent Pascal, IEEE Trans. Software Eng. 1,2,199 – 207, (1975).

[Bron, 1976]
8ron, C, and W. de Vries. A Pascal Compiler for the PDP-11 Minicomputers. Software – Practice and Experience. 6,109-116 (1976).

[Clark, 1982]
Clark, R., and S. Koehler, The UCSD Pascal Handbook. Prentice-Hall, 1982.

[Cooper, 1983]
Cooper, D., Standard Pascal. User Reference Manual. Norton, 1983.

[Dijkstra. 1966)
Dijkstra. E. W., Structured Programming. Tech. Report, Univ. of Eindhoven, 1966. also in: Dahl, O. – J. et al, Structured Programming, London: Academic Press 1972.

[Grosse-Lindmann, 1976]
Grosse-Lindemann, C. O., and H. H. Nagel, Postlude to a Pascal-Compiler Bootstrap on a DECSystem-10. Software – Practice and Experience, 6. 29-42, (1976).

[Habermann, 1973]
Habermann, A. N., Critical comments on the programming language Pascal, Ada Informatics 3,47 – 57 (1973).
[Hoare, 1972]
Hoare, CAR., Notes on Data Structuring.
In: Dahl, O. -J. et al, Structured Programming, London: Academic Press 1972.

[Hoare, 1973]
Hoare, CAR. and N. Wirth, An axiomatic definition of the programming language Pascal, Acta Informatics 2,335 – 355 (1973)

[Hoare. 1980]
Hoare. CAR.. The Emperor’s Old Clothes. Comm.ACM, 24, 2 (feb. 1980). 75 – 83 (Feb. 1980).

[ISO, 1983]
International Organization for Standardization, Specification for Computer Programming language Pascal, ISO 7185-1982.

[Jensen, 1974]
Jensen, K. and N. Wirth, Pascal- User Manual and Report, Springer-Verlag, 1974.

[Jensen, 1991]
Jensen, K., and N. Wirth. Revised by A. B. Mickel and J. F. Miner, Pascal, User Manual and Report, ISO Pascal Standard. Springer-Verlag, 1991.

[Lecarme, 1975]
Lecarme, 0. and P. Desjardlns, More comment] on the programming language Pascal, Ala Informatics 4. 231 – 243 (1975)

[Ledgard.1984]
Ledgard, H. The American Pascal Standard. Springer-Verlag, 1984.

[London, 1978]
London, R.L, J. V. Guttag, J. J. Homing, 8. W. Lampson. J. G. Mitchell, and G. J. Popek. Proof Rules for the Programming Language Euclid. Ada Informatics 10,1 – 26 (1978).

[Marmier, 1975]
E. Marmier. Automatic Verification of Pascal Programs. ETH-Dissertation No. 5629, Zurich, 1975

[Mitchell, 1978]
Mitchell, J. C W. Maybury, R. Sweet Mesa Language Manual. Xerox PARC Report CSL-78-1 (1978).

[Naur, 1963]
Naur, P., (ed.) Revised report on the algorithmic language Algol 60, Comm. ACM 3, 299 – 316 (1960), and Comm. ACM 6,1 – 17 (1963)

[Nori, 1981]
Nori, K.V., et al., The Pascal P-code compiler: Implementation notes.
In: D.W. Barron, ed., Pascal – The language and its implementation. Wiley 1981.

[Tesler, 1985]
Tesler, L, Object Pascal Report.
Structured Programming (formerly Structured Language World), 9, 3, (1985), 10-14.

[van Wijngaarden, 1969]
van Wijngaarden, A., (Ed.), Report on the algorithmic language Algol 68. Numer. Math. 14, 79 – 218 (1969)

[Welsh, 1972]
Welsh, J., and C. Quinn. A Pascal Compiler for ICL1900 Series Computers. Software, Practice and Experience 2. 73-77 (1972).

[Welsh, 1977] Welsh. J., W. J. Sneeringer, and CAR. Hoare, Ambiguities and Insecurities in Pascal.
Software. Practice and Experience, 7, (1977), 685 – 696.
Also in: D.W. Barron. ed., Pascal- The language and its implementation. Wiley 1981.

[Welsh, 1984] Welsh, J., and D. Bustard, Sequential Program Structures, Prentice-Hall Int’l, 1984.

[Welsh, 1986] Welsh, J., and A Hay, A Model Implementation of Standard Pascal. Prentice-Hall Int’l, 1986.

[Wichmann, 1983]
Wichmann, B., and Ciechanowicz, Pascal Compiler Validation. Wiley, 1983.

[Wirth, 1966]
Wirth, N. and CAR. Hoare, A Contribution to the development of ALGOL, Comm. ACM 9,6,413 – 432 (June 1966)

[Wirth, 1970]
Wirth, N.. The Programming Language Pascal, Tech. Rep. 1, Fachgruppe Computer-Wissenschaften, ETH, Nov. 1970, andAda Informatics 1.35 – 63 (1971)

[Wirth, 1971a]
Wirth, N.. Program Development by Step-wise Refinement. Comm. ACM 14, 4,221 -227 (April 1971)

[Wirth, 1971b]
Wirth, N., The design of a Pascal compiler. Software, Practice and Experience 1,309 – 333 (1971).

[Wirth, 1975]
Wirth, N. An assessment of the programming language Pascal. lEEETrans. on Software Eng.. 1. 2 (June 1975), 192 -198.

[Wirth, 1981]
Wirth. N. Pascal-S: A subset and its implementation. In: D.W. Barron, ed., Pascal – The language and its implementation. Wiley 1981.

[Wirth, 1982]
Wirth, N., Programming in Modula-2. Springer-Verlag, 1982.

[Wirth, 1988]
Wirth, N., The Programming Language Oberon. Software, Practice and Experience 18,7 (July 1988), 671 – 690.

History of Modula and Lilith

A Brief History of Modula and Lilith

N. Wirth

Abstract
In the years 1978-1980 the workstation Lilith was developed at ETH Zurich. It featured a microprogrammed processor, a high-resolution display, and a mouse. The entire software was programmed in Modula-2, a language derived from Pascal within the Lilith project. It featured the module with explicit interface specifications, and its implementation included the facility of separate compilation of modules with complete type checking.

Introduction

In order that a sizeable, technical project be undertaken, two prerequisites must be established. There must be a manifest need for a solution of a relevant problem, and there must exist an idea leading towards a solution of the problem. Such conditions emerged during a sabbatical leave which the author had the good fortune to spend at the Xerox Palo Alto Research Center (PARC) in 1977. The problem I am referring to was the need for a structured programming language for building large, complex systems, and the solution, or rather the feature needed, was a construct for expressing units of separately compiled system components, now known as modules.

I had become confronted with the language Mesa [1], developed at PARC, which contained such a construct. However, I felt that the rather baroque descendant of Pascal could be molded better in accordance with the spirit of Pascal by a new attempt, by defining a more frugal language [2, 3]. Its name Modula witnesses the dominance of the module concept in our concerns, a name which – perhaps unfortunately – was chosen in preference over Pascal++.

The need for a structured language with a module facility was less pronounced in the software community at large than in our immediate environment. An explanation of this requires some digression. The computing facilities available in 1977 were essentially large scale mainframes hosting sophisticated time-sharing systems, accessible only via terminals and remote satellites. The revolutionary concept of the powerful, personal workstation – the Alto computer developed at PARC – appeared to me like a revelation [4]. I was immediately convinced that there was no point in continuing development of software, except if based on and oriented towards this novel computing environment. However, such devices not being available on the market, there remained only one path to proceed, namely to design and build one on our own. Again, there was a recogized need and an idea of a solution. The project produced the workstation Lilith [5, 6].

There is no point in creating new hardware without new software. A basic operating system, utility programs, and first applications were to be developed concurrently, and therefore a programming language and its compiler were required as well. In fact, the primary incentive for designing Modula-2 was the need for a simple, allround language capable of expressing the whole range of programs needed to render Lilith into a powerful software development tool. The explicit goal was to use one and the same language for all Lilith software. Evidently, Modula and Lilith grew as a couple, and it would be futile to record the history of one without that of the other.

Although a preliminary document stating certain goals and concepts of the new language was written in 1977, the effective language design took place in 1978-79. Concurrently, a compiler implementation project was launched. The available machinery was a single DEC PDP-11 with a 64K byte store. The single-pass strategy of our Pascal compilers could not be adopted; a multipass approach was unavoidable in view of the small memory. It had actually been the Mesa implementation at PARC which had proved possible what I had believed to be impracticable, namely to build a complete compiler operating on a small computer. The first Modula compiler, written by K. van Le (1977), consisted of 7 passes, each generating sequential output (intermediate code) written onto the (2 MB) disk. This number was reduced in a second design by U. Ammann to 5 passes (1979). The first pass, the scanner, generated a token string and a hash table of identifiers. The second pass (parser) performed syntax analysis, and the third the task of type checking. Passes 4 and 5 were devoted to code generation. This compiler was operational in early 1979.

Lilith
By this time the first prototype of Lilith, designed by R. Ohran and the author, was barely operational too. The primary design goal had been an architecture optimally tailored for interpreting M-code, the Modula compiler’s equivalent of Pascal’s P-code. It must be recalled that the era of unlimited memory lay still 10 years in the future. Hence, high code density was considered to be of paramount importance for complex system implementation on small workstations. Lilith was organized as a word-addressed 16-bit computer, M-code as a byte stream. Memory size was 216 words (128K bytes). The prototype was built with 4K x 1bit parts.

The best choice for obtaining high code density was to define a fairly large variety of instructions, some of them rather complex, and to build the hardware as a micro-code interpreter. Each M-code instruction consisted of one or several bytes. The first byte was the operation code according to which a sequence of microcode instructions was selected for interpretation. The advantages of this scheme were manifold:

1. The actual hardware could be reasonably simple and therefore fast. The clock frequency being about 7 MHz, a cycle time of 140ns resulted. ALU-functions were addition and logical operations.

2. Simple M-code instructions were implementable with very short sequences of two or three micro-instructions only, requiring an execution time of 280 or 420 ns.

3. More complex instructions, such as multiplication and division, could be implemented by longer sequences of micro-instructions without requiring additional hardware (19 instr. for multiplication, 36 for division).

4. The same operation could be represented with different address or value parameter lengths of 1, 2, or 4 bytes, again without complicating hardware.

Two factors contributed primarily to the achieved high code density, which turned out to be superior to commercial processors by the remarkable factor of 2.5 (M68000) to 3.5 (I8086).

1. The use of several address and operand lengths of 4, 8, 16, and 32 bits. It turned out that more than 70% of all instruction parameters had values between 0 and 15, in which case the operand was packed together with the operation code in a single byte.

2. Lilith was equipped with a stack for intermediate results occuring in the evaluation of expressions. This stack was implemented as a 16-word, fast SRAM. M-code therefore contained no register numbers, as they were implied by the stack scheme.

The most visible innovation brought about by the personal workstation was the high-resolution, bit-mapped display, dramatically contrasting with the then customary displays with 24 lines of 80 characters. Together with the Mouse, it opened a new era of interactive man-machine interfaces based on windows and cursors, scroll bars and title bars, pop-menus and icons. The bit-mapped display with its data stored in main memory required special facilities to generate the video signal with sufficiently high bandwidth. Lilith featured a display of 768 x 592 pixels, which was improved in later versions to the vertical format with 704 x 928 pixels; 50 interlaced half frames required a bandwidth of almost 30 MHz.

The micro-code scheme proved to be most convenient for implementing a small set of raster operations for drawing lines, drawing characters, copying and replicating pixel-blocks. These operations were directly represented as single M-code instructions. The corresponding micro-code routines performed raster operations with spectacular efficiency. As far as the hardware was concerned, the only raster-op specific facility was a barrel shifter allowing an arbitray shift amount in a single micro-cycle. No commercial microprocessor offered a comparable facility.

In the meantime, a new Modula compiler was designed by L. Geissmann and Ch. Jacobi with the PDP-11 compiler as a guide, but taking advantage of the features of Lilith. It consisted of four passes, code generation being simplified by the new architecture. Development took place on the PDP-11, followed by transporting it to Lilith. In the same manner, parts of the operating system Medos was implemented by S. Knudsen – a system essentially following in the footsteps of batch systems with program load and execute commands entered from the keyboard. Concurrently, display and window software was designed by Ch. Jacobi. It served as the basis of the first application programs, such as a text editor – mostly used for program editing – featuring the well-known techniques of a cursor/mouse interface and pop-up menus.

Modula-2 was now in daily use and quickly proved its worth as a powerful, efficiently implemented language. In December 1980, the first pilot series of 20 Liliths, manufactured in Utah under the supervision of R. Ohran, were delivered to ETH Zurich. Further software development proceeded with a more than a 20-fold hardware power at our disposal. A genuine personal workstation environment had successfully been established.

Modula
From the preceding account it should become clear under which conditions Modula came into existence. It is evident that our primary concern was not a language satisfying all wishes of all programmers, but the creation of a sufficiently powerful tool for implementing an entire software system in a short time with the limited manpower at hand. Modula-2 was, so to speak, a subordinate goal, a means to an end.

Modules and separate compilation
The decision to move from Pascal to a successor was primarily due to the high importance attributed to a clean formulation of the module concept and its implementation as a facility for separate compilation. Therefore, the solution adopted in several commercial extensions of Pascal, namely the merging of units in source form, was rejected. The notion of modules with explicit lists of imported and exported identifiers was adopted from Mesa. Also the symbol file as precompiled information about a module’s exported objects, stemmed from Mesa. The module concept gave rise to a new view of Lilith’s operating system: the traditional division into system and application programs was replaced by a single hierarchy of modules which could be expanded or shrunk as momentary needs dictated.

Modula’s idea of a module was that of a syntactic unit encapsulated by a wall through which identifiers are invisible except if listed in the export or import list. Since syntactic constructs are usually defined recursively, modules are nestable with obvious consequences for identifier visibility. The inner, or local module turned out to be of limited usefulness in practice and was rarely used. Considering that implementations must handle global and local modules differently – the global ones are separately compilable – local modules should probably not have been introduced in the first place. Import lists could assume one of two forms. The simpler form consists of a list of module identifiers:

IMPORT M0, M1;

An object x exported from module M1, for example, is then referenced in a client of M1 by the qualified identifier M1.x. The second form of import list seeks to abbreviate program texts, letting the qualification be omitted by specifying the source of an identifier directly:

FROM M1 IMPORT x, y, z;

Like most abbreviations, its value is rather questionable. Not only does the compiler become more complicated, but certain negative side-effects are unavoidable: identifier clashes. If two modules export an object with the same name, a clash occurs if they are imported in the second form, even if the identifier in question was not used.
On the other hand, critics of Modula expressed the opinion that our concept was too simple and restrictive. Their view of the module interface – i.e. of Modula’s definition part – was that of a selection of the module’s set of objects. Modula allowed to define a single selection only, whereas a multitude of different selections for different clients might be useful. This facility was implemented for the language Cedar [7]. It gives rise to further extensions and complications. An obvious desire is to derive unions and intersections of selections.

A further point perhaps worth mentioning in this connection is the handling of exported enumeration types. The desire to avoid long export lists led to the (exceptional) rule that the export of an enumeration type identifier implies the export of all constant identifiers of that type. As nice as this may sound for the abbreviation enthusiast, it also has negative consequences, again in the form of identifier clashes. This occurs if two enumeration types are imported which happen to have at least one common constant identifier.

Furthermore, identifiers may now appear that are neither locally declared, nor qualified by a module name, nor visible in an import list; an entirely unexpected situation in a structured language.

Static type checking
Modula-2’s design followed uncompromisingly the rule – the dogma even – of strictly static typing, thereby empowering a compiler to perform all type consistency checks and avoiding any of them to be delayed until run-time.

In this respect, Pascal contained a few mistakes that had to be mended. One of them was the incompleteness of parameter specifications adopted from Algol 60 as exemplified by the following declarations:

PROCEDURE P(x, y: INTEGER); BEGIN … END ;

PROCEDURE Q(p: PROCEDURE);
VAR a, b: REAL;
BEGIN … p(a + b) … END ;
… Q(P) …

Here, the call P(a+b) does not comply with the parameter specifications of P, but this fact cannot be discovered by a static text analysis. Modula mended this deficiency by requiring complete parameter specifications:

PROCEDURE Q(p: PROCEDURE (REAL, REAL));

Pascal permitted the use of integer as well as real operands in expressions (mixed expressions), postulating automatic conversion of number representation wherever necessary. The presence of a large number of basic types complicates the generation of code considerably, and hence it was decided that no automatic conversion would be generated. This, in turn, required that the language rules must prohibit mixed expressions. This decision is indeed questionable, but perhaps understandable in view of our own immediate needs, which were very modest in the realm of arithmetic.
The type CARDINAL
How, then, can the introduction of type CARDINAL be explained, of natural numbers as a further basic data type? The need arose from the necessity to represent addresses as a numeric type. Lilith being a 16-bit machine with a store of 216 words, a useless sign bit could not be afforded. As unproblematic as this new type at first appeared, it gave rise to several problems. How, for example, is a comparison i < c to be implemented efficiently?

The question is simply avoided, if mixed expressions are prohibited. Two distinct sets of instructions are required for integer (signed) and cardinal (unsigned) arithmetic. Even if the same instruction can be used for addition, there exists a difference in the definition of overflow. The situation is even more problematic for division. In mathematics, the quotient q = x DIV y and the remainder r = x MOD y are defined by the relationships

x = q*y + r, 0 <= r < y

Integer division, in contrast to real division, is asymmetric with respect to 0. For example

10 DIV 3 = 3 10 MOD 3 = 1
-10 DIV 3 = -4 -10 MOD 3 = 2

Most computers, however, offer instructions that are wrong with respect to the established definition. They treat integer division like real division with symmetry respective to 0:

(-x) DIV y = -(x DIV y)
(-x) MOD y = -(x MOD y)

If integer division is implemented correctly, divisions by powers of 2 can be made more efficient by simple shifts. In the case of wrong division instructions, this is not possible:

x * 2n = left(x, n) x DIV 2n = right(x, n)

The following processors, among others, perform integer division contrary to its mathematical definition: Motorola M680x0, Intel 80×86, Intel 80960, DEC VAX, IBM Power. Even worse, the language Ada institutionalizes this mistake by declaring it as standard.
We decided to adopt the wrong arithmetic in order to stay consistent with the mistakes others were demanding. In hindsight this was, of course, a grave mistake. The compiler uses shift instructions for multiplying and dividing by powers of 2 in the case of expressions of type CARDINAL only.

In summary, then, the type CARDINAL was justified by practical needs for 16-bit address handling. From the point of view of mathematicians (who have invented negative numbers for the sake of algebraic completeness), however, it must be called a step backwards, as it introduced difficulties for the programmer that did not exist beforehand. Consider, for example, the following statements performing an operation Q for x = N-1, … , 1, 0:

x := N-1;
WHILE x >= 0 DO Q; x := x-1 END

If x is of type INTEGER, the program is correct; but if it is of type CARDINAL, it leads to an underflow which is avoided by the correct, alternative formulation

x := N;
WHILE x > 0 DO x := x-1; Q END

The advent of the 32-bit computer some 6 years later provided the welcome opportunity to forget the type CARDINAL. The episode shows how inadequacies in hardware may force the language implementor to accept complications and compromises, if he wishes to fully exploit the available facilities. The lesson is, that a memory size of 2n requires arithmetic with (at least) n+1 bit integers.
Procedure types
An uncontroversial, fairly straight-forward, and most essential innovation was the procedure type, also adopted from Mesa. In a restricted form it had been present also in Pascal, namely in the form of parametric procedures. Hence, the concept needed only to be generalized, i.e. made applicable to parameters and variables. Although the facility was sparingly used in Lilith software – with the exception of later editors and some instances in the operating system – it is nothing less than the basis for object-oriented programming, with record (object) fields having procedures as “values”. Nevertheless, Modula-2 cannot be claimed to support object-oriented programming.

The single facility missing is one for building new data types based on others, i.e. of types that are (upward) compatible with existing types, or – in object-oriented terminology – that inherit properties from existing types. This essential facility was introduced in Modula’s successor Oberon [8, 9], where it was appropriately called type extension [10].

Although Modula does not support object-oriented programming, it at least makes it possible through the use of the type ADDRESS and the rule that an address value may be assigned to any pointer variable. This implies of course that type checking be overruled, a sacrifice of security that weighs heavily. The “trick” lies in providing every record type that is to be extensible with an additional field of type ADDRESS. Record extensions then require a second block of storage, separately allocated, whose pointer is assigned to that field. Needless to say, the additional indirection in accessing fields of the extension, as well as the cumbersome formulation for breaching the type system, are unsatisfactory. Oberon’s neatly integrated concept of type extensions solves these problems admirably well; its price is a run-time check of type consistency in those cases where it is inherently unavoidable.

Low-level facilities
Facilities that make it possible to express situations which do not properly fit into the set of abstractions constituting the language are called low-level facilities. In a way, their presence is a symptom of the incompleteness of the set of provided abstractions. Given our task of building an entire operating system for Lilith, however, at the time they were unavoidable. I nevertheless believe that they were introduced too light-heartedly, in the naive belief that programmers would use them only as a last resort.

In particular, the concept of type transfer function was a major mistake. It allows the type identifier to be used in expressions as function identifier: The value of T(x) is equal to x, whereby x is interpreted as being of type T. This interpretation inherently depends on the underlying (binary) representation of the type of x and of T. Therefore, every program making use of this facility is inherently implementation-dependent, a clear contradiction of the fundamental goal of high-level languages, i.e. of abstraction. The situation is particularly deplorable, because no hint is given of such dependence in a module’s heading.

Into the same category of easily misused features belongs the variant record. The real stumbling block is the variant without tag field. The tag field’s value is supposed to indicate the structure currently assumed by the record. If a tag is missing, no possibility exists for checking (or determining) the current variant. It is exactly this lack which can be misused to access record fields with “wrong” types:

VAR x: RECORD
CASE : INTEGER OF
0: a: INTEGER
| 1: b: CARDINAL
| 2: c: BITSET
END
END

From the assignment x.a := -16 follow the equalities x.b = 65520 and x.c = {4 .. 15}. Or might the latter be x.c = {0 .. 11} ? Needless to say, such programs are ill-defined, hard to understand, and they defy the concept of abstraction and portability.
After 1982, Modula-2 aroused interest in several industrial organizations. The first commercial compiler was built by Logitech S.A., and others followed [11]. IBM produced a compiler and programmed its AS-400 operating system in Modula, and DEC’s Systems Research Center adopted Modula-2 for its internal projects, at the same time extending the language into Modula-2+ [12]. Its creators found Modula-2 lacking in three areas: Concurrency, storage management, and exception handling.

Concurrency
A project to investigate techniques and problems in multiprogramming had been undertaken at our Institute during the years 1974-76. It led to a small, experimental language implemented on the PDP-11 computer [13]. Its basis for multiprogramming were monitors and conditions (here called signals) as proposed by C.A.R. Hoare [14]. Monitors were generalized into modules; the concept of encapsulation was thereby disconnected from that of mutual exclusion. Interrupt handling was integrated into process scheduling in the sense that process switches could be triggered either by programmed signal operations (send, wait) or by interrupts. These were considered as external signals, thereby conceptually unifying declared signals and interrupts.

The project led to the conclusion that there existed no clearly preferrable set of basic constructs for expressing concurrent processes and their cooperation, but that processes and threads, signals and semaphores, monitors and critical regions all have their advantages and disadvantages, depending on particular applications. Hence, it was decided that only the very basic notion of coroutines would be included in Modula-2, and that higher abstractions should be programmed as modules based on coroutines. This decision was even more plausible, because the demands of the Lilith software as a single-user operating system were easily met without sophisticated facilities for multiprogramming, which therefore did not merit attention with high priority. However, the belief that coroutines would also be a suitable basis for genuine multiprocessor implementations was mistaken, as was pointed out by the Modula-2+ authors.

We have also abandoned the belief that interrupt handling should be treated by the same mechanism as programmed process switching. Interrupts are typically subject to specific real-time conditions. Real-time response is impaired beyond acceptable limits, if interrupts are handled by very general, complicated switching and scheduling routines.

Storage management and garbage collection
Modula-2, like Pascal, features pointers and thereby implies dynamic storage allocation. Allocation of a variable x^ is expressed by the intrinsic procedure NEW(x), typically implemented by a system call.

The premise underlying Modula-2 implementation was that programs would handle pools of dynamically allocated variables, one for each (record) type. Such individually programmed handlers could be tuned optimally to the specific conditions of an application, thus guaranteeing the most effective use of storage. This was of great importance at the time, considering the small storage size of computers and the absence of centralized storage management. Today’s very much larger memories render this strategy obsolete and make a flexible, centralized storage management indispensible.

Were it not for the unfortunate and abundant features for breaching the type system, Modula-2 would still satisfy the needs imposed by centralized management, i.e. allow operation on the basis of global garbage collection. A garbage collector must, however, be able to rely on uncorruptible type information at run-time. Type breaching must be impossible. In a way, Modula-2 was too “powerful”. This reminds us of the old wisdom that the power of a language is not only determined by what it allows to express, but equally much by what it prohibits to express.

Exception handling
By handling an exception we generally understand the transfer of control to an implicitly designated statement sequence upon the rare occurrence of a certain condition. What distinguishes, then, “exception handling” from conventional if-statements, except the rarity of the condition? Three items come to mind:

The statement sequence (exception handler) resides in a module different from where the exception is detected (raised).

Several entered procedures need to be terminated exceptionally (aborted). Separating the program text for handling the rare condition from the program specifying the “regular” behaviour is supposed to improve program clarity. Only the second item calls for a facility that is not expressible by conventional constructs. The question then is: Does such a new facility correspond to an important abstraction that truly clarifies programs, or is it merely a convenient abbreviation for frequently occurring situations? And: Is an implementation possible that does not impair efficiency?

The first question can never be answered conclusively. It dependes not only on personal preferences and programming style, but also on the applications considered. A proposal for inclusion of an exception handling construct was already contained in a first draft of Modula-2, but dropped later, because a need for it was not convincingly established. The development of entire operating systems [15] at least showed that the omission of exception handling was not entirely mistaken. The emergence of systems with languages where exception handling was available and – as a consequence – heavily used and frequently even misused, suggests that the decision was even right.

Nevertheless, I am aware that other people – no less experienced in system construction – do not share this view. One fact, however, is undisputed: a feature can always be added to, but never removed from a language. It is therefore recommended to start out with features of established indispensability only.

Later developments
During 1984, the author designed a new compiler for Modula-2. I felt that many parts of compilation could be handled more simply and more efficiently, if use were made of the now available larger store (which, by today’s measures, was still very small). Lilith’s 64K word memory and its high code density allowed the realization of a single-pass compiler. This implied a dramatic reduction of disk operations, which happen to contribute the largest part to compilation time. Indeed, compilation time of the compiler itself was reduced from some 4 minutes to a scant 45 seconds.

The new compiler retained, however, the partitioning of tasks. Instead of each task constituting a pass – with sequential input from and output to disk – it constituted a module with a mostly procedural interface. Common data structures, such as the symbol table, were defined in a data definition module imported by (almost) all other modules. These modules represented a scanner, a parser, a code generator, and a handler of symbol files.

When a new compiler is developed, the temptation to include some changes in the language is seldom resisted. Also in the present case, some clarifications and changes were postulated and incorporated in a revised language definition. The only significant change, however, concerned definition modules (or rather: the definition part of a module). The change postulated that all identifiers declared in a definition part were exported, and this made export lists superfluous.

On the side of the operating system a significant step forward was the implementation of a network. We adopted the experimental Ethernet technology from PARC, a bus topology with a 3 MHz bandwidth and Manchester encoding, yielding a transmission rate of 3 Mbits/s. Both hardware interfaces and the software were developed by J. Hoppe. The services were dominated by file transfer, and therefore the network was connected with the file system at a low level, such that access to both remote and local files was distinguished by a file-name prefix only.

The presence of a network connecting all workstations called for servers, i.e. for “impersonal workstations”. The first server that went into operation in 1982 was a print server connected to a laser printer (Canon LBP-10). Due to the flexibility and power of Lilith, a regular workstation could be used for generating the bitmap and for driving the printer. The hardware interface consisted merely of a direct memory access channel (DMA), effectively similar to a display driver, but suitably adapted to accept synchronization signals from the printer. Since memory was too small to hold an entire page (about 1 Mbit), bitmap generation and data transfer to the printer video had to proceed concurrently. It fortunately turned out that Lilith was sufficiently powerful to allow bitmap generation on-the-fly. Thereby Lilith became the first computer in Europe to make full use of laser printing capabilities, printing texts with various fonts and styles, and graphics ranging from scanned pictures to circuit schematics.

The second server, operational in 1983, was a file server using, in contrast to regular workstations, not only a 10 MByte cartridge disk, but a high-volume (500 MByte) Winchester drive (Fujitsu Eagle), which constituted leading edge-technology at the time. The large size required a different organization of the file system. Also, the concept of stable storage was implemented. This research was performed by F. Ostler.

A sizable effort extending over the years 1981-1985 went into the development of applications. In the forefront were document editors making use of all the novel facilities put at our disposal by Lilith, from bitmapped display and mouse to network and laser printer. The first attempt by J. Gutknecht and W. Winiger led to the editor Andra [16]. It employed the piece list technique for representing the edited text, pioneered by B. Lampson at PARC, as well as windows and pop-up menus for its user interface. It allowed the use of multiple fonts and various formatting modes, and it considered a document as being hierarchically structured. The follow-up project by J. Gutknecht and H. Schar led to the editor Lara [17] which became the chief application program for Lilith users for many years.

Other significant applications were a set of tools for interactive font design, implemented by E. Kohen, and a line drwing graphics editor, developed by the author. The common characteristic of all these tools was that they went into immediate use by our own team, thus providing feedback not only about their own appropriateness, but also on the adequacy of Modula and the entire system.

Lilith and Modula-2 were the backbone tools for education in programming at the Computer Science Department of ETH. About 60 Liliths were in use by 1982, offering a modern environment for programming and for document preparation about five years before similar tools were available commercially. The Lilith computers were decommissioned in 1990; they had been in daily use for 10 years.

References

1. Mesa Language Manual. Oct. 1976, Xerox PARC.

2. N. Wirth. Programming in Modula-2. Springer-Verlag, 1982.

3. J. Gutknecht. Tutorial on Modula-2. BYTE, Aug. 1984, 157-176.

4. C. P. Thacker, et al. Alto: A Personal Computer. CSL-79-11. Xerox PARC.

5. N. Wirth. Lilith: A Personal Computer for the Software Engineer. Proc. 5th Int’l Conf. on Software Engineering. San Diego, 1981. IEEE 81CH1627-9.

6. R. Ohran. Lilith and Modula-2. BYTE, Aug. 1984, 181-192.

7. W. Teitelman. A Tour through Cedar. IEEE Software, 1, 2 (April 1984) 44-73.

8. N. Wirth. The Programming Language Oberon. Software – Practice and Experience, 18, 7 (July 1988), 671-690.

9. M. Reiser and N. Wirth. Programming in Oberon. Addison-Wesley, 1992.

10. N. Wirth. Type Extension. ACM TOPLAS, 10, 2 (April 1988), 204-214.

11. P.H. Hartel and D. Starreveld. Modula-2 Implementation Overview. J. of Pascal, Ada, and Modula-2. July/Aug. 1985, 9-23.

12. P. Rovner. Extending Modula-2 to Build Large, Integrated Systems. IEEE Software, Nov. 1986, 46-57.

13. N. Wirth. Modula: A Language for Modular Multiprogramming. Software – Practice and Experience, 7 (1977), 3-35.

14. C.A.R. Hoare. Monitors: An Operating Systems Structuring Concept. Comm. ACM, 17, 10 (Oct. 1974), 549-557.

15. N. Wirth and J. Gutknecht. Project Oberon. Addison-Wesley, 1992.

16. J. Gutknecht, W. Winiger. Andra: The Document Preparation System of the Personal Workstation Lilith. Software- Practice & Experience, 14, (1984), 73-100.

17. J. Gutknecht. Concepts of the Text Editor Lara. Comm. ACM, 28, 9 (Sept. 1985), 942-960.