package java_cup;

import java.util.Enumeration;
import java.io.*;
import java_cup.runtime.*;

/** This class serves as the main driver for the JavaCup system.
 *  It accepts user options and coordinates overall control flow.
 *  The main flow of control includes the following activities: 
 *  <ul>
 *    <li> Parse user supplied arguments and options.
 *    <li> Open output files.
 *    <li> Parse the specification from standard input.
 *    <li> Check for unused terminals, non-terminals, and productions.
 *    <li> Build the state machine, tables, etc.
 *    <li> Output the generated code.
 *    <li> Close output files.
 *    <li> Print a summary if requested.
 *  </ul>
 *
 *  Options to the main program include: <dl>
 *   <dt> -package name  
 *   <dd> specify package generated classes go in [default none]
 *   <dt> -parser name   
 *   <dd> specify parser class name [default "parser"]
 *   <dt> -symbols name  
 *   <dd> specify name for symbol constant class [default "sym"]
 *   <dt> -interface
 *   <dd> emit symbol constant <i>interface</i>, rather than class
 *   <dt> -nonterms      
 *   <dd> put non terminals in symbol constant class
 *   <dt> -expect #      
 *   <dd> number of conflicts expected/allowed [default 0]
 *   <dt> -compact_red   
 *   <dd> compact tables by defaulting to most frequent reduce
 *   <dt> -nowarn        
 *   <dd> don't warn about useless productions, etc.
 *   <dt> -nosummary     
 *   <dd> don't print the usual summary of parse states, etc.
 *   <dt> -progress      
 *   <dd> print messages to indicate progress of the system
 *   <dt> -time          
 *   <dd> print time usage summary
 *   <dt> -dump_grammar  
 *   <dd> produce a dump of the symbols and grammar
 *   <dt> -dump_states   
 *   <dd> produce a dump of parse state machine
 *   <dt> -dump_tables   
 *   <dd> produce a dump of the parse tables
 *   <dt> -dump          
 *   <dd> produce a dump of all of the above
 *   <dt> -debug         
 *   <dd> turn on debugging messages within JavaCup 
 *   <dt> -nopositions
 *   <dd> don't generate the positions code
 *   <dt> -locations
 *   <dd> generate handles xleft/xright for symbol positions in actions
 *   <dt> -noscanner
 *   <dd> don't refer to java_cup.runtime.Scanner in the parser
 *        (for compatibility with old runtimes)
 *   <dt> -version
 *   <dd> print version information for JavaCUP and halt.
 *   </dl>
 *
 * @version last updated: 7/3/96
 * @author  Frank Flannery
 */

public class Main {

	/*-----------------------------------------------------------*/
	/*--- Constructor(s) ----------------------------------------*/
	/*-----------------------------------------------------------*/
	/** Only constructor is private, so we do not allocate any instances of this
	    class. */
	private Main() {
	}

	/*-------------------------*/
	/* Options set by the user */
	/*-------------------------*/
	/** User option -- do we print progress messages. */
	protected static boolean print_progress = false;
	/** User option -- do we produce a dump of the state machine */
	protected static boolean opt_dump_states = false;
	/** User option -- do we produce a dump of the parse tables */
	protected static boolean opt_dump_tables = false;
	/** User option -- do we produce a dump of the grammar */
	protected static boolean opt_dump_grammar = false;
	/** User option -- do we show timing information as a part of the summary */
	protected static boolean opt_show_timing = false;
	/** User option -- do we run produce extra debugging messages */
	protected static boolean opt_do_debug = false;
	/** User option -- do eclipse debug symbols */
	protected static boolean opt_do_debugsymbols = false;
	/** User option -- do we compact tables by making most common reduce the 
	    default action */
	protected static boolean opt_compact_red = false;
	/** User option -- should we include non terminal symbol numbers in the 
	    symbol constant class. */
	protected static boolean include_non_terms = false;
	/** User option -- do not print a summary. */
	protected static boolean no_summary = false;
	/** User option -- number of conflicts to expect */
	protected static int expect_conflicts = 0;

	/* frankf added this 6/18/96 */
	/** User option -- should generator generate code for left/right values? */
	protected static boolean lr_values = true;
	protected static boolean locations = false;
	protected static boolean xmlactions = false;
	protected static boolean genericlabels = false;

	/** User option -- should symbols be put in a class or an interface? [CSA]*/
	protected static boolean sym_interface = false;

