program LispKit(Input, Output, InFile, OutFile); (*-----------------------------------------------------------------*) (* *) (* Reference model lazy interactive SECD machine, 3 *) (* -- version 3a April 83 *) (* -- IMPLODE and EXPLODE instructions, version 3b May 83 *) (* *) (* Machine specific code has been omitted from this text *) (* *) (*-----------------------------------------------------------------*) (* *) (* (c) Copyright P Henderson, G A Jones, S B Jones *) (* Oxford University Computing Laboratory *) (* Programming Research Group *) (* 8-11 Keble Road *) (* OXFORD OX1 3QD *) (* *) (*-----------------------------------------------------------------*) (* *) (* Documentation: *) (* *) (* P Henderson, G A Jones, S B Jones *) (* The LispKit Manual *) (* Oxford University Computing Laboratory *) (* Programming Research Group technical monograph PRG-32 *) (* Oxford, August 1983 *) (* *) (* P Henderson *) (* Functional Programming: Application and Implementation. *) (* Prentice-Hall International, London, 1980 *) (* *) (*-----------------------------------------------------------------*) (*------------------ Machine dependent constants ------------------*) label 99; const TopCell = { omitted } ; (* size of heap storage *) (*--------------- Machine dependent file management ---------------*) { omitted } (*------------------- Character input and output ------------------*) procedure GetChar(var ch : char); { omitted } procedure PutChar(ch : char); { omitted } (*------- Machine dependent initialisation and finalisation -------*) procedure Initialise(Version, SubVersion : char); { omitted } procedure Terminate; { omitted } (*--------- The code which follows is in Standard Pascal ----------*) (* As far as is possible, it is also machine independednt. The *) (* most obvious machine dependency is that the character code of *) (* the host machine has been assumed to be ISO-7 or similar. *) (*-----------------------------------------------------------------*) procedure Machine; const Version = '3'; SubVersion = 'b'; type TokenType = (Numeric, Alpha, Delimiter); var Marked, IsAtom, IsNumb : packed array [1..TopCell] of 0..1; {-----------------------------------------------} { Cell type coding: IsAtom IsNumb } { Cons 0 0 } { Recipe 0 1 } { Number 1 1 } { Symbol 1 0 } {-----------------------------------------------} Head, Tail : array[1..TopCell] of integer; {-----------------------------------------------} { Head is also used for value of integer, IVAL } { pointer to symbol, SVAL } { BODY of recipe } { Tail is also used for ENVironment of recipe } {-----------------------------------------------} S, E, C, D, W, SymbolTable : integer; NILL, T, F, OpenParen, Point, CloseParen : integer; FreeCell : integer; Halted : boolean; InCh : char; InTokType : TokenType; (*------------------ Garbage collection routines ------------------*) procedure CollectGarbage; procedure Mark(n : integer); begin if Marked[n] = 0 then begin Marked[n] := 1; if (IsAtom[n] = 0) or (IsNumb[n] = 0) then begin Mark(Head[n]); Mark(Tail[n]) end end end {Mark}; procedure MarkAccessibleStore; begin Mark(NILL); Mark(T); Mark(F); Mark(OpenParen); Mark(Point); Mark(CloseParen); Mark(S); Mark(E); Mark(C); Mark(D); Mark(W) end; procedure ScanSymbols(var i : integer); begin if i <> NILL then if Marked[Head[Head[i]]] = 1 then begin Marked[i] := 1; Marked[Head[i]] := 1; ScanSymbols(Tail[i]) end else begin i := Tail[i]; ScanSymbols(i) end end; procedure ConstructFreeList; var i : integer; begin for i := 1 to TopCell do if Marked[i] = 0 then begin Tail[i] := FreeCell; FreeCell := i end else Marked[i] := 0 end; begin MarkAccessibleStore; ScanSymbols(SymbolTable); FreeCell := 0; ConstructFreeList; if FreeCell = 0 then begin writeln(Output, 'Cell store overflow'); Terminate end end {CollectGarbage}; (*------------------ Storage allocation routines ------------------*) function Cell : integer; begin if FreeCell = 0 then CollectGarbage; Cell := FreeCell; FreeCell := Tail[FreeCell] end {Cell}; function Cons : integer; var i : integer; begin i := Cell; IsAtom[i] := 0; IsNumb[i] := 0; Head[i] := NILL; Tail[i] := NILL; Cons := i end {Cons}; function Recipe : integer; var i : integer; begin i := Cell; IsAtom[i] := 0; IsNumb[i] := 1; Head[i] := NILL; Tail[i] := NILL; Recipe := i end {Recipe}; function Symb : integer; var i : integer; begin i := Cell; IsAtom[i] := 1; IsNumb[i] := 0; Head[i] := NILL; Tail[i] := NILL; Symb := i end {Symb}; function Numb : integer; var i : integer; begin i := Cell; IsAtom[i] := 1; IsNumb[i] := 1; Numb := i end {Numb}; function IsCons(i : integer) : boolean; begin IsCons := (IsAtom[i] = 0) and (IsNumb[i] = 0) end; function IsRecipe(i : integer) : boolean; begin IsRecipe := (IsAtom[i] = 0) and (IsNumb[i] = 1) end; function IsNumber(i : integer) : boolean; begin IsNumber := (IsAtom[i] = 1) and (IsNumb[i] = 1) end; function IsSymbol(i : integer) : boolean; begin IsSymbol := (IsAtom[i] = 1) and (IsNumb[i] = 0) end; function IsNill(i : integer) : boolean; begin IsNill := IsSymbol(i) and (Head[i] = Head[NILL]) end; procedure Store(var T : integer); var Si, Sij, Tj : integer; found : boolean; begin Tj := T; if IsAtom[Tj] = 1 then Tj := NILL else begin while IsAtom[Tail[Tj]] = 0 do Tj := Tail[Tj]; Tail[Tj] := NILL end; Si := SymbolTable; found := false; while (not found) and (Si <> NILL) do begin Sij := Head[Head[Si]]; Tj := T; found := true; while found and (Tj <> NILL) and (Sij <> NILL) do begin if Head[Tj] <> Head[Sij] then if Head[Head[Tj]] = Head[Head[Sij]] then Head[Tj] := Head[Sij] else found := false; Tj := Tail[Tj]; Sij := Tail[Sij] end; if found then found := Tj = Sij; if found then T := Head[Si] else Si := Tail[Si] end; if not found then begin Tj := T; (* NB: T may be an alias for W *) W := Cons; Tail[W] := Tj; Head[W] := Symb; Head[Head[W]] := Tail[W]; Tail[W] := SymbolTable; SymbolTable := W; T := Head[W] end end {Store}; procedure InitListStorage; var i : integer; function List(ch : char) : integer; begin W := Cons; Head[W] := Numb; Head[Head[W]] := ord(ch); List := W end {List}; procedure OneChar(var reg : integer; ch : char); begin reg := List(ch); Store(reg) end {OneChar}; begin FreeCell := 1; for i := 1 to TopCell - 1 do begin Marked[i] := 0; Tail[i] := i + 1 end; Marked[TopCell] := 0; Tail[TopCell] := 0; NILL := Symb; Head[NILL] := NILL; Tail[NILL] := NILL; S := NILL; E := NILL; C := NILL; D := NILL; W := NILL; T := NILL; F := NILL; OpenParen := NILL; Point := NILL; CloseParen := NILL; Head[NILL] := List('N'); Tail[Head[NILL]] := List('I'); Tail[Tail[Head[NILL]]] := List('L'); SymbolTable := Cons; { Head[SymbolTable] := NILL; the symbol ... } { Tail[SymbolTable] := NILL; the empty list ... } OneChar(T, 'T'); OneChar(F, 'F'); OneChar(OpenParen, '('); OneChar(Point, '.'); OneChar(CloseParen, ')') end {InitListStorage}; procedure Update(x, y : integer); begin IsAtom[x] := IsAtom[y]; IsNumb[x] := IsNumb[y]; Head[x] := Head[y]; Tail[x] := Tail[y] end {Update}; (*--------------------- Token input and output --------------------*) procedure GetToken(var Token : integer); var x : char; p : integer; begin while InCh = ' ' do GetChar(InCh); x := InCh; GetChar(InCh); if (('0' <= x) and (x <= '9')) or ( ((x = '-') or (x = '+')) and ('0' <= InCh) and (InCh <= '9')) then begin InTokType := Numeric; Token := Numb; if (x = '+') or (x = '-') then Head[Token] := 0 else Head[Token] := ord(x) - ord('0'); while ('0' <= InCh) and (InCh <= '9') do begin Head[Token] := (10 * Head[Token]) + (ord(InCh) - ord('0')); GetChar(InCh) end; if x = '-' then Head[Token] := - Head[Token] end else if (x = '(') or (x = ')') or (x = '.') then begin InTokType := Delimiter; if x = '(' then Token := OpenParen else if x = '.' then Token := Point else Token := CloseParen end else begin InTokType := Alpha; Token := Cons; p := Token; Head[p] := Numb; Head[Head[p]] := ord(x); while not ( (InCh = '(') or (InCh = ')') or (InCh = '.') or (InCh = ' ') ) do begin Tail[p] := Cons; p := Tail[p]; Head[p] := Numb; Head[Head[p]] := ord(InCh); GetChar(InCh) end; Store(Token) end end {GetToken}; procedure PutSymbol(Symbol : integer); var p : integer; begin p := Head[Symbol]; while p <> NILL do begin PutChar(chr(Head[Head[p]])); p := Tail[p] end; PutChar(' ') end {PutSymbol}; procedure PutNumber(Number : integer); procedure PutN(n : integer); begin if n > 9 then PutN(n div 10); PutChar(chr(ord('0') + (n mod 10))) end; begin if Head[Number] < 0 then begin PutChar('-'); PutN(-Head[Number]) end else PutN(Head[Number]); PutChar(' ') end {PutNumber}; procedure PutRecipe(E : integer); begin PutChar('*'); PutChar('*'); PutChar('R'); PutChar('E'); PutChar('C'); PutChar('I'); PutChar('P'); PutChar('E'); PutChar('*'); PutChar('*'); PutChar(' ') end {PutRecipe}; (*----------------- S-expression input and output -----------------*) procedure GetExp(var E : integer); procedure GetList(var E : integer); begin if E = CloseParen then E := NILL else begin W := Cons; Head[W] := E; E := W; if Head[E] = OpenParen then begin GetToken(Head[E]); GetList(Head[E]) end; GetToken(Tail[E]); if Tail[E] = Point then begin GetExp(Tail[E]); GetToken(W) end else GetList(Tail[E]) end end {GetList}; begin GetToken(E); if E = OpenParen then begin GetToken(E); GetList(E) end end {GetExp}; procedure PutExp(E : integer); var p : integer; begin if IsRecipe(E) then PutRecipe(E) else if IsSymbol(E) then PutSymbol(E) else if IsNumber(E) then PutNumber(E) else begin PutSymbol(OpenParen); p := E; while IsCons(p) do begin PutExp(Head[p]); p := Tail[p] end; if not IsNill(p) then begin PutSymbol(Point); PutExp(p) end; PutSymbol(CloseParen) end end {PutExp}; procedure LoadBootstrapProgram; begin InCh := ' '; GetExp(S); (* NB GetExp corrupts W *) E := Tail[S]; C := Head[S]; S := NILL; D := NILL; W := NILL end {LoadBootstrapProgram}; (*------------- Microcode for SECD machine operations -------------*) procedure LDX; var Wx, i : integer; begin Wx := E; for i := 1 to Head[Head[Head[Tail[C]]]] do Wx := Tail[Wx]; Wx := Head[Wx]; for i := 1 to Head[Tail[Head[Tail[C]]]] do Wx := Tail[Wx]; Wx := Head[Wx]; W := Cons; Head[W] := Wx; Tail[W] := S; S := W; C := Tail[Tail[C]] end {LDX}; procedure LDCX; begin W := Cons; Head[W] := Head[Tail[C]]; Tail[W] := S; S := W; C := Tail[Tail[C]] end {LDCX}; procedure LDFX; begin W := Cons; Head[W] := Cons; Head[Head[W]] := Head[Tail[C]]; Tail[Head[W]] := E; Tail[W] := S; S := W; C := Tail[Tail[C]] end {LDFX}; procedure APX; begin W := Cons; Head[W] := Tail[Tail[S]]; Tail[W] := Cons; Head[Tail[W]] := E; Tail[Tail[W]] := Cons; Head[Tail[Tail[W]]] := Tail[C]; Tail[Tail[Tail[W]]] := D; D := W; W := Cons; Head[W] := Head[Tail[S]]; Tail[W] := Tail[Head[S]]; E := W; C := Head[Head[S]]; S := NILL end {APX}; procedure RTNX; begin W := Cons; Head[W] := Head[S]; Tail[W] := Head[D]; S := W; E := Head[Tail[D]]; C := Head[Tail[Tail[D]]]; D := Tail[Tail[Tail[D]]] end {RTNX}; procedure DUMX; begin W := Cons; Head[W] := NILL; Tail[W] := E; E := W; C := Tail[C] end {DUMX}; procedure RAPX; begin W := Cons; Head[W] := Tail[Tail[S]]; Tail[W] := Cons; Head[Tail[W]] := Tail[E]; Tail[Tail[W]] := Cons; Head[Tail[Tail[W]]] := Tail[C]; Tail[Tail[Tail[W]]] := D; D := W; E := Tail[Head[S]]; Head[E] := Head[Tail[S]]; C := Head[Head[S]]; S := NILL end {RAPX}; procedure SELX; begin W := Cons; Head[W] := Tail[Tail[Tail[C]]]; Tail[W] := D; D := W; if Head[Head[S]] = Head[T] then C := Head[Tail[C]] else C := Head[Tail[Tail[C]]]; S := Tail[S] end {SELX}; procedure JOINX; begin C := Head[D]; D := Tail[D] end {JOINX}; procedure CARX; begin W := Cons; Head[W] := Head[Head[S]]; Tail[W] := Tail[S]; S := W; C := Tail[C] end {CARX}; procedure CDRX; begin W := Cons; Head[W] := Tail[Head[S]]; Tail[W] := Tail[S]; S := W; C := Tail[C] end {CDRX}; procedure ATOMX; begin W := Cons; if IsAtom[Head[S]] = 1 then Head[W] := T else Head[W] := F; Tail[W] := Tail[S]; S := W; C := Tail[C] end {ATOMX}; procedure CONSX; begin W := Cons; Head[W] := Cons; Head[Head[W]] := Head[S]; Tail[Head[W]] := Head[Tail[S]]; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {CONSX}; procedure EQX; begin W := Cons; if ( ( IsSymbol(Head[S]) and IsSymbol(Head[Tail[S]]) ) or ( IsNumber(Head[S]) and IsNumber(Head[Tail[S]]) ) ) and (Head[Head[S]] = Head[Head[Tail[S]]]) then Head[W] := T else Head[W] := F; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {EQX}; procedure ADDX; begin W := Cons; Head[W] := Numb; Head[Head[W]] := Head[Head[Tail[S]]] + Head[Head[S]]; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {ADDX}; procedure SUBX; begin W := Cons; Head[W] := Numb; Head[Head[W]] := Head[Head[Tail[S]]] - Head[Head[S]]; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {SUBX}; procedure MULX; begin W := Cons; Head[W] := Numb; Head[Head[W]] := Head[Head[Tail[S]]] * Head[Head[S]]; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {MULX}; procedure DIVX; begin W := Cons; Head[W] := Numb; Head[Head[W]] := Head[Head[Tail[S]]] div Head[Head[S]]; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {DIVX}; procedure REMX; begin W := Cons; Head[W] := Numb; Head[Head[W]] := Head[Head[Tail[S]]] mod Head[Head[S]]; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {REMX}; procedure LEQX; begin W := Cons; if Head[Head[Tail[S]]] <= Head[Head[S]] then Head[W] := T else Head[W] := F; Tail[W] := Tail[Tail[S]]; S := W; C := Tail[C] end {LEQX}; procedure STOPX; begin if IsAtom[Head[S]] = 1 then Halted := true else begin W := Cons; Head[W] := Tail[S]; Tail[W] := Cons; Head[Tail[W]] := E; Tail[Tail[W]] := Cons; Head[Tail[Tail[W]]] := C; Tail[Tail[Tail[W]]] := D; D := W; C := Head[Head[Head[S]]]; W := Cons; Head[W] := Tail[Head[S]]; Tail[W] := Tail[Head[Head[S]]]; E := W; S := NILL end end {STOPX}; procedure LDEX; begin W := Cons; Tail[W] := S; S := W; Head[W] := Recipe; Head[Head[W]] := Head[Tail[C]]; Tail[Head[W]] := E; C := Tail[Tail[C]] end {LDEX}; procedure UPDX; begin Update(Head[Head[D]],Head[S]); S := Head[D]; E := Head[Tail[D]]; C := Head[Tail[Tail[D]]]; D := Tail[Tail[Tail[D]]] end {UPDX}; procedure AP0X; begin if IsRecipe(Head[S]) then begin W := Cons; Head[W] := S; Tail[W] := Cons; Head[Tail[W]] := E; Tail[Tail[W]] := Cons; Head[Tail[Tail[W]]] := Tail[C]; Tail[Tail[Tail[W]]] := D; D := W; C := Head[Head[S]]; E := Tail[Head[S]]; S := NILL end else C := Tail[C] end {AP0X}; procedure READX; begin W := Cons; Tail[W] := S; S := W; GetExp(Head[S]); C := Tail[C] end {READX}; procedure PRINTX; begin PutExp(Head[S]); S := Tail[S]; C := Tail[C] end {PRINTX}; procedure IMPLODEX; begin W := Cons; Head[W] := Head[S]; Tail[W] := Tail[S]; S := W; if IsNumber(Head[S]) then if Head[Head[S]] = ord(' ') then Head[S] := NILL else begin W := Cons; Head[W] := Head[S]; Head[S] := W end; Store(Head[S]); C := Tail[C] end {IMPLODEX}; procedure EXPLODEX; begin W := Cons; Head[W] := Head[Head[S]]; Tail[W] := Tail[S]; S := W; C := Tail[C] end {EXPLODEX}; (*-------------------- Instruction decode loop --------------------*) procedure FetchExecuteLoop; label 1; begin Halted := false; 1: case Head[Head[C]] of 1: LDX; 11: CDRX; 2: LDCX; 12: ATOMX; 3: LDFX; 13: CONSX; 4: APX; 14: EQX; 5: RTNX; 15: ADDX; 6: DUMX; 16: SUBX; 7: RAPX; 17: MULX; 8: SELX; 18: DIVX; 9: JOINX; 19: REMX; 10: CARX; 20: LEQX; 21: begin STOPX; if Halted then Terminate end; 22: LDEX; 25: READX; 23: UPDX; 26: PRINTX; 24: AP0X; 27: IMPLODEX; 28: EXPLODEX end; goto 1 end {FetchExecuteLoop}; (*------------------ body of procedure Machine --------------------*) begin Initialise(Version, SubVersion); InitListStorage; LoadBootstrapProgram; FetchExecuteLoop end {Machine}; (*------------------- body of program LispKit ---------------------*) begin Machine; 99: end {LispKit}.