/*********************************************************************
 * Author: B. Alex Bridges                                           *
 * Login ID: brid0129                                                *
 * Class: CPSC-431, Winter 2000                                      *
 * Project: Laboratory Exercise 3                                    *
 * Description: This program is a lexical analyzer, the parser       *
 *              portion of a syntax analyzer, and a semantic analyzer*
 *              for the lanaguage Micro Modula-2.                    *
 * Contents: Methods for doing the syntax analyzing.                 *
 *********************************************************************/

/* IMPORTS */
import java.io.*;


class Syntax
{
  /* CONSTANTS */
    final static boolean b_debug = false; // CONTROLS EXTRA DEBUG OUTPUT

  /* GLOBAL VARIABLES */
    public static StreamTokenizer stmT_file_in; // STREAM OF INPUT
    public static Token Token_look_ahead;       // LOOK AHEAD TOKEN FOR PARSING
    public static boolean b_errors = false;     // ERROR STATUS
  
  /*************************************************************************
   * Method: parse                                                         *
   * Purpose: Parses the stream of input.                                  *
   * Input: --PARAMATERS--                                                 *
   *        => 'stmT_file_in' = The given stream of input.                 *
   * Output: --RETURNS--                                                   *
   *         => NONE                                                       *
   *************************************************************************/
  public static void parse(StreamTokenizer stmT_file_in_given)
  {
    /* LOCAL VARIABLES */
    // NONE

    stmT_file_in = stmT_file_in_given;

    /* SKIP THE COMMENTS */
    do
    {
      Token_look_ahead = Lexical.process(stmT_file_in);
      
      if(b_debug)
        System.out.println("=> Looking at "+Token_look_ahead.str_name+
                           " Token (line "+Token_look_ahead.int_line+": '"+
                           Token_look_ahead.str_actual+"').");
    } while( Token_look_ahead.str_name.equals("comment") );
   
    program_module();
  } // method parse

  /*************************************************************************
   * Method: match                                                         *
   * Purpose: Tries to match the given token name against the name for the *
   *          look ahead token.                                            *
   * Input: --PARAMATERS--                                                 *
   *        => 'str_name_given' = The token name which is expected.        *
   * Output: --RETURNS--                                                   *
   *         => NONE                                                       *
   *************************************************************************/
  public static void match(String str_name_given)
  {
    /* LOCAL VARIABLES */
    // NONE

    if( Token_look_ahead.str_name.equals(str_name_given) )
    {
      if(b_debug)
        System.out.println("=> Matched "+Token_look_ahead.str_name+
                           " Token (line "+Token_look_ahead.int_line+": '"+
                           Token_look_ahead.str_actual+"').");
      
      /* SKIP THE COMMENTS */
      do
      {
        Token_look_ahead = Lexical.process(stmT_file_in);
        
        if(b_debug)
          System.out.println("=> Looking at "+Token_look_ahead.str_name+
                             " Token (line "+Token_look_ahead.int_line+": '"+
                             Token_look_ahead.str_actual+"').");
      } while( Token_look_ahead.str_name.equals("comment") );
    } // if
    else
    {
      b_errors = true;
      
      System.out.println(" SYNTAX ERROR FOR match: '"+
                         Token_look_ahead.str_name+"' instead of '"+
                         str_name_given+"' (line "+
                         Token_look_ahead.int_line+")");
    } // else
  } // method match

  /*************************************************************************
   * Method: error                                                         *
   * Purpose: Handles the reporting of and recovery from syntax errors.    *
   * Input: --PARAMATERS--                                                 *
   *        => 'str_prod_given' = The associated production.               *
   *        => 'str_err_given'  = The assocated error message.             *
   * Output: --RETURNS--                                                   *
   *         => NONE                                                       *
   *************************************************************************/
  public static void error(String str_prod_given, String str_err_given)
  {
    /* LOCAL VARIABLES */
    // NONE

    /* MARK, PRODUCE, AND REPORT ERROR */
    b_errors = true;

    if(str_err_given == null)
      str_err_given = "Unexpected input of '"+Token_look_ahead.str_actual+"'.";
    
    System.out.println("\n SYNTAX ERROR FOR "+str_prod_given+": "+
                       str_err_given+" (line "+Token_look_ahead.int_line+").\n");


    /* SKIP AHEAD TO SYNCHRONIZED TOKEN */
    do
    {
      Token_look_ahead = Lexical.process(stmT_file_in);

      if(b_debug)
        System.out.println("=> Looking at "+Token_look_ahead.str_name+
                           " Token (line "+Token_look_ahead.int_line+": '"+
                           Token_look_ahead.str_actual+"').");
    } while( !( Token_look_ahead.str_name.equals("semicolon") ) );
  } // method error
  