	/** User option -- should generator suppress references to
	 *  java_cup.runtime.Scanner for compatibility with old runtimes? */
	protected static boolean suppress_scanner = false;

	/*----------------------------------------------------------------------*/
	/* Timing data (not all of these time intervals are mutually exclusive) */
	/*----------------------------------------------------------------------*/
	/** Timing data -- when did we start */
	protected static long start_time = 0;
	/** Timing data -- when did we end preliminaries */
	protected static long prelim_end = 0;
	/** Timing data -- when did we end parsing */
	protected static long parse_end = 0;
	/** Timing data -- when did we end checking */
	protected static long check_end = 0;
	/** Timing data -- when did we end dumping */
	protected static long dump_end = 0;
	/** Timing data -- when did we end state and table building */
	protected static long build_end = 0;
	/** Timing data -- when did we end nullability calculation */
	protected static long nullability_end = 0;
	/** Timing data -- when did we end first set calculation */
	protected static long first_end = 0;
	/** Timing data -- when did we end state machine construction */
	protected static long machine_end = 0;
	/** Timing data -- when did we end table construction */
	protected static long table_end = 0;
	/** Timing data -- when did we end checking for non-reduced productions */
	protected static long reduce_check_end = 0;
	/** Timing data -- when did we finish emitting code */
	protected static long emit_end = 0;
	/** Timing data -- when were we completely done */
	protected static long final_time = 0;

	/* Additional timing information is also collected in emit */

	/*-----------------------------------------------------------*/
	/*--- Main Program ------------------------------------------*/
	/*-----------------------------------------------------------*/

