home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / simtel / sigm / vols000 / vol071 / lisp.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1984-04-29  |  25.8 KB  |  872 lines

  1. {++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  2. {+        PASCAL/Z COMPILER OPTIONS        +}
  3. {++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  4. {$C- <<< CONTROL-C KEYPRESS CHECKING OFF >>>         }    
  5. {$F- <<< FLOATING POINT ERROR CHECKING OFF >>>         }
  6. {$M- <<< INTEGER MULT & DIVD ERROR CHECKING OFF         }
  7. {++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
  8.  
  9.  
  10. (* LAST EDITED: 11/29/81 rep *)
  11.  
  12. PROGRAM LISP {INPUT,OUTPUT};
  13. {
  14. +  PROGRAM TITLE:    THE ESSENCE OF A LISP INTERPRETER.
  15. +  WRITTEN BY:        W. TAYLOR AND L. COX
  16. +
  17. +  WRITTEN FOR:        US DEPT OF ENERGY
  18. +            CONTRACT # W-7405-ENG-48
  19. +
  20. +    FIRST DATA STARTED : 10/29/76
  21. +    LAST DATE MODIFIED : 12/10/76
  22. +
  23. + ENTERED BY RAY PENLEY 8 DEC 80.
  24. + -SOME IDENTIFIERS HAVE BEEN SLIGHTLY MODIFIED BECAUSE OF THE
  25. +  LIMITATION ON IDENTIFIER LENGTH OF 8 CHARACTERS.
  26. }
  27.     {++++++++++++++++++++++++++++++++++++++++++++++++}
  28.     {+    RESERVED WORDS TABLE LISP        +}
  29.     {++++++++++++++++++++++++++++++++++++++++++++++++}
  30. {
  31.     'APPEND    '    <
  32.     'ATOM      '    <  A VARIABLE OR LITERAL USED IN A LIST.
  33.     'REPLACEH  '    <
  34.     'REPLACET  '    <
  35.     'CAR       '    <  THE FIRST ELEMENT OF A LIST.
  36.     'COND      '    <
  37.     'COPY      '    <
  38.     'CONC      '    <
  39.     'CONS      '    <
  40.     'EQ        '    <
  41.     'QUOTE     '    <
  42.     'LABEL     '    <
  43.     'LAMBDA    '    <  FIRST ELEMENT OF A USER DEFINED FUNCTION.
  44.     'CDR       '    <  ALL ELEMENTS OF A LIST EXCEPT THE FIRST ELEMENT.
  45.     'FIN       '    <  FINISHED.
  46.  
  47. }
  48.  
  49. LABEL
  50.   1,    { USED TO RECOVER AFTER AN ERROR BY THE USER }
  51.   2;    { IN CASE THE END OF FILE IS REACHED BEFORE A FIN CARD }
  52.  
  53. CONST
  54.   MAXNODE = 600;
  55. {}INPUT = 0;    { Pascal/Z = console as input }
  56. {}IDLENGTH = 10;
  57.  
  58. TYPE
  59. {}ALFA = ARRAY [1..10] OF CHAR;
  60.   INPUTSYMBOL = (ATOM, PERIOD, LPAREN, RPAREN);
  61.   RESERVEWORDS = (RELACEHSYM, RELACETSYM, HEADSYM, TAILSYM, EQSYM, QUOTESYM,
  62.           ATOMSYM, CONDSYM, LABELSYM, LAMBDASYM, COPYSYM, APPENDSYM,
  63.           CONCSYM, CONSSYM);
  64.   STATUSTYPE = (UNMARKED, LEFT, RIGHT, MARKED);
  65.   SYMBEXPPTR = ^SYMBOLICEXPRESSION;
  66.   SYMBOLICEXPRESSION = RECORD
  67.              STATUS : STATUSTYPE;
  68.              NEXT   : SYMBEXPPTR;
  69.              CASE ANATOM: BOOLEAN OF
  70.                TRUE: (NAME: ALFA;
  71.                   CASE ISARESERVEDWORD: BOOLEAN OF
  72.                     TRUE: (RESSYM: RESERVEWORDS));
  73.                FALSE: (HEAD, TAIL: SYMBEXPPTR)
  74.             END;
  75.  
  76. {
  77.     Symbolicexpression is the record structure used    to implement
  78.     a LISP list.  This record has a tag field 'ANATOM' which tells
  79.     which kind of node a particular node represents (i.e. an atom
  80.     or a pair of pointers 'HEAD' and 'TAIL'), 'ANATOM' is always
  81.     checked before accessing either the name field or the head and
  82.     tail fields of a node.  Two pages ahead there are three diagrams
  83.     which should clarify the data structure.
  84. }
  85.  
  86. {    THE GLOBAL VARIABLES    }
  87.  
  88. VAR
  89. {}DUMMY        : CHAR;        { required in the Pascal/Z version }
  90.  
  91. { VARIABLES WHICH PASS INFORMATION FROM THE SCANNER TO THE READ ROUTINE }
  92.  
  93.   LOOKAHEADSYM,            { USED TO SAVE A SYMBOL WHEN WE BACK UP }
  94.   SYM        : INPUTSYMBOL;    { THE SYMBOL THAT WAS LAST SCANNED }
  95.   ID        : ALFA;        { NAME OF THE ATOM THAT WAS LAST READ }
  96.   ALREADYPEEKED    : BOOLEAN;    { TELLS 'NEXTSYM' WHETHER WE HAVE PEEKED }
  97.   CH        : CHAR;        { THE LAST CHAR READ FROM INPUT }
  98.   PTR        : SYMBEXPPTR;    { POINTER TO THE EXPRESSION BEING EVALUATED }
  99.  
  100.     { THE GLOBAL LISTS OF LISP NODES }
  101.  
  102.   FREELIST,    { POINTER TO THE LINEAR LIST OF FREE NODES }
  103.   NODELIST,    { POINTER USED TO MAKE A LINEAS SCAN OF ALL}
  104.         { THE NODES DURING GARBAGE COLLECTION.       }
  105.   ALIST    : SYMBEXPPTR;{ POINTER TO THE ASSOCIATION LIST }
  106.  
  107.     { TWO NODES WHICH HAVE CONSTANT VALUES }
  108.  
  109.   NILNODE,
  110.   TNODE    : SYMBOLICEXPRESSION;
  111.  
  112.     { VARIABLES USED TO IDENTIFY ATOMS WITH PRE-DEFINED MEANINGS }
  113.  
  114.   RESWORD    : RESERVEWORDS;
  115.   RESERVED    : BOOLEAN;
  116.   RESWORDS    : ARRAY [RESERVEWORDS] OF ALFA;
  117.   FREENODES    : INTEGER; { NUMBER OF CURRENTLY FREE NODES KNOWN }
  118.   NUMBEROFGCS    : INTEGER; { # OF GARBAGE COLLECTIONS MADE }
  119. {
  120.  
  121.                      \
  122.                       \
  123.     THE ATOM 'A' IS            ---\---
  124.     REPRESENTED BY --->             I     I
  125.                     I  A  I
  126.                     I     I
  127.                     -------
  128.  
  129.  
  130.                    \
  131.                     \
  132.                 -----\-----
  133.     THE DOTTED PAIR        I    I    I
  134.     '(A.B)' IS        I  / I \  I
  135.     REPESENTED BY --->    I /  I  \ I
  136.                 -/-------\-
  137.                 /         \
  138.                ----/----   ----\----
  139.                I       I   I       I
  140.                I   A   I   I   B   I
  141.                I       I   I       I
  142.                ---------   ---------
  143.  
  144.  
  145.                    \
  146.                     \
  147.                 -----\-----
  148.     THE LIST '(AB)'        I    I    I
  149.     IS REPRESENTED         I  / I \  I
  150.     BY --->            I /  I  \ I
  151.                 -/-------\-
  152.                 /         \
  153.                ----/----       \
  154.                I       I        \
  155.                I   A   I    -----\-----
  156.                I       I    I    I    I
  157.                ---------    I   /I\   I
  158.                     I  / I \  I
  159.                     --/-----\--
  160.                      /       \
  161.                     ----/---- ----\----
  162.                     I       I I       I
  163.                     I   B   I I  NIL  I
  164.                     I       I I       I
  165.                     --------- ---------
  166. }
  167. (*    *    THE GARBAGE COLLECTOR        *          *)
  168. {
  169.  In  general  there are two approaches to maintaining lists of available space
  170. in list processing systems... The  reference counter technique and the garbage
  171. collector technique.
  172.  
  173.  The reference counter technique requires that for  each  node  or  record  we
  174. maintain  a  count  of  the number of nodes which reference or point to it and
  175. update this count continuously. ie.  with  every  manipulation  In general, if
  176. circular or ring structures are permitted to develope this technique will  not
  177. be  able  to  reclaim  rings which are no longer in use and have been isolared
  178. from the active structure.
  179.  
  180.  The alternative method, garbage  collection,  does not function continuously,
  181. but is activated only when further storage is required and none is  available.
  182. The complete process consists of two stages.  A marking stage which identifies
  183. nodes  still  reachable (in use) and a collection stage where all nodes in the
  184. system are examined and those not in  use  are merged into a list of available
  185. space.  This is the technique we have chosen to implement here for reasons  of
  186. simplicity and to enhance the interactive nature of out system.
  187.  
  188.  The  marking  stage  is  theoretically simple, especially in LISP programming
  189. systems where all records are essentially the same size.  All that is required
  190. is a traversal of the active  list  structure, each time marking nodes 1 level
  191. deeper into the tree on each pass.  This is both crude and inefficient.
  192.  
  193.  Another alternative procedure which could be used would use a recursive  walk
  194. of  the  tree  structure to mark the nodes in use.  This requires the use of a
  195. stack to store  back  pointers  to  branches  not  taken.    This algorithm is
  196. efficient, but tend to  be  self  defeating  in  the  folowing  manner.    The
  197. requisite  stack  could  become  quite large (requiring significant amounts of
  198. storage).  However, the  reason  we  are  performing garbage collection in the
  199. first place is due to  an  insufficiency  of  storage  space.    Therefore  an
  200. usdesirable  situation  is likely to arise where the garbage collector's stack
  201. cannot expand to perform the marking  pass.  Even though there are significant
  202. amounts of free space waiting to be reclaimed.
  203.  
  204.  A solution to this dilema came when it was realized that space in  the  nodes
  205. themselves  (i.e.  the  left  and right pointers) could be used in lieu of the
  206. explicit stack.  In this way  the  stack  information can be embedded into the
  207. list itself as it is traversed.  This algorithm has been  discussed  in  Knuth
  208. and  in  Berztiss:  Data  Structures,  Theory  and  Practice (2nd ed.), and is
  209. implemented below.
  210.  
  211.  Since Pascal does not allow structures to be addressed both with pointers and
  212. as indexed arrays, an additional field has been added to sequentially link the
  213. nodes.  This pointer field is  set  on initial creation, and remains invarient
  214. throughout the run.  Using this field, we can simulate a linear  pass  through
  215. the  nodes  for  the  collection  stage.    Of  course, a marker field is also
  216. required.
  217. }
  218. (*    *    *    *    *    *    *    *)
  219.  
  220. PROCEDURE GARBAGEMAN;
  221.  
  222.   PROCEDURE MARK(LIST: SYMBEXPPTR);
  223.   VAR
  224.     FATHER, SON, CURRENT: SYMBEXPPTR;
  225.   BEGIN
  226.     FATHER := NIL;
  227.     CURRENT := LIST;
  228.     SON := CURRENT;
  229.     WHILE ( CURRENT<>NIL ) DO
  230.       WITH CURRENT^ DO
  231.     CASE STATUS OF
  232.       UNMARKED:
  233.         IF ( ANATOM ) THEN
  234.           STATUS := MARKED
  235.         ELSE
  236.           IF (HEAD^.STATUS <> UNMARKED) OR (HEAD = CURRENT) THEN
  237.         IF (TAIL^.STATUS <> UNMARKED) OR (TAIL = CURRENT) THEN
  238.            STATUS := MARKED
  239.         ELSE BEGIN
  240.           STATUS := RIGHT; SON := TAIL; TAIL := FATHER;
  241.           FATHER := CURRENT; CURRENT := SON
  242.         END
  243.           ELSE BEGIN
  244.         STATUS := LEFT; SON := HEAD; HEAD := FATHER;
  245.         FATHER := CURRENT; CURRENT := SON
  246.           END;
  247.       LEFT:
  248.         IF ( TAIL^.STATUS <> UNMARKED ) THEN BEGIN
  249.           STATUS := MARKED; FATHER := HEAD; HEAD := SON;
  250.           SON := CURRENT
  251.         END
  252.         ELSE BEGIN
  253.           STATUS := RIGHT; CURRENT := TAIL; TAIL := HEAD;
  254.           HEAD := SON; SON := CURRENT
  255.         END;
  256.       RIGHT:
  257.         BEGIN
  258.         STATUS := MARKED; FATHER := TAIL; TAIL := SON;
  259.         SON := CURRENT
  260.         END;
  261.       MARKED: CURRENT := FATHER
  262.     END { OF CASE }
  263.   END { OF MARK };
  264.  
  265.   PROCEDURE COLLECTFREENODES;
  266.   VAR
  267.     TEMP: SYMBEXPPTR;
  268.   BEGIN
  269.     WRITELN(' NUMBER OF FREE NODES BEFORE COLLECTION = ', FREENODES:1, '.');
  270.     FREELIST := NIL; FREENODES := 0; TEMP := NODELIST;
  271.     WHILE ( TEMP <> NIL ) DO BEGIN
  272.     IF ( TEMP^.STATUS <> UNMARKED ) THEN
  273.       TEMP^.STATUS := UNMARKED
  274.     ELSE BEGIN
  275.       FREENODES := FREENODES + 1; TEMP^.HEAD := FREELIST;
  276.       FREELIST := TEMP
  277.     END;
  278.     TEMP := TEMP^.NEXT;
  279.     END {WHILE};
  280.     WRITELN(' NUMBER OF FREE NODES AFTER COLLECTION = ', FREENODES:1,'.');
  281.   END { OF COLLECTFREENODES };
  282.  
  283. BEGIN{ GARBAGEMAN }
  284.   NUMBEROFGCS := NUMBEROFGCS + 1; WRITELN;
  285.   WRITELN(' GARBAGE COLLECTION. '); WRITELN; MARK(ALIST);
  286.   IF ( PTR <> NIL ) THEN MARK(PTR);
  287.   COLLECTFREENODES
  288. END{ OF GARBAGEMAN };
  289.  
  290. PROCEDURE POP(VAR SPTR: SYMBEXPPTR);
  291. BEGIN
  292.   IF ( FREELIST = NIL ) THEN BEGIN
  293.     WRITELN(' NOT ENOUGH SPACE TO EVALUATE THE EXPRESSION.');
  294. {}  GOTO 2;
  295.   END;
  296.   FREENODES := FREENODES - 1;
  297.   SPTR := FREELIST;
  298.   FREELIST := FREELIST^.HEAD;
  299. END{ OF POP };
  300.  
  301.  
  302. {    INPUT / OUTPUT UTILITY ROUTINES         }
  303.  
  304. PROCEDURE ERROR(NUMBER: INTEGER);
  305. BEGIN
  306.   WRITELN; WRITE('  ERROR   ', NUMBER:1, ', ');
  307.   CASE NUMBER OF
  308.     1: WRITELN('ATOM OR LPAREN EXPECTED IN THE S-EXPR.');
  309.     2: WRITELN('ATOM, LPAREN, OR RPAREN EXPECTED IN THE S-EXPR.');
  310.     3: WRITELN('LABEL AND LAMBDA ARE NOT NAMES OF FUNCTIONS.');
  311.     4: WRITELN('RPAREN EXPECTED IN THE S-EXPR.');
  312.     5: WRITELN('1ST ARGUMENT OF REPLACEH IS AN ATOM.');
  313.     6: WRITELN('1ST ARGUMENT OF REPLACET IS AN ATOM.');
  314.     7: WRITELN('ARGUMENT HEAD IS AN ATOM.');
  315.     8: WRITELN('ARGUMENT TAIL IS AN ATOM.');
  316.     9: WRITELN('1ST ARGUMENT OF APPEND IS NOT A LIST.');
  317.    10: WRITELN('COMMA OR RPAREN EXPECTED IN CONCATENATE.');
  318.    11: WRITELN('END OF FILE ENCOUNTERED BEFORE A "FIN" CARD.');
  319.    12: WRITELN('LAMBDA OR LABEL EXPECTED.');
  320.   END{CASE};
  321. {}IF NUMBER IN [11] THEN
  322.     GOTO 2
  323.   ELSE
  324.     GOTO 1
  325. END { OF ERROR };
  326.  
  327. PROCEDURE BACKUPINPUT;
  328. {    PUTS A LEFT PARENTHESIS INTO THE STREAM OF INPUT
  329.     SYMBOLS.  THIS MAKES PROCEDURE READEXPR EASIER
  330.     THAN IT OTHERWISE WOULD BE.
  331. }
  332. BEGIN
  333.   ALREADYPEEKED := TRUE; LOOKAHEADSYM := SYM; SYM := LPAREN
  334. END{ OF BACKUPINPUT };
  335.  
  336. PROCEDURE NEXTSYM;
  337. {    READS THE NEXT SYMBOL FROM THE INPUT FILE.  A SYMBOL IS    DEFINED
  338.     BY THE GOLBAL TYPE "INPUTSYMBOL".  THE GLOBAL VARIABLE 'SYM'
  339.     RETURNS THE TYPE OF THE NEXT SYMBOL READ.  THE GLOBAL VARIABLE
  340.     'ID' RETURNS THE NAME OF AN ATOM IF THE SYMBOL IS AN ATOM.  IF
  341.     THE SYMBOL IS A RESERVED WORD THE GLOBAL VARIABLE 'RESERVED' IS
  342.     SET TO TRUE AND THE GLOBAL VARIABLE 'RESWORD' TELLS WHICH RESERVED
  343.     WORD WAS READ.
  344. }
  345. VAR    I: INTEGER;
  346. BEGIN
  347.   IF ( ALREADYPEEKED ) THEN BEGIN
  348.       SYM := LOOKAHEADSYM; ALREADYPEEKED := FALSE
  349.   END
  350.   ELSE
  351.     BEGIN
  352.       WHILE ( CH=' ' ) DO BEGIN
  353.     IF ( EOLN(INPUT) ) THEN WRITELN;
  354.     READ(CH);
  355.       END{WHILE};
  356.       IF ( CH IN ['(','.',')'] ) THEN BEGIN
  357.     CASE CH OF
  358.       '(': SYM := LPAREN;
  359.       '.': SYM := PERIOD;
  360.       ')': SYM := RPAREN
  361.     END{CASE};
  362.     IF ( EOLN(INPUT) ) THEN WRITELN;
  363.     READ(CH);
  364.       END
  365.       ELSE BEGIN
  366.     SYM := ATOM; ID := '          ';
  367.     I := 0;
  368.     REPEAT
  369.       I := I + 1;
  370.       IF ( I < (IDLENGTH+1) ) THEN ID[I] := CH;
  371.       IF ( EOLN(INPUT) ) THEN WRITELN;
  372.       READ(CH);
  373.     UNTIL ( CH IN [' ','(','.',')'] );
  374.     RESWORD := RELACEHSYM;
  375.     WHILE (ID <> RESWORDS[RESWORD]) AND (RESWORD <> CONSSYM) DO
  376.       RESWORD := SUCC(RESWORD);
  377.     RESERVED := ( ID=RESWORDS[RESWORD] )
  378.       END
  379.     END  
  380. END{ OF NEXTSYM };
  381.  
  382. PROCEDURE READEXPR(VAR SPTR: SYMBEXPPTR);
  383. {
  384.     THIS PROCEDURE RECURSIVELY READS IN THE NEXT SYMBOLIC EXPRESSION
  385.     FROM THE INPUT FILE.  WHEN CALLED THE GLOBAL VARIABLE 'SYM' MUST
  386.     BE THE FIRST SYMBOL IN THE SYMBOLIC EXPRESSION TO BE READ.  A
  387.     POINTER TO THE SYMBOLIC EXPRESSION READ IS RETURNED VIA THE
  388.     VARIABLE PARAMETER SPTR.
  389.     EXPRESSIONS ARE READ AND STORED IN THE APPROPRIATE STRUCTURE
  390.     USING THE FOLLOWING GRAMMAR FOR SYMBOLIC EXPRESSIONS:
  391.  
  392.     <s-expr> ::= <atom>
  393.          or ( <s-expr> . <s-expr> )
  394.          or ( <s-expr> <s-expr> ... <s-expr> )
  395.  
  396.     WHERE ... MEANS AN ARBITRARY NUMBER OF. (I.E. ZERO OR MORE.)
  397.     TO PARSE USING THE THIRD RULE, THE IDENTITY
  398.         (ABC ... Z) = (A . (BC ... Z))
  399.     IS UTILIZED.  AN EXTRA LEFT PARENTHESIS IS INSERTED INTO THE
  400.     INPUT STREAM AS IF IT OCCURED AFTER THE IMAGINARY DOT.  WHEN
  401.     IT COMES TIME TO READ THE IMAGINARY MATCHING RIGHT PARENTHESIS 
  402.     IT IS JUST NOT READ (BECAUSE IT IS NOT THERE).
  403. }
  404. VAR    NXT: SYMBEXPPTR;
  405. BEGIN
  406.   POP(SPTR);
  407.   NXT := SPTR^.NEXT;
  408.   CASE SYM OF
  409.     RPAREN, PERIOD: ERROR(1);
  410.     ATOM:
  411.     WITH SPTR^ DO BEGIN {  <ATOM>  }
  412.       ANATOM := TRUE; NAME := ID; ISARESERVEDWORD := RESERVED;
  413.       IF ( RESERVED ) THEN RESSYM := RESWORD
  414.     END;
  415.     LPAREN:
  416.     WITH SPTR^ DO BEGIN
  417.       NEXTSYM;
  418.       IF ( SYM=PERIOD ) THEN ERROR(2)
  419.       ELSE
  420.         IF ( SYM=RPAREN ) THEN SPTR^ := NILNODE {   () = NIL   }
  421.         ELSE BEGIN
  422.         ANATOM := FALSE; READEXPR(HEAD); NEXTSYM;
  423.         IF ( SYM=PERIOD ) THEN BEGIN {   ( <S-EXPR> . <S-EXPR> )   }
  424.            NEXTSYM;  READEXPR(TAIL); NEXTSYM;
  425.            IF ( SYM<>RPAREN ) THEN ERROR(4)
  426.         END
  427.         ELSE BEGIN {   ( <S-EXPR> <S-EXPR> ... <S-EXPR> )   }
  428.           BACKUPINPUT; READEXPR(TAIL)
  429.         END
  430.         END
  431.     END{WITH}
  432.   END{CASE};
  433.   SPTR^.NEXT := NXT;
  434. END{ OF READEXPR };
  435.  
  436. PROCEDURE PRINTNAME(NAME: ALFA);
  437. {
  438.     PRINTS THE NAME OF AN ATOM WITH ONE TRAILING BLANK.
  439. }
  440. VAR    I: INTEGER;
  441. BEGIN
  442.   I := 1;
  443.   REPEAT
  444.     WRITE(NAME[I]);
  445.     I := I + 1
  446.   UNTIL (NAME[I]=' ') OR ( I=11 );
  447.   WRITE(' ');
  448. END{ OF PRINTNAME };
  449.  
  450. PROCEDURE PRINTEXPR(SPTR: SYMBEXPPTR);
  451. {
  452.     THE ALGORITHM FOR THIS PROCEDURE WAS PROVIDED BY WEISSMAN'S LISP
  453.     1.5 PRIMER, PG 125.  THIS PROCEDURE PRINTS THE SYMBOLIC
  454.     EXPRESSION POINTED TO BY THE ARGUMENT 'SPTR' IN THE LIST LIST
  455.     NOTATION. (THE SAME NOTATION IN WHICH EXPRESSIONS ARE READ.)
  456. }
  457. LABEL 1;
  458. BEGIN
  459.   IF ( SPTR^.ANATOM ) THEN
  460.     PRINTNAME(SPTR^.NAME)
  461.   ELSE BEGIN
  462.     WRITE('(');
  463.  1: WITH SPTR^ DO BEGIN
  464.     PRINTEXPR(HEAD);
  465.     IF ( TAIL^.ANATOM ) AND (TAIL^.NAME='NIL       ') THEN
  466.       WRITE(')')
  467.     ELSE IF ( TAIL^.ANATOM ) THEN BEGIN
  468.       WRITE('.'); PRINTEXPR(TAIL); WRITE(')')
  469.     END
  470.     ELSE BEGIN
  471.       SPTR := TAIL;
  472.       GOTO 1
  473.     END
  474.     END{WITH}
  475.   END
  476. END{ OF PRINTEXPR };
  477.  
  478. {    END OF I/O UTILITY ROUTINES    }
  479.  
  480.  
  481. {    THE EXPRESSION EVALUATOR EVAL       }
  482.  
  483. FUNCTION EVAL( E, ALIST: SYMBEXPPTR ): SYMBEXPPTR;
  484. {
  485.  Function eval evaluates the LISP expression 'e' using the association
  486.  list 'alist'. This function uses the following several local functions
  487.  to do so. The algorithm is a Pascal version of the classical LISP
  488.  problem of writing the LISP eval routine in pure LISP. The LISP version
  489.  of the code is as follows:
  490.  
  491.  (lambda (e alist)
  492.    cond
  493.      ((atom a) (lookup e alist))
  494.      ((atom (car e))
  495.        (cond ((eq (car e) (quote quote))
  496.            (cadr e))
  497.          ((eq (car e) (quote atom))
  498.            (atom (eval (card e) alist)
  499.          ((eq (car e) (quote eq))
  500.            (eq (eval (cadr e) alist)))
  501.          ((eq (car e) (quote car))
  502.            (car (eval (cadr e) alist)))
  503.          ((eq (car e) (quote cdr))
  504.            (cdr (eval (cadr e) alist)))
  505.          ((eq (car e) (quote cons)
  506.            (cons (eval (cadr e) alist)
  507.              (eval (caddr e) alist)
  508.          ((eq (car e) (quote cond)
  509.            (evcon (cdr e))
  510.          (t (eval (cons (lookup (car e) alist)
  511.            (cdr e)) alist )))
  512.      ((eq (caar e) (quote label))
  513.        (eval (cons (caddr e)
  514.          (cdr e)
  515.        (cons (cons (cadar e) (car e))
  516.          alist) ))
  517.    ((eq (caar e) (quote lambda))
  518.      (eval (caddar e)
  519.        (bindargs (cadar e) (cdr e) )))))
  520.  
  521.  
  522.     The resulting Pascal code follows:
  523. }
  524. VAR    TEMP, CAROFE, CAAROFE: SYMBEXPPTR;
  525. {
  526.     The first ten of the following local functions implement
  527.     ten of the primitives which operate on the LISP data
  528.     structure. The last three ; 'lookup', 'bindargs', and 'evcon'
  529.     are used by 'eval' to interpret a LISP expresson.
  530. }
  531.   FUNCTION REPLACEH(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR;
  532.   BEGIN
  533.     IF ( SPTR1^.ANATOM ) THEN ERROR(5)
  534.     ELSE SPTR1^.HEAD := SPTR2;
  535.     REPLACEH := SPTR1;
  536.   END{ OF REPLACEH };
  537.  
  538.   FUNCTION REPLACET(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR;
  539.   BEGIN
  540.     IF ( SPTR1^.ANATOM ) THEN ERROR(6)
  541.     ELSE SPTR1^.TAIL := SPTR2;
  542.     REPLACET := SPTR1;
  543.   END{ OF REPLACET };
  544.  
  545.   FUNCTION HEAD(SPTR: SYMBEXPPTR): SYMBEXPPTR;
  546.   BEGIN
  547.     IF ( SPTR^.ANATOM ) THEN ERROR(7)
  548.     ELSE HEAD := SPTR^.HEAD;
  549.   END{ OF HEAD };
  550.  
  551.   FUNCTION TAIL(SPTR: SYMBEXPPTR): SYMBEXPPTR;
  552.   BEGIN
  553.     IF ( SPTR^.ANATOM ) THEN ERROR(8)
  554.     ELSE TAIL := SPTR^.TAIL;
  555.   END{ OF TAIL };
  556.  
  557.   FUNCTION CONS(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR;
  558.   VAR    TEMP: SYMBEXPPTR;
  559.   BEGIN
  560.     POP(TEMP);
  561.     TEMP^.ANATOM := FALSE; TEMP^.HEAD := SPTR1;
  562.     TEMP^.TAIL := SPTR2; CONS := TEMP;
  563.   END{ OF CONS };
  564.  
  565.   FUNCTION COPY(SPTR: SYMBEXPPTR): SYMBEXPPTR;
  566.   {
  567.     THIS FUNCTION CREATES A COPY OF THE STRUCTURE
  568.     POINTED TO BY THE PARAMETER 'SPTR'
  569.   }
  570.   VAR    TEMP, NXT: SYMBEXPPTR;
  571.   BEGIN
  572.     IF ( SPTR^.ANATOM ) THEN BEGIN
  573.     POP(TEMP);
  574.     NXT := TEMP^.NEXT; TEMP^ := SPTR^;
  575.      TEMP^.NEXT := NXT; COPY := TEMP
  576.     END
  577.     ELSE
  578.     COPY := CONS(COPY(SPTR^.HEAD), COPY(SPTR^.TAIL));
  579.   END{ OF COPY };
  580.  
  581.   FUNCTION APPEND(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR;
  582.   {
  583.     THE RECURSIVE ALGORITHM IS FROM WEISSMAN, PG 97.
  584.   }
  585.   BEGIN
  586.     IF ( SPTR1^.ANATOM ) THEN
  587.       IF ( SPTR1^.NAME<>'NIL       ' ) THEN ERROR(9)
  588.       ELSE APPEND := SPTR2
  589.     ELSE
  590.       APPEND := CONS(COPY(SPTR1^.HEAD), APPEND(SPTR1^.TAIL,SPTR2));
  591.   END{ OF APPEND };
  592.  
  593.   FUNCTION CONC(SPTR1: SYMBEXPPTR): SYMBEXPPTR;
  594.   {
  595.     This function serves as the basic concatenation mechanism
  596.     for variable numbers of list expressions in the input stream.
  597.     The concatenation is handled recursively, using the identity:
  598.        conc(a,b,c,d) = conc(a,cons(b,cons(c,(cons(d,nil))))
  599.  
  600.     The routine is called when a conc(..... command has been
  601.     recognized on input, and its single argument is the first
  602.     expression in the chain.  It has the side effect of reading
  603.     all following input up to the parenthesis closing the
  604.     conc command.
  605.  
  606.     The procedure consists of the following steps-
  607.       1. call with 1st expression as argument.
  608.       2. read the next expression.
  609.       3. if the expression just read was not the last, recurse.
  610.       4. otherwise... unwind.
  611. }
  612.   VAR
  613.     SPTR2, NILPTR: SYMBEXPPTR;
  614.   BEGIN
  615.     IF ( SYM<>RPAREN ) THEN BEGIN
  616.     NEXTSYM; READEXPR(SPTR2); NEXTSYM;
  617.     CONC := CONS(SPTR1, CONC(SPTR2));
  618.     END
  619.     ELSE
  620.       IF ( SYM=RPAREN ) THEN BEGIN
  621.     NEW(NILPTR);
  622.     WITH NILPTR^ DO BEGIN
  623.       ANATOM := TRUE; NAME := 'NIL       ';
  624.     END{WITH};
  625.     CONC := CONS(SPTR1, NILPTR);
  626.       END
  627.       ELSE
  628.     ERROR(10);
  629.   END{ OF CONC };
  630.  
  631.   FUNCTION EQQ(SPTR1, SPTR2: SYMBEXPPTR): SYMBEXPPTR;
  632.   VAR    TEMP, NXT: SYMBEXPPTR;
  633.   BEGIN
  634.     POP(TEMP);
  635.     NXT := TEMP^.NEXT;
  636.     IF ( SPTR1^.ANATOM ) AND ( SPTR2^.ANATOM ) THEN
  637.       IF ( SPTR1^.NAME=SPTR2^.NAME ) THEN
  638.     TEMP^ := TNODE
  639.       ELSE if ( sptr1=sptr2 ) then
  640.     temp^ := tnode
  641.       else
  642.     temp^ := nilnode;
  643.     TEMP^.NEXT := NXT; EQQ := TEMP;
  644.   END{ OF EQQ };
  645.  
  646.   FUNCTION ATOM(SPTR: SYMBEXPPTR): SYMBEXPPTR;
  647.   VAR    TEMP, NXT: SYMBEXPPTR;
  648.   BEGIN
  649.     POP(TEMP);
  650.     NXT := TEMP^.NEXT;
  651.     IF ( SPTR^.ANATOM ) THEN
  652.       TEMP^ := TNODE
  653.     ELSE
  654.       TEMP^ := NILNODE;
  655.     TEMP^.NEXT := NXT; ATOM := TEMP;
  656.   END{ OF ATOM };
  657.  
  658.   FUNCTION LOOKUP(KEY, ALIST: SYMBEXPPTR): SYMBEXPPTR;
  659.   VAR
  660.     TEMP: SYMBEXPPTR;
  661.   BEGIN
  662.     TEMP := EQQ( HEAD( HEAD(ALIST)), KEY);
  663.     IF ( TEMP^.NAME='T         ' ) THEN
  664.       LOOKUP := TAIL(HEAD(ALIST))
  665.     ELSE
  666.       LOOKUP := LOOKUP(KEY, TAIL(ALIST))
  667.   END{ OF LOOKUP };
  668.  
  669.   FUNCTION BINDARGS(NAMES, VALUES: SYMBEXPPTR): SYMBEXPPTR;
  670.   VAR
  671.     TEMP, TEMP2: SYMBEXPPTR;
  672.   BEGIN
  673.     IF ( NAMES^.ANATOM ) AND (NAMES^.NAME='NIL       ') THEN
  674.       BINDARGS := ALIST
  675.     ELSE BEGIN
  676.     TEMP := CONS( HEAD(NAMES), EVAL(HEAD(VALUES), ALIST) );
  677.     TEMP2 := BINDARGS(TAIL(NAMES), TAIL(VALUES));
  678.     BINDARGS := CONS(TEMP, TEMP2)
  679.     END
  680.   END{ OF BINDARGS };
  681.  
  682.   FUNCTION EVCON(CONDPAIRS: SYMBEXPPTR): SYMBEXPPTR;
  683.   VAR
  684.     TEMP: SYMBEXPPTR;
  685.   BEGIN
  686.     TEMP := EVAL( HEAD(HEAD(CONDPAIRS)),ALIST );
  687.     IF ( TEMP^.ANATOM ) AND (TEMP^.NAME='NIL       ') THEN
  688.       EVCON := EVCON( TAIL(CONDPAIRS) )
  689.     ELSE
  690.       EVCON := EVAL( HEAD(TAIL(HEAD(CONDPAIRS))),ALIST )
  691.   END{ OF EVCON };
  692.  
  693.  
  694.   BEGIN    {   * E V A L *   }
  695.     IF ( E^.ANATOM ) THEN EVAL := LOOKUP(E, ALIST)
  696.     ELSE
  697.       BEGIN
  698.     CAROFE := HEAD(E);
  699.     IF ( CAROFE^.ANATOM ) THEN
  700.        IF NOT ( CAROFE^.ISARESERVEDWORD ) THEN
  701.          EVAL := EVAL( CONS(LOOKUP(CAROFE,ALIST),TAIL(E)), ALIST )
  702.        ELSE
  703.          CASE CAROFE^.RESSYM OF
  704.  
  705.            LABELSYM, LAMBDASYM: ERROR(3);
  706.  
  707.            QUOTESYM    : EVAL := HEAD(TAIL(E));
  708.  
  709.            ATOMSYM    : EVAL := ATOM(EVAL(HEAD(TAIL(E)),ALIST));
  710.  
  711.            EQSYM    : EVAL := EQQ(EVAL(HEAD(TAIL(E)),ALIST),
  712.                       EVAL(HEAD(TAIL(TAIL(E))), ALIST));
  713.  
  714.            HEADSYM    : EVAL := HEAD(EVAL(HEAD(TAIL(E)),ALIST));
  715.  
  716.            TAILSYM    : EVAL := TAIL(EVAL(HEAD(TAIL(E)),ALIST));
  717.  
  718.            CONSSYM    : EVAL := CONS(EVAL(HEAD(TAIL(E)),ALIST),
  719.                        EVAL(HEAD(TAIL(TAIL(E))), ALIST));
  720.  
  721.            CONDSYM    : EVAL := EVCON(TAIL(E));
  722.  
  723.            CONCSYM    : {};
  724.  
  725.            APPENDSYM : EVAL := APPEND(EVAL(HEAD(TAIL(E)),ALIST),
  726.                       EVAL(HEAD(TAIL(TAIL(E))), ALIST));
  727.  
  728.            RELACEHSYM : EVAL := REPLACEH(EVAL(HEAD(TAIL(E)),ALIST),
  729.                        EVAL(HEAD(TAIL(TAIL(E))), ALIST));
  730.  
  731.            RELACETSYM : EVAL := REPLACET(EVAL(HEAD(TAIL(E)),ALIST),
  732.                        EVAL(HEAD(TAIL(TAIL(E))), ALIST));
  733.          END{CASE}
  734.          ELSE
  735.        BEGIN
  736.          CAAROFE := HEAD(CAROFE);
  737.          IF ( CAAROFE^.ANATOM ) AND ( CAAROFE^.ISARESERVEDWORD ) THEN
  738.            IF NOT ( CAAROFE^.RESSYM IN [LABELSYM, LAMBDASYM] ) THEN
  739.          ERROR(12)
  740.            ELSE
  741.          CASE CAAROFE^.RESSYM OF
  742.            LABELSYM:
  743.               BEGIN
  744.             TEMP := CONS( CONS(HEAD(TAIL(CAROFE)),
  745.                          HEAD(TAIL(TAIL(CAROFE)))), ALIST);
  746.             EVAL := EVAL(CONS(HEAD(TAIL(TAIL(CAROFE))),
  747.                        TAIL(E)),TEMP)
  748.               END;
  749.            LAMBDASYM:
  750.               BEGIN
  751.             TEMP := BINDARGS(HEAD(TAIL(CAROFE)), TAIL(E));
  752.             EVAL := EVAL( HEAD( TAIL( TAIL(CAROFE))), TEMP)
  753.               END
  754.          END{ CASE }
  755.          ELSE
  756.            EVAL := EVAL(CONS(EVAL(CAROFE, ALIST), TAIL(E)), ALIST)
  757.           END   
  758.       END
  759. END{ OF EVAL };
  760.  
  761. PROCEDURE INITIALIZE;
  762. VAR    I: INTEGER;
  763.     TEMP, NXT: SYMBEXPPTR;
  764. BEGIN
  765.   ALREADYPEEKED := FALSE;
  766.   READ(CH);
  767.   NUMBEROFGCS := 0;
  768.   FREENODES := MAXNODE;
  769.   WITH NILNODE DO BEGIN
  770.     ANATOM := TRUE; NEXT := NIL; NAME := 'NIL       ';
  771.     STATUS := UNMARKED; ISARESERVEDWORD := FALSE
  772.   END;
  773.  
  774.   WITH TNODE DO BEGIN
  775.     ANATOM := TRUE; NEXT := NIL; NAME := 'T         ';
  776.     STATUS := UNMARKED; ISARESERVEDWORD := FALSE
  777.   END;
  778. {
  779.     ALLOCATE STORAGE AND MARK IT FREE
  780. }
  781.   FREELIST := NIL;
  782.   FOR I:=1 TO MAXNODE DO BEGIN
  783.     NEW(NODELIST); NODELIST^.NEXT := FREELIST;
  784.     NODELIST^.HEAD := FREELIST; NODELIST^.STATUS := UNMARKED;
  785.     FREELIST := NODELIST
  786.   END;
  787. {
  788.     INITIALIZE RESERVED WORD TABLE
  789. }
  790.   RESWORDS[ APPENDSYM   ] := 'APPEND    ';
  791.   RESWORDS[ ATOMSYM     ] := 'ATOM      ';
  792.   RESWORDS[ HEADSYM     ] := 'CAR       ';
  793.   RESWORDS[ TAILSYM     ] := 'CDR       ';
  794.   RESWORDS[ CONDSYM     ] := 'COND      ';
  795.   RESWORDS[ COPYSYM     ] := 'COPY      ';
  796.   RESWORDS[ CONCSYM     ] := 'CONC      ';
  797.   RESWORDS[ CONSSYM     ] := 'CONS      ';
  798.   RESWORDS[ EQSYM       ] := 'EQ        ';
  799.   RESWORDS[ LABELSYM    ] := 'LABEL     ';
  800.   RESWORDS[ LAMBDASYM   ] := 'LAMBDA    ';
  801.   RESWORDS[ QUOTESYM    ] := 'QUOTE     ';
  802.   RESWORDS[ RELACEHSYM  ] := 'REPLACEH  ';
  803.   RESWORDS[ RELACETSYM  ] := 'REPLACET  ';
  804. {
  805.     INITIALIZE THE A-LIST WITH  T  AND  NIL
  806. }
  807.   POP(ALIST);
  808.   ALIST^.ANATOM := FALSE;
  809.   ALIST^.STATUS := UNMARKED;
  810.   POP(ALIST^.TAIL);
  811.   NXT := ALIST^.TAIL^.NEXT;
  812.   ALIST^.TAIL^ := NILNODE;
  813.   ALIST^.TAIL^.NEXT := NXT;
  814.   POP(ALIST^.HEAD);
  815. {
  816.     BIND NIL TO THE ATOM NIL
  817. }
  818.   WITH ALIST^.HEAD^ DO BEGIN
  819.     ANATOM := FALSE; STATUS := UNMARKED; POP(HEAD);
  820.     NXT := HEAD^.NEXT; HEAD^ := NILNODE; HEAD^.NEXT := NXT;
  821.     POP(TAIL); NXT := TAIL^.NEXT; TAIL^ := NILNODE;
  822.     TAIL^.NEXT := NXT
  823.   END;
  824.   POP(TEMP);
  825.   TEMP^.ANATOM := FALSE;
  826.   TEMP^.STATUS := UNMARKED;
  827.   TEMP^.TAIL := ALIST;
  828.   ALIST := TEMP;
  829.   POP(ALIST^.HEAD);
  830. {
  831.     BIND  T  TO THE ATOM  T
  832. }
  833.   WITH ALIST^.HEAD^ DO BEGIN
  834.     ANATOM := FALSE;  STATUS := UNMARKED; POP(HEAD);
  835.     NXT := HEAD^.NEXT; HEAD^ := TNODE; HEAD^.NEXT := NXT;
  836.     POP(TAIL); NXT := TAIL^.NEXT; TAIL^ := TNODE;
  837.     TAIL^.NEXT := NXT
  838.   END
  839. END{ OF INITIALIZE };
  840.  
  841.  
  842.  
  843. BEGIN{+        LISP MAIN PROGRAM        +}
  844.   WRITELN(' * EVAL *');
  845.   INITIALIZE;
  846.   NEXTSYM;
  847.   READEXPR(PTR);
  848. {}READLN(DUMMY);
  849.   WRITELN;
  850.   WHILE NOT ( PTR^.ANATOM ) OR ( PTR^.NAME<>'FIN       ' ) DO BEGIN
  851.     WRITELN;
  852.     WRITELN(' * VALUE *');
  853.     PRINTEXPR( EVAL(PTR, ALIST) );
  854. 1:  WRITELN;
  855.     WRITELN;
  856.     IF ( EOF(INPUT) ) THEN ERROR(11);
  857.     PTR := NIL;
  858.     { CALL THE } GARBAGEMAN;
  859.     WRITELN; WRITELN;
  860.     WRITELN(' * EVAL *');
  861.     NEXTSYM;
  862.     READEXPR(PTR);
  863. {}  READLN(DUMMY);
  864.     WRITELN;
  865.   END;
  866. 2:WRITELN; WRITELN;
  867.   WRITELN(' TOTAL NUMBER OF GARBAGE COLLECTIONS = ', NUMBEROFGCS:1,'.');
  868.   WRITELN;
  869.   WRITELN(' FREE NODES LEFT UPON EXIT = ', FREENODES:1, '.');
  870.   WRITELN
  871. END { OF LISP }.
  872.