  /*************************************************************************
   * Method: type_error                                                    *
   * Purpose: Handles the reporting of semantic type errors.      *
   * Input: --PARAMATERS--                                                 *
   *        => 'str_prod_given' = The associated production.               *
   *        => 'str_err_given'  = The assocated error message.             *
   * Output: --RETURNS--                                                   *
   *         => NONE                                                       *
   *************************************************************************/
  public static void type_error(String str_prod_given, String str_err_given)
  {
    /* LOCAL VARIABLES */
    // NONE

    /* MARK, PRODUCE, AND REPORT ERROR */
    b_errors = true;

    if(str_err_given == null)
      str_err_given = "Unexpected input of '"+Token_look_ahead.str_actual+"'.";
    
    System.out.println("\n TYPE ERROR FOR "+str_prod_given+": "+
                       str_err_given+" (line "+Token_look_ahead.int_line+").\n");
  } // method type_error
  
  /*************************************************************************
   * Methods to handle the various productions.                            *
   *************************************************************************/
  public static void program_module()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the program_module production . . . ");
    
    // program_module => module_header imports block identifier "."
    //
    // FIRST(program_module) = 
    // FIRST(module_header) = 
    // "MODULE"

    if( Token_look_ahead.str_name.equals("module") )
    {
      module_header();
      imports();
      block();
      match("identifier");
      match("period");
      
      if( Token_look_ahead.str_ttype.equals("TT_EOF") && !b_errors )
        System.out.println("\n End of file successfully reached.");
      else
        System.out.println("\n End of file NOT successfully reached.");
    } // if
    else
    {
      error("program_module",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the program_module production . . . ");
  } // method program_module

  public static void module_header()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the module_header production . . . ");
    
    // module_header => "MODULE" identifier ";"
    //
    // FIRST(module_header) = "MODULE"

    if( Token_look_ahead.str_name.equals("module") )
    {
      match("module");
      match("identifier");
      match("semicolon");
    } // if
    else
    {
      error("module_header",null);
    } // else 

    if(b_debug)
      System.out.println("Exiting the module_header production . . . ");
  } // method module_header

  public static void imports()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the imports production . . . ");
    
    // imports => import imports | null
    //
    // FIRST(imports) = "FROM" , null

    if( Token_look_ahead.str_name.equals("from") )
    {
      import_();
      imports();
    } // if

