home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.pdx.edu / 2014.02.ftp.ee.pdx.edu.tar / ftp.ee.pdx.edu / pub / users / Harry / compilers / p1 / Proj1-Starter.java < prev    next >
Text File  |  2005-09-30  |  22KB  |  840 lines

  1. //
  2. // The "E" Language Interpreter
  3. //
  4. // This program parses, checks, and interprets a program written in the
  5. // "E" language.  The language is described in the project assignment.
  6. //
  7. // Program Author History:
  8. //    Andrew Tolmach        -- 10/07/02
  9. //    Harry Porter          -- 01/10/03
  10. //    Harry Porter          -- 01/19/04
  11. //    <Your name here>      -- <date>
  12. //
  13. //
  14.  
  15. import java.util.*;
  16. import java.io.*;
  17.  
  18.  
  19.  
  20. // ------------------------  Proj1 -----------------------------------
  21. //
  22. // Driver for reading, checking, and executing "E" programs.
  23. //
  24. class Proj1 {
  25.     public static void main (String [] args) {
  26.         try {
  27.             Reader rdr = new FileReader (args[0]); 
  28.             Expr expr = Parser.parseProgram (rdr);
  29.             System.out.println ("Abstract Syntax Tree = " + expr);
  30.             boolean ok = expr.check ();
  31.             if (!ok) {
  32.                 throw new CompileTimeError ("Semantic errors occured... Aborting!", 0);
  33.             }
  34.             int result = expr.interpret ();
  35.             System.out.println ("Result = " + result);
  36.         } catch (ArrayIndexOutOfBoundsException e) {
  37.             System.err.println ("Expecting filename as command line argument");
  38.         } catch (FileNotFoundException fnfexn) {
  39.             System.err.println ("File not found: " + args[0]);
  40.         } catch (CompileTimeError e) {
  41.             System.err.println (e.getMessage ());
  42.         }
  43.     }
  44. }
  45.  
  46.  
  47.  
  48. // ---------------------- CompileTimeError -----------------------
  49. //
  50. // Each instance of this exception class contains a message
  51. // with the line number and some text describing the problem.
  52. //
  53. class CompileTimeError extends Exception {
  54.     CompileTimeError (String message, int lineno) {
  55.         super ("Error on line " + lineno + ": " + message);
  56.     }
  57. }
  58.  
  59.  
  60.  
  61. // ---------------------- Token ----------------------------
  62. //
  63. // This class encapsulates definitions related to tokens.
  64. // This class is never instantiated.
  65. //
  66. class Token {
  67.  
  68.     //
  69.     // Constructor -- This makes creating instances impossible.
  70.     //
  71.     private Token () { }
  72.  
  73.     //
  74.     // Token kinds
  75.     //
  76.     // This list must match the "stringOf" array.  Keywords and symbols
  77.     // must be kept together and their order must match the corresponding
  78.     // "keywords" and "symbols" arrays.
  79.     //
  80.     final static int 
  81.         VAR = 0,        // 'var' keyword
  82.         SET = 1,        // 'set' keyword
  83.         ID = 2,         // identifier
  84.         NUM = 3,        // numeric literal
  85.         EQ = 4,         // =
  86.         LBRACE = 5,     // {
  87.         RBRACE = 6,     // }
  88.         LPAREN = 7,     // (
  89.         RPAREN = 8,     // )
  90.         PLUS = 9,       // +
  91.         SEMI = 10,      // ;
  92.         EOF = 11;       // end of file
  93.  
  94.     //
  95.     // Printable representations of token kinds.  This list must match the
  96.     // token kind codes, above.
  97.     //
  98.     final static String [] stringOf = {
  99.         "'var'",
  100.         "'set'", 
  101.         "identifier",
  102.         "number", 
  103.         "'='",
  104.         "'{'",
  105.         "'}'",
  106.         "'('",
  107.         "')'",
  108.         "'+'",
  109.         "';'",
  110.         "end of file"
  111.     };
  112.  
  113.     //
  114.     // Keyword List
  115.     //
  116.     // This array must correspond to the token type codes given above.
  117.     //
  118.     final static String [] keywords = {
  119.         "var",
  120.         "set"
  121.     };
  122.  
  123.     //
  124.     // Token kind value corresponding to first keyword.
  125.     //
  126.     final static int firstKeyword = VAR; 
  127.  
  128.     //
  129.     // Symbol List
  130.     //
  131.     // This array must correspond to the token type codes given above.
  132.     //
  133.     final static char [] symbols = {
  134.         '=',
  135.         '{',
  136.         '}',
  137.         '(',
  138.         ')',
  139.         '+',
  140.         ';'
  141.     };
  142.  
  143.     //
  144.     // Token kind value corresponding to first symbol.
  145.     //
  146.     final static int firstSymbol = EQ;
  147. }
  148.  
  149.  
  150.  
  151. // ------------------------ Lexer ----------------------------
  152. //
  153. // This class is intended to be instantiated exactly once.  The instance
  154. // of this class is a lexical analyzer for a particular input source.
  155. // The key method is "getToken", which reads and returns the next token.
  156. //
  157. class Lexer {
  158.  
  159.     //
  160.     // Fields
  161.     //
  162.     int lineno;                       // Current line number
  163.     Object attribute;                 // Attribute for most recently returned
  164.                                       //   token, or null if inapplicable
  165.     private PushbackReader inSource;  // Source from which we are reading
  166.  
  167.  
  168.     //
  169.     // Constants
  170.     //
  171.     static final char                 // Newline character, determined in a portable way.
  172.         NEWLINE = System.getProperty ("line.separator").charAt (0);
  173.  
  174.  
  175.     //
  176.     // Constructor
  177.     //
  178.     // Creates a new Lexer.  The parameter "rdr" is the input source
  179.     // that will be used by this Lexer.  We wrap "rdr" in a "PushbackReader",
  180.     // which will allow us to "unread" characters from the input stream.
  181.     //
  182.     Lexer (Reader rdr) {
  183.         inSource = new PushbackReader (rdr);
  184.         lineno = 1;
  185.     }
  186.  
  187.  
  188.     //
  189.     // getToken () --> int
  190.     //
  191.     // This method reads the next token from the input source.  It
  192.     // returns a code indicating the type of the new token.  If there is
  193.     // additional info about the token (e.g., the value of a NUM or chars of
  194.     // an ID), this information is stored in the "attribute" field.
  195.     //
  196.     // If errors occur, this method throws a "CompileTimeError" exception,
  197.     // describing the problem.
  198.     //
  199.     int getToken ()
  200.         throws CompileTimeError
  201.     {
  202.         try {
  203.             attribute = null;
  204.             while (true) {
  205.                 char c = (char) inSource.read ();
  206.                 if (c == (char) (-1)) {
  207.                     return Token.EOF;
  208.                 } else if (c == NEWLINE) {
  209.                     lineno++;
  210.                     continue;
  211.                 } else if (c == '\r') {
  212.                     continue;
  213.                 } else if (Character.isWhitespace (c)) {
  214.                     continue;
  215.                 } else if (Character.isLetter (c)) {
  216.                     String str = new String ();
  217.                     str = str + c;
  218.                     c = (char) inSource.read ();
  219.                     while (Character.isLetterOrDigit (c)) {
  220.                         str = str + c;
  221.                         c = (char) inSource.read ();
  222.                     }
  223.                     inSource.unread (c);
  224.                     for (int i = 0; i < Token.keywords.length; i++) {
  225.                         if (Token.keywords[i].equals (str)) {
  226.                             return Token.firstKeyword + i;
  227.                         }
  228.                     }
  229.                     attribute = str;
  230.                     return Token.ID;
  231.                 } else if (Character.isDigit (c)) {
  232.                     String str = new String ();
  233.                     str = str + c;
  234.                     c = (char) inSource.read ();
  235.                     while (Character.isDigit (c)) {
  236.                         str = str + c;
  237.                         c = (char) inSource.read ();
  238.                     }
  239.                     inSource.unread (c);
  240.                     attribute = Integer.valueOf (str);
  241.                     return Token.NUM;
  242.                 } else {
  243.                     for (int i = 0; i < Token.symbols.length; i++) {
  244.                         if (Token.symbols[i] == c) {
  245.                             return Token.firstSymbol + i;
  246.                         }
  247.                     }
  248.                     throw new CompileTimeError ("Invalid character \'"  + c
  249.                                + "\' in source", lineno);
  250.                 }
  251.             }
  252.         } catch (IOException exn) {         // from read or unread
  253.             throw new CompileTimeError ("I/O error: " + exn.getMessage(), lineno);
  254.         }
  255.     }
  256.  
  257. }
  258.  
  259.  
  260.  
  261. // ------------------------ AST Classes ------------------------
  262. //
  263. // This group of classes is used to construct the AST (Abstract Syntax Tree).
  264. // Each type of node in the AST is represented by an instance of one of these
  265. // classes.  Here is the class hierarchy:
  266. //
  267. //    Expr             -- An abstract class
  268. //      VarExpr        -- Decribes a variable
  269. //      ConstExpr      -- Describes a NUM constant
  270. //      PlusExpr       -- Describes an expression of the form "X + Y"
  271. //      SetExpr        -- Describes an expression of the form "set X = expr"
  272. //      BlockExpr      -- Describes an expression of the form "{ ... }"
  273. //
  274. // Each node in the tree understands the following messages:
  275. //
  276. //    toString  -- Returns a printable version of the AST.
  277. //    check     -- Returns true if this subtree is semantically okay.
  278. //    interpret -- Returns the integer result of the interpretation.
  279. //
  280.  
  281.  
  282.  
  283. // ------------------------ Expr --------------------------
  284. //
  285. abstract class Expr {
  286.  
  287.  
  288.     //
  289.     // toString () --> String
  290.     //
  291.     // This method returns a printable version of the tree.
  292.     //
  293.     abstract public String toString (); 
  294.  
  295.  
  296.     //
  297.     // check () --> boolean
  298.     //
  299.     // This method checks the entire tree.  It makes sure all variables are
  300.     // declared before being used.
  301.     //
  302.     boolean check() {
  303.         return check(Env.empty);
  304.     }
  305.  
  306.  
  307.     //
  308.     // check (env) --> boolean
  309.     //
  310.     // This method checks the sub-tree rooted at the receiver.  It makes
  311.     // sure all variables are declared before being used.  It is passed an
  312.     // environment "env" telling which variables are already defined.  It
  313.     // returns "true" if everything is okay.  If errors arise, they will be
  314.     // printed on "stderr" and this method will return "false".
  315.     //
  316.     abstract boolean check(Env env);
  317.  
  318.  
  319.     //
  320.     // interpret () --> int
  321.     //
  322.     // This method executes the program and returns the result.
  323.     //
  324.     int interpret () {
  325.         System.err.println ("Interpret not yet implemented!");
  326.         return -1;
  327.     }
  328.  
  329. }
  330.  
  331.  
  332.  
  333. // ----------------------------- VarExpr --------------------------------
  334. //
  335. final class VarExpr extends Expr {
  336.  
  337.     //
  338.     // Fields
  339.     //
  340.     private String nameOfVar;
  341.  
  342.  
  343.     //
  344.     // Constructor
  345.     //
  346.     VarExpr (String s) {
  347.         nameOfVar = s;
  348.     }
  349.  
  350.  
  351.     //
  352.     // toString () --> String
  353.     //
  354.     // This method returns a printable version of this node.
  355.     //
  356.     public String toString () {
  357.         return nameOfVar;
  358.     }
  359.  
  360.  
  361.     //
  362.     // check (env) --> boolean
  363.     //
  364.     // This method checks that this variable has been declared, i.e.,
  365.     // that an entry for it can be found in "env".  It this variable
  366.     // is not defined, it prints a message and returns false.
  367.     //
  368.     boolean check (Env env) {
  369.         if (Env.find (env, nameOfVar) != null) {
  370.             return true;
  371.         } else {
  372.             System.err.println ("Semantic error: The variable \"" 
  373.                     + nameOfVar + "\" was used but was not declared!");
  374.             return false;
  375.         }
  376.     }
  377.  
  378. }
  379.  
  380.  
  381.  
  382. // ----------------------------- ConstExpr --------------------------------
  383. //
  384. final class ConstExpr extends Expr {
  385.  
  386.     //
  387.     // Fields
  388.     //
  389.     private int intVal;
  390.  
  391.  
  392.     //
  393.     // Constructor
  394.     //
  395.     ConstExpr (int i) {
  396.         intVal = i;
  397.     }
  398.  
  399.  
  400.     //
  401.     // toString () --> String
  402.     //
  403.     // This method returns a printable version of this node.
  404.     //
  405.     public String toString () {
  406.         return Integer.toString (intVal);
  407.     }
  408.  
  409.  
  410.     //
  411.     // check (env) --> boolean
  412.     //
  413.     // This method checks this node for semantic errors and returns
  414.     // true if everything is okay.
  415.     //
  416.     boolean check (Env env) {
  417.         return true;
  418.     }
  419.  
  420. }
  421.  
  422.  
  423.  
  424. // ----------------------------- PlusExpr --------------------------------
  425. //
  426. final class PlusExpr extends Expr {
  427.  
  428.     //
  429.     // Fields
  430.     //
  431.     private Expr leftOperand;     // Pointer to tree representing left sub-expr
  432.     private Expr rightOperand;    // Pointer to tree representing right sub-expr
  433.  
  434.  
  435.     //
  436.     // Constructor
  437.     //
  438.     PlusExpr (Expr l, Expr r) {
  439.         leftOperand = l;
  440.         rightOperand = r;
  441.     }
  442.  
  443.  
  444.     //
  445.     // toString () --> String
  446.     //
  447.     // This method returns a printable version of this sub-tree.
  448.     //
  449.     public String toString () {
  450.         return "(+ " + leftOperand + " " + rightOperand + ")";
  451.     }
  452.  
  453.  
  454.     //
  455.     // check (env) --> boolean
  456.     //
  457.     // This method checks this expression tree for semantic errors
  458.     // and returns true if everything is okay.  It makes sure the
  459.     // sub-trees are okay with a recursive call to check each of them.
  460.     //
  461.     boolean check(Env env) {
  462.         boolean leftOK = leftOperand.check (env);
  463.         boolean rightOK = rightOperand.check (env);
  464.         return leftOK && rightOK;
  465.     }
  466.  
  467. }
  468.  
  469.  
  470.  
  471. // ----------------------------- SetExpr --------------------------------
  472. //
  473. final class SetExpr extends Expr {
  474.  
  475.     //
  476.     // Fields
  477.     //
  478.     private String varName;         // ID to the left of "="
  479.     private Expr expr;              // Expression to the right of "="
  480.  
  481.  
  482.     //
  483.     // Constructor
  484.     //
  485.     SetExpr (String i, Expr e) {
  486.         varName = i;
  487.         expr = e;
  488.     }
  489.  
  490.  
  491.     //
  492.     // toString () --> String
  493.     //
  494.     // This method returns a printable version of this sub-tree.
  495.     //
  496.     public String toString () {
  497.         return "(set " + varName + " " + expr + ")";
  498.     }
  499.  
  500.  
  501.     //
  502.     // check (env) --> boolean
  503.     //
  504.     // This method checks this set expression tree for semantic errors
  505.     // and returns true if everything is okay.
  506.     //
  507.     boolean check (Env env) {
  508.         if (Env.find (env, varName) != null)
  509.             return expr.check (env);
  510.         else {
  511.             System.err.println ("Semantic error: Variable \"" 
  512.                         + varName + "\" set but not declared");
  513.             expr.check (env);
  514.             return false;
  515.         }
  516.     }
  517.  
  518. }
  519.  
  520.  
  521.  
  522. // ----------------------------- BlockExpr --------------------------------
  523. //
  524. final class BlockExpr extends Expr {
  525.  
  526.     //
  527.     // Fields
  528.     //
  529.     private List idList;        // A List of Strings, representing declared IDs
  530.     private List exprList;      // A List of Expr's
  531.  
  532.  
  533.     //
  534.     // Constructor
  535.     //
  536.     BlockExpr (List is, List es) {
  537.         idList = is;
  538.         exprList = es;
  539.     }
  540.  
  541.  
  542.     //
  543.     // toString () --> String
  544.     //
  545.     // This method returns a printable version of this sub-tree.
  546.     //
  547.     public String toString() {
  548.         String r = "(block ( ";
  549.         for (Iterator it = idList.iterator (); it.hasNext (); ) {
  550.             r += (String) it.next() + " ";
  551.         }
  552.         r += ") ( ";
  553.         for (Iterator it = exprList.iterator (); it.hasNext (); ) {
  554.             r += ((Expr) it.next ()) + " ";
  555.         }
  556.         return r + "))";
  557.     }
  558.  
  559.  
  560.     //
  561.     // check (env) --> boolean
  562.     //
  563.     // This method checks this block for semantic errors and returns
  564.     // true if everything is okay.  It works as follows:
  565.     //
  566.     // First, run thru the idList.  For each ID, add it to the environment.
  567.     // Second, run thru the exprList.  For each Expr, call "check" recursively
  568.     // to make sure it is okay.
  569.     // 
  570.     //
  571.     boolean check(Env env) {
  572.         // System.out.println ("Checking block...");
  573.         for (Iterator it = idList.iterator (); it.hasNext (); ) {
  574.             String id = (String) it.next ();
  575.             env = new Env (id, env); 
  576.         }
  577.         // System.out.println ("  Environment = " + env);
  578.         boolean isOkay = true;
  579.         for (Iterator it = exprList.iterator (); it.hasNext (); ) {
  580.             Expr expr = (Expr) it.next ();
  581.             isOkay = expr.check (env) && isOkay;
  582.         }
  583.         return isOkay;
  584.     }
  585.  
  586. }
  587.  
  588.  
  589.  
  590. // ------------------------------ Env ------------------------------
  591. //
  592. // Each instance of this class is an element in a linked list.  Each
  593. // element in the list contains a "key", which is a String.  The linked
  594. // list is used to represent an "environment", which is a set of ID's.
  595. //
  596. class Env {
  597.  
  598.     //
  599.     // Fields
  600.     //
  601.     String key;                // The ID
  602.     Env next;                  // Pointer to next element in the list
  603.  
  604.  
  605.     //
  606.     // Constant: "empty"
  607.     //
  608.     // The empty environment is represented as an empty list.
  609.     //
  610.     static final Env empty = null;
  611.  
  612.  
  613.     //
  614.     // Constructor
  615.     //
  616.     Env (String k, Env n) {
  617.         key = k;
  618.         next = n;
  619.     }
  620.  
  621.  
  622.     //
  623.     // toString ()
  624.     //
  625.     // Return a printable representation of this list.
  626.     //
  627.     public String toString () {
  628.         String r = "[ ";
  629.         for (Env env = this; env != null; env = env.next) { 
  630.             r += env.key + " ";
  631.         }
  632.         r += "]";
  633.         return r;
  634.     }
  635.  
  636.  
  637.     //
  638.     // find (env, targetKey) --> Env
  639.     //
  640.     // This method searches this linked list for the targetKey.  If it
  641.     // finds the targetKey in the list, it returns a pointer to the Env
  642.     // element containing it.  Otherwise, it returns null.
  643.     //
  644.     static Env find (Env env, String targetKey) {
  645.         for (; env != null; env = env.next) {
  646.             if (env.key.equals (targetKey)) {
  647.                 return env;
  648.             }
  649.         }
  650.         return null;
  651.     }
  652.  
  653. }
  654.  
  655.  
  656.  
  657. // --------------------------- Parser ------------------------------
  658. //
  659. // This class is never instantiated.  Its methods comprise a
  660. // "recursive descent" parser for the E language.
  661. //
  662. final class Parser {
  663.  
  664.     //
  665.     // Constructor -- This makes creating instances impossible.
  666.     //
  667.     private Parser () { }
  668.  
  669.  
  670.     //
  671.     // Static fields
  672.     //
  673.     static Lexer lexer;                 // The lexer we are using
  674.     static int tok;                     // The current token
  675.  
  676.  
  677.     //
  678.     // syntaxError (int)
  679.     //
  680.     // This method throws an error when an unexpected token is
  681.     // encountered.  The parameter is the kind of token we are expecting.
  682.     //
  683.     static void syntaxError (int expected)
  684.         throws CompileTimeError
  685.     {
  686.         throw new CompileTimeError("Expecting " + Token.stringOf [expected]
  687.                                + ", but found " + Token.stringOf [tok]
  688.                                + " instead!",
  689.                                lexer.lineno);
  690.     }
  691.  
  692.  
  693.     //
  694.     // scan ()
  695.     //
  696.     // Move to the next token.  Will modify the "tok" and "attribute"
  697.     // variables on the side.  Will throw a CompileTimeError if problems
  698.     // occur within the Lexer.
  699.     //
  700.     static void scan ()
  701.         throws CompileTimeError
  702.     {
  703.         tok = lexer.getToken ();
  704.     }
  705.  
  706.  
  707.     //
  708.     // mustHave (expectedKind)
  709.     //
  710.     // This method is passed an expected kind of token.  If the current
  711.     // token matches, then it will advance to the next token.  Else it will
  712.     // throw a "CompileTimeError".
  713.     //
  714.     static void mustHave (int expectedKind)
  715.         throws CompileTimeError
  716.     {
  717.         if (tok == expectedKind) {
  718.             tok = lexer.getToken ();
  719.         } else {
  720.             syntaxError (expectedKind);
  721.         }
  722.     }
  723.  
  724.  
  725.     //
  726.     // parseProgram (reader) --> Expr
  727.     //
  728.     // This method will parse an "E" program.  It will construct and return
  729.     // an Abstract Syntax Tree (AST) representing the parsed program.  The argument
  730.     // is the stream from which to get the tokens.  If errors arise, it will
  731.     // throw a "CompileTimeError".
  732.     //
  733.     static Expr parseProgram (Reader rd)
  734.         throws CompileTimeError
  735.     {
  736.         lexer = new Lexer (rd);
  737.         tok = lexer.getToken ();
  738.         Expr e = parseExpr ();
  739.         mustHave (Token.EOF);
  740.         return e;
  741.     }
  742.  
  743.  
  744.     //
  745.     // parseId () --> String
  746.     //
  747.     // This method parses an Identifier and returns a String representing the
  748.     // ID.  If errors arise, it will throw a "CompileTimeError".
  749.     //
  750.     static String parseId ()
  751.         throws CompileTimeError
  752.     {
  753.         if (tok == Token.ID) {
  754.             String i = (String) lexer.attribute;
  755.             scan ();
  756.             return i;
  757.         } else {
  758.             syntaxError (Token.ID);
  759.             return "";
  760.         }
  761.     }
  762.  
  763.  
  764.     //
  765.     // parseExpr () --> Expr
  766.     //
  767.     // This method parses an expression and returns an AST representing the
  768.     // expression.  If any problems arise, it will throw a "CompileTimeError".
  769.     //
  770.     static Expr parseExpr ()
  771.         throws CompileTimeError
  772.     {
  773.         if (tok == Token.SET) {
  774.             scan ();
  775.             String i = parseId ();
  776.             mustHave (Token.EQ);
  777.             Expr e = parseExpr ();
  778.             return new SetExpr (i, e);
  779.         } else {
  780.             Expr el = parseTerm ();
  781.             if (tok == Token.PLUS) {
  782.                 scan ();
  783.                 Expr er = parseTerm ();
  784.                 return new PlusExpr (el, er);
  785.             } else {
  786.                 return el;
  787.             }
  788.         }
  789.     }
  790.  
  791.  
  792.     //
  793.     // parseTerm () --> Expr
  794.     //
  795.     // This method parses a term and returns an AST representing the
  796.     // term.  If any problems arise, it will throw a "CompileTimeError".
  797.     //
  798.     static Expr parseTerm ()
  799.         throws CompileTimeError
  800.     {
  801.         if (tok == Token.NUM) {
  802.             int n = ((Integer) lexer.attribute).intValue ();
  803.             scan ();
  804.             return new ConstExpr (n);
  805.         } else if (tok == Token.LBRACE) {
  806.             scan ();
  807.             ArrayList is = new ArrayList ();
  808.             if (tok == Token.VAR) {
  809.                 scan ();
  810.                 String id = parseId ();
  811.                 is.add (id);
  812.                 while (tok != Token.SEMI) {
  813.                     id = parseId ();
  814.                     is.add (id);
  815.                 }
  816.                 scan ();         // Move past the SEMI
  817.             }
  818.             ArrayList es = new ArrayList ();
  819.             Expr e = parseExpr ();
  820.             es.add (e);
  821.             while (tok != Token.RBRACE) {
  822.                 mustHave (Token.SEMI);
  823.                 e = parseExpr ();
  824.                 es.add(e);
  825.             }
  826.             scan ();             // Move past the RBRACE
  827.             return new BlockExpr (is, es);
  828.         } else if (tok == Token.LPAREN) {
  829.             scan ();
  830.             Expr e = parseExpr ();
  831.             mustHave (Token.RPAREN);
  832.             return e;
  833.         } else {
  834.             String i = parseId ();
  835.             return new VarExpr (i);
  836.         }
  837.     }
  838.  
  839. }
  840.