package java_cup;

import java.util.Hashtable;
import java.util.Enumeration;

/** 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() ? "*" : "");
	}

	/*-----------------------------------------------------------*/
}