    if(b_debug)
      System.out.println("Exiting the imports production . . . ");
  } // method imports

  public static void import_()
  {
    /* LOCAL VARIABLES */
    String str_list; // LIST OF IDENTIFIERS

    if(b_debug)
      System.out.println("Entering the import_ production . . . ");
    
    // import => "FROM" identifier "IMPORT" identifier_list ";"
    //
    // FIRST(import) = "FROM"
    
    if( Token_look_ahead.str_name.equals("from") )
    {
      match("from");
      match("identifier");
      match("import");
      str_list = identifier_list();
      match("semicolon");
    } // if
    else
    {
      error("import_",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the import_ production . . . ");
  } // method import_
  
  public static void block()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the block production . . . ");
    
    // block => declarations "BEGIN" statement_sequence "END"
    //
    // FIRST(block) = "VAR" , "TYPE" , "PROCEDURE" , "BEGIN"
 
    if( Token_look_ahead.str_name.equals("var")  ||
        Token_look_ahead.str_name.equals("type") ||
        Token_look_ahead.str_name.equals("procedure") )
    {
      declarations();
    } // if
    if( Token_look_ahead.str_name.equals("begin") )
    {
      match("begin");
      statement_sequence();
      match("end");
    } // if
    else
    {
      error("block",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the block production . . . ");
  } // method block
  
  public static void declarations()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the declarations production . . . ");
    
    // declarations => declaration declarations | null
    // FIRST(declaration) = "VAR" , "TYPE" , "PROCEDURE" , null
    
    if( Token_look_ahead.str_name.equals("var")  ||
        Token_look_ahead.str_name.equals("type") ||
        Token_look_ahead.str_name.equals("procedure") )
    {
      declaration();
      declarations();
    } // if

    if(b_debug)
      System.out.println("Exiting the declarations production . . . ");
  } // method declarations
  
  public static void declaration()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the declaration production . . . ");
    
    // declaration => "VAR" variable_decls |
    //                "TYPE" type_decls |
    //                procedure_declaration
    //
    // FIRST(declaration) = "VAR" , "TYPE" , "PROCEDURE"
    
    if( Token_look_ahead.str_name.equals("var") )
    {
      match("var");
      variable_decls();
    } // if
    else if( Token_look_ahead.str_name.equals("type") )
    {
      match("type");
      type_decls();
    } // if
    else if( Token_look_ahead.str_name.equals("procedure") )
    {
      procedure_declaration();
    } // else
    else
    {
      error("declaration",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the declaration production . . . ");
  } // method declaration
  
  public static void variable_decls()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the variable_decls production . . . ");
    
    // variable_decls => variable_decl varibale_decls  | null
    //
    // FIRST(variable_decls) = identifier , null
    
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      variable_decl();
      variable_decls();
    } // if
    
    if(b_debug)
      System.out.println("Exiting the variable_decls production . . . ");
  } // method variable_decls
  
  public static void variable_decl()
  {
    /* LOCAL VARIABLES */
    String str_list; // LIST OF IDENTIFIERS
    String str_type; // RESULTING TYPE
    int int_pos;     // POSITION OF THE NEXT SPACE IN THE LIST
    int int_index;   // RESULTING INDEX INTO SYMBOL TABLE

    if(b_debug)
      System.out.println("Entering the variable_decl production . . . ");
    
    // variable_decl => identifier_list ":" type ";"
    //
    // FIRST(variable_decl) = identifier
    
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      str_list = identifier_list();
      match("colon");
      str_type = type();
      match("semicolon");

      /* UPDATE EACH IDENTIFIER IN LIST WITH RESULTING TYPE */
      if(b_debug)
        System.out.println("The resulting identifier list is '"+str_list+
                           "' and type is '"+str_type+"'.");

      do
      {
        int_pos = str_list.indexOf(' ');

        if(b_debug)
          System.out.println("The next space position is '"+
                             String.valueOf(int_pos)+"'");

        // => ONE ITEM IN LIST
        if( int_pos == -1)
          int_index = Symbol_Table.update(str_list,str_type);
        // => TWO+ ITEMS IN LIST
        else
        {
          int_index = Symbol_Table.update( str_list.substring(0,int_pos),str_type );
          str_list = str_list.substring(int_pos+1);
        } // else
      } // do
      while(int_pos != -1);
    } // if
    else
    {
      error("variable_decl",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the variable_decl production . . . ");
  } // method variable_decl
  
  public static void type_decls()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the type_decls production . . . ");
    
    // type_decls => type_decl type_decls | null
    //
    // FIRST(type_decls) = identifier , null
    
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      type_decl();
      type_decls();
    } // if

    if(b_debug)
      System.out.println("Exiting the type_decls production . . . ");
  } // method type_decls
  
  public static void type_decl()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the type_decl production . . . ");
    
    // type_decl => identifier "=" type ";"
    //
    // FIRST(type_decl) = identifier
    
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      match("identifier");
      match("=");
      type();
      match("semicolon");
    } // if
    else
    {
      error("type_decl",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the type_decl production . . . ");
  } // method type_decl
  
  public static void procedure_declaration()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the procedure_declaration production . . . ");
    
    // procedure_declaration => procedure_header block identifier ";"
    //
    // FIRST(procedure_declaration) = "PROCEDURE"

    if( Token_look_ahead.str_name.equals("procedure") )
    {
      procedure_header();
      block();
      match("identifier");
      match("semicolon");
    } // if
    else
    {
      error("procedure_declaration",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the procedure_declaration production . . . ");
  } // method procedure_declaration
  
  public static void procedure_header()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the procedure_header production . . . ");
    
    // procedure_header => "PROCEDURE" identifier procedure_header'
    // procedure_header' => "(" parameter_list ")" opt_func ";" | ";"
    //
    // FIRST (procedure_header) = "PROCEDURE"
    // FIRST (procedure_header') = "(" , ";"
    
    if( Token_look_ahead.str_name.equals("procedure") )
    {
      match("procedure");
      match("identifier");
      
      if( Token_look_ahead.str_name.equals("l_paran") )
      {
        match("l_paran");
        parameter_list();
        match("r_paran");
        opt_func();
      } // if
      match("semicolon");
    } // if
    else
    {
      error("procedure_header",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the procedure_header production . . . ");
  } // method procedure_header
  
  public static void parameter_list()
  {
    /* LOCAL VARIABLES */
    // NONE

    if(b_debug)
      System.out.println("Entering the parameter_list production . . . ");
    
    // parameter_list => parameter_group parameter_list'
    // parameter_list' => ";" parameter_group parameter_list' | null
    //
    // FIRST(parameter_list) = "VAR" , identifier
    // FIRST(parameter_list') = ";" , null
    
    if( Token_look_ahead.str_name.equals("var") ||
        Token_look_ahead.str_name.equals("identifier") )
    {
      parameter_group();
      while(true)
      {
        if( Token_look_ahead.str_name.equals("semicolon") )
        {
          match("semicolon");
          parameter_group();
          continue;
        } // if
        else
          break;
      } // while
    } // if
    else
    {
      error("parameter_list",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the parameter_list production . . . ");
  } // method parameter_list
  
  public static void parameter_group()
  {
    /* LOCAL VARIABLES */
    String str_list; // LIST OF IDENTIFIERS
    String str_type; // RESULTING TYPE
    int int_pos;     // POSITION OF THE NEXT SPACE IN THE LIST
    int int_index;   // RESULTING INDEX INTO SYMBOL TABLE

    if(b_debug)
      System.out.println("Entering the parameter_group production . . . ");
    
    // parameter_group => "VAR" identifier_list ":" type_name |
    //                    identifier_list ":" type_name
    //
    // FIRST(paremeter_group) = "VAR" , identifier
    
    if( Token_look_ahead.str_name.equals("var") )
    {
      match("var");
    } // if
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      str_list = identifier_list();
      match("colon");
      str_type = type_name();

      /* UPDATE EACH IDENTIFIER IN LIST WITH RESULTING TYPE */
      if(b_debug)
        System.out.println("The resulting identifier list is '"+str_list+
                           "' and type is '"+str_type+"'.");

      do
      {
        int_pos = str_list.indexOf(' ');

        if(b_debug)
          System.out.println("The next space position is '"+
                             String.valueOf(int_pos)+"'");

        // => ONE ITEM IN LIST
        if( int_pos == -1)
          int_index = Symbol_Table.update(str_list,str_type);
        // => TWO+ ITEMS IN LIST
        else
        {
          int_index = Symbol_Table.update( str_list.substring(0,int_pos),str_type );
          str_list = str_list.substring(int_pos+1);
        } // else
      } // do
      while(int_pos != -1);
    } // if
    else
    {
      error("parameter_group",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the parameter_group production . . . ");
  } // method parameter_group
  
  public static String opt_func()
  {
    /* LOCAL VARIABLES */
    String str_type;  // WORKING AND RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the opt_func production . . . ");

    // opt_func => ":" type_name
    //
    // FIRST(opt_func) = ":"
    if( Token_look_ahead.str_name.equals("colon") )
    {
      match("colon");
      str_type = type_name();
    } // if
    else
    {
      str_type = null;
      error("opt_func",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the opt_func production . . . ");
    
    return(str_type);
  } // method opt_func

  public static String statement_sequence()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type2; // ITEM2 TYPE
    String str_type3; // ITEM3 TYPE
    String str_op;    // OPERATOR
    String str_type;  // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the statement_sequence production . . . ");

    // statement_sequence => statement statement_sequence'
    // statement_sequence' => ";" statement statement_sequence' | null
    //
    // FIRST(statement_sequence) = null , ";" , identifier , 
    //                             "WHILE" , "REPEAT" , "IF", "RETURN"
    // FIRST(statement_sequence') = ";" , null
    
    if( Token_look_ahead.str_name.equals("semicolon")  ||
        Token_look_ahead.str_name.equals("identifier") ||
        Token_look_ahead.str_name.equals("while")      ||
        Token_look_ahead.str_name.equals("repeat")     ||
        Token_look_ahead.str_name.equals("if")         ||
        Token_look_ahead.str_name.equals("return") )
    {
      str_type1 = statement();
      
      while(true)
      {
        if( Token_look_ahead.str_name.equals("semicolon") )
        {
          str_op = Token_look_ahead.str_actual;
          match("semicolon");
          str_type2 = statement();
          
          /* VERIFY THAT THE ITEM1 + ITEM2 TYPES ARE APPROPRIATE */
          //if( str_op.equals(",") )
          //{
            // => BOTH TERMS MUST BE VOID
            if( str_type1.indexOf("VOID") != -1 && 
                str_type2.indexOf("VOID") != -1 )
                str_type = "VOID";
            // => SPECIAL CASE: BOTH TERMS MUST BE NONE
            else if( str_type1.indexOf("<none>") != -1 && 
                     str_type2.indexOf("<none>") != -1 )
                str_type = "VOID";           
            else
            {
              str_type = null;
              type_error("statement_sequence",
                         "'"+str_type1+"' vs. '"+str_type2+"' for '"+str_op+"'");
            } // else
            
            if(b_debug)
              System.out.println("statement_sequence: '"+str_type+"' <- '"+
                                 str_type1+"' vs. '"+str_type2+"' for '"+
                                 str_op+"'");
          //} // if
          //else
          //{
          //  str_type = null;
          //  error("statement_sequence",null);
          //} // else
          
          continue;
        } // if
        else
        {
          str_type = str_type1;
          break;
        } // else
      } // while
    } // if
    else
    {
      str_type = null;
      error("statement_sequence",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the statement_sequence production . . . ");
    
    return(str_type);
  } // method statement_sequence
  
  public static String statement()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type2; // ITEM2 TYPE
    String str_type3; // ITEM3 TYPE
    String str_op;    // OPERATOR
    String str_type;  // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the statement production . . . ");

    // statement => null |
    //              identifier statement'
    //              "WHILE" expression "DO" statement_sequence "END" |
    //              "REPEAT" statement_sequence "UNTIL" expression |
    //              "IF" expression "THEN" statement_sequence statement'' |
    //              "RETURN" expression
    // statement' => null |
    //               "(" expression_list ")" |
    //               ":=" expression |
    //               [" simple_expression "]" ":=" expression
    // statement'' => "END" |
    //                "ELSE" statement_sequence "END"
    //
    // FIRST(statement) = null , identifier , 
    //                    "WHILE" , "REPEAT" , "IF", "RETURN"
    // FIRST(statement') = null , "(" , ":=", "["
    // FIRST(statement'') = "END" , "ELSE"
    
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      match("identifier");
      if( Token_look_ahead.str_name.equals("l_paran") )
      {
        match("l_paran");
        expression_list();
        match("r_paran");
        str_type = "<none>";
      } // if
      else if( Token_look_ahead.str_name.equals("assignment") )
      {
        match("assignment");
        str_type1 = expression();
        str_type = str_type1;
      } // if
      else if( Token_look_ahead.str_name.equals("l_bracket") )
      {
        match("l_bracket");
        str_type1 = simple_expression();
        match("r_bracket");
        match("assignment");
        str_type2 = expression();
        
        if( str_type1.indexOf("INTEGER") != -1 )
            str_type = str_type2;
        else
        {
          str_type = null;
          type_error("statement",
                     "'"+str_type1+"' vs. INTEGER for array index expression");
        } // else  
      } // if
      else
        str_type="VOID";
    } // if
    else if( Token_look_ahead.str_name.equals("while") )
    {
      match("while");
      str_type1 = expression();
      match("do");
      str_type2 = statement_sequence();
      match("end");
      
      if( str_type1.indexOf("BOOLEAN") != -1 )
          str_type = str_type2;
      else
      {
        str_type = null;
        type_error("statement",
                   "'"+str_type1+"' vs. BOOLEAN for while condition");
      } // else
    } // if
    else if( Token_look_ahead.str_name.equals("repeat") )
    {
      match("repeat");
      str_type1 = statement_sequence();
      match("until");
      str_type2 = expression();
      
      if( str_type2.indexOf("BOOLEAN") != -1 )
          str_type = str_type1;
      else
      {
        str_type = null;
        type_error("statement",
                   "'"+str_type1+"' vs. BOOLEAN for until condition");
      } // else
    } // if
    else if( Token_look_ahead.str_name.equals("if") )
    {
      match("if");
      str_type1 = expression();
      match("then");
      str_type2 = statement_sequence();
           
      if( Token_look_ahead.str_name.equals("else") )
      {
        match("else");
        str_type3 = statement_sequence();
      } // if
      match("end");

      if( str_type1.indexOf("BOOLEAN") != -1 )
          str_type = str_type2;
      else
      {
        str_type = null;
        type_error("statement",
                   "'"+str_type1+"' vs. BOOLEAN for if condition");
      } // else
    } // if
    else
      str_type = "VOID";
    
    if(b_debug)
      System.out.println("Exiting the statement production . . . ");
    
    return(str_type);
  } // method statement
  
  public static void expression_list()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type2; // ITEM2 TYPE
    
    if(b_debug)
      System.out.println("Entering the expression_list production . . . ");

    // expression_list => expression expression_list'
    // expression_list' => "," expression expression_list' | null
    //
    // FIRST(expression_list) = add_op , 
    //                          string_constant , 
    //                          integer , real , 
    //                          identifier , 
    //                          "("
    // FIRST(expression_list') = "," , null

    if( Token_look_ahead.str_name.equals("add_op")     ||
        Token_look_ahead.str_name.equals("str_const")  ||
        Token_look_ahead.str_name.equals("integer")    ||
        Token_look_ahead.str_name.equals("real")       ||
        Token_look_ahead.str_name.equals("identifier") ||
        Token_look_ahead.str_name.equals("l_paran") )
    {
      str_type1 = expression();
      
      while(true)
      {
        if( Token_look_ahead.str_name.equals("comma") )
        {
          match("comma");
          str_type2 = expression();
          continue;
        } // if
        else
          break;
      } // while
    } // if
    else
    {
      error("expression_list",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the expression_list production . . . ");
  } // method expression_list
  
  public static String expression()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type2; // ITEM2 TYPE
    String str_op;    // OPERATOR
    String str_type;  // RESULTING TYPE
    
    if(b_debug)
      System.out.println("Entering the expression production . . . ");

    // expression => simple_expression expression'
    // expression' => rel_op simple_expression | null
    //
    // FIRST(expression) = add_op , 
    //                     string_constant , 
    //                     integer , real , 
    //                     identifier , 
    //                     "("
    // FIRST(expression') = rel_op , null
    
    if( Token_look_ahead.str_name.equals("add_op")     ||
        Token_look_ahead.str_name.equals("str_const")  ||
        Token_look_ahead.str_name.equals("integer")    ||
        Token_look_ahead.str_name.equals("real")       ||
        Token_look_ahead.str_name.equals("identifier") ||
        Token_look_ahead.str_name.equals("l_paran") )
    {
      str_type1 = simple_expression();

      while(true)
      {
        if( Token_look_ahead.str_name.equals("rel_op") )
        {
          str_op = Token_look_ahead.str_actual;
          match("rel_op");
          str_type2 = simple_expression();
          
          /* VERIFY THAT THE ITEM1 + ITEM2 TYPES ARE APPROPRIATE */
          //if( str_op.equals("<")  ||
          //    str_op.equals("<=") || 
          //    str_op.equals("=")  || 
          //    str_op.equals("#")  || 
          //    str_op.equals(">=") || 
          //    str_op.equals(">") )
          //{
            // => BOTH TERMS MUST BE BOOLEAN
            if( str_type1.indexOf("BOOLEAN") != -1 && 
                str_type2.indexOf("BOOLEAN") != -1 )
                str_type = "BOOLEAN";
            else
            {
              str_type = null;
              type_error("expression",
                         "'"+str_type1+"' vs. '"+str_type2+"' for '"+str_op+"'");
            } // else
            
            if(b_debug)
              System.out.println("expression: '"+str_type+"' <- '"+
                                 str_type1+"' vs. '"+str_type2+"' for '"+
                                 str_op+"'");
          //} // if
          //else
          //{
          //  str_type = null;
          //  error("expression",null);
          //} // else
          
          continue;
        } // if
        else
        {
          str_type = str_type1;
          break;
        } // else
      } // while
    } // if
    else
    {
      str_type = null;
      error("expression",null);
    } // else
   
    if(b_debug)
      System.out.println("Exiting the expression production . . . ");
    
    return(str_type);
  } // method expression
  
  public static String simple_expression()
  {
    /* LOCAL VARIABLES */
    String str_type; // WORKING AND RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the simple_expression production . . . ");

    // simple_expression => add_op uns_simple_expression | uns_simple_expression
    //
    // FIRST(simple_expression) = add_op , 
    //                            string_constant , 
    //                            integer , real , 
    //                            identifier , 
    //                            "("
    
    if( Token_look_ahead.str_name.equals("add_op") )
    {
      match("add_op");
    } // if
    if( Token_look_ahead.str_name.equals("str_const")  ||
        Token_look_ahead.str_name.equals("integer")    ||
        Token_look_ahead.str_name.equals("real")       ||
        Token_look_ahead.str_name.equals("identifier") ||
        Token_look_ahead.str_name.equals("l_paran") )
    {
      str_type = uns_simple_expression();
    } // if
    else
    {
      str_type = null;
      error("simple_expression",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the simple_expression production . . . ");
    
    return(str_type);
  } // method simple_expression
  
  public static String uns_simple_expression()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type2; // ITEM2 TYPE
    String str_op;    // OPERATOR
    String str_type;  // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the uns_simple_expression production . . . ");

    // uns_simple_expression => term uns_simple_expression'
    // uns_simple_expression' => add_op term uns_simple_expression' | null
    //
    // FIRST(uns_simple_expression) = string_constant , 
    //                                integer , real , 
    //                                identifier , 
    //                                "("
    // FIRST(uns_simple_expression') = add_op , null

    if( Token_look_ahead.str_name.equals("str_const")  ||
        Token_look_ahead.str_name.equals("integer")    ||
        Token_look_ahead.str_name.equals("real")       ||
        Token_look_ahead.str_name.equals("identifier") ||
        Token_look_ahead.str_name.equals("l_paran") )
    {
      str_type1 = term();

      while(true)
      {
        if( Token_look_ahead.str_name.equals("add_op") )
        {
          str_op = Token_look_ahead.str_actual;
          match("add_op");
          str_type2 = term();
          
          /* VERIFY THAT THE ITEM1 + ITEM2 TYPES ARE APPROPRIATE */
          //if( str_op.equals("+") || str_op.equals("-") )
          //{
            // => BOTH TERMS MUST MATCH
            if( str_type1.compareToIgnoreCase("INTEGER") == 0 && 
                str_type2.compareToIgnoreCase("INTEGER") == 0 )
              str_type = str_type1;
            else if( str_type1.compareToIgnoreCase("REAL") == 0 && 
                     str_type2.compareToIgnoreCase("REAL") == 0)
              str_type = str_type1;
            else if( str_type1.indexOf("NUMBER") != -1 && 
                     str_type2.indexOf("NUMBER") != -1 )
              str_type = str_type1;
            else
            {
              str_type = null;
              type_error("uns_simple_expression",
                         "'"+str_type1+"' vs. '"+str_type2+"' for '"+str_op+"'");
            } // else
            
            if(b_debug)
              System.out.println("uns_simple_expression: '"+str_type+"' <- '"+
                                 str_type1+"' vs. '"+str_type2+"' for '"+
                                 str_op+"'");
          //} // if
          //else
          //{
          //  str_type = null;
          //  error("uns_simple_expression",null);
          //} // else
          
          continue;
        } // if
        else
        {
          str_type = str_type1;
          break;
        } // else
      } // while
    } // if
    else
    {
      str_type = null;
      error("uns_simple_expression",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the uns_simple_expression production . . . ");
    
    return(str_type);
  } // method uns_simple_expression
  
  public static String term()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type2; // ITEM2 TYPE
    String str_op;    // OPERATOR
    String str_type;  // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the term production . . . ");

    // term => factor term'
    // term' => mul_op factor term' | null
    //
    // FIRST(term) = string_constant , 
    //               integer , real , 
    //               identifier , 
    //               "("
    // FIRST(term') = mul_op , null

    if( Token_look_ahead.str_name.equals("str_const")  ||
        Token_look_ahead.str_name.equals("integer")    ||
        Token_look_ahead.str_name.equals("real")       ||
        Token_look_ahead.str_name.equals("identifier") ||
        Token_look_ahead.str_name.equals("l_paran") )
    {   
      str_type1 = factor();

      while(true)
      {
        if( Token_look_ahead.str_name.equals("mul_op") )
        {
          str_op = Token_look_ahead.str_actual;
          match("mul_op");
          str_type2 = factor();
          
          /* VERIFY THAT THE ITEM1 + ITEM2 TYPES ARE APPROPRIATE */
          if( str_op.equals("DIV") || str_op.equals("MOD") )
          {
            // => BOTH TERMS MUST BE INTEGER
            if( str_type1.indexOf("INTEGER") != -1 && 
                str_type2.indexOf("INTEGER") != -1 )
              str_type = "INTEGER";
            else
              type_error("term",
                         "'"+str_type1+"' vs. '"+str_type2+"' for '"+str_op+"'");
          } // if
          else if( str_op.equals("/") )
          {
            // => BOTH TERMS MUST BE REAL
            if( str_type1.indexOf("REAL") != -1 && 
                str_type2.indexOf("REAL") != -1 )
              str_type = "REAL";
            else
              type_error("term",
                         "'"+str_type1+"' vs. '"+str_type2+"' for '"+str_op+"'");
          } // if
          else
          {
            // => BOTH TERMS MUST MATCH
            if( str_type1.compareToIgnoreCase("INTEGER") == 0 && 
                str_type2.compareToIgnoreCase("INTEGER") == 0 )
              str_type = str_type1;
            else if( str_type1.compareToIgnoreCase("REAL") == 0 && 
                     str_type2.compareToIgnoreCase("REAL") == 0)
              str_type = str_type1;
            else if( str_type1.indexOf("NUMBER") != -1 && 
                     str_type2.indexOf("NUMBER") != -1 )
              str_type = str_type1;
            else
            {
              str_type = null;
              type_error("term",
                         "'"+str_type1+"' vs. '"+str_type2+"' for '"+str_op+"'");
            } // else
            
            if(b_debug)
              System.out.println("term: '"+str_type+"' <- '"+
                                 str_type1+"' vs. '"+str_type2+"' for '"+
                                 str_op+"'");
          } // else
          
          continue;
        } // if
        else
        {
          str_type = str_type1;
          break;
        } // else
      } // while
    } // if
    else
    {
      str_type = null;
      error("term",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the term production . . . ");
    
    return(str_type);
  } // method term
  
  public static String factor()
  {
    /* LOCAL VARIABLES */
    String str_type1; // ITEM1 TYPE
    String str_type;  // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the factor production . . . ");

    // factor => constant |
    //           identifier factor' |
    //           "(" expression ")"
    // factor' => null |
    //            "(" expression_list ")" |
    //            "[" simple_expression "]"
    //
    // FIRST(factor) = string_constant , 
    //                 integer , real , 
    //                 identifier , 
    //                 "("
    // FIRST(factor') = null , "(" , "["
    
    if( Token_look_ahead.str_name.equals("str_const") ||
        Token_look_ahead.str_name.equals("integer")   ||
        Token_look_ahead.str_name.equals("real") )
    {
      str_type1 = constant();
      str_type = str_type1;
    } // if
    else if( Token_look_ahead.str_name.equals("identifier") )
    {
      match("identifier");
      
      if( Token_look_ahead.str_name.equals("l_paran") )
      {
        match("l_paran");
        expression_list();
        match("r_paran");
        str_type="<none>";
      } // if
      else if( Token_look_ahead.str_name.equals("l_bracket") )
      {
        match("l_bracket");
        str_type1 = simple_expression();
        match("r_bracket");

        if( str_type1.indexOf("INTEGER") != -1 )
            str_type = str_type1;
        else
        {
          str_type = null;
          type_error("statement",
                     "'"+str_type1+"' vs. INTEGER for array index expression");
        } // else

      } // if
      else
        str_type="NULL";
      
    } // if  
    else if( Token_look_ahead.str_name.equals("l_paran") )
    {
      match("l_paran");
      str_type1 = expression();
      match("r_paran");
      str_type = str_type1;
    } // if
    else
    {
      str_type = null;
      error("factor",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the factor production . . . ");
    
    return(str_type);
  } // method factor
  
  public static String constant()
  {
    /* LOCAL VARIABLES */
    String str_type; // WORKING AND RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the constant production . . . ");

    // constant => string_constant | number
    //
    // FIRST(constant) = string_constant , integer , real
    
    if( Token_look_ahead.str_name.equals("str_const") )
    {
      str_type = "STR_CONST";
      match("str_const");
    } // if
    else if( Token_look_ahead.str_name.equals("integer") ||
             Token_look_ahead.str_name.equals("real") )
    {
      str_type = number();
    } // if
    else
    {
      str_type = null;
      error("constant",null);
    } // else
    
    if(b_debug)
      System.out.println("Exiting the constant production . . . ");
    
    return(str_type);
  } // method constant
  
  public static String number()
  {
    /* LOCAL VARIABLES */
    String str_type; // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the number production . . . ");

    // number => integer | real
    //
    // FIRST(number) = integer , real
    //
    // NOTE: In order to correctly handle "END <identifier>.", I had to 
    //       drop the parseNumbers option from the StreamTokenizer for 
    //       the syntax analyzer.  As a result it is not able to properly
    //       distinquish between integer and real numbers.  It is treating 
    //       all of them as reals.  As a work around, I have therefore put 
    //       both names in the type information.
    //
    
    if( Token_look_ahead.str_name.equals("integer") )
    {
      str_type = "NUMBER(INTEGER/REAL)";
      match("integer");
    } // if
    else if( Token_look_ahead.str_name.equals("real") )
    {
      str_type = "NUMBER(REAL/INTEGER)";
      match("real");
    } // if
    else
    {
      str_type = null;
      error("number",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the number production . . . ");
    
    return(str_type);
  } // method number
  
  public static String identifier_list()
  {
    /* LOCAL VARIABLES */
    String str_list; // LIST OF IDENTIFIERS SO THAT THEY MAY LATER BE TYPED

    if(b_debug)
      System.out.println("Entering the identifier_list production . . . ");

    // identifier_list => identifier identifier_list'
    // identifier_list' => "," identifier identifier_list' | null
    //
    // FIRST(identifier_list) = identifier
    // FIRST(identifier_list) = "," , null
    
    if( Token_look_ahead.str_name.equals("identifier") )
    {
      str_list = Token_look_ahead.str_actual+" ";
      match("identifier");

      while(true)
      {
        if( Token_look_ahead.str_name.equals("comma") )
        {
          match("comma");
          str_list = str_list+Token_look_ahead.str_actual+" ";
          match("identifier");
          continue;
        } // if
        else
          break;
      } // while
    } // if
    else
    {
      str_list = null;
      error("identifier_list",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the identifier_list production . . . ");
    
    return( str_list.trim() );
  } // method identifier_list
  
  public static String type()
  {
    /* LOCAL VARIABLES */
    int int_lower;   // LOWER BOUNDS OF ARRAY
    int int_upper;   // UPPER BOUNDS OF ARRAY
    String str_type; // WORKING AND RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the type production . . . ");

    // type => type_name | 
    //         "ARRAY" "[" integer ".." integer "]" "OF" type_name
    //
    // FIRST(type) = identifier , "ARRAY"
    
    if( Token_look_ahead.str_name.equals("array") )
    {
      match("array");
      match("l_bracket");
      int_lower = Integer.parseInt(Token_look_ahead.str_actual);
      match("integer");
      match("ellipsis");
      int_upper = Integer.parseInt(Token_look_ahead.str_actual);
      match("integer");
      match("r_bracket");
      match("of");
      str_type = type_name();      
      str_type = "ARRAY("+String.valueOf(int_lower)+".."+
                 String.valueOf(int_upper)+","+str_type+")";
    } // if
    if( Token_look_ahead.str_name.equals("type_name") )
    {
      str_type = type_name();
    } // if
    else
    {
      str_type = null;
      error("type",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the type production . . . ");
    
    return(str_type);
  } // method type
  
  public static String type_name()
  {
    /* LOCAL VARIABLES */
    String str_type; // RESULTING TYPE

    if(b_debug)
      System.out.println("Entering the type_name production . . . ");

    // type_name => identifier
    //
    // FIRST(type_name) = identifier
    //
    // NOTE: This kind of identifier is recognized by the 
    //       lexical analyzer and therefore has been given 
    //       the name "type_name" as opposed to "identifier".
    
    if( Token_look_ahead.str_name.equals("type_name") )
    {
      str_type = Token_look_ahead.str_actual;
      match("type_name");
    } // if
    else
    {
      str_type = null;
      error("type_name",null);
    } // else

    if(b_debug)
      System.out.println("Exiting the type_name production . . . ");
    
    return(str_type);
  } // method type_name
} // class Syntax

