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 */ @SuppressWarnings ( { "all", "unchecked" } ) 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 () ? "*" : "" ); } /*-----------------------------------------------------------*/ }