home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d524 / kamin.lha / Kamin / P-Distr.lzh / smalltalk.p < prev    next >
Text File  |  1990-03-13  |  32KB  |  1,074 lines

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