	/** The main driver for the system. 
	 * @param argv an array of strings containing command line arguments.
	 */
	public static void main(String argv[])
			throws internal_error, java.io.IOException, java.lang.Exception
	{
		boolean did_output = false;

		start_time = System.currentTimeMillis();

		/** clean all static members, that contain remaining stuff from earlier calls */
		terminal.clear();
		production.clear();
		action_production.clear();
		emit.clear();
		non_terminal.clear();
		parse_reduce_row.clear();
		parse_action_row.clear();
		lalr_state.clear();

		/* process user options and arguments */
		parse_args(argv);

		/* frankf 6/18/96
		hackish, yes, but works */
		emit.set_lr_values(lr_values);
		emit.set_locations(locations);
		emit.set_xmlactions(xmlactions);
		emit.set_genericlabels(genericlabels);
		/* open output set_xmlactionsfiles */
		if (print_progress)
			System.err.println("Opening files...");
		/* use a buffered version of standard input */
		input_file = new BufferedInputStream(System.in);

		prelim_end = System.currentTimeMillis();

		/* parse spec into internal data structures */
		if (print_progress)
			System.err.println("Parsing specification from standard input...");
		parse_grammar_spec();

		parse_end = System.currentTimeMillis();

		/* don't proceed unless we are error free */
		if (ErrorManager.getManager().getErrorCount() == 0)
		{
			/* check for unused bits */
			if (print_progress)
				System.err.println("Checking specification...");
			check_unused();

			check_end = System.currentTimeMillis();

			/* build the state machine and parse tables */
			if (print_progress)
				System.err.println("Building parse tables...");
			build_parser();

			build_end = System.currentTimeMillis();

			/* output the generated code, if # of conflicts permits */
			if (ErrorManager.getManager().getErrorCount() != 0) {
				// conflicts! don't emit code, don't dump tables.
				opt_dump_tables = false;
			} else { // everything's okay, emit parser.
				if (print_progress)
					System.err.println("Writing parser...");
				open_files();
				emit_parser();
				did_output = true;
			}
		}
		/* fix up the times to make the summary easier */
		emit_end = System.currentTimeMillis();

		/* do requested dumps */
		if (opt_dump_grammar)
			dump_grammar();
		if (opt_dump_states)
			dump_machine();
		if (opt_dump_tables)
			dump_tables();

		dump_end = System.currentTimeMillis();

		/* close input/output files */
		if (print_progress)
			System.err.println("Closing files...");
		close_files();

		/* produce a summary if desired */
		if (!no_summary)
			emit_summary(did_output);

		/* If there were errors during the run,
		 * exit with non-zero status (makefile-friendliness). --CSA */
		if (ErrorManager.getManager().getErrorCount() != 0)
			System.exit(100);
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Print a "usage message" that described possible command line options, 
	 *  then exit.
	 * @param message a specific error message to preface the usage message by.
	 */
	protected static void usage(String message)
	{
		System.err.println();
		System.err.println(message);
		System.err.println();
		System.err.println(
				version.title_str + "\n" +
						"Usage: " + version.program_name + " [options] [filename]\n" +
						"  and expects a specification file on standard input if no filename is given.\n" +
						"  Legal options include:\n" +
						"    -package name  specify package generated classes go in [default none]\n" +
						"    -destdir name  specify the destination directory, to store the generated files in\n" +
						"    -parser name   specify parser class name [default \"parser\"]\n" +
						"    -typearg args  specify type arguments for parser class\n" +
						"    -symbols name  specify name for symbol constant class [default \"sym\"]\n" +
						"    -interface     put symbols in an interface, rather than a class\n" +
						"    -nonterms      put non terminals in symbol constant class\n" +
						"    -expect #      number of conflicts expected/allowed [default 0]\n" +
						"    -compact_red   compact tables by defaulting to most frequent reduce\n" +
						"    -nowarn        don't warn about useless productions, etc.\n" +
						"    -nosummary     don't print the usual summary of parse states, etc.\n" +
						"    -nopositions   don't propagate the left and right token position values\n" +
						"    -locations     generate handles xleft/xright for symbol positions in actions\n" +
						"    -xmlactions    make the generated parser yield its parse tree as XML\n" +
						"    -genericlabels automatically generate labels to all symbols in XML mode\n" +
						"    -noscanner     don't refer to java_cup.runtime.Scanner\n" +
						"    -progress      print messages to indicate progress of the system\n" +
						"    -time          print time usage summary\n" +
						"    -dump_grammar  produce a human readable dump of the symbols and grammar\n" +
						"    -dump_states   produce a dump of parse state machine\n" +
						"    -dump_tables   produce a dump of the parse tables\n" +
						"    -dump          produce a dump of all of the above\n" +
						"    -version       print the version information for CUP and exit\n"
				);
		System.exit(1);
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Parse command line options and arguments to set various user-option
	 *  flags and variables. 
	 * @param argv the command line arguments to be parsed.
	 */
	protected static void parse_args(String argv[])
	{
		int len = argv.length;
		int i;

		/* parse the options */
		for (i = 0; i < len; i++)
		{
			/* try to get the various options */
			if (argv[i].equals("-package"))
			{
				/* must have an arg */
				if (++i >= len || argv[i].startsWith("-") ||
						argv[i].endsWith(".cup"))
					usage("-package must have a name argument");

				/* record the name */
				emit.package_name = argv[i];
			}
			else if (argv[i].equals("-destdir"))
			{
				/* must have an arg */
				if (++i >= len || argv[i].startsWith("-") ||
						argv[i].endsWith(".cup"))
					usage("-destdir must have a name argument");
				/* record the name */
				Main.dest_dir = new java.io.File(argv[i]);
			}
			else if (argv[i].equals("-parser"))
			{
				/* must have an arg */
				if (++i >= len || argv[i].startsWith("-") ||
						argv[i].endsWith(".cup"))
					usage("-parser must have a name argument");

				/* record the name */
				emit.parser_class_name = argv[i];
			}
			else if (argv[i].equals("-symbols"))
			{
				/* must have an arg */
				if (++i >= len || argv[i].startsWith("-") ||
						argv[i].endsWith(".cup"))
					usage("-symbols must have a name argument");

				/* record the name */
				emit.symbol_const_class_name = argv[i];
			}
			else if (argv[i].equals("-nonterms"))
			{
				include_non_terms = true;
			}
			else if (argv[i].equals("-expect"))
			{
				/* must have an arg */
				if (++i >= len || argv[i].startsWith("-") ||
						argv[i].endsWith(".cup"))
					usage("-expect must have a name argument");

				/* record the number */
				try {
					expect_conflicts = Integer.parseInt(argv[i]);
				} catch (NumberFormatException e) {
					usage("-expect must be followed by a decimal integer");
				}
			}
			else if (argv[i].equals("-compact_red"))
				opt_compact_red = true;
			else if (argv[i].equals("-nosummary"))
				no_summary = true;
			else if (argv[i].equals("-nowarn"))
				emit.nowarn = true;
			else if (argv[i].equals("-dump_states"))
				opt_dump_states = true;
			else if (argv[i].equals("-dump_tables"))
				opt_dump_tables = true;
			else if (argv[i].equals("-progress"))
				print_progress = true;
			else if (argv[i].equals("-dump_grammar"))
				opt_dump_grammar = true;
			else if (argv[i].equals("-dump"))
				opt_dump_states = opt_dump_tables = opt_dump_grammar = true;
			else if (argv[i].equals("-time"))
				opt_show_timing = true;
			else if (argv[i].equals("-debug"))
				opt_do_debug = true;
			else if (argv[i].equals("-debugsymbols"))
				opt_do_debugsymbols = true;
			/* frankf 6/18/96 */
			else if (argv[i].equals("-nopositions"))
				lr_values = false;
			else if (argv[i].equals("-locations"))
				locations = true;
			else if (argv[i].equals("-xmlactions"))
				xmlactions = true;
			else if (argv[i].equals("-genericlabels"))
				genericlabels = true;
			/* CSA 12/21/97 */
			else if (argv[i].equals("-interface"))
				sym_interface = true;
			/* CSA 23-Jul-1999 */
			else if (argv[i].equals("-noscanner"))
				suppress_scanner = true;
			/* CSA 23-Jul-1999 */
			else if (argv[i].equals("-version")) {
				System.out.println(version.title_str);
				System.exit(1);
			}
			/* TUM changes; suggested by Henning Niss 20050628*/
			else if (argv[i].equals("-typearg")) {
				if (++i >= len || argv[i].startsWith("-") ||
						argv[i].endsWith(".cup"))
					usage("-symbols must have a name argument");

				/* record the typearg */
				emit.class_type_argument = argv[i];
			}

			/* CSA 24-Jul-1999; suggestion by Jean Vaucher */
			else if (!argv[i].startsWith("-") && i == len - 1) {
				/* use input from file. */
				try {
					System.setIn(new FileInputStream(argv[i]));
				} catch (java.io.FileNotFoundException e) {
					usage("Unable to open \"" + argv[i] + "\" for input");
				}
			}
			else
			{
				usage("Unrecognized option \"" + argv[i] + "\"");
			}
		}
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/*-------*/
	/* Files */
	/*-------*/

	/** Input file.  This is a buffered version of System.in. */
	protected static BufferedInputStream input_file;

	/** Output file for the parser class. */
	protected static PrintWriter parser_class_file;

	/** Output file for the symbol constant class. */
	protected static PrintWriter symbol_class_file;

	/** Output directory. */
	protected static File dest_dir = null;

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Open various files used by the system. */
	protected static void open_files()
	{
		File fil;
		String out_name;

		/* open each of the output files */

		/* parser class */
		out_name = emit.parser_class_name + ".java";
		fil = new File(dest_dir, out_name);
		try {
			parser_class_file = new PrintWriter(
					new BufferedOutputStream(new FileOutputStream(fil), 4096));
		} catch (Exception e) {
			System.err.println("Can't open \"" + out_name + "\" for output");
			System.exit(3);
		}

		/* symbol constants class */
		out_name = emit.symbol_const_class_name + ".java";
		fil = new File(dest_dir, out_name);
		try {
			symbol_class_file = new PrintWriter(
					new BufferedOutputStream(new FileOutputStream(fil), 4096));
		} catch (Exception e) {
			System.err.println("Can't open \"" + out_name + "\" for output");
			System.exit(4);
		}
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Close various files used by the system. */
	protected static void close_files() throws java.io.IOException
	{
		if (input_file != null)
			input_file.close();
		if (parser_class_file != null)
			parser_class_file.close();
		if (symbol_class_file != null)
			symbol_class_file.close();
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Parse the grammar specification from standard input.  This produces
	 *  sets of terminal, non-terminals, and productions which can be accessed
	 *  via static variables of the respective classes, as well as the setting
	 *  of various variables (mostly in the emit class) for small user supplied
	 *  items such as the code to scan with.
	 */
	protected static void parse_grammar_spec() throws java.lang.Exception
	{
		parser parser_obj;

		/* create a parser and parse with it */
		ComplexSymbolFactory csf = new ComplexSymbolFactory();
		parser_obj = new parser(new Lexer(csf), csf);
		parser_obj.setDebugSymbols(opt_do_debugsymbols);
		try {
			if (opt_do_debug)
				parser_obj.debug_parse();
			else
				parser_obj.parse();
		} catch (Exception e)
		{
			/* something threw an exception.  catch it and emit a message so we 
			   have a line number to work with, then re-throw it */
			ErrorManager.getManager().emit_error("Internal error: Unexpected exception");
			throw e;
		}
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Check for unused symbols.  Unreduced productions get checked when
	 *  tables are created.
	 */
	protected static void check_unused()
	{
		terminal term;
		non_terminal nt;

		/* check for unused terminals */
		for (Enumeration t = terminal.all(); t.hasMoreElements();)
		{
			term = (terminal) t.nextElement();

			/* don't issue a message for EOF */
			if (term == terminal.EOF)
				continue;

			/* or error */
			if (term == terminal.error)
				continue;

			/* is this one unused */
			if (term.use_count() == 0)
			{
				/* count it and warn if we are doing warnings */
				emit.unused_term++;
				if (!emit.nowarn)
				{
					ErrorManager.getManager().emit_warning(
							"Terminal \"" + term.name() + "\" was declared but never used");
				}
			}
		}

		/* check for unused non terminals */
		for (Enumeration n = non_terminal.all(); n.hasMoreElements();)
		{
			nt = (non_terminal) n.nextElement();

			/* is this one unused */
			if (nt.use_count() == 0)
			{
				/* count and warn if we are doing warnings */
				emit.unused_term++;
				if (!emit.nowarn)
				{
					ErrorManager.getManager().emit_warning(
							"Non terminal \"" + nt.name() + "\" was declared but never used");
				}
			}
		}

	}

	/* . . . . . . . . . . . . . . . . . . . . . . . . .*/
	/* . . Internal Results of Generating the Parser . .*/
	/* . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Start state in the overall state machine. */
	protected static lalr_state start_state;

	/** Resulting parse action table. */
	protected static parse_action_table action_table;

	/** Resulting reduce-goto table. */
	protected static parse_reduce_table reduce_table;

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Build the (internal) parser from the previously parsed specification.
	 *  This includes:<ul>
	 *    <li> Computing nullability of non-terminals.
	 *    <li> Computing first sets of non-terminals and productions.
	 *    <li> Building the viable prefix recognizer machine.
	 *    <li> Filling in the (internal) parse tables.
	 *    <li> Checking for unreduced productions.
	 *  </ul>
	 */
	protected static void build_parser() throws internal_error
	{
		/* compute nullability of all non terminals */
		if (opt_do_debug || print_progress)
			System.err.println("  Computing non-terminal nullability...");
		non_terminal.compute_nullability();

		nullability_end = System.currentTimeMillis();

		/* compute first sets of all non terminals */
		if (opt_do_debug || print_progress)
			System.err.println("  Computing first sets...");
		non_terminal.compute_first_sets();

		first_end = System.currentTimeMillis();

		/* build the LR viable prefix recognition machine */
		if (opt_do_debug || print_progress)
			System.err.println("  Building state machine...");
		start_state = lalr_state.build_machine(emit.start_production);

		machine_end = System.currentTimeMillis();

		/* build the LR parser action and reduce-goto tables */
		if (opt_do_debug || print_progress)
			System.err.println("  Filling in tables...");
		action_table = new parse_action_table();
		reduce_table = new parse_reduce_table();
		for (Enumeration st = lalr_state.all(); st.hasMoreElements();)
		{
			lalr_state lst = (lalr_state) st.nextElement();
			lst.build_table_entries(
					action_table, reduce_table);
		}

		table_end = System.currentTimeMillis();

		/* check and warn for non-reduced productions */
		if (opt_do_debug || print_progress)
			System.err.println("  Checking for non-reduced productions...");
		action_table.check_reductions();

		reduce_check_end = System.currentTimeMillis();

		/* if we have more conflicts than we expected issue a message and die */
		if (emit.num_conflicts > expect_conflicts)
		{
			ErrorManager.getManager().emit_error("*** More conflicts encountered than expected " +
					"-- parser generation aborted");
			// indicate the problem.
			// we'll die on return, after clean up.
		}
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Call the emit routines necessary to write out the generated parser. */
	protected static void emit_parser() throws internal_error
	{
		emit.symbols(symbol_class_file, include_non_terms, sym_interface);
		emit.parser(parser_class_file, action_table, reduce_table,
				start_state.index(), emit.start_production, opt_compact_red,
				suppress_scanner);
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Helper routine to optionally return a plural or non-plural ending. 
	 * @param val the numerical value determining plurality.
	 */
	protected static String plural(int val)
	{
		if (val == 1)
			return "";
		else
			return "s";
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Emit a long summary message to standard error (System.err) which 
	 *  summarizes what was found in the specification, how many states were
	 *  produced, how many conflicts were found, etc.  A detailed timing 
	 *  summary is also produced if it was requested by the user.
	 * @param output_produced did the system get far enough to generate code.
	 */
	protected static void emit_summary(boolean output_produced)
	{
		final_time = System.currentTimeMillis();

		if (no_summary)
			return;

		System.err.println("------- " + version.title_str +
				" Parser Generation Summary -------");

		/* error and warning count */
		System.err.println("  " + ErrorManager.getManager().getErrorCount() + " error" +
				plural(ErrorManager.getManager().getErrorCount()) + " and "
				+ ErrorManager.getManager().getWarningCount() +
				" warning" + plural(ErrorManager.getManager().getWarningCount()));

		/* basic stats */
		System.err.print("  " + terminal.number() + " terminal" +
				plural(terminal.number()) + ", ");
		System.err.print(non_terminal.number() + " non-terminal" +
				plural(non_terminal.number()) + ", and ");
		System.err.println(production.number() + " production" +
				plural(production.number()) + " declared, ");
		System.err.println("  producing " + lalr_state.number() +
				" unique parse states.");

		/* unused symbols */
		System.err.println("  " + emit.unused_term + " terminal" +
				plural(emit.unused_term) + " declared but not used.");
		System.err.println("  " + emit.unused_non_term + " non-terminal" +
				plural(emit.unused_term) + " declared but not used.");

		/* productions that didn't reduce */
		System.err.println("  " + emit.not_reduced + " production" +
				plural(emit.not_reduced) + " never reduced.");

		/* conflicts */
		System.err.println("  " + emit.num_conflicts + " conflict" +
				plural(emit.num_conflicts) + " detected" +
				" (" + expect_conflicts + " expected).");

		/* code location */
		if (output_produced)
			System.err.println("  Code written to \"" + emit.parser_class_name +
					".java\", and \"" + emit.symbol_const_class_name + ".java\".");
		else
			System.err.println("  No code produced.");

		if (opt_show_timing)
			show_times();

		System.err.println(
				"---------------------------------------------------- (" +
						version.title_str + ")");
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Produce the optional timing summary as part of an overall summary. */
	protected static void show_times()
	{
		long total_time = final_time - start_time;

		System.err.println(". . . . . . . . . . . . . . . . . . . . . . . . . ");
		System.err.println("  Timing Summary");
		System.err.println("    Total time       "
				+ timestr(final_time - start_time, total_time));
		System.err.println("      Startup        "
				+ timestr(prelim_end - start_time, total_time));
		System.err.println("      Parse          "
				+ timestr(parse_end - prelim_end, total_time));
		if (check_end != 0)
			System.err.println("      Checking       "
					+ timestr(check_end - parse_end, total_time));
		if (check_end != 0 && build_end != 0)
			System.err.println("      Parser Build   "
					+ timestr(build_end - check_end, total_time));
		if (nullability_end != 0 && check_end != 0)
			System.err.println("        Nullability  "
					+ timestr(nullability_end - check_end, total_time));
		if (first_end != 0 && nullability_end != 0)
			System.err.println("        First sets   "
					+ timestr(first_end - nullability_end, total_time));
		if (machine_end != 0 && first_end != 0)
			System.err.println("        State build  "
					+ timestr(machine_end - first_end, total_time));
		if (table_end != 0 && machine_end != 0)
			System.err.println("        Table build  "
					+ timestr(table_end - machine_end, total_time));
		if (reduce_check_end != 0 && table_end != 0)
			System.err.println("        Checking     "
					+ timestr(reduce_check_end - table_end, total_time));
		if (emit_end != 0 && build_end != 0)
			System.err.println("      Code Output    "
					+ timestr(emit_end - build_end, total_time));
		if (emit.symbols_time != 0)
			System.err.println("        Symbols      "
					+ timestr(emit.symbols_time, total_time));
		if (emit.parser_time != 0)
			System.err.println("        Parser class "
					+ timestr(emit.parser_time, total_time));
		if (emit.action_code_time != 0)
			System.err.println("          Actions    "
					+ timestr(emit.action_code_time, total_time));
		if (emit.production_table_time != 0)
			System.err.println("          Prod table "
					+ timestr(emit.production_table_time, total_time));
		if (emit.action_table_time != 0)
			System.err.println("          Action tab "
					+ timestr(emit.action_table_time, total_time));
		if (emit.goto_table_time != 0)
			System.err.println("          Reduce tab "
					+ timestr(emit.goto_table_time, total_time));

		System.err.println("      Dump Output    "
				+ timestr(dump_end - emit_end, total_time));
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Helper routine to format a decimal based display of seconds and
	 *  percentage of total time given counts of milliseconds.   Note: this
	 *  is broken for use with some instances of negative time (since we don't 
	 *  use any negative time here, we let if be for now).
	 * @param time_val   the value being formatted (in ms).
	 * @param total_time total time percentages are calculated against (in ms).
	 */
	protected static String timestr(long time_val, long total_time)
	{
		boolean neg;
		long ms = 0;
		long sec = 0;
		long percent10;
		String pad;

		/* work with positives only */
		neg = time_val < 0;
		if (neg)
			time_val = -time_val;

		/* pull out seconds and ms */
		ms = time_val % 1000;
		sec = time_val / 1000;

		/* construct a pad to blank fill seconds out to 4 places */
		if (sec < 10)
			pad = "   ";
		else if (sec < 100)
			pad = "  ";
		else if (sec < 1000)
			pad = " ";
		else
			pad = "";

		/* calculate 10 times the percentage of total */
		percent10 = (time_val * 1000) / total_time;

		/* build and return the output string */
		return (neg ? "-" : "") + pad + sec + "." +
				((ms % 1000) / 100) + ((ms % 100) / 10) + (ms % 10) + "sec" +
				" (" + percent10 / 10 + "." + percent10 % 10 + "%)";
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Produce a human readable dump of the grammar. */
	public static void dump_grammar() throws internal_error
	{
		System.err.println("===== Terminals =====");
		for (int tidx = 0, cnt = 0; tidx < terminal.number(); tidx++, cnt++)
		{
			System.err.print("[" + tidx + "]" + terminal.find(tidx).name() + " ");
			if ((cnt + 1) % 5 == 0)
				System.err.println();
		}
		System.err.println();
		System.err.println();

		System.err.println("===== Non terminals =====");
		for (int nidx = 0, cnt = 0; nidx < non_terminal.number(); nidx++, cnt++)
		{
			System.err.print("[" + nidx + "]" + non_terminal.find(nidx).name() + " ");
			if ((cnt + 1) % 5 == 0)
				System.err.println();
		}
		System.err.println();
		System.err.println();

		System.err.println("===== Productions =====");
		for (int pidx = 0; pidx < production.number(); pidx++)
		{
			production prod = production.find(pidx);
			System.err.print("[" + pidx + "] " + prod.lhs().the_symbol().name() + " ::= ");
			for (int i = 0; i < prod.rhs_length(); i++)
				if (prod.rhs(i).is_action())
					System.err.print("{action} ");
				else
					System.err.print(
							((symbol_part) prod.rhs(i)).the_symbol().name() + " ");
			System.err.println();
		}
		System.err.println();
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Produce a (semi-) human readable dump of the complete viable prefix 
	 *  recognition state machine. 
	 */
	public static void dump_machine()
	{
		lalr_state ordered[] = new lalr_state[lalr_state.number()];

		/* put the states in sorted order for a nicer display */
		for (Enumeration s = lalr_state.all(); s.hasMoreElements();)
		{
			lalr_state st = (lalr_state) s.nextElement();
			ordered[st.index()] = st;
		}

		System.err.println("===== Viable Prefix Recognizer =====");
		for (int i = 0; i < lalr_state.number(); i++)
		{
			if (ordered[i] == start_state)
				System.err.print("START ");
			System.err.println(ordered[i]);
			System.err.println("-------------------");
		}
	}

	/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

	/** Produce a (semi-) human readable dumps of the parse tables */
	public static void dump_tables()
	{
		System.err.println(action_table);
		System.err.println(reduce_table);
	}

	/*-----------------------------------------------------------*/

}
