home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d524 / kamin.lha / Kamin / P-Distr.lzh / chap1.p < prev    next >
Text File  |  1988-11-21  |  22KB  |  796 lines

  1. (*****************************************************************
  2.  *                     DECLARATIONS                              *
  3.  *****************************************************************)
  4. program chapter1 (input, output);
  5.  
  6. label 99;
  7.  
  8. const
  9.    NAMELENG = 20;      (* Maximum length of a name *)
  10.    MAXNAMES = 100;     (* Maximum number of different names *)
  11.    MAXINPUT = 500;     (* 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.    NUMBER = integer;
  22.    NAME = 1 .. MAXNAMES; (* a NAME is an index in printNames *)
  23.       
  24.    BUILTINOP = (IFOP,WHILEOP,SETOP,BEGINOP,PLUSOP,MINUSOP,
  25.              TIMESOP,DIVOP,EQOP,LTOP,GTOP,PRINTOP);
  26.    VALUEOP = PLUSOP .. PRINTOP;
  27.    CONTROLOP = IFOP .. BEGINOP;
  28.  
  29.    EXP = ^EXPREC;
  30.    EXPLIST = ^EXPLISTREC;
  31.    ENV = ^ENVREC;
  32.    VALUELIST = ^VALUELISTREC;
  33.    NAMELIST = ^NAMELISTREC;
  34.    FUNDEF = ^FUNDEFREC;
  35.  
  36.    EXPTYPE = (VALEXP,VAREXP,APEXP);
  37.    EXPREC = record
  38.                case etype: EXPTYPE of
  39.                   VALEXP: (num: NUMBER);
  40.                   VAREXP: (varble: NAME);
  41.                   APEXP: (optr: NAME; args: EXPLIST)
  42.             end;
  43.  
  44.    EXPLISTREC = record
  45.                head: EXP;
  46.                tail: EXPLIST
  47.             end;
  48.  
  49.    VALUELISTREC = record
  50.                head: NUMBER;
  51.                tail: VALUELIST
  52.             end;
  53.  
  54.    NAMELISTREC = record
  55.                head: NAME;
  56.                tail: NAMELIST
  57.             end;
  58.  
  59.    ENVREC = record
  60.                vars: NAMELIST;
  61.                values: VALUELIST
  62.             end;
  63.  
  64.    FUNDEFREC = record
  65.                funname: NAME;
  66.                formals: NAMELIST;
  67.                body: EXP;
  68.                nextfundef: FUNDEF
  69.             end;
  70.  
  71. var
  72.    fundefs: FUNDEF;
  73.    
  74.    globalEnv: ENV;
  75.    
  76.    currentExp: EXP;
  77.    
  78.    userinput: array [1..MAXINPUT] of char;
  79.    inputleng, pos: 0..MAXINPUT;
  80.    
  81.    printNames: array [NAME] of NAMESTRING;
  82.    numNames, numBuiltins: NAME;
  83.    
  84.    quittingtime: Boolean;
  85.  
  86. (*****************************************************************
  87.  *                     DATA STRUCTURE OP'S                       *
  88.  *****************************************************************)
  89.  
  90. (* mkVALEXP - return an EXP of type VALEXP with num n            *)
  91. function mkVALEXP (n: NUMBER): EXP;
  92. var e: EXP;
  93. begin
  94.    new(e);
  95.    e^.etype := VALEXP;
  96.    e^.num := n;
  97.    mkVALEXP := e
  98. end; (* mkVALEXP *)
  99.  
  100. (* mkVAREXP - return an EXP of type VAREXP with varble nm        *)
  101. function mkVAREXP (nm: NAME): EXP;
  102. var e: EXP;
  103. begin
  104.    new(e);
  105.    e^.etype := VAREXP;
  106.    e^.varble := nm;
  107.    mkVAREXP := e
  108. end; (* mkVAREXP *)
  109.  
  110. (* mkAPEXP - return EXP of type APEXP w/ optr op and args el     *)
  111. function mkAPEXP (op: NAME; el: EXPLIST): EXP;
  112. var e: EXP;
  113. begin
  114.    new(e);
  115.    e^.etype := APEXP;
  116.    e^.optr := op;
  117.    e^.args := el;
  118.    mkAPEXP := e
  119. end; (* mkAPEXP *)
  120.  
  121. (* mkExplist - return an EXPLIST with head e and tail el         *)
  122. function mkExplist (e: EXP; el: EXPLIST): EXPLIST;
  123. var newel: EXPLIST;
  124. begin
  125.    new(newel);
  126.    newel^.head := e;
  127.    newel^.tail := el;
  128.    mkExplist := newel
  129. end; (* mkExplist *)
  130.  
  131. (* mkNamelist - return a NAMELIST with head n and tail nl        *)
  132. function mkNamelist (nm: NAME; nl: NAMELIST): NAMELIST;
  133. var newnl: NAMELIST;
  134. begin
  135.    new(newnl);
  136.    newnl^.head := nm;
  137.    newnl^.tail := nl;
  138.    mkNamelist := newnl
  139. end; (* mkNamelist *)
  140.  
  141. (* mkValuelist - return an VALUELIST with head n and tail vl     *)
  142. function mkValuelist (n: NUMBER; vl: VALUELIST): VALUELIST;
  143. var newvl: VALUELIST;
  144. begin
  145.    new(newvl);
  146.    newvl^.head := n;
  147.    newvl^.tail := vl;
  148.    mkValuelist := newvl
  149. end; (* mkValuelist *)
  150.  
  151. (* mkEnv - return an ENV with vars nl and values vl              *)
  152. function mkEnv (nl: NAMELIST; vl: VALUELIST): ENV;
  153. var rho: ENV;
  154. begin
  155.    new(rho);
  156.    rho^.vars := nl;
  157.    rho^.values := vl;
  158.    mkEnv := rho
  159. end; (* mkEnv *)
  160.  
  161. (* lengthVL - return length of VALUELIST vl                      *)
  162. function lengthVL (vl: VALUELIST): integer;
  163. var i: integer;
  164. begin
  165.    i := 0;
  166.    while vl <> nil do begin
  167.       i := i+1;
  168.       vl := vl^.tail
  169.       end;
  170.    lengthVL := i
  171. end; (* lengthVL *)
  172.  
  173. (* lengthNL - return length of NAMELIST nl                       *)
  174. function lengthNL (nl: NAMELIST): integer;
  175. var i: integer;
  176. begin
  177.    i := 0;
  178.    while nl <> nil do begin
  179.       i := i+1;
  180.       nl := nl^.tail
  181.       end;
  182.    lengthNL := i
  183. end; (* lengthNL *)
  184.  
  185. (*****************************************************************
  186.  *                     NAME MANAGEMENT                           *
  187.  *****************************************************************)
  188.  
  189. (* fetchFun - get function definition of fname from fundefs      *)
  190. function fetchFun (fname: NAME): FUNDEF;
  191. var
  192.    f: FUNDEF;
  193.    found: Boolean;
  194. begin
  195.    found := false;
  196.    f := fundefs;
  197.    while (f <> nil) and not found do
  198.       if f^.funname = fname
  199.       then found := true
  200.       else f := f^.nextfundef;
  201.    fetchFun := f
  202. end; (* fetchFun *)
  203.  
  204. (* newFunDef - add new function fname w/ parameters nl, body e   *)
  205. procedure newFunDef (fname: NAME; nl: NAMELIST; e: EXP);
  206. var f: FUNDEF;
  207. begin
  208.    f := fetchFun(fname);
  209.    if f = nil (* fname not yet defined as a function *)
  210.    then begin
  211.            new(f);
  212.            f^.nextfundef := fundefs; (* place new FUNDEFREC *)
  213.            fundefs := f              (* on fundefs list *)
  214.         end;
  215.    f^.funname := fname;
  216.    f^.formals := nl;
  217.    f^.body := e
  218. end; (* newFunDef *)
  219.  
  220. (* initNames - place all pre-defined names into printNames       *)
  221. procedure initNames;
  222. var i: integer;
  223. begin
  224.    fundefs := nil;
  225.    i := 1;
  226.    printNames[i] := 'if                  '; i := i+1;
  227.    printNames[i] := 'while               '; i := i+1;
  228.    printNames[i] := 'set                 '; i := i+1;
  229.    printNames[i] := 'begin               '; i := i+1;
  230.    printNames[i] := '+                   '; i := i+1;
  231.    printNames[i] := '-                   '; i := i+1;
  232.    printNames[i] := '*                   '; i := i+1;
  233.    printNames[i] := '/                   '; i := i+1;
  234.    printNames[i] := '=                   '; i := i+1;
  235.    printNames[i] := '<                   '; i := i+1;
  236.    printNames[i] := '>                   '; i := i+1;
  237.    printNames[i] := 'print               ';
  238.    numNames := i;
  239.    numBuiltins := i
  240. end; (* initNames *)
  241.  
  242. (* install - insert new name into printNames                     *)
  243. function install (nm: NAMESTRING): NAME;
  244. var
  245.    i: integer;
  246.    found: Boolean;
  247. begin
  248.    i := 1; found := false;
  249.    while (i <= numNames) and not found
  250.    do if nm = printNames[i]
  251.       then found := true
  252.       else i := i+1;
  253.    if not found
  254.    then begin
  255.            if i > MAXNAMES
  256.            then begin
  257.                    writeln('No more room for names');
  258.                    goto 99
  259.                 end;
  260.            numNames := i;
  261.            printNames[i] := nm
  262.         end;
  263.    install := i
  264. end; (* install *)
  265.  
  266. (* prName - print name nm                                        *)
  267. procedure prName (nm: NAME);
  268. var i: integer;
  269. begin
  270.    i := 1;
  271.    while i <= NAMELENG
  272.    do if printNames[nm][i] <> ' '
  273.       then begin
  274.               write(printNames[nm][i]);
  275.               i := i+1
  276.            end
  277.       else i := NAMELENG+1
  278. end; (* prName *)
  279.  
  280. (* primOp - translate NAME optr to corresponding BUILTINOP       *)
  281. function primOp (optr: NAME): BUILTINOP;
  282. var
  283.    op: BUILTINOP;
  284.    i: integer;
  285. begin
  286.    op := IFOP; (* N.B. IFOP is first value in BUILTINOPS *)
  287.    for i := 1 to optr-1 do op := succ(op);
  288.    primOp := op
  289. end; (* primOp *)
  290.  
  291. (*****************************************************************
  292.  *                        INPUT                                  *
  293.  *****************************************************************)
  294.  
  295. (* isDelim - check if c is a delimiter                           *)
  296. function isDelim (c: char): Boolean;
  297. begin
  298.    isDelim := c in ['(', ')', ' ', COMMENTCHAR]
  299. end; (* isDelim *)
  300.  
  301. (* skipblanks - return next non-blank position in userinput      *)
  302. function skipblanks (p: integer): integer;
  303. begin
  304.    while userinput[p] = ' ' do p := p+1;
  305.    skipblanks := p
  306. end; (* skipblanks *)
  307.  
  308. (* matches - check if string nm matches userinput[s .. s+leng]   *)
  309. function matches (s: integer; leng: NAMESIZE;
  310.                    nm: NAMESTRING): Boolean;
  311. var
  312.    match: Boolean;
  313.    i: integer;
  314. begin
  315.    match := true; i := 1;
  316.    while match and (i <= leng) do begin
  317.       if userinput[s] <> nm[i] then match := false;
  318.       i := i+1;
  319.       s := s+1
  320.       end;
  321.    if not isDelim(userinput[s]) then match := false;
  322.    matches := match
  323. end; (* matches *)
  324.  
  325. (* reader - read char's into userinput; be sure input not blank  *)
  326. procedure reader;
  327.  
  328. (* readInput - read char's into userinput                        *)
  329.    procedure readInput;
  330.  
  331.    var c: char;
  332.  
  333. (* nextchar - read next char - filter tabs and comments          *)
  334.       procedure nextchar (var c: char);
  335.       begin
  336.          read(c);
  337.          if c = chr(TABCODE)
  338.          then c := ' '
  339.          else if c = COMMENTCHAR
  340.               then begin while not eoln do read(c); c := ' ' end
  341.       end; (* nextchar *)
  342.  
  343. (* readParens - read char's, ignoring newlines, to matching ')'  *)
  344.       procedure readParens;
  345.       var
  346.          parencnt: integer; (* current depth of parentheses *)
  347.          c: char;
  348.       begin
  349.          parencnt := 1; (* '(' just read *)
  350.          repeat
  351.             if eoln then write(PROMPT2);
  352.             nextchar(c);
  353.             pos := pos+1;
  354.             if pos = MAXINPUT
  355.             then begin
  356.                     writeln('User input too long');
  357.                     goto 99
  358.                  end;
  359.             userinput[pos] := c;
  360.             if c = '(' then parencnt := parencnt+1;
  361.             if c = ')' then parencnt := parencnt-1
  362.          until parencnt = 0
  363.       end; (* readParens *)
  364.  
  365.    begin (* readInput *)
  366.       write(PROMPT);
  367.       pos := 0;
  368.       repeat
  369.          pos := pos+1;
  370.          if pos = MAXINPUT
  371.          then begin
  372.                  writeln('User input too long');
  373.                  goto 99
  374.               end;
  375.          nextchar(c);
  376.          userinput[pos] := c;
  377.          if userinput[pos] = '(' then readParens
  378.       until eoln;
  379.       inputleng := pos;
  380.       userinput[pos+1] := COMMENTCHAR (* sentinel *)
  381.    end; (* readInput *)
  382.  
  383. begin (* reader *)
  384.     repeat
  385.        readInput;
  386.        pos := skipblanks(1);
  387.     until pos <= inputleng (* ignore blank lines *)
  388. end; (* reader *)
  389.  
  390. (* parseName - return (installed) NAME starting at userinput[pos]*)
  391. function parseName: NAME;
  392. var
  393.    nm: NAMESTRING; (* array to accumulate characters *)
  394.    leng: NAMESIZE; (* length of name *)
  395. begin
  396.    leng := 0;
  397.    while (pos <= inputleng) and not isDelim(userinput[pos])
  398.    do begin
  399.          if leng = NAMELENG
  400.          then begin
  401.                  writeln('Name too long, begins: ', nm);
  402.                  goto 99
  403.               end;
  404.          leng := leng+1;
  405.          nm[leng] := userinput[pos];
  406.          pos := pos+1
  407.       end;
  408.    if leng = 0
  409.    then begin
  410.            writeln('Error: expected name, instead read: ',
  411.                    userinput[pos]);
  412.            goto 99
  413.         end;
  414.    for leng := leng+1 to NAMELENG do nm[leng] := ' ';
  415.    pos := skipblanks(pos); (* skip blanks after name *)
  416.    parseName := install(nm)
  417. end; (* parseName *)
  418.  
  419. (* isNumber - check if a number begins at pos                    *)
  420. function isNumber (pos: integer): Boolean;
  421.  
  422. (* isDigits - check if sequence of digits begins at pos          *)
  423.    function isDigits (pos: integer): Boolean;
  424.    begin
  425.       if not (userinput[pos] in ['0'..'9'])
  426.       then isDigits := false
  427.       else begin
  428.               isDigits := true;
  429.               while userinput[pos] in ['0'..'9'] do pos := pos+1;
  430.               if not isDelim(userinput[pos])
  431.               then isDigits := false
  432.            end
  433.    end; (* isDigits *)
  434.  
  435. begin (* isNumber *)
  436.    isNumber := isDigits(pos) or
  437.               ((userinput[pos] = '-') and isDigits(pos+1))
  438. end; (* isNumber *)
  439.  
  440. (* parseVal - return number starting at userinput[pos]           *)
  441. function parseVal: NUMBER;
  442. var n, sign: integer;
  443. begin
  444.    n := 0; sign := 1;
  445.    if userinput[pos] = '-'
  446.    then begin
  447.            sign := -1;
  448.            pos := pos+1
  449.         end;
  450.    while userinput[pos] in ['0'..'9'] do
  451.       begin
  452.          n := 10*n + (ord(userinput[pos]) - ord('0'));
  453.          pos := pos+1
  454.       end;
  455.    pos := skipblanks(pos); (* skip blanks after number *)
  456.    parseVal := n*sign
  457. end; (* parseVal *)
  458.  
  459. function parseEL: EXPLIST; forward;
  460.  
  461. (* parseExp - return EXP starting at userinput[pos]              *)
  462. function parseExp: EXP;
  463. var
  464.    nm: NAME;
  465.    el: EXPLIST;
  466. begin
  467.    if userinput[pos] = '('
  468.    then begin   (* APEXP *)
  469.            pos := skipblanks(pos+1); (* skip '( ..' *)
  470.            nm := parseName;
  471.            el := parseEL;
  472.            parseExp := mkAPEXP(nm, el)
  473.         end
  474.    else if isNumber(pos)
  475.         then parseExp := mkVALEXP(parseVal)   (* VALEXP *)
  476.         else parseExp := mkVAREXP(parseName)  (* VAREXP *)
  477. end; (* parseExp *)
  478.  
  479. (* parseEL - return EXPLIST starting at userinput[pos]           *)
  480. function parseEL;
  481. var
  482.    e: EXP;
  483.    el: EXPLIST;
  484. begin
  485.    if userinput[pos] = ')'
  486.    then begin
  487.            pos := skipblanks(pos+1); (* skip ') ..' *)
  488.            parseEL := nil
  489.         end
  490.    else begin
  491.            e := parseExp;
  492.            el := parseEL;
  493.            parseEL := mkExplist(e, el)
  494.         end
  495. end; (* parseEL *)
  496.  
  497. (* parseNL - return NAMELIST starting at userinput[pos]          *)
  498. function parseNL: NAMELIST;
  499. var
  500.    nm: NAME;
  501.    nl: NAMELIST;
  502. begin
  503.    if userinput[pos] = ')'
  504.    then begin
  505.            pos := skipblanks(pos+1); (* skip ') ..' *)
  506.            parseNL := nil
  507.         end
  508.    else begin
  509.            nm := parseName;
  510.            nl := parseNL;
  511.            parseNL := mkNamelist(nm, nl)
  512.         end
  513. end; (* parseNL *)
  514.  
  515. (* parseDef - parse function definition at userinput[pos]        *)
  516. function parseDef: NAME;
  517. var
  518.    fname: NAME;        (* function name *)
  519.    nl: NAMELIST;       (* formal parameters *)
  520.    e: EXP;             (* body *)
  521. begin
  522.    pos := skipblanks(pos+1); (* skip '( ..' *)
  523.    pos := skipblanks(pos+6); (* skip 'define ..' *)
  524.    fname := parseName;
  525.    pos := skipblanks(pos+1); (* skip '( ..' *)
  526.    nl := parseNL;
  527.    e := parseExp;
  528.    pos := skipblanks(pos+1); (* skip ') ..' *)
  529.    newFunDef(fname, nl, e);
  530.    parseDef := fname
  531. end; (* parseDef *)
  532.  
  533. (*****************************************************************
  534.  *                     ENVIRONMENTS                              *
  535.  *****************************************************************)
  536.  
  537. (* emptyEnv - return an environment with no bindings             *)
  538. function emptyEnv: ENV;
  539. begin
  540.    emptyEnv := mkEnv(nil, nil)
  541. end; (* emptyEnv *)
  542.  
  543. (* bindVar - bind variable nm to value n in environment rho      *)
  544. procedure bindVar (nm: NAME; n: NUMBER; rho: ENV);
  545. begin
  546.    rho^.vars := mkNamelist(nm, rho^.vars);
  547.    rho^.values := mkValuelist(n, rho^.values)
  548. end; (* bindVar *)
  549.  
  550. (* findVar - look up nm in rho                                   *)
  551. function findVar (nm: NAME; rho: ENV): VALUELIST;
  552. var
  553.    nl: NAMELIST;
  554.    vl: VALUELIST;
  555.    found: Boolean;
  556. begin
  557.    found := false;
  558.    nl := rho^.vars;
  559.    vl := rho^.values;
  560.    while (nl <> nil) and not found do
  561.       if nl^.head = nm
  562.       then found := true
  563.       else begin
  564.               nl := nl^.tail;
  565.               vl := vl^.tail
  566.            end;
  567.    findVar := vl
  568. end; (* findVar *)
  569.  
  570. (* assign - assign value n to variable nm in rho                 *)
  571. procedure assign (nm: NAME; n: NUMBER; rho: ENV);
  572. var varloc: VALUELIST;
  573. begin
  574.    varloc := findVar(nm, rho);
  575.    varloc^.head := n
  576. end; (* assign *)
  577.  
  578. (* fetch - return number bound to nm in rho                      *)
  579. function fetch (nm: NAME; rho: ENV): NUMBER;
  580. var vl: VALUELIST;
  581. begin
  582.    vl := findVar(nm, rho);
  583.    fetch := vl^.head
  584. end; (* fetch *)
  585.  
  586. (* isBound - check if nm is bound in rho                         *)
  587. function isBound (nm: NAME; rho: ENV): Boolean;
  588. begin
  589.    isBound := findVar(nm, rho) <> nil
  590. end; (* isBound *)
  591.  
  592. (*****************************************************************
  593.  *                     NUMBERS                                   *
  594.  *****************************************************************)
  595.  
  596. (* prValue - print number n                                      *)
  597. procedure prValue (n: NUMBER);
  598. begin
  599.    write(n:1)
  600. end; (* prValue *)
  601.  
  602. (* isTrueVal - return true if n is a true (non-zero) value       *)
  603. function isTrueVal (n: NUMBER): Boolean;
  604. begin
  605.    isTrueVal := n <> 0
  606. end; (* isTrueVal *)
  607.  
  608. (* applyValueOp - apply VALUEOP op to arguments in VALUELIST vl  *)
  609. function applyValueOp (op: VALUEOP; vl: VALUELIST): NUMBER;
  610.  
  611. var n, n1, n2: NUMBER;
  612.  
  613. (* arity - return number of arguments expected by op             *)
  614.    function arity (op: VALUEOP): integer;
  615.    begin
  616.       if op in [PLUSOP .. GTOP] then arity := 2 else arity := 1
  617.    end; (* arity *)
  618.  
  619. begin (* applyValueOp *)
  620.    if arity(op) <> lengthVL(vl)
  621.    then begin
  622.            write('Wrong number of arguments to ');
  623.            prName(ord(op)+1);
  624.            writeln;
  625.            goto 99
  626.         end;
  627.    n1 := vl^.head; (* 1st actual *)
  628.    if arity(op) = 2 then n2 := vl^.tail^.head; (* 2nd actual *)
  629.    case op of
  630.       PLUSOP: n := n1+n2;
  631.       MINUSOP: n := n1-n2;
  632.       TIMESOP: n := n1*n2;
  633.       DIVOP: n := n1 div n2;
  634.       EQOP: if n1 = n2 then n := 1 else n := 0;
  635.       LTOP: if n1 < n2 then n := 1 else n := 0;
  636.       GTOP: if n1 > n2 then n := 1 else n := 0;
  637.       PRINTOP:
  638.          begin prValue(n1); writeln; n := n1 end
  639.    end; (* case *)
  640.    applyValueOp := n
  641. end; (* applyValueOp *)
  642.  
  643. (*****************************************************************
  644.  *                     EVALUATION                                *
  645.  *****************************************************************)
  646.  
  647. (* eval - return value of expression e in local environment rho  *)
  648. function eval (e: EXP; rho: ENV): NUMBER;
  649.  
  650. var op: BUILTINOP;
  651.  
  652. (* evalList - evaluate each expression in el                     *)
  653.    function evalList (el: EXPLIST): VALUELIST;
  654.    var
  655.       h: NUMBER;
  656.       t: VALUELIST;
  657.    begin
  658.       if el = nil then evalList := nil
  659.       else begin
  660.               h := eval(el^.head, rho);
  661.               t := evalList(el^.tail);
  662.               evalList := mkValuelist(h, t)
  663.            end
  664.    end; (* evalList *)
  665.  
  666. (* applyUserFun - look up definition of nm and apply to actuals  *)
  667.    function applyUserFun (nm: NAME; actuals: VALUELIST): NUMBER;
  668.    var
  669.       f: FUNDEF;
  670.       rho: ENV;
  671.    begin
  672.       f := fetchFun(nm);
  673.       if f = nil
  674.       then begin
  675.               write('Undefined function: ');
  676.               prName(nm);
  677.               writeln;
  678.               goto 99
  679.            end;
  680.       with f^ do begin
  681.          if lengthNL(formals) <> lengthVL(actuals)
  682.          then begin
  683.                  write('Wrong number of arguments to: ');
  684.                  prName(nm);
  685.                  writeln;
  686.                  goto 99
  687.               end;
  688.          rho := mkEnv(formals, actuals);
  689.          applyUserFun := eval(body, rho)
  690.          end
  691.    end; (* applyUserFun *)
  692.  
  693. (* applyCtrlOp - apply CONTROLOP op to args in rho               *)
  694.    function applyCtrlOp (op: CONTROLOP;
  695.                        args: EXPLIST): NUMBER;
  696.    var n: NUMBER;
  697.    begin
  698.       with args^ do
  699.          case op of
  700.            IFOP:
  701.               if isTrueVal(eval(head, rho))
  702.               then applyCtrlOp := eval(tail^.head, rho)
  703.               else applyCtrlOp := eval(tail^.tail^.head, rho);
  704.            WHILEOP:
  705.               begin
  706.                  n := eval(head, rho);
  707.                  while isTrueVal(n)
  708.                  do begin
  709.                        n := eval(tail^.head, rho);
  710.                        n := eval(head, rho)
  711.                     end;
  712.                  applyCtrlOp := n
  713.               end;
  714.            SETOP:
  715.               begin
  716.                  n := eval(tail^.head, rho);
  717.                  if isBound(head^.varble, rho)
  718.                  then assign(head^.varble, n, rho)
  719.                  else if isBound(head^.varble, globalEnv)
  720.                       then assign(head^.varble, n, globalEnv)
  721.                       else bindVar(head^.varble, n, globalEnv);
  722.                  applyCtrlOp := n
  723.               end;
  724.            BEGINOP: 
  725.               begin
  726.                  while args^.tail <> nil do
  727.                     begin
  728.                        n := eval(args^.head, rho);
  729.                        args := args^.tail
  730.                     end;
  731.                  applyCtrlOp := eval(args^.head, rho)
  732.               end
  733.          end (* case and with *)
  734.    end; (* applyCtrlOp *)
  735.  
  736. begin (* eval *)
  737.    with e^ do
  738.       case etype of
  739.          VALEXP:
  740.             eval := num;
  741.          VAREXP:
  742.             if isBound(varble, rho)
  743.             then eval := fetch(varble, rho)
  744.             else if isBound(varble, globalEnv)
  745.                  then eval := fetch(varble, globalEnv)
  746.                  else begin
  747.                          write('Undefined variable: ');
  748.                          prName(varble);
  749.                          writeln;
  750.                          goto 99
  751.                       end;
  752.          APEXP: 
  753.             if optr > numBuiltins
  754.             then eval := applyUserFun(optr, evalList(args))
  755.             else begin
  756.                     op := primOp(optr);
  757.                     if op in [IFOP .. BEGINOP]
  758.                     then eval := applyCtrlOp(op, args)
  759.                     else eval := applyValueOp(op,
  760.                                      evalList(args))
  761.                  end
  762.       end (* case and with *)
  763. end; (* eval *)
  764.  
  765. (*****************************************************************
  766.  *                     READ-EVAL-PRINT LOOP                      *
  767.  *****************************************************************)
  768.  
  769. begin (* chapter1 main *)
  770.    initNames;
  771.    globalEnv := emptyEnv;
  772.  
  773.    quittingtime := false;
  774. 99:
  775.    while not quittingtime do begin
  776.       reader;
  777.       if matches(pos, 4, 'quit                ')
  778.       then quittingtime := true
  779.       else if (userinput[pos] = '(') and
  780.               matches(skipblanks(pos+1), 6, 'define              ')
  781.            then begin
  782.                    prName(parseDef);
  783.                    writeln
  784.                 end
  785.            else begin
  786.                    currentExp := parseExp;
  787.                    prValue(eval(currentExp, emptyEnv));
  788.                    writeln;
  789.                    writeln
  790.                 end
  791.       end (* while *)
  792. end. (* chapter1 *)
  793.    
  794.  
  795.  
  796.