home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d524 / kamin.lha / Kamin / P-Distr.lzh / sasl.p < prev    next >
Text File  |  1989-06-27  |  31KB  |  1,052 lines

  1. (*****************************************************************
  2.  *                     DECLARATIONS                              *
  3.  *****************************************************************)
  4. program sasl (input, output);
  5.  
  6. label 99;
  7.  
  8. const
  9.    NAMELENG = 20;      (* Maximum length of a name *)
  10.    MAXNAMES = 300;     (* Maximum number of different names *)
  11.    MAXINPUT = 4000;    (* Maximum length of an input *)
  12.    PROMPT = '-> ';
  13.    PROMPT2 = '> ';
  14.    COMMENTCHAR = ';';
  15.    TABCODE = 9;        (* in ASCII *)
  16.  
  17. type
  18.    NAMESIZE = 0..NAMELENG;
  19.    NAMESTRING = packed array [1..NAMELENG] of char;
  20.  
  21.    NAME = 1 .. MAXNAMES; (* a NAME is an index in printNames *)
  22.  
  23.    BUILTINOP = (IFOP,PLUSOP,MINUSOP,
  24.                 TIMESOP,DIVOP,EQOP,LTOP,GTOP,CONSOP,
  25.                 CAROP,CDROP,NUMBERPOP,SYMBOLPOP,LISTPOP,
  26.                 NULLPOP,PRIMOPPOP,CLOSUREPOP);
  27.    VALUEOP = PLUSOP .. CLOSUREPOP;
  28.    CONTROLOP = IFOP .. IFOP;      
  29.  
  30.    SEXP = ^SEXPREC;
  31.    EXP = ^EXPREC;
  32.    EXPLIST = ^EXPLISTREC;
  33.    ENV = ^ENVREC;
  34.    VALUELIST = ^VALUELISTREC;
  35.    NAMELIST = ^NAMELISTREC;
  36.  
  37.    SEXPTYPE = (NILSXP,NUMSXP,SYMSXP,LISTSXP,
  38.                CLOSXP,PRIMSXP,THUNK);
  39.    SEXPREC = record
  40.                case sxptype: SEXPTYPE of
  41.                   NILSXP: ();
  42.                   NUMSXP: (intval: integer);
  43.                   SYMSXP: (symval: NAME);
  44.                   LISTSXP: (carval, cdrval: SEXP);
  45.                   CLOSXP, THUNK: (clofun: EXP; cloenv: ENV);
  46.                   PRIMSXP: (primval: BUILTINOP)
  47.             end;
  48.  
  49.    EXPTYPE = (VALEXP,VAREXP,APEXP,LAMEXP);
  50.    EXPREC = record
  51.                case etype: EXPTYPE of
  52.                   VALEXP: (sxp: SEXP);
  53.                   VAREXP: (varble: NAME);
  54.                   APEXP: (optr: EXP; args: EXPLIST);
  55.                   LAMEXP: (formals: NAMELIST; lambdabody: EXP)
  56.             end;
  57.  
  58.    EXPLISTREC = record
  59.                head: EXP;
  60.                tail: EXPLIST
  61.             end;
  62.  
  63.    VALUELISTREC = record
  64.                head: SEXP;
  65.                tail: VALUELIST
  66.             end;
  67.  
  68.    NAMELISTREC = record
  69.                head: NAME;
  70.                tail: NAMELIST
  71.             end;
  72.  
  73.    ENVREC = record
  74.                vars: NAMELIST;
  75.                values: VALUELIST;
  76.                enclosing: ENV
  77.             end;
  78.  
  79. var
  80.    globalEnv: ENV;
  81.    
  82.    currentExp: EXP;
  83.    
  84.    userinput: array [1..MAXINPUT] of char;
  85.    inputleng, pos: 0..MAXINPUT;
  86.    
  87.    printNames: array [NAME] of NAMESTRING;
  88.    numNames, numBuiltins: NAME;
  89.  
  90.    setName: NAME;
  91.    setVal: SEXP;
  92.  
  93.    nilValue, trueValue: SEXP;
  94.  
  95.    quittingtime: Boolean;
  96.  
  97. (*****************************************************************
  98.  *                     DATA STRUCTURE OP'S                       *
  99.  *****************************************************************)
  100.  
  101. (* mkVALEXP - return an EXP of type VALEXP with sxp s            *)
  102. function mkVALEXP (s: SEXP): EXP;
  103. var e: EXP;
  104. begin
  105.    new(e);
  106.    e^.etype := VALEXP;
  107.    e^.sxp := s;
  108.    mkVALEXP := e
  109. end; (* mkVALEXP *)
  110.  
  111. (* mkVAREXP - return an EXP of type VAREXP with varble nm        *)
  112. function mkVAREXP (nm: NAME): EXP;
  113. var e: EXP;
  114. begin
  115.    new(e);
  116.    e^.etype := VAREXP;
  117.    e^.varble := nm;
  118.    mkVAREXP := e
  119. end; (* mkVAREXP *)
  120.  
  121. (* mkAPEXP - return EXP of type APEXP w/ optr op and args el     *)
  122. function mkAPEXP (op: EXP; el: EXPLIST): EXP;
  123. var e: EXP;
  124. begin
  125.    new(e);
  126.    e^.etype := APEXP;
  127.    e^.optr := op;
  128.    e^.args := el;
  129.    mkAPEXP := e
  130. end; (* mkAPEXP *)
  131.  
  132. (* mkLAMEXP - return EXP of type LAMEXP w/ formals f and body b  *)
  133. function mkLAMEXP (f: NAMELIST; b: EXP): EXP;
  134. var e: EXP;
  135. begin
  136.    new(e);
  137.    e^.etype := LAMEXP;
  138.    e^.formals := f;
  139.    e^.lambdabody := b;
  140.    mkLAMEXP := e
  141. end; (* mkLAMEXP *)
  142.  
  143. (* mkSExp - return SEXP of type t (but no value)                 *)
  144. function mkSExp (t: SEXPTYPE): SEXP;
  145. var s: SEXP;
  146. begin
  147.    new(s);
  148.    s^.sxptype := t;
  149.    mkSExp := s
  150. end; (* mkSExp *)
  151.  
  152. (* mkPRIMSXP - return SEXP of type PRIMSXP w/ value op           *)
  153. function mkPRIMSXP (op: BUILTINOP): SEXP;
  154. var result: SEXP;
  155. begin
  156.    new(result);
  157.    result^.sxptype := PRIMSXP;
  158.    result^.primval := op;
  159.    mkPRIMSXP := result
  160. end; (* mkPRIMSXP *)
  161.  
  162. (* mkCLOSXP - return SEXP of type CLOSXP w/ expr e and env rho   *)
  163. function mkCLOSXP (e: EXP; rho: ENV): SEXP;
  164. var result: SEXP;
  165. begin
  166.    new(result);
  167.    result^.sxptype := CLOSXP;
  168.    result^.clofun := e;
  169.    result^.cloenv := rho;
  170.    mkCLOSXP := result
  171. end; (* mkCLOSXP *)
  172.  
  173. (* mkTHUNK - return SEXP of type THUNK w/ expr e and env rho     *)
  174. function mkTHUNK (e: EXP; rho: ENV): SEXP;
  175. var result: SEXP;
  176. begin
  177.    new(result);
  178.    result^.sxptype := THUNK;
  179.    result^.clofun := e;
  180.    result^.cloenv := rho;
  181.    mkTHUNK := result
  182. end; (* mkTHUNK *)
  183.  
  184. (* mkExplist - return an EXPLIST with head e and tail el         *)
  185. function mkExplist (e: EXP; el: EXPLIST): EXPLIST;
  186. var newel: EXPLIST;
  187. begin
  188.    new(newel);
  189.    newel^.head := e;
  190.    newel^.tail := el;
  191.    mkExplist := newel
  192. end; (* mkExplist *)
  193.  
  194. (* mkNamelist - return a NAMELIST with head n and tail nl        *)
  195. function mkNamelist (nm: NAME; nl: NAMELIST): NAMELIST;
  196. var newnl: NAMELIST;
  197. begin
  198.    new(newnl);
  199.    newnl^.head := nm;
  200.    newnl^.tail := nl;
  201.    mkNamelist := newnl
  202. end; (* mkNamelist *)
  203.  
  204. (* mkValuelist - return an VALUELIST with head s and tail vl     *)
  205. function mkValuelist (s: SEXP; vl: VALUELIST): VALUELIST;
  206. var newvl: VALUELIST;
  207. begin
  208.    new(newvl);
  209.    newvl^.head := s;
  210.    newvl^.tail := vl;
  211.    mkValuelist := newvl
  212. end; (* mkValuelist *)
  213.  
  214. (* mkEnv - return an ENV with vars nl, value vl, enclosing rho   *)
  215. function mkEnv (nl: NAMELIST; vl: VALUELIST; rho: ENV): ENV;
  216. var newrho: ENV;
  217. begin
  218.    new(newrho);
  219.    newrho^.vars := nl;
  220.    newrho^.values := vl;
  221.    newrho^.enclosing := rho;
  222.    mkEnv := newrho
  223. end; (* mkEnv *)
  224.  
  225. (* lengthVL - return length of VALUELIST vl                      *)
  226. function lengthVL (vl: VALUELIST): integer;
  227. var i: integer;
  228. begin
  229.    i := 0;
  230.    while vl <> nil do begin
  231.       i := i+1;
  232.       vl := vl^.tail
  233.       end;
  234.    lengthVL := i
  235. end; (* lengthVL *)
  236.  
  237. (* lengthNL - return length of NAMELIST nl                       *)
  238. function lengthNL (nl: NAMELIST): integer;
  239. var i: integer;
  240. begin
  241.    i := 0;
  242.    while nl <> nil do begin
  243.       i := i+1;
  244.       nl := nl^.tail
  245.       end;
  246.    lengthNL := i
  247. end; (* lengthNL *)
  248.  
  249. (*****************************************************************
  250.  *                     NAME MANAGEMENT                           *
  251.  *****************************************************************)
  252.  
  253. (* initNames - place all pre-defined names into printNames       *)
  254. procedure initNames;
  255. var i: integer;
  256. begin
  257.    i := 1;
  258.    printNames[i] := 'if                  '; i := i+1;
  259.    printNames[i] := '+                   '; i := i+1;
  260.    printNames[i] := '-                   '; i := i+1;
  261.    printNames[i] := '*                   '; i := i+1;
  262.    printNames[i] := '/                   '; i := i+1;
  263.    printNames[i] := '=                   '; i := i+1;
  264.    printNames[i] := '<                   '; i := i+1;
  265.    printNames[i] := '>                   '; i := i+1;
  266.    printNames[i] := 'cons                '; i := i+1;
  267.    printNames[i] := 'car                 '; i := i+1;
  268.    printNames[i] := 'cdr                 '; i := i+1;
  269.    printNames[i] := 'number?             '; i := i+1;
  270.    printNames[i] := 'symbol?             '; i := i+1;
  271.    printNames[i] := 'list?               '; i := i+1;
  272.    printNames[i] := 'null?               '; i := i+1;
  273.    printNames[i] := 'primop?             '; i := i+1;
  274.    printNames[i] := 'closure?            '; i := i+1;
  275.    printNames[i] := 'T                   ';
  276.    numNames := i;
  277.    numBuiltins := i
  278. end; (* initNames *)
  279.  
  280. (* install - insert new name into printNames                     *)
  281. function install (nm: NAMESTRING): NAME;
  282. var
  283.    i: integer;
  284.    found: Boolean;
  285. begin
  286.    i := 1; found := false;
  287.    while (i <= numNames) and not found
  288.    do if nm = printNames[i]
  289.       then found := true
  290.       else i := i+1;
  291.    if not found
  292.    then begin
  293.            if i > MAXNAMES
  294.            then begin
  295.                    writeln('No more room for names');
  296.                    goto 99
  297.                 end;
  298.            numNames := i;
  299.            printNames[i] := nm
  300.         end;
  301.    install := i
  302. end; (* install *)
  303.  
  304. (* prName - print name nm                                        *)
  305. procedure prName (nm: NAME);
  306. var i: integer;
  307. begin
  308.    i := 1;
  309.    while i <= NAMELENG
  310.    do if printNames[nm][i] <> ' '
  311.       then begin
  312.               write(printNames[nm][i]);
  313.               i := i+1
  314.            end
  315.       else i := NAMELENG+1
  316. end; (* prName *)
  317.  
  318. (*****************************************************************
  319.  *                        INPUT                                  *
  320.  *****************************************************************)
  321.  
  322. (* isDelim - check if c is a delimiter                           *)
  323. function isDelim (c: char): Boolean;
  324. begin
  325.    isDelim := c in ['(', ')', ' ', COMMENTCHAR]
  326. end; (* isDelim *)
  327.  
  328. (* skipblanks - return next non-blank position in userinput      *)
  329. function skipblanks (p: integer): integer;
  330. begin
  331.    while userinput[p] = ' ' do p := p+1;
  332.    skipblanks := p
  333. end; (* skipblanks *)
  334.  
  335. (* matches - check if string nm matches userinput[s .. s+leng]   *)
  336. function matches (s: integer; leng: NAMESIZE;
  337.                    nm: NAMESTRING): Boolean;
  338. var
  339.    match: Boolean;
  340.    i: integer;
  341. begin
  342.    match := true; i := 1;
  343.    while match and (i <= leng) do begin
  344.       if userinput[s] <> nm[i] then match := false;
  345.       i := i+1;
  346.       s := s+1
  347.       end;
  348.    if not isDelim(userinput[s]) then match := false;
  349.    matches := match
  350. end; (* matches *)
  351.  
  352. (* reader - read char's into userinput; be sure input not blank  *)
  353. procedure reader;
  354.  
  355. (* readInput - read char's into userinput                        *)
  356.    procedure readInput;
  357.  
  358.    var c: char;
  359.  
  360. (* nextchar - read next char - filter tabs and comments          *)
  361.       procedure nextchar (var c: char);
  362.       begin
  363.          read(c);
  364.          if c = chr(TABCODE)
  365.          then c := ' '
  366.          else if c = COMMENTCHAR
  367.               then begin while not eoln do read(c); c := ' ' end
  368.       end; (* nextchar *)
  369.  
  370. (* readParens - read char's, ignoring newlines, to matching ')'  *)
  371.       procedure readParens;
  372.       var
  373.          parencnt: integer; (* current depth of parentheses *)
  374.          c: char;
  375.       begin
  376.          parencnt := 1; (* '(' just read *)
  377.          repeat
  378.             if eoln then write(PROMPT2);
  379.             nextchar(c);
  380.             pos := pos+1;
  381.             if pos = MAXINPUT
  382.             then begin
  383.                     writeln('User input too long');
  384.                     goto 99
  385.                  end;
  386.             userinput[pos] := c;
  387.             if c = '(' then parencnt := parencnt+1;
  388.             if c = ')' then parencnt := parencnt-1
  389.          until parencnt = 0
  390.       end; (* readParens *)
  391.  
  392.    begin (* readInput *)
  393.       write(PROMPT);
  394.       pos := 0;
  395.       repeat
  396.          pos := pos+1;
  397.          if pos = MAXINPUT
  398.          then begin
  399.                  writeln('User input too long');
  400.                  goto 99
  401.               end;
  402.          nextchar(c);
  403.          userinput[pos] := c;
  404.          if userinput[pos] = '(' then readParens
  405.       until eoln;
  406.       inputleng := pos;
  407.       userinput[pos+1] := COMMENTCHAR (* sentinel *)
  408.    end; (* readInput *)
  409.  
  410. begin (* reader *)
  411.     repeat
  412.        readInput;
  413.        pos := skipblanks(1);
  414.     until pos <= inputleng (* ignore blank lines *)
  415. end; (* reader *)
  416.  
  417. (* parseName - return (installed) NAME starting at userinput[pos]*)
  418. function parseName: NAME;
  419. var
  420.    nm: NAMESTRING; (* array to accumulate characters *)
  421.    leng: NAMESIZE; (* length of name *)
  422. begin
  423.    leng := 0;
  424.    while (pos <= inputleng) and not isDelim(userinput[pos])
  425.    do begin
  426.          if leng = NAMELENG
  427.          then begin
  428.                  writeln('Name too long, begins: ', nm);
  429.                  goto 99
  430.               end;
  431.          leng := leng+1;
  432.          nm[leng] := userinput[pos];
  433.          pos := pos+1
  434.       end;
  435.    if leng = 0
  436.    then begin
  437.            writeln('Error: expected name, instead read: ',
  438.                    userinput[pos]);
  439.            goto 99
  440.         end;
  441.    for leng := leng+1 to NAMELENG do nm[leng] := ' ';
  442.    pos := skipblanks(pos); (* skip blanks after name *)
  443.    parseName := install(nm)
  444. end; (* parseName *)
  445.  
  446. (* isNumber - check if a number begins at pos                    *)
  447. function isNumber (pos: integer): Boolean;
  448.  
  449. (* isDigits - check if sequence of digits begins at pos          *)
  450.    function isDigits (pos: integer): Boolean;
  451.    begin
  452.       if not (userinput[pos] in ['0'..'9'])
  453.       then isDigits := false
  454.       else begin
  455.               isDigits := true;
  456.               while userinput[pos] in ['0'..'9'] do pos := pos+1;
  457.               if not isDelim(userinput[pos])
  458.               then isDigits := false
  459.            end
  460.    end; (* isDigits *)
  461.  
  462. begin (* isNumber *)
  463.    isNumber := isDigits(pos) or
  464.               ((userinput[pos] = '-') and isDigits(pos+1))
  465. end; (* isNumber *)
  466.  
  467. (* isValue - check if a number or quoted const begins at pos     *)
  468. function isValue (pos: integer): Boolean;
  469. begin
  470.    isValue:= (userinput[pos] = '''') or isNumber(pos)
  471. end; (* isValue *)
  472.  
  473. (* parseVal - return S-expression starting at userinput[pos]     *)
  474. function parseVal: SEXP;
  475.  
  476. (* parseSExp - return quoted S-expr starting at userinput[pos]   *)
  477.    function parseSExp: SEXP;
  478.  
  479.    var s: SEXP;
  480.  
  481. (* parseInt - return number starting at userinput[pos]           *)
  482.       function parseInt: SEXP;
  483.       var sum, sign: integer;
  484.       begin
  485.          s := mkSExp(NUMSXP);
  486.          sum := 0; sign := 1;
  487.          if userinput[pos] = '-'
  488.          then begin
  489.                  sign := -1;
  490.                  pos := pos+1
  491.               end;
  492.          while userinput[pos] in ['0'..'9'] do begin
  493.             sum := 10*sum + (ord(userinput[pos]) - ord('0'));
  494.             pos := pos+1
  495.             end;
  496.          s^.intval := sum * sign;
  497.          pos := skipblanks(pos); (* skip blanks after number *)
  498.          parseInt := s
  499.       end; (* parseInt *)
  500.  
  501. (* parseSym - return symbol starting at userinput[pos]           *)
  502.       function parseSym: SEXP;
  503.       begin
  504.          s := mkSExp(SYMSXP);
  505.          s^.symval := parseName;
  506.          parseSym := s
  507.       end; (* parseSym *)
  508.  
  509. (* parseList - return list starting at userinput[pos]            *)
  510.       function parseList: SEXP;
  511.       var car, cdr: SEXP;
  512.       begin
  513.          if userinput[pos] = ')'
  514.          then begin
  515.                  parseList := mkSExp(NILSXP);
  516.                  pos := skipblanks(pos+1)
  517.               end
  518.          else begin
  519.                  car := parseSExp;
  520.                  cdr := parseList;
  521.                  s := mkSExp(LISTSXP);
  522.                  s^.carval := car;
  523.                  s^.cdrval := cdr;
  524.                  parseList := s
  525.               end
  526.       end; (* parseList *)
  527.  
  528.    begin (* parseSExp *)
  529.       if isNumber(pos)
  530.       then parseSExp := parseInt
  531.       else if userinput[pos] = '('
  532.            then begin
  533.                    pos := skipblanks(pos+1);
  534.                    parseSExp := parseList
  535.                 end
  536.            else parseSExp := parseSym
  537.    end; (* parseSExp *)
  538.  
  539. begin (* parseVal *)
  540.    if userinput[pos] = '''' then pos := pos+1;
  541.    parseVal := parseSExp
  542. end; (* parseVal *)
  543.  
  544. function parseEL: EXPLIST; forward;
  545. function parseNL: NAMELIST; forward;
  546.  
  547. (* parseExp - return EXP starting at userinput[pos]              *)
  548. function parseExp: EXP;
  549. var
  550.    op, body: EXP;
  551.    nl: NAMELIST;
  552.    el: EXPLIST;
  553. begin
  554.    if userinput[pos] = '('
  555.    then begin
  556.            pos := skipblanks(pos+1); (* skip '( ..' *)
  557.            if matches(pos, 6, 'lambda              ')
  558.            then begin   (* LAMEXP *)
  559.                    pos := skipblanks(pos+6);  (* skip 'lambda ..' *)
  560.                    pos := skipblanks(pos+1); (* skip '( ..' *)
  561.                    nl := parseNL;
  562.                    body := parseExp;
  563.                    pos := skipblanks(pos+1); (* skip ') ..' *)
  564.                    parseExp := mkLAMEXP(nl, body)         
  565.                 end
  566.            else begin   (* APEXP *)
  567.                    op := parseExp;
  568.                    el := parseEL;
  569.                    parseExp := mkAPEXP(op, el)
  570.                 end
  571.         end
  572.    else if isValue(pos)
  573.         then parseExp := mkVALEXP(parseVal)   (* VALEXP *)
  574.         else parseExp := mkVAREXP(parseName)  (* VAREXP *)
  575. end; (* parseExp *)
  576.  
  577. (* parseEL - return EXPLIST starting at userinput[pos]           *)
  578. function parseEL;
  579. var
  580.    e: EXP;
  581.    el: EXPLIST;
  582. begin
  583.    if userinput[pos] = ')'
  584.    then begin
  585.            pos := skipblanks(pos+1); (* skip ') ..' *)
  586.            parseEL := nil
  587.         end
  588.    else begin
  589.            e := parseExp;
  590.            el := parseEL;
  591.            parseEL := mkExplist(e, el)
  592.         end
  593. end; (* parseEL *)
  594.  
  595. (* parseNL - return NAMELIST starting at userinput[pos]          *)
  596. function parseNL;
  597. var
  598.    nm: NAME;
  599.    nl: NAMELIST;
  600. begin
  601.    if userinput[pos] = ')'
  602.    then begin
  603.            pos := skipblanks(pos+1); (* skip ') ..' *)
  604.            parseNL := nil
  605.         end
  606.    else begin
  607.            nm := parseName;
  608.            nl := parseNL;
  609.            parseNL := mkNamelist(nm, nl)
  610.         end
  611. end; (* parseNL *)
  612.  
  613. (* parseSet - read top-level definition                          *)
  614. function parseSet: EXP;
  615. var e: EXP;
  616. begin
  617.    pos := skipblanks(pos+1); (* skip '( ..' *)
  618.    pos := skipblanks(pos+3); (* skip 'set ..' *)
  619.    setName := parseName;
  620.    e := parseExp;
  621.    pos := skipblanks(pos+1); (* skip ') ..' *)
  622.    parseSet := e
  623. end; (* parseSet *)
  624.  
  625. (*****************************************************************
  626.  *                     ENVIRONMENTS                              *
  627.  *****************************************************************)
  628.  
  629. (* emptyEnv - return an environment with no bindings             *)
  630. function emptyEnv: ENV;
  631. begin
  632.    emptyEnv := mkEnv(nil, nil, nil)
  633. end; (* emptyEnv *)
  634.  
  635. (* bindVar - bind variable nm to value s in environment rho      *)
  636. procedure bindVar (nm: NAME; s: SEXP; rho: ENV);
  637. begin
  638.    rho^.vars := mkNamelist(nm, rho^.vars);
  639.    rho^.values := mkValuelist(s, rho^.values)
  640. end; (* bindVar *)
  641.  
  642. (*  extendEnv - extend environment rho by binding vars to vals   *)
  643. function extendEnv (rho: ENV;
  644.                    vars: NAMELIST;
  645.                    vals: VALUELIST): ENV;
  646. begin
  647.    extendEnv := mkEnv(vars, vals, rho)
  648. end; (* extendEnv *)
  649.  
  650. (* findVar - look up nm in rho                                   *)
  651. function findVar (nm: NAME; rho: ENV): VALUELIST;
  652.  
  653. var vl: VALUELIST;
  654.  
  655. (* findVarInFrame - look up nm in one frame                      *)
  656.    function findVarInFrame (nl: NAMELIST;
  657.                             vl: VALUELIST): VALUELIST;
  658.    var found: Boolean;
  659.    begin
  660.       found := false;
  661.       while (nl <> nil) and not found do
  662.          if nl^.head = nm
  663.          then found := true
  664.          else begin
  665.                  nl := nl^.tail;
  666.                  vl := vl^.tail
  667.               end; (* while *)
  668.       findVarInFrame := vl
  669.    end; (* findVarInFrame *)
  670.  
  671. begin (* findVar *)
  672.    repeat
  673.       vl := findVarInFrame(rho^.vars, rho^.values);
  674.       rho := rho^.enclosing
  675.    until (vl <> nil) or (rho = nil);
  676.    findVar := vl
  677. end; (* findVar *)
  678.  
  679. (* assign - assign value s to variable nm in rho                 *)
  680. procedure assign (nm: NAME; s: SEXP; rho: ENV);
  681. var varloc: VALUELIST;
  682. begin
  683.    varloc := findVar(nm, rho);
  684.    varloc^.head := s
  685. end; (* assign *)
  686.  
  687. (* fetch - return SEXP bound to nm in rho                        *)
  688. function fetch (nm: NAME; rho: ENV): SEXP;
  689. var vl: VALUELIST;
  690. begin
  691.    vl := findVar(nm, rho);
  692.    fetch := vl^.head
  693. end; (* fetch *)
  694.  
  695. (* isBound - check if nm is bound in rho                         *)
  696. function isBound (nm: NAME; rho: ENV): Boolean;
  697. begin
  698.    isBound := findVar(nm, rho) <> nil
  699. end; (* isBound *)
  700.  
  701. (*****************************************************************
  702.  *                     S-EXPRESSIONS                             *
  703.  *****************************************************************)
  704.  
  705. (* prValue - print S-expression s                                *)
  706. procedure prValue (s: SEXP);
  707. var s1: SEXP;
  708. begin
  709.    with s^ do
  710.       case sxptype of
  711.          NILSXP: write('()');
  712.          NUMSXP: write(intval:1);
  713.          SYMSXP: prName(symval);
  714.          PRIMSXP:
  715.             begin
  716.                write('<primitive: ');
  717.                prName(ord(primval)+1);
  718.                write('>')
  719.             end;
  720.          CLOSXP: write('<closure>');
  721.          THUNK: write('...');
  722.          LISTSXP:
  723.             begin
  724.                write('(');
  725.                prValue(carval);
  726.                s1 := cdrval;
  727.                while s1^.sxptype = LISTSXP do begin
  728.                   write(' ');
  729.                   prValue(s1^.carval);
  730.                   s1 := s1^.cdrval
  731.                   end;
  732.                if s1^.sxptype = THUNK
  733.                then write(' ...)')
  734.                else write(')')
  735.             end
  736.       end (* case and with *)
  737. end; (* prValue *)
  738.  
  739. (* isTrueVal - return true if s is true (non-NIL) value          *)
  740. function isTrueVal (s: SEXP): Boolean;
  741. begin
  742.    isTrueVal := s^.sxptype <> NILSXP
  743. end; (* isTrueVal *)
  744.  
  745. procedure evalThunk (s: SEXP); forward;
  746.  
  747. (* applyValueOp - apply VALUEOP op to arguments in VALUELIST vl  *)
  748. function applyValueOp (op: VALUEOP; vl: VALUELIST): SEXP;
  749.  
  750. var
  751.    result: SEXP;
  752.    s1, s2: SEXP;
  753.  
  754. (* applyArithOp - apply binary, arithmetic VALUEOP to arguments  *)
  755.    procedure applyArithOp (n1, n2: integer);
  756.    begin
  757.       result := mkSExp(NUMSXP);
  758.       with result^ do
  759.          case op of
  760.             PLUSOP: intval := n1+n2;
  761.             MINUSOP: intval := n1-n2;
  762.             TIMESOP: intval := n1*n2;
  763.             DIVOP: intval := n1 div n2
  764.          end
  765.    end; (* applyArithOp *)
  766.  
  767. (* applyRelOp - apply binary, relational VALUEOP to arguments    *)
  768.    procedure applyRelOp (n1, n2: integer) ;
  769.    begin
  770.       case op of
  771.          LTOP: if n1 < n2 then result := trueValue;
  772.          GTOP: if n1 > n2 then result := trueValue
  773.       end
  774.    end; (* applyRelOp *)
  775.  
  776. (* arity - return number of arguments expected by op             *)
  777.    function arity (op: VALUEOP): integer;
  778.    begin
  779.       if op in [PLUSOP .. CONSOP] then arity := 2 else arity := 1
  780.    end; (* arity *)
  781.  
  782. begin (* applyValueOp *)
  783.    if arity(op) <> lengthVL(vl)
  784.    then begin
  785.            write('Wrong number of arguments to ');
  786.            prName(ord(op)+1);
  787.            writeln;
  788.            goto 99
  789.         end;
  790.    result := nilValue;
  791.    s1 := vl^.head; (* 1st actual *)
  792.    if arity(op) = 2 then s2 := vl^.tail^.head; (* 2nd actual *)
  793.    if op <> CONSOP then evalThunk(s1);
  794.    if op in [PLUSOP .. GTOP] then evalThunk(s2);
  795.    if op in [PLUSOP .. DIVOP, LTOP .. GTOP]
  796.    then if (s1^.sxptype = NUMSXP)
  797.            and (s2^.sxptype = NUMSXP)
  798.         then if op in [PLUSOP .. DIVOP]
  799.              then applyArithOp(s1^.intval, s2^.intval)
  800.              else applyRelOp(s1^.intval, s2^.intval)
  801.         else begin
  802.                 write('Non-arithmetic arguments to ');
  803.                 prName(ord(op)+1);
  804.                 writeln;
  805.                 goto 99
  806.              end
  807.    else with s1^ do
  808.            case op of
  809.               EQOP:
  810.                  if (sxptype = NILSXP)
  811.                     and (s2^.sxptype = NILSXP)
  812.                  then result := trueValue
  813.                  else if (sxptype = NUMSXP)
  814.                          and (s2^.sxptype = NUMSXP)
  815.                          and (intval = s2^.intval)
  816.                       then result := trueValue
  817.                       else if (sxptype = SYMSXP)
  818.                               and (s2^.sxptype = SYMSXP)
  819.                               and (symval = s2^.symval)
  820.                            then result := trueValue;
  821.               CONSOP:
  822.                  begin
  823.                     result := mkSExp(LISTSXP);
  824.                     with result^ do begin
  825.                        carval := s1;
  826.                        cdrval := s2
  827.                        end
  828.                  end;
  829.               CAROP:
  830.                  if sxptype <> LISTSXP
  831.                  then begin
  832.                          write('Error: car applied to non-list: ');
  833.                          prValue(s1);
  834.                          writeln
  835.                       end
  836.                  else begin
  837.                          evalThunk(carval);
  838.                          result := carval
  839.                       end;
  840.               CDROP:
  841.                  if sxptype <> LISTSXP
  842.                  then begin
  843.                          write('Error: cdr applied to non-list: ');
  844.                          prValue(s1);
  845.                          writeln
  846.                       end
  847.                  else begin
  848.                          evalThunk(cdrval);
  849.                          result := cdrval
  850.                       end;
  851.               NUMBERPOP:
  852.                  if sxptype = NUMSXP then result := trueValue;
  853.               SYMBOLPOP:
  854.                  if sxptype = SYMSXP then result := trueValue;
  855.               LISTPOP:
  856.                  if sxptype = LISTSXP then result := trueValue;
  857.               NULLPOP:
  858.                  if sxptype = NILSXP then result := trueValue;
  859.               PRIMOPPOP:
  860.                  if sxptype = PRIMSXP then result := trueValue;
  861.               CLOSUREPOP:
  862.                  if sxptype = CLOSXP then result := trueValue
  863.            end; (* case and with *)
  864.    applyValueOp := result
  865. end; (* applyValueOp *)
  866.  
  867. (*****************************************************************
  868.  *                     EVALUATION                                *
  869.  *****************************************************************)
  870.  
  871. (* eval - return value of expression e in local environment rho  *)
  872. function eval (e: EXP; rho: ENV): SEXP;
  873.  
  874. var
  875.    s: SEXP;
  876.    op: SEXP;
  877.    primname: BUILTINOP;
  878.  
  879. (* evalList - evaluate each expression in el                     *)
  880.    function evalList (el: EXPLIST): VALUELIST;
  881.    var
  882.       h: SEXP;
  883.       t: VALUELIST;
  884.    begin
  885.       if el = nil then evalList := nil
  886.       else begin
  887.               h := mkTHUNK(el^.head, rho);
  888.               t := evalList(el^.tail);
  889.               evalList := mkValuelist(h, t)
  890.            end
  891.    end; (* evalList *)
  892.  
  893. (* applyClosure - apply SEXP op of type CLOSXP to actuals        *)
  894.    function applyClosure (op: SEXP; actuals: VALUELIST): SEXP;
  895.    var
  896.       fun, body: EXP;
  897.       forms: NAMELIST;
  898.       savedrho, newrho: ENV;
  899.    begin
  900.       fun := op^.clofun;
  901.       savedrho := op^.cloenv;
  902.       forms := fun^.formals;
  903.       body := fun^.lambdabody;
  904.       if lengthNL(forms) <> lengthVL(actuals)
  905.       then begin
  906.               writeln('Wrong number of arguments to closure');
  907.               goto 99
  908.            end;
  909.       newrho := extendEnv(savedrho, forms, actuals);
  910.       applyClosure := eval(body, newrho)
  911.    end; (* applyClosure *)
  912.  
  913. (* applyCtrlOp - apply CONTROLOP op to args in rho               *)
  914.    function applyCtrlOp (op: CONTROLOP;
  915.                        args: EXPLIST): SEXP;
  916.    begin
  917.       with args^ do
  918.          case op of
  919.            IFOP:
  920.               if isTrueVal(eval(head, rho))
  921.               then applyCtrlOp := eval(tail^.head, rho)
  922.               else applyCtrlOp := eval(tail^.tail^.head, rho)
  923.          end (* case and with *)
  924.    end; (* applyCtrlOp *)
  925.  
  926. begin (* eval *)
  927.    with e^ do
  928.       case etype of
  929.          VALEXP:
  930.             eval := sxp;
  931.          VAREXP:
  932.             begin
  933.                if isBound(varble, rho)
  934.                then s := fetch(varble, rho)
  935.                else begin
  936.                        write('Undefined variable: ');
  937.                        prName(varble);
  938.                        writeln;
  939.                        goto 99
  940.                     end;
  941.                evalThunk(s);
  942.                eval := s
  943.             end;
  944.          APEXP: 
  945.             begin
  946.                op := eval(optr, rho);
  947.                if op^.sxptype = PRIMSXP
  948.                then begin
  949.                        primname := op^.primval;
  950.                        if primname = IFOP
  951.                        then eval :=
  952.                               applyCtrlOp(primname, args)
  953.                        else eval := applyValueOp(primname,
  954.                                            evalList(args))
  955.                     end
  956.                else eval :=
  957.                       applyClosure(op, evalList(args))
  958.             end;
  959.          LAMEXP:
  960.             eval := mkCLOSXP(e, rho)
  961.       end (* case and with *)
  962. end; (* eval *)
  963.  
  964. (* evalThunk - evaluate thunk and replace it by its value        *)
  965. procedure evalThunk;
  966.  
  967. var result: SEXP;
  968.  
  969. (* copyValue - copy SEXP s1 into s2                              *)
  970.    procedure copyValue (s1, s2: SEXP);
  971.    begin
  972.       with s1^ do begin
  973.          s2^.sxptype := sxptype;
  974.          case sxptype of
  975.             NILSXP: ;
  976.             NUMSXP: s2^.intval := intval;
  977.             SYMSXP: s2^.symval := symval;
  978.             PRIMSXP: s2^.primval := primval;
  979.             LISTSXP:
  980.                begin
  981.                   s2^.carval := carval;
  982.                   s2^.cdrval := cdrval
  983.                end;
  984.             CLOSXP, THUNK:
  985.                begin
  986.                   s2^.clofun := clofun;
  987.                   s2^.cloenv := cloenv
  988.                end
  989.          end (* case *)
  990.       end (* with *)
  991.    end; (* copyValue *)
  992.  
  993. begin (* evalThunk *)
  994.    with s^ do
  995.       if sxptype = THUNK
  996.       then begin
  997.               result := eval(clofun, cloenv);
  998.               copyValue(result, s)
  999.            end
  1000. end; (* evalThunk *)
  1001.  
  1002. (*****************************************************************
  1003.  *                     READ-EVAL-PRINT LOOP                      *
  1004.  *****************************************************************)
  1005.  
  1006. (* initGlobalEnv - assign primitive function values to names     *)
  1007. procedure initGlobalEnv;
  1008. var op: BUILTINOP;
  1009. begin
  1010.     globalEnv := emptyEnv;
  1011.     for op := IFOP to CLOSUREPOP do
  1012.        bindVar(ord(op)+1, mkPRIMSXP(op), globalEnv)
  1013. end; (* initGlobalEnv *)
  1014.  
  1015. begin (* sasl main *)
  1016.    initNames;
  1017.  
  1018.    nilValue := mkSExp(NILSXP);
  1019.    trueValue := mkSExp(SYMSXP); trueValue^.symval := numNames;
  1020.  
  1021.    initGlobalEnv;
  1022.  
  1023.    quittingtime := false;
  1024. 99:
  1025.    while not quittingtime do begin
  1026.       reader;
  1027.       if matches(pos, 4, 'quit                ')
  1028.       then quittingtime := true
  1029.       else if (userinput[pos]='(') and
  1030.               matches(skipblanks(pos+1), 3, 'set                 ')
  1031.            then begin
  1032.                    currentExp := parseSet;
  1033.                    setVal := eval(currentExp, globalEnv);
  1034.                    if isBound(setName, globalEnv)
  1035.                    then assign(setName, setVal, globalEnv)
  1036.                    else bindVar(setName, setVal, globalEnv);
  1037.                    prValue(setVal);
  1038.                    writeln;
  1039.                    writeln
  1040.                 end
  1041.            else begin
  1042.                    currentExp := parseExp;
  1043.                    prValue(eval(currentExp, globalEnv));
  1044.                    writeln;
  1045.                    writeln
  1046.                 end
  1047.       end (* while *)
  1048. end. (* sasl *)
  1049.    
  1050.  
  1051.  
  1052.