{++++++++++++++++++++++++++++++++++++++++++++++++++++++++} {+ PASCAL/Z COMPILER OPTIONS +} {++++++++++++++++++++++++++++++++++++++++++++++++++++++++} {$C- <<< CONTROL-C KEYPRESS CHECKING OFF >>> } {$F- <<< FLOATING POINT ERROR CHECKING OFF >>> } {$M- <<< INTEGER MULT & DIVD ERROR CHECKING OFF } {++++++++++++++++++++++++++++++++++++++++++++++++++++++++} (* LAST EDITED: 11/29/81 rep *) PROGRAM LISP {INPUT,OUTPUT}; { + PROGRAM TITLE: THE ESSENCE OF A LISP INTERPRETER. + WRITTEN BY: W. TAYLOR AND L. COX + + WRITTEN FOR: US DEPT OF ENERGY + CONTRACT # W-7405-ENG-48 + + FIRST DATA STARTED : 10/29/76 + LAST DATE MODIFIED : 12/10/76 + + ENTERED BY RAY PENLEY 8 DEC 80. + -SOME IDENTIFIERS HAVE BEEN SLIGHTLY MODIFIED BECAUSE OF THE + LIMITATION ON IDENTIFIER LENGTH OF 8 CHARACTERS. } {++++++++++++++++++++++++++++++++++++++++++++++++} {+ RESERVED WORDS TABLE LISP +} {++++++++++++++++++++++++++++++++++++++++++++++++} { 'APPEND ' < 'ATOM ' < A VARIABLE OR LITERAL USED IN A LIST. 'REPLACEH ' < 'REPLACET ' < 'CAR ' < THE FIRST ELEMENT OF A LIST. 'COND ' < 'COPY ' < 'CONC ' < 'CONS ' < 'EQ ' < 'QUOTE ' < 'LABEL ' < 'LAMBDA ' < FIRST ELEMENT OF A USER DEFINED FUNCTION. 'CDR ' < ALL ELEMENTS OF A LIST EXCEPT THE FIRST ELEMENT. 'FIN ' < FINISHED. } LABEL 1, { USED TO RECOVER AFTER AN ERROR BY THE USER } 2; { IN CASE THE END OF FILE IS REACHED BEFORE A FIN CARD } CONST MAXNODE = 600; {}INPUT = 0; { Pascal/Z = console as input } {}IDLENGTH = 10; TYPE {}ALFA = ARRAY [1..10] OF CHAR; INPUTSYMBOL = (ATOM, PERIOD, LPAREN, RPAREN); RESERVEWORDS = (RELACEHSYM, RELACETSYM, HEADSYM, TAILSYM, EQSYM, QUOTESYM, ATOMSYM, CONDSYM, LABELSYM, LAMBDASYM, COPYSYM, APPENDSYM, CONCSYM, CONSSYM); STATUSTYPE = (UNMARKED, LEFT, RIGHT, MARKED); SYMBEXPPTR = ^SYMBOLICEXPRESSION; SYMBOLICEXPRESSION = RECORD STATUS : STATUSTYPE; NEXT : SYMBEXPPTR; CASE ANATOM: BOOLEAN OF TRUE: (NAME: ALFA; CASE ISARESERVEDWORD: BOOLEAN OF TRUE: (RESSYM: RESERVEWORDS)); FALSE: (HEAD, TAIL: SYMBEXPPTR) END; { Symbolicexpression is the record structure used to implement a LISP list. This record has a tag field 'ANATOM' which tells which kind of node a particular node represents (i.e. an atom or a pair of pointers 'HEAD' and 'TAIL'), 'ANATOM' is always checked before accessing either the name field or the head and tail fields of a node. Two pages ahead there are three diagrams which should clarify the data structure. } { THE GLOBAL VARIABLES } VAR {}DUMMY : CHAR; { required in the Pascal/Z version } { VARIABLES WHICH PASS INFORMATION FROM THE SCANNER TO THE READ ROUTINE } LOOKAHEADSYM, { USED TO SAVE A SYMBOL WHEN WE BACK UP } SYM : INPUTSYMBOL; { THE SYMBOL THAT WAS LAST SCANNED } ID : ALFA; { NAME OF THE ATOM THAT WAS LAST READ } ALREADYPEEKED : BOOLEAN; { TELLS 'NEXTSYM' WHETHER WE HAVE PEEKED } CH : CHAR; { THE LAST CHAR READ FROM INPUT } PTR : SYMBEXPPTR; { POINTER TO THE EXPRESSION BEING EVALUATED } { THE GLOBAL LISTS OF LISP NODES } FREELIST, { POINTER TO THE LINEAR LIST OF FREE NODES } NODELIST, { POINTER USED TO MAKE A LINEAS SCAN OF ALL} { THE NODES DURING GARBAGE COLLECTION. } ALIST : SYMBEXPPTR;{ POINTER TO THE ASSOCIATION LIST } { TWO NODES WHICH HAVE CONSTANT VALUES } NILNODE, TNODE : SYMBOLICEXPRESSION; { VARIABLES USED TO IDENTIFY ATOMS WITH PRE-DEFINED MEANINGS } RESWORD : RESERVEWORDS; RESERVED : BOOLEAN; RESWORDS : ARRAY [RESERVEWORDS] OF ALFA; FREENODES : INTEGER; { NUMBER OF CURRENTLY FREE NODES KNOWN } NUMBEROFGCS : INTEGER; { # OF GARBAGE COLLECTIONS MADE } { \ \ THE ATOM 'A' IS ---\--- REPRESENTED BY ---> I I I A I I I ------- \ \ -----\----- THE DOTTED PAIR I I I '(A.B)' IS I / I \ I REPESENTED BY ---> I / I \ I -/-------\- / \ ----/---- ----\---- I I I I I A I I B I I I I I --------- --------- \ \ -----\----- THE LIST '(AB)' I I I IS REPRESENTED I / I \ I BY ---> I / I \ I -/-------\- / \ ----/---- \ I I \ I A I -----\----- I I I I I --------- I /I\ I I / I \ I --/-----\-- / \ ----/---- ----\---- I I I I I B I I NIL I I I I I --------- --------- } (* * THE GARBAGE COLLECTOR * *) { In general there are two approaches to maintaining lists of available space in list processing systems... The reference counter technique and the garbage collector technique. The reference counter technique requires that for each node or record we maintain a count of the number of nodes which reference or point to it and update this count continuously. ie. with every manipulation In general, if circular or ring structures are permitted to develope this technique will not be able to reclaim rings which are no longer in use and have been isolared from the active structure. The alternative method, garbage collection, does not function continuously, but is activated only when further storage is required and none is available. The complete process consists of two stages. A marking stage which identifies nodes still reachable (in use) and a collection stage where all nodes in the system are examined and those not in use are merged into a list of available space. This is the technique we have chosen to implement here for reasons of simplicity and to enhance the interactive nature of out system. The marking stage is theoretically simple, especially in LISP programming systems where all records are essentially the same size. All that is required is a traversal of the active list structure, each time marking nodes 1 level deeper into the tree on each pass. This is both crude and inefficient. Another alternative procedure which could be used would use a recursive walk of the tree structure to mark the nodes in use. This requires the use of a stack to store back pointers to branches not taken. This algorithm is efficient, but tend to be self defeating in the folowing manner. The requisite stack could become quite large (requiring significant amounts of storage). However, the reason we are performing garbage collection in the first place is due to an insufficiency of storage space. Therefore an usdesirable situation is likely to arise where the garbage collector's stack cannot expand to perform the marking pass. Even though there are significant amounts of free space waiting to be reclaimed. A solution to this dilema came when it was realized that space in the nodes themselves (i.e. the left and right pointers) could be used in lieu of the explicit stack. In this way the stack information can be embedded into the list itself as it is traversed. This algorithm has been discussed in Knuth and in Berztiss: Data Structures, Theory and Practice (2nd ed.), and is implemented below. Since Pascal does not allow structures to be addressed both with pointers and as indexed arrays, an additional field has been added to sequentially link the nodes. This pointer field is set on initial creation, and remains invarient throughout the run. Using this field, we can simulate a linear pass through the nodes for the collection stage. Of course, a marker field is also required. } (* * * * * * * *) PROCEDURE GARBAGEMAN; PROCEDURE MARK(LIST: SYMBEXPPTR); VAR FATHER, SON, CURRENT: SYMBEXPPTR; BEGIN FATHER := NIL; CURRENT := LIST; SON := CURRENT; WHILE ( CURRENT<>NIL ) DO WITH CURRENT^ DO CASE STATUS OF UNMARKED: IF ( ANATOM ) THEN STATUS := MARKED ELSE IF (HEAD^.STATUS <> UNMARKED) OR (HEAD = CURRENT) THEN IF (TAIL^.STATUS <> UNMARKED) OR (TAIL = CURRENT) THEN STATUS := MARKED ELSE BEGIN STATUS := RIGHT; SON := TAIL; TAIL := FATHER; FATHER := CURRENT; CURRENT := SON END ELSE BEGIN STATUS := LEFT; SON := HEAD; HEAD := FATHER; FATHER := CURRENT; CURRENT := SON END; LEFT: IF ( TAIL^.STATUS <> UNMARKED ) THEN BEGIN STATUS := MARKED; FATHER := HEAD; HEAD := SON; SON := CURRENT END ELSE BEGIN STATUS := RIGHT; CURRENT := TAIL; TAIL := HEAD; HEAD := SON; SON := CURRENT END; RIGHT: BEGIN STATUS := MARKED; FATHER := TAIL; TAIL := SON; SON := CURRENT END; MARKED: CURRENT := FATHER END { OF CASE } END { OF MARK }; PROCEDURE COLLECTFREENODES; VAR TEMP: SYMBEXPPTR; BEGIN WRITELN(' NUMBER OF FREE NODES BEFORE COLLECTION = ', FREENODES:1, '.'); FREELIST := NIL; FREENODES := 0; TEMP := NODELIST; WHILE ( TEMP <> NIL ) DO BEGIN IF ( TEMP^.STATUS <> UNMARKED ) THEN TEMP^.STATUS := UNMARKED ELSE BEGIN FREENODES := FREENODES + 1; TEMP^.HEAD := FREELIST; FREELIST := TEMP END; TEMP := TEMP^.NEXT; END {WHILE}; WRITELN(' NUMBER OF FREE NODES AFTER COLLECTION = ', FREENODES:1,'.'); END { OF COLLECTFREENODES }; BEGIN{ GARBAGEMAN } NUMBEROFGCS := NUMBEROFGCS + 1; WRITELN; WRITELN(' GARBAGE COLLECTION. '); WRITELN; MARK(ALIST); IF ( PTR <> NIL ) THEN MARK(PTR); COLLECTFREENODES END{ OF GARBAGEMAN }; PROCEDURE POP(VAR SPTR: SYMBEXPPTR); BEGIN IF ( FREELIST = NIL ) THEN BEGIN WRITELN(' NOT ENOUGH SPACE TO EVALUATE THE EXPRESSION.'); {} GOTO 2; END; FREENODES := FREENODES - 1; SPTR := FREELIST; FREELIST := FREELIST^.HEAD; END{ OF POP }; { INPUT / OUTPUT UTILITY ROUTINES } PROCEDURE ERROR(NUMBER: INTEGER); BEGIN WRITELN; WRITE(' ERROR ', NUMBER:1, ', '); CASE NUMBER OF 1: WRITELN('ATOM OR LPAREN EXPECTED IN THE S-EXPR.'); 2: WRITELN('ATOM, LPAREN, OR RPAREN EXPECTED IN THE S-EXPR.'); 3: WRITELN('LABEL AND LAMBDA ARE NOT NAMES OF FUNCTIONS.'); 4: WRITELN('RPAREN EXPECTED IN THE S-EXPR.'); 5: WRITELN('1ST ARGUMENT OF REPLACEH IS AN ATOM.'); 6: WRITELN('1ST ARGUMENT OF REPLACET IS AN ATOM.'); 7: WRITELN('ARGUMENT HEAD IS AN ATOM.'); 8: WRITELN('ARGUMENT TAIL IS AN ATOM.'); 9: WRITELN('1ST ARGUMENT OF APPEND IS NOT A LIST.'); 10: WRITELN('COMMA OR RPAREN EXPECTED IN CONCATENATE.'); 11: WRITELN('END OF FILE ENCOUNTERED BEFORE A "FIN" CARD.'); 12: WRITELN('LAMBDA OR LABEL EXPECTED.'); END{CASE}; {}IF NUMBER IN [11] THEN GOTO 2 ELSE GOTO 1 END { OF ERROR }; PROCEDURE BACKUPINPUT; { PUTS A LEFT PARENTHESIS INTO THE STREAM OF INPUT SYMBOLS. THIS MAKES PROCEDURE READEXPR EASIER THAN IT OTHERWISE WOULD BE. } BEGIN ALREADYPEEKED := TRUE; LOOKAHEADSYM := SYM; SYM := LPAREN END{ OF BACKUPINPUT }; PROCEDURE NEXTSYM; { READS THE NEXT SYMBOL FROM THE INPUT FILE. A SYMBOL IS DEFINED BY THE GOLBAL TYPE "INPUTSYMBOL". THE GLOBAL VARIABLE 'SYM' RETURNS THE TYPE OF THE NEXT SYMBOL READ. THE GLOBAL VARIABLE 'ID' RETURNS THE NAME OF AN ATOM IF THE SYMBOL IS AN ATOM. IF THE SYMBOL IS A RESERVED WORD THE GLOBAL VARIABLE 'RESERVED' IS SET TO TRUE AND THE GLOBAL VARIABLE 'RESWORD' TELLS WHICH RESERVED WORD WAS READ. } VAR I: INTEGER; BEGIN IF ( ALREADYPEEKED ) THEN BEGIN SYM := LOOKAHEADSYM; ALREADYPEEKED := FALSE END ELSE BEGIN WHILE ( CH=' ' ) DO BEGIN IF ( EOLN(INPUT) ) THEN WRITELN; READ(CH); END{WHILE}; IF ( CH IN ['(','.',')'] ) THEN BEGIN CASE CH OF '(': SYM := LPAREN; '.': SYM := PERIOD; ')': SYM := RPAREN END{CASE}; IF ( EOLN(INPUT) ) THEN WRITELN; READ(CH); END ELSE BEGIN SYM := ATOM; ID := ' '; I := 0; REPEAT I := I + 1; IF ( I < (IDLENGTH+1) ) THEN ID[I] := CH; IF ( EOLN(INPUT) ) THEN WRITELN; READ(CH); UNTIL ( CH IN [' ','(','.',')'] ); RESWORD := RELACEHSYM; WHILE (ID <> RESWORDS[RESWORD]) AND (RESWORD <> CONSSYM) DO RESWORD := SUCC(RESWORD); RESERVED := ( ID=RESWORDS[RESWORD] ) END END END{ OF NEXTSYM }; PROCEDURE READEXPR(VAR SPTR: SYMBEXPPTR); { THIS PROCEDURE RECURSIVELY READS IN THE NEXT SYMBOLIC EXPRESSION FROM THE INPUT FILE. WHEN CALLED THE GLOBAL VARIABLE 'SYM' MUST BE THE FIRST SYMBOL IN THE SYMBOLIC EXPRESSION TO BE READ. A POINTER TO THE SYMBOLIC EXPRESSION READ IS RETURNED VIA THE VARIABLE PARAMETER SPTR. EXPRESSIONS ARE READ AND STORED IN THE APPROPRIATE STRUCTURE USING THE FOLLOWING GRAMMAR FOR SYMBOLIC EXPRESSIONS: ::= or ( . ) or ( ... ) WHERE ... MEANS AN ARBITRARY NUMBER OF. (I.E. ZERO OR MORE.) TO PARSE USING THE THIRD RULE, THE IDENTITY (ABC ... Z) = (A . (BC ... Z)) IS UTILIZED. AN EXTRA LEFT PARENTHESIS IS INSERTED INTO THE INPUT STREAM AS IF IT OCCURED AFTER THE IMAGINARY DOT. WHEN IT COMES TIME TO READ THE IMAGINARY MATCHING RIGHT PARENTHESIS IT IS JUST NOT READ (BECAUSE IT IS NOT THERE). } VAR NXT: SYMBEXPPTR; BEGIN POP(SPTR); NXT := SPTR^.NEXT; CASE SYM OF RPAREN, PERIOD: ERROR(1); ATOM: WITH SPTR^ DO BEGIN { } ANATOM := TRUE; NAME := ID; ISARESERVEDWORD := RESERVED; IF ( RESERVED ) THEN RESSYM := RESWORD END; LPAREN: WITH SPTR^ DO BEGIN NEXTSYM; IF ( SYM=PERIOD ) THEN ERROR(2) ELSE IF ( SYM=RPAREN ) THEN SPTR^ := NILNODE { () = NIL } ELSE BEGIN ANATOM := FALSE; READEXPR(HEAD); NEXTSYM; IF ( SYM=PERIOD ) THEN BEGIN { ( . ) } NEXTSYM; READEXPR(TAIL); NEXTSYM; IF ( SYM<>RPAREN ) THEN ERROR(4) END ELSE BEGIN { ( ... ) } BACKUPINPUT; READEXPR(TAIL) END END END{WITH} END{CASE}; SPTR^.NEXT := NXT; END{ OF READEXPR }; PROCEDURE PRINTNAME(NAME: ALFA); { PRINTS THE NAME OF AN ATOM WITH ONE TRAILING BLANK. } VAR I: INTEGER; BEGIN I := 1; REPEAT WRITE(NAME[I]); I := I + 1 UNTIL (NAME[I]=' ') OR ( I=11 ); WRITE(' '); END{ OF PRINTNAME }; PROCEDURE PRINTEXPR(SPTR: SYMBEXPPTR); { THE ALGORITHM FOR THIS PROCEDURE WAS PROVIDED BY WEISSMAN'S LISP 1.5 PRIMER, PG 125. THIS PROCEDURE PRINTS THE SYMBOLIC EXPRESSION POINTED TO BY THE ARGUMENT 'SPTR' IN THE LIST LIST NOTATION. (THE SAME NOTATION IN WHICH EXPRESSIONS ARE READ.) } LABEL 1; BEGIN IF ( SPTR^.ANATOM ) THEN PRINTNAME(SPTR^.NAME) ELSE BEGIN WRITE('('); 1: WITH SPTR^ DO BEGIN PRINTEXPR(HEAD); IF ( TAIL^.ANATOM ) AND (TAIL^.NAME='NIL ') THEN WRITE(')') ELSE IF ( TAIL^.ANATOM ) THEN BEGIN WRITE('.'); PRINTEXPR(TAIL); WRITE(')') END ELSE BEGIN SPTR := TAIL; GOTO 1 END END{WITH} END END{ OF PRINTEXPR }; { END OF I/O UTILITY ROUTINES } { THE EXPRESSION EVALUATOR EVAL } FUNCTION EVAL( E, ALIST: SYMBEXPPTR ): SYMBEXPPTR; { Function eval evaluates the LISP expression 'e' using the association list 'alist'. This function uses the following several local functions to do so. The algorithm is a Pascal version of the classical LISP problem of writing the LISP eval routine in pure LISP. The LISP version of the code is as follows: (lambda (e alist) cond ((atom a) (lookup e alist)) ((atom (car e)) (cond ((eq (car e) (quote quote)) (cadr e)) ((eq (car e) (quote atom)) (atom (eval (card e) alist) ((eq (car e) (quote eq)) (eq (eval (cadr e) alist))) ((eq (car e) (quote car)) (car (eval (cadr e) alist))) ((eq (car e) (quote cdr)) (cdr (eval (cadr e) alist))) ((eq (car e) (quote cons) (cons (eval (cadr e) alist) (eval (caddr e) alist) ((eq (car e) (quote cond) (evcon (cdr e)) (t (eval (cons (lookup (car e) alist) (cdr e)) alist ))) ((eq (caar e) (quote label)) (eval (cons (caddr e) (cdr e) (cons (cons (cadar e) (car e)) alist) )) ((eq (caar e) (quote lambda)) (eval (caddar e) (bindargs (cadar e) (cdr e) ))))) The resulting Pascal code follows: } VAR TEMP, CAROFE, CAAROFE: SYMBEXPPTR; { The first ten of the following local functions implement ten of the primitives which operate on the LISP data structure. The last three ; 'lookup', 'bindargs', and 'evcon' are used by 'eval' to interpret a LISP expresson. } FUNCTION REPLACEH(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR; BEGIN IF ( SPTR1^.ANATOM ) THEN ERROR(5) ELSE SPTR1^.HEAD := SPTR2; REPLACEH := SPTR1; END{ OF REPLACEH }; FUNCTION REPLACET(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR; BEGIN IF ( SPTR1^.ANATOM ) THEN ERROR(6) ELSE SPTR1^.TAIL := SPTR2; REPLACET := SPTR1; END{ OF REPLACET }; FUNCTION HEAD(SPTR: SYMBEXPPTR): SYMBEXPPTR; BEGIN IF ( SPTR^.ANATOM ) THEN ERROR(7) ELSE HEAD := SPTR^.HEAD; END{ OF HEAD }; FUNCTION TAIL(SPTR: SYMBEXPPTR): SYMBEXPPTR; BEGIN IF ( SPTR^.ANATOM ) THEN ERROR(8) ELSE TAIL := SPTR^.TAIL; END{ OF TAIL }; FUNCTION CONS(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR; VAR TEMP: SYMBEXPPTR; BEGIN POP(TEMP); TEMP^.ANATOM := FALSE; TEMP^.HEAD := SPTR1; TEMP^.TAIL := SPTR2; CONS := TEMP; END{ OF CONS }; FUNCTION COPY(SPTR: SYMBEXPPTR): SYMBEXPPTR; { THIS FUNCTION CREATES A COPY OF THE STRUCTURE POINTED TO BY THE PARAMETER 'SPTR' } VAR TEMP, NXT: SYMBEXPPTR; BEGIN IF ( SPTR^.ANATOM ) THEN BEGIN POP(TEMP); NXT := TEMP^.NEXT; TEMP^ := SPTR^; TEMP^.NEXT := NXT; COPY := TEMP END ELSE COPY := CONS(COPY(SPTR^.HEAD), COPY(SPTR^.TAIL)); END{ OF COPY }; FUNCTION APPEND(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR; { THE RECURSIVE ALGORITHM IS FROM WEISSMAN, PG 97. } BEGIN IF ( SPTR1^.ANATOM ) THEN IF ( SPTR1^.NAME<>'NIL ' ) THEN ERROR(9) ELSE APPEND := SPTR2 ELSE APPEND := CONS(COPY(SPTR1^.HEAD), APPEND(SPTR1^.TAIL,SPTR2)); END{ OF APPEND }; FUNCTION CONC(SPTR1: SYMBEXPPTR): SYMBEXPPTR; { This function serves as the basic concatenation mechanism for variable numbers of list expressions in the input stream. The concatenation is handled recursively, using the identity: conc(a,b,c,d) = conc(a,cons(b,cons(c,(cons(d,nil)))) The routine is called when a conc(..... command has been recognized on input, and its single argument is the first expression in the chain. It has the side effect of reading all following input up to the parenthesis closing the conc command. The procedure consists of the following steps- 1. call with 1st expression as argument. 2. read the next expression. 3. if the expression just read was not the last, recurse. 4. otherwise... unwind. } VAR SPTR2, NILPTR: SYMBEXPPTR; BEGIN IF ( SYM<>RPAREN ) THEN BEGIN NEXTSYM; READEXPR(SPTR2); NEXTSYM; CONC := CONS(SPTR1, CONC(SPTR2)); END ELSE IF ( SYM=RPAREN ) THEN BEGIN NEW(NILPTR); WITH NILPTR^ DO BEGIN ANATOM := TRUE; NAME := 'NIL '; END{WITH}; CONC := CONS(SPTR1, NILPTR); END ELSE ERROR(10); END{ OF CONC }; FUNCTION EQQ(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR; VAR TEMP, NXT: SYMBEXPPTR; BEGIN POP(TEMP); NXT := TEMP^.NEXT; IF ( SPTR1^.ANATOM ) AND ( SPTR2^.ANATOM ) THEN IF ( SPTR1^.NAME=SPTR2^.NAME ) THEN TEMP^ := TNODE ELSE if ( sptr1=sptr2 ) then temp^ := tnode else temp^ := nilnode; TEMP^.NEXT := NXT; EQQ := TEMP; END{ OF EQQ }; FUNCTION ATOM(SPTR: SYMBEXPPTR): SYMBEXPPTR; VAR TEMP, NXT: SYMBEXPPTR; BEGIN POP(TEMP); NXT := TEMP^.NEXT; IF ( SPTR^.ANATOM ) THEN TEMP^ := TNODE ELSE TEMP^ := NILNODE; TEMP^.NEXT := NXT; ATOM := TEMP; END{ OF ATOM }; FUNCTION LOOKUP(KEY, ALIST: SYMBEXPPTR): SYMBEXPPTR; VAR TEMP: SYMBEXPPTR; BEGIN TEMP := EQQ( HEAD( HEAD(ALIST)), KEY); IF ( TEMP^.NAME='T ' ) THEN LOOKUP := TAIL(HEAD(ALIST)) ELSE LOOKUP := LOOKUP(KEY, TAIL(ALIST)) END{ OF LOOKUP }; FUNCTION BINDARGS(NAMES, VALUES: SYMBEXPPTR): SYMBEXPPTR; VAR TEMP, TEMP2: SYMBEXPPTR; BEGIN IF ( NAMES^.ANATOM ) AND (NAMES^.NAME='NIL ') THEN BINDARGS := ALIST ELSE BEGIN TEMP := CONS( HEAD(NAMES), EVAL(HEAD(VALUES), ALIST) ); TEMP2 := BINDARGS(TAIL(NAMES), TAIL(VALUES)); BINDARGS := CONS(TEMP, TEMP2) END END{ OF BINDARGS }; FUNCTION EVCON(CONDPAIRS: SYMBEXPPTR): SYMBEXPPTR; VAR TEMP: SYMBEXPPTR; BEGIN TEMP := EVAL( HEAD(HEAD(CONDPAIRS)),ALIST ); IF ( TEMP^.ANATOM ) AND (TEMP^.NAME='NIL ') THEN EVCON := EVCON( TAIL(CONDPAIRS) ) ELSE EVCON := EVAL( HEAD(TAIL(HEAD(CONDPAIRS))),ALIST ) END{ OF EVCON }; BEGIN { * E V A L * } IF ( E^.ANATOM ) THEN EVAL := LOOKUP(E, ALIST) ELSE BEGIN CAROFE := HEAD(E); IF ( CAROFE^.ANATOM ) THEN IF NOT ( CAROFE^.ISARESERVEDWORD ) THEN EVAL := EVAL( CONS(LOOKUP(CAROFE,ALIST),TAIL(E)), ALIST ) ELSE CASE CAROFE^.RESSYM OF LABELSYM, LAMBDASYM: ERROR(3); QUOTESYM : EVAL := HEAD(TAIL(E)); ATOMSYM : EVAL := ATOM(EVAL(HEAD(TAIL(E)),ALIST)); EQSYM : EVAL := EQQ(EVAL(HEAD(TAIL(E)),ALIST), EVAL(HEAD(TAIL(TAIL(E))), ALIST)); HEADSYM : EVAL := HEAD(EVAL(HEAD(TAIL(E)),ALIST)); TAILSYM : EVAL := TAIL(EVAL(HEAD(TAIL(E)),ALIST)); CONSSYM : EVAL := CONS(EVAL(HEAD(TAIL(E)),ALIST), EVAL(HEAD(TAIL(TAIL(E))), ALIST)); CONDSYM : EVAL := EVCON(TAIL(E)); CONCSYM : {}; APPENDSYM : EVAL := APPEND(EVAL(HEAD(TAIL(E)),ALIST), EVAL(HEAD(TAIL(TAIL(E))), ALIST)); RELACEHSYM : EVAL := REPLACEH(EVAL(HEAD(TAIL(E)),ALIST), EVAL(HEAD(TAIL(TAIL(E))), ALIST)); RELACETSYM : EVAL := REPLACET(EVAL(HEAD(TAIL(E)),ALIST), EVAL(HEAD(TAIL(TAIL(E))), ALIST)); END{CASE} ELSE BEGIN CAAROFE := HEAD(CAROFE); IF ( CAAROFE^.ANATOM ) AND ( CAAROFE^.ISARESERVEDWORD ) THEN IF NOT ( CAAROFE^.RESSYM IN [LABELSYM, LAMBDASYM] ) THEN ERROR(12) ELSE CASE CAAROFE^.RESSYM OF LABELSYM: BEGIN TEMP := CONS( CONS(HEAD(TAIL(CAROFE)), HEAD(TAIL(TAIL(CAROFE)))), ALIST); EVAL := EVAL(CONS(HEAD(TAIL(TAIL(CAROFE))), TAIL(E)),TEMP) END; LAMBDASYM: BEGIN TEMP := BINDARGS(HEAD(TAIL(CAROFE)), TAIL(E)); EVAL := EVAL( HEAD( TAIL( TAIL(CAROFE))), TEMP) END END{ CASE } ELSE EVAL := EVAL(CONS(EVAL(CAROFE, ALIST), TAIL(E)), ALIST) END END END{ OF EVAL }; PROCEDURE INITIALIZE; VAR I: INTEGER; TEMP, NXT: SYMBEXPPTR; BEGIN ALREADYPEEKED := FALSE; READ(CH); NUMBEROFGCS := 0; FREENODES := MAXNODE; WITH NILNODE DO BEGIN ANATOM := TRUE; NEXT := NIL; NAME := 'NIL '; STATUS := UNMARKED; ISARESERVEDWORD := FALSE END; WITH TNODE DO BEGIN ANATOM := TRUE; NEXT := NIL; NAME := 'T '; STATUS := UNMARKED; ISARESERVEDWORD := FALSE END; { ALLOCATE STORAGE AND MARK IT FREE } FREELIST := NIL; FOR I:=1 TO MAXNODE DO BEGIN NEW(NODELIST); NODELIST^.NEXT := FREELIST; NODELIST^.HEAD := FREELIST; NODELIST^.STATUS := UNMARKED; FREELIST := NODELIST END; { INITIALIZE RESERVED WORD TABLE } RESWORDS[ APPENDSYM ] := 'APPEND '; RESWORDS[ ATOMSYM ] := 'ATOM '; RESWORDS[ HEADSYM ] := 'CAR '; RESWORDS[ TAILSYM ] := 'CDR '; RESWORDS[ CONDSYM ] := 'COND '; RESWORDS[ COPYSYM ] := 'COPY '; RESWORDS[ CONCSYM ] := 'CONC '; RESWORDS[ CONSSYM ] := 'CONS '; RESWORDS[ EQSYM ] := 'EQ '; RESWORDS[ LABELSYM ] := 'LABEL '; RESWORDS[ LAMBDASYM ] := 'LAMBDA '; RESWORDS[ QUOTESYM ] := 'QUOTE '; RESWORDS[ RELACEHSYM ] := 'REPLACEH '; RESWORDS[ RELACETSYM ] := 'REPLACET '; { INITIALIZE THE A-LIST WITH T AND NIL } POP(ALIST); ALIST^.ANATOM := FALSE; ALIST^.STATUS := UNMARKED; POP(ALIST^.TAIL); NXT := ALIST^.TAIL^.NEXT; ALIST^.TAIL^ := NILNODE; ALIST^.TAIL^.NEXT := NXT; POP(ALIST^.HEAD); { BIND NIL TO THE ATOM NIL } WITH ALIST^.HEAD^ DO BEGIN ANATOM := FALSE; STATUS := UNMARKED; POP(HEAD); NXT := HEAD^.NEXT; HEAD^ := NILNODE; HEAD^.NEXT := NXT; POP(TAIL); NXT := TAIL^.NEXT; TAIL^ := NILNODE; TAIL^.NEXT := NXT END; POP(TEMP); TEMP^.ANATOM := FALSE; TEMP^.STATUS := UNMARKED; TEMP^.TAIL := ALIST; ALIST := TEMP; POP(ALIST^.HEAD); { BIND T TO THE ATOM T } WITH ALIST^.HEAD^ DO BEGIN ANATOM := FALSE; STATUS := UNMARKED; POP(HEAD); NXT := HEAD^.NEXT; HEAD^ := TNODE; HEAD^.NEXT := NXT; POP(TAIL); NXT := TAIL^.NEXT; TAIL^ := TNODE; TAIL^.NEXT := NXT END END{ OF INITIALIZE }; BEGIN{+ LISP MAIN PROGRAM +} WRITELN(' * EVAL *'); INITIALIZE; NEXTSYM; READEXPR(PTR); {}READLN(DUMMY); WRITELN; WHILE NOT ( PTR^.ANATOM ) OR ( PTR^.NAME<>'FIN ' ) DO BEGIN WRITELN; WRITELN(' * VALUE *'); PRINTEXPR( EVAL(PTR, ALIST) ); 1: WRITELN; WRITELN; IF ( EOF(INPUT) ) THEN ERROR(11); PTR := NIL; { CALL THE } GARBAGEMAN; WRITELN; WRITELN; WRITELN(' * EVAL *'); NEXTSYM; READEXPR(PTR); {} READLN(DUMMY); WRITELN; END; 2:WRITELN; WRITELN; WRITELN(' TOTAL NUMBER OF GARBAGE COLLECTIONS = ', NUMBEROFGCS:1,'.'); WRITELN; WRITELN(' FREE NODES LEFT UPON EXIT = ', FREENODES:1, '.'); WRITELN END { OF LISP }. .