package java_cup ; import java.util.Enumeration ; import java.util.Hashtable ; /** * This class represents a non-terminal symbol in the grammar. Each non terminal * has a textual name, an index, and a string which indicates the type of object * it will be implemented with at runtime (i.e. the class of object that will be * pushed on the parse stack to represent it). * * @version last updated: 11/25/95 * @author Scott Hudson */ public class non_terminal extends symbol { /*-----------------------------------------------------------*/ /*--- Constructor(s) ----------------------------------------*/ /*-----------------------------------------------------------*/ /** * Full constructor. * * @param nm the name of the non terminal. * @param tp the type string for the non terminal. */ public non_terminal ( String nm , String tp ) { /* super class does most of the work */ super ( nm , tp ) ; /* add to set of all non terminals and check for duplicates */ Object conflict = _all.put ( nm , this ) ; if ( conflict != null ) // can't throw an exception here because these are used in static // initializers, so we crash instead // was: // throw new internal_error("Duplicate non-terminal ("+nm+") created"); ( new internal_error ( "Duplicate non-terminal (" + nm + ") created" ) ) .crash ( ) ; /* assign a unique index */ _index = next_index ++ ; /* add to by_index set */ _all_by_index.put ( new Integer ( _index ) , this ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** * Constructor with default type. * * @param nm the name of the non terminal. */ public non_terminal ( String nm ) { this ( nm , null ) ; } /*-----------------------------------------------------------*/ /*--- (Access to) Static (Class) Variables ------------------*/ /*-----------------------------------------------------------*/ /** * Table of all non-terminals -- elements are stored using name strings as the * key */ protected static Hashtable _all = new Hashtable ( ) ; // Hm Added clear to clear all static fields public static void clear ( ) { _all.clear ( ) ; _all_by_index.clear ( ) ; next_index = 0 ; next_nt = 0 ; } /** Access to all non-terminals. */ public static Enumeration all ( ) { return _all.elements ( ) ; } /** lookup a non terminal by name string */ public static non_terminal find ( String with_name ) { if ( with_name == null ) return null ; else return ( non_terminal ) _all.get ( with_name ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Table of all non terminals indexed by their index number. */ protected static Hashtable _all_by_index = new Hashtable ( ) ; /** Lookup a non terminal by index. */ public static non_terminal find ( int indx ) { Integer the_indx = new Integer ( indx ) ; return ( non_terminal ) _all_by_index.get ( the_indx ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Total number of non-terminals. */ public static int number ( ) { return _all.size ( ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Static counter to assign unique indexes. */ protected static int next_index = 0 ; /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Static counter for creating unique non-terminal names */ static protected int next_nt = 0 ; /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** special non-terminal for start symbol */ public static final non_terminal START_nt = new non_terminal ( "$START" ) ; /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** flag non-terminals created to embed action productions */ public boolean is_embedded_action = false ; /* added 24-Mar-1998, CSA */ /*-----------------------------------------------------------*/ /*--- Static Methods ----------------------------------------*/ /*-----------------------------------------------------------*/ /** * Method for creating a new uniquely named hidden non-terminal using the * given string as a base for the name (or "NT$" if null is passed). * * @param prefix base name to construct unique name from. */ static non_terminal create_new ( String prefix ) throws internal_error { return create_new ( prefix , null ) ; // TUM 20060608 embedded actions patch } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** static routine for creating a new uniquely named hidden non-terminal */ static non_terminal create_new ( ) throws internal_error { return create_new ( null ) ; } /** * TUM 20060608 bugfix for embedded action codes */ static non_terminal create_new ( String prefix , String type ) throws internal_error { if ( prefix == null ) prefix = "NT$" ; return new non_terminal ( prefix + next_nt ++ , type ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Compute nullability of all non-terminals. */ public static void compute_nullability ( ) throws internal_error { boolean change = true ; non_terminal nt ; Enumeration e ; production prod ; /* repeat this process until there is no change */ while ( change ) { /* look for a new change */ change = false ; /* consider each non-terminal */ for ( e = all ( ) ; e.hasMoreElements ( ) ; ) { nt = ( non_terminal ) e.nextElement ( ) ; /* only look at things that aren't already marked nullable */ if ( ! nt.nullable ( ) ) { if ( nt.looks_nullable ( ) ) { nt._nullable = true ; change = true ; } } } } /* do one last pass over the productions to finalize all of them */ for ( e = production.all ( ) ; e.hasMoreElements ( ) ; ) { prod = ( production ) e.nextElement ( ) ; prod.set_nullable ( prod.check_nullable ( ) ) ; } } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** * Compute first sets for all non-terminals. This assumes nullability has * already computed. */ public static void compute_first_sets ( ) throws internal_error { boolean change = true ; Enumeration n ; Enumeration p ; non_terminal nt ; production prod ; terminal_set prod_first ; /* repeat this process until we have no change */ while ( change ) { /* look for a new change */ change = false ; /* consider each non-terminal */ for ( n = all ( ) ; n.hasMoreElements ( ) ; ) { nt = ( non_terminal ) n.nextElement ( ) ; /* consider every production of that non terminal */ for ( p = nt.productions ( ) ; p.hasMoreElements ( ) ; ) { prod = ( production ) p.nextElement ( ) ; /* get the updated first of that production */ prod_first = prod.check_first_set ( ) ; /* if this going to add anything, add it */ if ( ! prod_first.is_subset_of ( nt._first_set ) ) { change = true ; nt._first_set.add ( prod_first ) ; } } } } } /*-----------------------------------------------------------*/ /*--- (Access to) Instance Variables ------------------------*/ /*-----------------------------------------------------------*/ /** Table of all productions with this non terminal on the LHS. */ protected Hashtable _productions = new Hashtable ( 11 ) ; /** Access to productions with this non terminal on the LHS. */ public Enumeration productions ( ) { return _productions.elements ( ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Total number of productions with this non terminal on the LHS. */ public int num_productions ( ) { return _productions.size ( ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Add a production to our set of productions. */ public void add_production ( production prod ) throws internal_error { /* catch improper productions */ if ( prod == null || prod.lhs ( ) == null || prod.lhs ( ).the_symbol ( ) != this ) throw new internal_error ( "Attempt to add invalid production to non terminal production table" ) ; /* add it to the table, keyed with itself */ _productions.put ( prod , prod ) ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Nullability of this non terminal. */ protected boolean _nullable ; /** Nullability of this non terminal. */ public boolean nullable ( ) { return _nullable ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** First set for this non-terminal. */ protected terminal_set _first_set = new terminal_set ( ) ; /** First set for this non-terminal. */ public terminal_set first_set ( ) { return _first_set ; } /*-----------------------------------------------------------*/ /*--- General Methods ---------------------------------------*/ /*-----------------------------------------------------------*/ /** Indicate that this symbol is a non-terminal. */ public boolean is_non_term ( ) { return true ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** Test to see if this non terminal currently looks nullable. */ protected boolean looks_nullable ( ) throws internal_error { /* look and see if any of the productions now look nullable */ for ( Enumeration e = productions ( ) ; e.hasMoreElements ( ) ; ) /* if the production can go to empty, we are nullable */ if ( ( ( production ) e.nextElement ( ) ).check_nullable ( ) ) return true ; /* none of the productions can go to empty, so we are not nullable */ return false ; } /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */ /** convert to string */ public String toString ( ) { return super.toString ( ) + "[" + index ( ) + "]" + ( nullable ( ) ? "*" : "" ) ; } /*-----------------------------------------------------------*/ }