package pascal;

import java.util.Vector;
import rebelsky.compiler.lexer.CharToken;
import rebelsky.compiler.lexer.Identifier;
import rebelsky.compiler.lexer.IntegerToken;
import rebelsky.compiler.lexer.RealToken;
import rebelsky.compiler.lexer.StringToken;
import rebelsky.compiler.lexer.Token;
import rebelsky.compiler.lexer.TokenStream;
import rebelsky.compiler.misc.Symbol;
import rebelsky.compiler.parser.Node;
import rebelsky.compiler.parser.ParseException;
import rebelsky.compiler.parser.Parser;

/**
 * A simple parser for Pascal.
 *    
 * @author Samuel A. Rebelsky
 * @version 1.0.3 of April 2004
 */
public class PascalParser
//  implements Parser
{
  // +----------------------+----------------------------------------------
  // | Implementation Notes |
  // +----------------------+
/*
  You can find the original Pascal grammar in grammar.txt.  Notes on
  modifications appear in this section and the next.

  I've implemented the language with a simple predictive parser.  Almost
  every nonterminal has a parseNAME procedure that returns a Node.

  The parse tree I generate does not include many of the useless nodes
  (e.g., the terminals used only for parsing; terms, factors, and any 
  other nonterminals used for disambiguating; nonterminals whose primary
  purpose is to build lists).

  For nonterminals, such as variable_declarations, that can have a
  sequence of "children", I make only one node with lots of children,
  rather than a sequence of nodes.  Hence, the arity of something
  like variable_declarations is the number of declarations.

  The standard Pascal grammar sometimes requires a little lookahead
  (e.g., in deciding whether a statement is a procedure call or an
  assignment).  In those cases, I've often made variants of the
  parseNAME procedure that take the consumed token as a parameter.
 */

  // +--------------+------------------------------------------------------
  // | Grammar Mods |
  // +--------------+
/*
  I've tried to list most of my grammar modifications below.  I've
  probably missed some.  If you notice others, let me know.

  o "Syntactic sugar" does not appear in the parse tree.

  o Nonterminals like <factor> and <term> don't appear in the parse tree.
    Rather, all those kinds of things appear as expressions.
 
  o I've rewritten the <procedure_heading> and <function_heading> 
    rules as follows

    <function_heading> ::= FUNCTION ID <formals> 
                           COLON <type_identifier> SEMICOLON
    <procedure_heading> ::= PROCEDURE ID <formals> SEMICOLON
    <formals> ::= epsilon
    <formals> ::= OPEN_PAREN
                  <formal_parameter_section>
                  { SEMICOLON <formal_parameter_section> }
                  CLOSE_PAREN

  o For constants prefaced by a plus or minus, I've used the 
    corresponding plus or minus as the node symbol.

  o Similarly, for variable paramters and function parameters, I've
    used the function/procedure as the node symbol.

  o I never return <simple_type> or <structured_type>, since their
    primary use is to organize the grammar.  Rather, I return the
    corresponding simple or structured type (e.g., scalar or array).

  o Rather than dealing with unlabeled statements, I've included a
    new <labeled_statement> nonterminal.  In a variation from the
    original language, you can nest labels.

  o Because of hack to the grammar, I support <fixed_part> <variant_part>
    for the body of a record.  (Note that the rule hould be
    <fixed_part> SEMICOLON <variant_part>, which I also support.)

 */

  // +---------+-----------------------------------------------------------
  // | Methods |
  // +---------+

  /**
   * Parse a stream of tokens, advancing that stream as necessary.
   *
   * @exception ParseException
   *   If parsing fails.
   */
  public Node parse(TokenStream tokens)
    throws ParseException, Exception
  {
    if (!(tokens instanceof PascalTokenizer))
      boom("Can only parse with Pascal tokens.");
    try {
      return parseProgram((PascalTokenizer) tokens);
    }
    // The following few lines are something of a hack.  I
    // pass along parse exceptions and turn other exceptions
    // (e.g., TokenExceptions) to ParseExceptions.
    catch (ParseException e) { throw e; }
    // catch (Exception e) { throw new ParseException(e.toString()); }
  } // parse(TokenStream)


  // +------------------------+--------------------------------------------
  // | Private Helper Methods |
  // +------------------------+

  /**
   * My favorite way of reporting errors.  Essentially, a shorthand
   * for "throw new ParseException(...)".
   */
  private void boom(String message)
    throws ParseException
  {
    throw new ParseException(message);
  } // boom(String)

  /**
   * Consume a particular token.
   */
  private void consume(Token desired, PascalTokenizer tokens)
    throws ParseException,Exception
  {
    if (tokens.peek() == desired)
      tokens.next();
    else
      wrongToken(desired, tokens.peek());
  } // consume(Token,PascalTokenizer)
  
  /**
   * Consume an identifier from a token stream or throw an exception.
   */
  private Node getID(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    if (tokens.peek() instanceof Identifier)
      return new Node(tokens.next());
    else
      throw new ParseException("Expected IDENTIFIER, found " 
                               + tokens.peek().toString());
  } // getID(PascalTokenizer)
  
  /**
   * Consume a sequence of one or more identifiers from a token stream and
   * put them in a vector of nodes.
   */
  private Vector getIDs(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector identifiers = new Vector();
    identifiers.add(getID(tokens));
    while (tokens.peek() == PascalTokens.TCOMMA) {
      tokens.next();
      identifiers.add(getID(tokens));
    } // while
   return identifiers;
  } // getIDs(PascalTokenizer)

  /**
   * Consume an integer from a token stream or throw an exception.
   */
  private Node getInteger(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    if (tokens.peek() instanceof IntegerToken)
      return new Node(tokens.next());
    else
      throw new ParseException("Expected INTEGER, found " 
                               + tokens.peek().toString());
  } // getInteger(PascalTokenizer)

  /**
   * Determine if a token is an additive operator.
   */
  private boolean isAddOp(Token tok) {
    return ( (tok == PascalTokens.TPLUS) 
             || (tok == PascalTokens.TMINUS) 
             || (tok == PascalTokens.TOR) );
  } // isAddOp(Token)

  /**
   * Determine if a token is a multiplicative operator.
   */
  private boolean isMulOp(Token tok) {
    return ( (tok == PascalTokens.TTIMES) 
             || (tok == PascalTokens.TDIVIDE) 
             || (tok == PascalTokens.TMOD) 
             || (tok == PascalTokens.TAND) );
  } // isMulOp(Token)

  /**
   * Determine if a token is a relational operator.
   */
  private boolean isRelOp(Token tok) {
    return ( (tok == PascalTokens.TEQUALS) 
             || (tok == PascalTokens.TNOTEQUALS) 
             || (tok == PascalTokens.TLESSTHAN) 
             || (tok == PascalTokens.TLESSEQ) 
             || (tok == PascalTokens.TGREATERTHAN) 
             || (tok == PascalTokens.TGREATEREQ) );
  } // isRelOp(Token)

  /**
   * Give up because the wrong token was found when expecting
   * something particular (a nonterminal or a terminal).
   */
  private Node wrongToken(Symbol expected, Token found)
    throws ParseException
  {
    throw new ParseException("Expected: " + expected.toString() + 
                             "; Found: " + found.toString());
  } // wrongToken(Symbol, Token)


  // +-----------------------+---------------------------------------------
  // | Private Parse Methods |
  // +-----------------------+

  /*
     Each of these procedures throws a ParseException for obvious
     reasons (that is, parsing failed).  Each can also throw a
     host of other exceptions, typically when tokenizing fails.
   */
  private Node parseAddingOperator(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    if (isAddOp(tokens.peek())) {
      return new Node(tokens.next());
    }
    else {
      return wrongToken(PascalNonterminals.ADDING_OPERATOR, tok);
    }
  }  // parseAddingOperator(PascalTokenizer)

  private Node parseArrayType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TARRAY, tokens);
    consume(PascalTokens.TOPENBRACKET, tokens);
    children.add(parseSimpleType(tokens));
    while (tokens.peek() == PascalTokens.TCOMMA) {
      tokens.next();
      children.add(parseSimpleType(tokens));
    } // while
    consume(PascalTokens.TCLOSEBRACKET, tokens);
    consume(PascalTokens.TOF, tokens);
    children.add(parseType(tokens));
    return new Node(PascalNonterminals.ARRAY_TYPE, children);
  } // parseArrayType(PascalTokenizer)

  private Node parseAssignment(Identifier id, PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    // Parse the variable
    children.add(parseVariable(id, tokens));
    // Check for the assignment symbol
    if (tokens.peek() != PascalTokens.TBECOMES)
      return wrongToken(PascalNonterminals.ASSIGNMENT, tokens.peek());
    tokens.next();
    // Parse the expression
    children.add(parseExpression(tokens));
    // Build and return the assignment statement 
    return new Node(PascalNonterminals.ASSIGNMENT, children);
  } // parseAssignment(id, PascalTokenizer)

  private Node parseBlock(PascalTokenizer tokens)
    throws ParseException,Exception
  {
     Vector children = new Vector();
     children.add(parseLabelDeclarations(tokens));
     // System.err.println("Got labels!");
     children.add(parseConstantDefinitions(tokens));
     // System.err.println("Got constants!");
     children.add(parseTypeDefinitions(tokens));
     // System.err.println("Got types!");
     children.add(parseVariableDeclarations(tokens));
     // System.err.println("Got variables!");
     children.add(parseProcFuncDeclarations(tokens));
     // System.err.println("Got procedures and functions!");
     children.add(parseCompoundStatement(tokens));
     // System.err.println("Got compound statement!");
     return new Node(PascalNonterminals.BLOCK, children);
  } // parseBlock(PascalTokenizer)
  
  private Node parseCaseListElement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // Follow[<case_list_element>] = { SEMICOLON, END }
    if ( (tok == PascalTokens.TSEMICOLON)
         || (tok == PascalTokens.TEND) )
      return null;
    // Optimistic approach: Assume it's in good form.
    else {
      Vector children = new Vector();
      children.add(parseConstant(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(parseConstant(tokens));
      } // while
      consume(PascalTokens.TCOLON, tokens);
      children.add(parseStatement(tokens));
      return new Node(PascalNonterminals.CASE_LIST_ELEMENT, children);
    } // else
  } // parseCaseListElement(PascalTokenizer)

  private Node parseCaseStatement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TCASE, tokens);
    children.add(parseExpression(tokens));
    consume(PascalTokens.TOF, tokens);
    children.add(parseCaseListElement(tokens));
    while (tokens.peek() == PascalTokens.TSEMICOLON) {
      tokens.next();
      children.add(parseCaseListElement(tokens));
    } // while
    consume(PascalTokens.TEND, tokens);
    return new Node(PascalNonterminals.CASE_STATEMENT, children);
  } // parseCaseStatement(PascalTokenizer)

  private Node parseCompoundStatement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // Sanity check: Compound statements begin with begin.
    consume(PascalTokens.TBEGIN, tokens);
    // Prepare to build a list of statements.
    Vector statements = new Vector();
    // Read the first statement, if there is one
    if (tokens.peek() != PascalTokens.TEND) {
      statements.add(parseStatement(tokens));
      // Keep reading statements until we hit the end of the compound statement.
      while (tokens.peek() == PascalTokens.TSEMICOLON) {
        tokens.next();
        statements.add(parseStatement(tokens));
      } // while
    } // no end
    // Get the END symbol.
    consume(PascalTokens.TEND, tokens);
    // Build the nice new list
    return new Node(PascalNonterminals.COMPOUND_STATEMENT, statements);
  } // parseCompoundStatement(PascalTokenizer)
 
  private Node parseConstant(PascalTokenizer tokens)
    throws ParseException,Exception
  {
     Token tok = tokens.peek();
     if (tok instanceof StringToken) {
       tokens.next();
       return new Node(tok);
     }
     else {
       Vector children = new Vector();
       if ( (tok == PascalTokens.TPLUS) 
            || (tok == PascalTokens.TMINUS) ) {
         tokens.next();
         children.add(new Node(tok));
       }
       else {
         children.add(null);
       }
       children.add(parseUnsignedNumber(tokens));
       return new Node(PascalNonterminals.CONSTANT, children);
     }
  } // parseConstant(PascalTokenizer)
  
  private Node parseConstantDefinition(PascalTokenizer tokens)
    throws ParseException,Exception
  {
     Vector children = new Vector();
     children.add(getID(tokens));
     consume(PascalTokens.TEQUALS, tokens);
     children.add(parseConstant(tokens));
     return new Node(PascalNonterminals.CONSTANT_DEFINITION, children);
  } // parseConstantDefinition(PascalTokenizer)
  
  private Node parseConstantDefinitions(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    if (tokens.peek() == PascalTokens.TCONST) {
      tokens.next();
      Vector children = new Vector();
      children.add(parseConstantDefinition(tokens));
      consume(PascalTokens.TSEMICOLON, tokens);
      while (tokens.peek() instanceof Identifier) {
        children.add(parseConstantDefinition(tokens));
        consume(PascalTokens.TSEMICOLON, tokens);
      } // while
      return new Node(PascalNonterminals.CONSTANT_DEFINITIONS, children);
    }
    else {
      return null;
    }
  } // parseConstantDefinitions(PascalTokenizer)
  
  private Node parseDirection(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    if ( (tok == PascalTokens.TTO) || (tok == PascalTokens.TDOWNTO) ) {
      tokens.next();
      return new Node(tok);
    } 
    else {
      throw new ParseException("Expection to/downto in for statement, found: "
                               + tok.toString());
    } // invalid direction
  } // parseDirection(PascalTokenizer)

  private Node parseElement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Node exp = parseExpression(tokens);
    if (tokens.peek() == PascalTokens.TDOTDOT) {
      tokens.next();
      Vector children = new Vector();
      children.add(exp);
      children.add(parseExpression(tokens));
      return new Node(PascalNonterminals.RANGE, children);
    }
    else 
      return exp;
  } // parseElement(PascalTokenizer)

  private Node parseExpression(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // Special case: Strings
    if (tokens.peek() instanceof StringToken)
      return new Node(tokens.next());
    // Other expressions need to begin with simple expressions
    Node primary = parseSimpleExpression(tokens);
    // Expressions can sometimes have relational operators
    if (isRelOp(tokens.peek())) {
      Vector children = new Vector();
      children.add(primary);
      children.add(parseRelationalOperator(tokens));
      children.add(parseSimpleExpression(tokens));
      return new Node(PascalNonterminals.EXPRESSION, children);
    }
    // If there's no relational operator, stick with the current
    // expression
    else {
      return primary;
    }
  } // parseExpression(PascalTokenizer)

  private Node parseFactor(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();

    // Unsigned constants that aren't named.
    if ( (tok instanceof IntegerToken) 
         || (tok instanceof RealToken) ) {
      return new Node(tokens.next());
    }

    // Things that begin with identifiers: Variables, Constants, and 
    // Function calls.
    else if (tok instanceof Identifier) {
      tokens.next();
      if (tokens.peek() == PascalTokens.TOPENPAREN) {
        return parseFunctionCall((Identifier) tok, tokens);
      }
      else {
        return parseVariable((Identifier) tok, tokens);
      }
    } // things that start with identifiers

    // Parenthesized expressions.
    else if (tok == PascalTokens.TOPENPAREN) {
      // Consume the open paren
      tokens.next();
      // Get the subexpression
      Node result = parseExpression(tokens);
      // Make sure the end paren is there.
      consume(PascalTokens.TCLOSEPAREN, tokens);
      // Don't forget to return the intermediate result
      return result;
    } // OPENPAREN

    // Negation of factors
    else if (tok == PascalTokens.TNOT) {
      tokens.next();
      return new Node(tok, parseFactor(tokens));
    } // NOT

    // Sets
    else if (tok == PascalTokens.TOPENBRACKET) {
      return parseSetValue(tokens);
    } // OPENBRACKET

    // Everything else
    else {
      // Whoops, something's wrong
      return wrongToken(PascalNonterminals.FACTOR, tokens.peek());
    }
  } // parseFactor(PascalTokenizer)

  private Node parseFileType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    consume(PascalTokens.TFILE, tokens);
    consume(PascalTokens.TOF, tokens);
    return new Node(PascalNonterminals.FILE_TYPE, parseType(tokens));
  } // parseFileType(PascalTokenizer)

  /**
   * Bad design decision: If the fixed part is followed by a
   * semicolon, consumes that semicolon, too.
   */
  private Node parseFixedPart(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    // Special case: Empty fixed part.
    if (tokens.peek() == PascalTokens.TCASE)
      return null;
    else {
      Vector children = new Vector();
      children.add(parseRecordSection(tokens));
      while (tokens.peek() == PascalTokens.TSEMICOLON) {
        tokens.next();
        if (tokens.peek() != PascalTokens.TCASE) 
          children.add(parseRecordSection(tokens));
      } // while
      return new Node(PascalNonterminals.FIXED_PART, children);
    } // normal case
  } // parseFixedPart(PascalTokenizer)

  private Node parseFormalParameterSection(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // First[<parameter_group>] = { ID }
    if (tok instanceof Identifier)
      return parseParameterGroup(tokens);
    else if ((tok == PascalTokens.TVAR) || (tok == PascalTokens.TFUNCTION)) {
      tokens.next();
      return new Node(tok, parseParameterGroup(tokens));
    }
    else if (tok == PascalTokens.TPROCEDURE) {
      tokens.next();
      return new Node(tok, getIDs(tokens));
    } 
    else {
      throw new ParseException("Expected formal param section, found: "
                               + tok.toString());
    }
  } // parseFormalParameterSection(PascalTokenizer)

  private Node parseFormals(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // Follow[<formals>] = { SEMICOLON, COLON }
    if ( (tok == PascalTokens.TSEMICOLON) || (tok == PascalTokens.TCOLON) )
      return null;
    else if (tok == PascalTokens.TOPENPAREN) {
      Vector children = new Vector();
      // Skip the open paren
      tokens.next();
      // Read each section
      children.add(parseFormalParameterSection(tokens));
      while (tokens.peek() == PascalTokens.TSEMICOLON) {
        tokens.next();
        children.add(parseFormalParameterSection(tokens));
      }
      consume(PascalTokens.TCLOSEPAREN, tokens);
      return new Node(PascalNonterminals.FORMALS, children);
    } // else
    else {
      throw new ParseException("Expected formal parameters, found: "
                               + tok.toString());
    }
  } // parseFormals(PascalTokenizer)

  private Node parseForStatement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TFOR, tokens);
    children.add(getID(tokens));
    consume(PascalTokens.TBECOMES, tokens);
    children.add(parseExpression(tokens));
    children.add(parseDirection(tokens));
    children.add(parseExpression(tokens));
    consume(PascalTokens.TDO, tokens);
    children.add(parseStatement(tokens));
    return new Node(PascalNonterminals.FOR_STATEMENT, children);
  } // parseForStatement(PascalTokenizer)

  private Node parseFunctionCall(Identifier id, PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    children.add(new Node(id));
    consume(PascalTokens.TOPENPAREN, tokens);
    if (tokens.peek() != PascalTokens.TCLOSEPAREN) {
      children.add(parseExpression(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(parseExpression(tokens));
      } // while
    } // if we don't immediately see a close paren.
    consume(PascalTokens.TCLOSEPAREN, tokens);
    return new Node(PascalNonterminals.FUNCTION_CALL, children);
  } // parseFunctionCall(Identifier, PascalTokenizer)

  /**
   * Function declarations have four parts: name, formals (may be null),
   * return type, and body (a block).
   */
  private Node parseFunctionDeclaration(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TFUNCTION, tokens);
    children.add(getID(tokens));
    children.add(parseFormals(tokens));
    consume(PascalTokens.TCOLON, tokens);
    children.add(new Node(PascalNonterminals.TYPE_IDENTIFIER, getID(tokens)));
    consume(PascalTokens.TSEMICOLON, tokens);
    children.add(parseBlock(tokens));
    return new Node(PascalNonterminals.FUNCTION_DECLARATION, children);
  } // parseFunctionDeclaration(PascalTokenizer)
  
  private Node parseGoto(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    consume(PascalTokens.TGOTO, tokens);
    return new Node(PascalNonterminals.GOTO_STATEMENT, getInteger(tokens));
  } // parseGoto(PascalTokenizer)

  private Node parseIdentifierList(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    return new Node(PascalNonterminals.IDENTIFIER_LIST, getIDs(tokens));
  } // parseIdentifierList(PascalTokenizer)

  private Node parseIf(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // By looking ahead after the then part to see if we have
    // an else, we get the appropriate kind of binding, even
    // though the original grammar is ambiguous on this issue.
    Vector children = new Vector();
    consume(PascalTokens.TIF, tokens);
    children.add(parseExpression(tokens));
    consume(PascalTokens.TTHEN, tokens);
    children.add(parseStatement(tokens));
    // Optional else part
    if (tokens.peek() == PascalTokens.TELSE) {
      tokens.next();
      children.add(parseStatement(tokens));
    }
    return new Node(PascalNonterminals.CONDITIONAL, children);
  } // parseIf(PascalTokenizer)

  private Node parseLabelDeclarations(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    if (tokens.peek() == PascalTokens.TLABEL) {
      tokens.next();
      Vector children = new Vector();
      children.add(getInteger(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(getInteger(tokens));
      } // while
      consume(PascalTokens.TSEMICOLON, tokens);
      return new Node(PascalNonterminals.LABEL_DECLARATIONS, children);
    } // if we see the label
    else {
      return null;
    } // no labels
  } // parseLabelDeclarations(PascalTokenizer)     
  
  private Node parseMultiplyingOperator(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    if (isMulOp(tokens.peek())) 
      return new Node(tokens.next());
    else 
      return wrongToken(PascalNonterminals.MULTIPLYING_OPERATOR, tokens.peek());
  }  // parseMultiplyingOperator(PascalTokenizer)

  /**
   * Structures that are packed.
   */
  public Node parsePackedType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    consume(PascalTokens.TPACKED, tokens);
    return new Node(PascalNonterminals.PACKED, parseStructuredType(tokens));
  } // parsePackedType(PascalTokenizer)

  /**
   * Parse a group of parameters (identifier list followed by type).
   */
  public Node parseParameterGroup(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // Get all the identifiers.
    Vector children = getIDs(tokens);
    // Skip the colon.
    consume(PascalTokens.TCOLON, tokens);
    // Add the type
    children.add(getID(tokens));
    // Get the type and return the node.
    return new Node(PascalNonterminals.PARAMETER_GROUP, children);
  } // parseParameterGroup(PascalTokenizer)

  private Node parsePointerType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    consume(PascalTokens.TPOINTER, tokens);
    return new Node(PascalNonterminals.POINTER, 
                    new Node(PascalNonterminals.TYPE_IDENTIFIER, getID(tokens)));
  } // parsePointerType(PascalTokenizer)

  private Node parseProcedureCall(Identifier id, PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    children.add(new Node(id));
    if (tokens.peek() == PascalTokens.TOPENPAREN) {
      tokens.next();
      children.add(parseExpression(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(parseExpression(tokens));
      } // while
      consume(PascalTokens.TCLOSEPAREN, tokens);
    } // if there's an open paren
    return new Node(PascalNonterminals.PROCEDURE_CALL, children);
  } // parseProcedureCall(PascalTokenizer)

  /**
   * Procedure declarations have three parts: ID, Formals (may be null),
   * and body (a block).
   */
  private Node parseProcedureDeclaration(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TPROCEDURE, tokens);
    children.add(getID(tokens));
    children.add(parseFormals(tokens));
    consume(PascalTokens.TSEMICOLON, tokens);
    children.add(parseBlock(tokens));
    return new Node(PascalNonterminals.PROCEDURE_DECLARATION, children);
  } // parseProcedureDeclaration(PascalTokenizer)
  
  private Node parseProcFuncDeclarations(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // Follow[<procfuncdeclarations>] = { BEGIN }
    Token tok;
    Vector children = new Vector();
    while ((tok = tokens.peek()) != PascalTokens.TBEGIN) {
      if (tok == PascalTokens.TFUNCTION) 
        children.add(parseFunctionDeclaration(tokens));
      else if (tok == PascalTokens.TPROCEDURE)
        children.add(parseProcedureDeclaration(tokens));
      else
        throw new ParseException("Expected procedure/function declaration; "
                                 + "found: " + tok.toString());
      consume(PascalTokens.TSEMICOLON, tokens);
    } // while
    return new Node(PascalNonterminals.PROCFUNC_DECLARATIONS, children);
  } // parseProcFuncDeclarations(PascalTokenizer)     

  private Node parseProgram(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    children.add(parseProgramHeading(tokens));
    children.add(parseBlock(tokens));
    consume(PascalTokens.TDOT, tokens);
    return new Node(PascalNonterminals.PROGRAM, children);
  } // parseProgram(PascalTokenizer)

  private Node parseProgramHeading(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TPROGRAM, tokens);
    children.add(getID(tokens));
    consume(PascalTokens.TOPENPAREN, tokens);
    children.add(parseIdentifierList(tokens));
    consume(PascalTokens.TCLOSEPAREN, tokens);
    consume(PascalTokens.TSEMICOLON, tokens);
    return new Node(PascalNonterminals.PROGRAM_HEADING, children);
  } // parseProgramHeading(PascalTokenizer)
  
  /** 
   * Parse a section of a record.
   */
  private Node parseRecordSection(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // Follow[<record_section>] = { SEMICOLON, END, CLOSEPAREN }
    if ( (tok == PascalTokens.TSEMICOLON)
         || (tok == PascalTokens.TEND)
         || (tok == PascalTokens.TCLOSEPAREN) ) {
      return null;
    } // epsilon rule
    else {
      Vector children = new Vector();
      children.add(getID(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(getID(tokens));
      } // while
      consume(PascalTokens.TCOLON, tokens);
      children.add(parseType(tokens));
      return new Node(PascalNonterminals.RECORD_SECTION, children);
    } // non-epsilon rule
  } // parseRecordSection(PascalTokenizer)

  /**
   * Records will always have two children: The fixed part and the
   * variant part.  Either part may be null.
   */
  private Node parseRecordType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TRECORD, tokens);
    children.add(parseFixedPart(tokens));
    children.add(parseVariantPart(tokens));
    consume(PascalTokens.TEND, tokens);
    // And we build the node
    return new Node(PascalNonterminals.RECORD_TYPE, children);
  } // parseRecordType(PascalTokinezer)

  private Node parseRelationalOperator(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    if (isRelOp(tokens.peek())) 
      return new Node(tokens.next());
    else 
      return wrongToken(PascalNonterminals.RELATIONAL_OPERATOR, tokens.peek());
  }  // parseRelationalOperator(PascalTokenizer)

  private Node parseRepeatStatement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TREPEAT, tokens);
    children.add(parseStatement(tokens));
    while (tokens.peek() == PascalTokens.TSEMICOLON) {
      tokens.next();
      children.add(parseStatement(tokens));
    } // while
    consume(PascalTokens.TUNTIL, tokens);
    children.add(parseExpression(tokens));
    return new Node(PascalNonterminals.REPEAT_STATEMENT, children);
  } // parseRepeatStatement(PascalTokenizer)

  private Node parseSetValue(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TOPENBRACKET, tokens);
    // Sets can be empty, so we check for the close bracket.
    if (tokens.peek() != PascalTokens.TCLOSEBRACKET) {
      children.add(parseElement(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(parseElement(tokens));
      } // while
    }
    consume(PascalTokens.TCLOSEBRACKET, tokens);
    return new Node(PascalNonterminals.SET, children);
  } // parseSetValue(PascalTokenizer)

  private Node parseSetType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    consume(PascalTokens.TSET, tokens);
    consume(PascalTokens.TOF, tokens);
    return new Node(PascalNonterminals.SET_TYPE, parseSimpleType(tokens));
  } // parseSetType(PascalTokenizer)

  private Node parseSimpleExpression(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // We can put a sign before a term
    if ( (tok == PascalTokens.TPLUS) || (tok == PascalTokens.TMINUS) ) {
      tokens.next();
      return new Node(tok, parseTerm(tokens));
    }
    else {
      // Every other expression begins with a term, so get it.
      Node result = parseTerm(tokens);
      // If the next symbol is an adding operator, there should be
      // that operator and another term, which together make a
      // a compound simple expression.
      while (isAddOp(tokens.peek())) {
        // Three part expression, so build the children
        Vector children = new Vector();
        children.add(result);
        children.add(parseAddingOperator(tokens));
        children.add(parseTerm(tokens));
        // Update the result
        result = new Node(PascalNonterminals.EXPRESSION, children);
      } // while
      // That's it, we're done.
      return result;
    } // no initial sign
  } // parseSimpleExpression(PascalTokenizer)

  private Node parseSimpleType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // Open parens signal scalar types, which are lists of identifiers.
    if (tok == PascalTokens.TOPENPAREN) {
      tokens.next();
      Vector contents = getIDs(tokens);
      consume(PascalTokens.TCLOSEPAREN, tokens);
      return new Node(PascalNonterminals.SCALAR_TYPE, contents);
    } // Open paren: scalar type
    // Identifiers may signal type identifiers or starts of subranges
    else if (tok instanceof Identifier) {
      Node id = getID(tokens);
      if (tokens.peek() == PascalTokens.TDOTDOT) {
        tokens.next();
        Vector children = new Vector();
        Vector grandchildren = new Vector();
        grandchildren.add(null);
        grandchildren.add(id);
        children.add(new Node(PascalNonterminals.CONSTANT, grandchildren));
        children.add(parseConstant(tokens));
        return new Node(PascalNonterminals.SUBRANGE_TYPE, children);
      } // if it's an ellipses
      else // Not an ellipses, must be a single identifier.
        return new Node(PascalNonterminals.TYPE_IDENTIFIER, id);
    } // starts with an identifier
    // As a default, we'll assume it's a subrange type.
    else {
      Vector children = new Vector();
      children.add(parseConstant(tokens));
      consume(PascalTokens.TDOTDOT, tokens);
      children.add(parseConstant(tokens));
      return new Node(PascalNonterminals.SUBRANGE_TYPE, children);
    } // everything else
  } // parseSimpleType(PascalTokenizer)

  private Node parseStatement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();

    // Integers mark labeled statements.
    if (tok instanceof IntegerToken) {
      Vector children = new Vector();
      children.add(getInteger(tokens));
      consume(PascalTokens.TCOLON, tokens);
      children.add(parseStatement(tokens));
      return new Node(PascalNonterminals.LABELED_STATEMENT, children);
    } // integer

    // Identifiers begin assignment statements and procedure calls.
    else if (tok instanceof Identifier) {
      tokens.next();
      Token tmp = tokens.peek();
      // Start of parameter list or in Follow[ProcedureCall]:
      //   Statement
      if ( (tmp == PascalTokens.TOPENPAREN) 
           || (tmp == PascalTokens.TSEMICOLON) 
           || (tmp == PascalTokens.TELSE)
           || (tmp == PascalTokens.TEND)
           || (tmp == PascalTokens.TUNTIL) ) {
        return parseProcedureCall((Identifier) tok, tokens);
      }
      // Anything else: Assignment statement
      else {
        return parseAssignment((Identifier) tok, tokens);
      }
    } // if tok is an identifier

    // The BEGIN token begins compound statements
    else if (tok == PascalTokens.TBEGIN) {
      return parseCompoundStatement(tokens);
    } // BEGIN: Compound.

    // The CASE token begins case statements.
    else if (tok == PascalTokens.TCASE) {
      return parseCaseStatement(tokens);
    } // CASE: Case statements

    // The FOR token begins for statements.
    else if (tok == PascalTokens.TFOR) {
      return parseForStatement(tokens);
    } // FOR

    // The GOTO token begins goto statements.
    else if (tok == PascalTokens.TGOTO) {
      return parseGoto(tokens);
    } // GOTO: Go to

    // The IF token begins conditional statements.
    else if (tok == PascalTokens.TIF) {
      return parseIf(tokens);
    } // IF: Conditional

    // The REPEAT token begins repeat .. until loops.
    else if (tok == PascalTokens.TREPEAT) {
      return parseRepeatStatement(tokens);
    } // REPEAT: Repeat .. Until loop

    // The WHILE token begins while loops.
    else if (tok == PascalTokens.TWHILE) {
      return parseWhileLoop(tokens);
    } // WHILE: While Loop
  
    // The WITH token begins with statements, which we don't like.
    else if (tok == PascalTokens.TWITH) {
      return parseWithStatement(tokens);
    } // WITH: With statement

    // The SEMICOLON, END, and UNTIL tokens follow empty statements.
    else if ( (tok == PascalTokens.TSEMICOLON) 
              || (tok == PascalTokens.TEND) 
              || (tok == PascalTokens.TUNTIL) ) {
      return null;
    } // SEMICOLON/END: Empty

     // Anything else is an error.
    else
      return wrongToken(PascalNonterminals.STATEMENT, tok);
  } // parseStatement(PascalTokenizer)
 
  private Node parseStructuredType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    if (tok == PascalTokens.TARRAY) {
      return parseArrayType(tokens);
    }
    else if (tok == PascalTokens.TRECORD) {
      return parseRecordType(tokens);
    }
    else if (tok == PascalTokens.TSET) {
      return parseSetType(tokens);
    }
    else if (tok == PascalTokens.TFILE) {
      return parseFileType(tokens);
    }
    else {
      throw new Exception("Parsing structured type, found " +
                          tok.toString());
    }
  } //  parseStructuredType(PascalTokenizer)

  private Node parseTerm(PascalTokenizer tokens) 
    throws ParseException,Exception
  {
    // Every term begins with a factor, so get it.
    Node result = parseFactor(tokens);
    // If the next symbol is a multiplying operator, there should be
    // the operator and another factor, which together make a 
    // compound term.
    while (isMulOp(tokens.peek())) {
      // Three part expression, so build the children
      Vector children = new Vector();
      children.add(result);
      children.add(parseMultiplyingOperator(tokens));
      children.add(parseFactor(tokens));
      // Update the result
      result = new Node(PascalNonterminals.EXPRESSION, children);
    } // while
    // That's it, we're done.
    return result;
  } // parseTerm(PascalTokenizer)

  private Node parseType(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // First[<simple_type>] == { OPENPAREN, PLUS, MINUS, INTEGER, ID }
    if ( (tok == PascalTokens.TOPENPAREN) 
         || (tok == PascalTokens.TPLUS)
         || (tok == PascalTokens.TMINUS)
         || (tok instanceof IntegerToken)
         || (tok instanceof Identifier) ) {
      return parseSimpleType(tokens);
    }
    // First[<packed_type>] = { PACKED }
    else if (tok == PascalTokens.TPACKED) {
      return parsePackedType(tokens);
    }
    // First[<structured_type>] = { ARRAY, RECORD, SET, FILE }
    else if ( (tok == PascalTokens.TARRAY)
              || (tok == PascalTokens.TRECORD)
              || (tok == PascalTokens.TSET)
              || (tok == PascalTokens.TFILE) ) {
      return parseStructuredType(tokens);
    }
    // First[<pointer_type>] = { POINTER }
    else if (tok == PascalTokens.TPOINTER) {
      return parsePointerType(tokens);
    }
    // Those are all the types we know about.
    else 
      throw new ParseException("Tried to match a type, found: "
                               + tok.toString());
  } // parseType(PascalTokenizer)

  private Node parseTypeDefinition(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    children.add(getID(tokens));
    consume(PascalTokens.TEQUALS, tokens);
    children.add(parseType(tokens));
    return new Node(PascalNonterminals.TYPE_DEFINITION, children);
  } // parseTypeDefinition(PascalTokenizer)

  private Node parseTypeDefinitions(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    if (tokens.peek() == PascalTokens.TTYPE) {
      tokens.next();
      Vector children = new Vector();
      while (tokens.peek() instanceof Identifier) {
        children.add(parseTypeDefinition(tokens));
        consume(PascalTokens.TSEMICOLON, tokens);
      } // while
      return new Node(PascalNonterminals.TYPE_DEFINITIONS, children);
    }
    else
      return null;
  } // parseTypeDefinitions(PascalTokenizer)
  
  private Node parseUnsignedNumber(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.next();
    if ( (tok instanceof IntegerToken) 
         || (tok instanceof RealToken)
         || (tok instanceof Identifier) )
      return new Node(tok);
    else
      throw new ParseException("Expected: unsigned number; Found: " 
                               + tok.toString());
  } // parseUnsignedNumber(PascalTokenizer)
  
  private Node parseVariable(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    if (tok instanceof Identifier) {
      tokens.next();
      return parseVariable((Identifier) tok, tokens);
    }
    else
      throw new Exception("Attempting to match a variable; found: "
                          + tok.toString());
  } // parseVariable(PascalTokenizer)

  private Node parseVariable(Identifier id, PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // Strategy: 
    // * Keep the variable in "base".  
    // * Repeatedly look ahead at the next token.  If it signals
    //   part of a variable (i.e. a field or array selection or a
    //   pointer dereference), incorporate it and update "base".
    //   Note that this is possible because the operations are all
    //   left-associative.
    Node base = new Node(PascalNonterminals.VARIABLE_NAME, new Node(id));
    boolean done = false;
    while (!done) {
      Token tok = tokens.peek();
      // Do we have a field?
      if (tok == PascalTokens.TDOT) {
        tokens.next();
        Vector children = new Vector();
        children.add(base);
        children.add(getID(tokens));
        base = new Node(PascalNonterminals.FIELD_SELECT, children);
      } // DOT: Field reference
      // Do we have an array reference?
      else if (tok == PascalTokens.TOPENBRACKET) {
        tokens.next();
        Vector children = new Vector();
        children.add(base);
        children.add(parseExpression(tokens));
        while (tokens.peek() == PascalTokens.TCOMMA) {
          tokens.next();
          children.add(parseExpression(tokens));
        } // while we have more array indices
        consume(PascalTokens.TCLOSEBRACKET, tokens);
        base = new Node(PascalNonterminals.ARRAY_SELECT, children);
      } // OPEN BRACKET: Array reference
      // Do we have a pointer dereference
      else if (tok == PascalTokens.TPOINTER) {
        tokens.next();
        Vector children = new Vector();
        children.add(base);
        base = new Node(PascalNonterminals.DEREFERENCE, children);
      } // UP ARROW: Dereference
      else {
        done = true;
      }
    } // while
    // Variable 'base' stores the latest version.
    return base;
  } // parseVariable(Identifier, PascalTokenizer)

  private Node parseVariableDeclaration(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    // Get all the identifiers
    Vector children = getIDs(tokens);
    consume(PascalTokens.TCOLON, tokens);
    // Ready for the type
    children.add(parseType(tokens));
    // We're done
    return new Node(PascalNonterminals.VARIABLE_DECLARATION, children);
  } // parseVariableDeclaration(PascalTokenizer)

  private Node parseVariableDeclarations(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    // Variable declarations are not required, so check for empty declarations.
    if (tokens.peek() != PascalTokens.TVAR)
      return new Node(PascalNonterminals.VARIABLE_DECLARATIONS);
    // Skip over the VARIABLE token.
    tokens.next();
    // Keep reading the variable declarations until we run out.
    while (tokens.peek() instanceof Identifier) {
      children.add(parseVariableDeclaration(tokens));
      if (tokens.peek() != PascalTokens.TSEMICOLON)
        return wrongToken(PascalTokens.TSEMICOLON, tokens.peek());
      tokens.next();
    } // while
    return new Node(PascalNonterminals.VARIABLE_DECLARATIONS, children);
  } // parseVariableDeclarations(PascalTokenizer)

  /**
   * Variants have n parts.
   * 0 ... n-3: Constants that serve as labels/selectors
   * n-2: optional fixed part
   * n-1: optional variant part
   */
  private Node parseVariant(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Token tok = tokens.peek();
    // Follow[<variant>] = { SEMICOLON, END }
    if ( (tok == PascalTokens.TSEMICOLON) || (tok == PascalTokens.TEND) ) {
      return null;
    }
    // Normal structure
    else {
      Vector children = new Vector();
      children.add(parseConstant(tokens));
      while (tokens.peek() == PascalTokens.TCOMMA) {
        tokens.next();
        children.add(parseConstant(tokens));
      } // while
      consume(PascalTokens.TCOLON, tokens);
      consume(PascalTokens.TOPENPAREN, tokens);
      children.add(parseFixedPart(tokens));
      children.add(parseVariantPart(tokens));
      consume(PascalTokens.TCLOSEPAREN, tokens);
      return new Node(PascalNonterminals.VARIANT, children);
    } // Normal structure
  } // parseVariant(PascalTokenizer)
  

  /**
   * Variant parts may be empty.
   * Nonempty variant parts have a number of children:
   * 0: Variable name (if it exists, null otherwise)
   * 1: Type of switch
   * 2 .. n: variants
   */
  private Node parseVariantPart(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    if (tokens.peek() == PascalTokens.TCASE) {
      Vector children = new Vector();
      consume(PascalTokens.TCASE, tokens);
      Node id = getID(tokens);
      // Special case: Optional tag included
      if (tokens.peek() == PascalTokens.TCOLON) {
        tokens.next();
        children.add(id);      // Variable name.
        Node type = getID(tokens);
        children.add(new Node(PascalNonterminals.TYPE_IDENTIFIER, type));
      }
      else {
        children.add(null); // Variable name
        children.add(new Node(PascalNonterminals.TYPE_IDENTIFIER, id));
      } // else
      consume(PascalTokens.TOF, tokens);
      children.add(parseVariant(tokens));
      while (tokens.peek() == PascalTokens.TSEMICOLON) {
        tokens.next();
        children.add(parseVariant(tokens));
      } // while
      return new Node(PascalNonterminals.VARIANT_PART, children);
    } // nonempty variant
    // Empty variant
    else {
      return null;
    }
  } // parseVariantPart(PascalTokenizer)

  private Node parseWhileLoop(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TWHILE, tokens);
    children.add(parseExpression(tokens));
    consume(PascalTokens.TDO, tokens);
    children.add(parseStatement(tokens));
    return new Node(PascalNonterminals.WHILE_LOOP, children);
  } // parseWhileLoop(PascalTokenizer)
 
  private Node parseWithStatement(PascalTokenizer tokens)
    throws ParseException,Exception
  {
    Vector children = new Vector();
    consume(PascalTokens.TWITH, tokens);
    children.add(parseVariable(tokens));
    while (tokens.peek() == PascalTokens.TCOMMA) {
      tokens.next();
      children.add(parseVariable(tokens));
    }
    consume(PascalTokens.TDO, tokens);
    children.add(parseStatement(tokens));
    return new Node(PascalNonterminals.WITH_STATEMENT, children);
  } // parseWithStatement(PascalTokenizer)
     
} // class PascalParser

