|
package java_cup;
|
|
import java.io.PrintStream;
|
import java.util.Date;
|
import java.util.Enumeration;
|
import java.util.Stack;
|
|
/**
|
* This class handles emitting generated code for the resulting parser.
|
* The various parse tables must be constructed, etc. before calling any
|
* routines in this class.<p>
|
*
|
* Three classes are produced by this code:
|
* <dl>
|
* <dt> symbol constant class
|
* <dd> this contains constant declarations for each terminal (and
|
* optionally each non-terminal).
|
* <dt> action class
|
* <dd> this non-public class contains code to invoke all the user actions
|
* that were embedded in the parser specification.
|
* <dt> parser class
|
* <dd> the specialized parser class consisting primarily of some user
|
* supplied general and initialization code, and the parse tables.
|
* </dl><p>
|
*
|
* Three parse tables are created as part of the parser class:
|
* <dl>
|
* <dt> production table
|
* <dd> lists the LHS non terminal number, and the length of the RHS of
|
* each production.
|
* <dt> action table
|
* <dd> for each state of the parse machine, gives the action to be taken
|
* (shift, reduce, or error) under each lookahead symbol.<br>
|
* <dt> reduce-goto table
|
* <dd> when a reduce on a given production is taken, the parse stack is
|
* popped back a number of elements corresponding to the RHS of the
|
* production. This reveals a prior state, which we transition out
|
* of under the LHS non terminal symbol for the production (as if we
|
* had seen the LHS symbol rather than all the symbols matching the
|
* RHS). This table is indexed by non terminal numbers and indicates
|
* how to make these transitions.
|
* </dl><p>
|
*
|
* In addition to the method interface, this class maintains a series of
|
* public global variables and flags indicating how misc. parts of the code
|
* and other output is to be produced, and counting things such as number of
|
* conflicts detected (see the source code and public variables below for
|
* more details).<p>
|
*
|
* This class is "static" (contains only static data and methods).<p>
|
*
|
* @see java_cup.main
|
* @version last update: 11/25/95
|
* @author Scott Hudson
|
*/
|
|
/* Major externally callable routines here include:
|
symbols - emit the symbol constant class
|
parser - emit the parser class
|
|
In addition the following major internal routines are provided:
|
emit_package - emit a package declaration
|
emit_action_code - emit the class containing the user's actions
|
emit_production_table - emit declaration and init for the production table
|
do_action_table - emit declaration and init for the action table
|
do_reduce_table - emit declaration and init for the reduce-goto table
|
|
Finally, this class uses a number of public instance variables to communicate
|
optional parameters and flags used to control how code is generated,
|
as well as to report counts of various things (such as number of conflicts
|
detected). These include:
|
|
prefix - a prefix string used to prefix names that would
|
otherwise "pollute" someone else's name space.
|
package_name - name of the package emitted code is placed in
|
(or null for an unnamed package.
|
symbol_const_class_name - name of the class containing symbol constants.
|
parser_class_name - name of the class for the resulting parser.
|
action_code - user supplied declarations and other code to be
|
placed in action class.
|
parser_code - user supplied declarations and other code to be
|
placed in parser class.
|
init_code - user supplied code to be executed as the parser
|
is being initialized.
|
scan_code - user supplied code to get the next token.
|
start_production - the start production for the grammar.
|
import_list - list of imports for use with action class.
|
num_conflicts - number of conflicts detected.
|
nowarn - true if we are not to issue warning messages.
|
not_reduced - count of number of productions that never reduce.
|
unused_term - count of unused terminal symbols.
|
unused_non_term - count of unused non terminal symbols.
|
*_time - a series of symbols indicating how long various
|
sub-parts of code generation took (used to produce
|
optional time reports in main).
|
*/
|
|
public class emit {
|
|
/*-----------------------------------------------------------*/
|
/*--- Constructor(s) ----------------------------------------*/
|
/*-----------------------------------------------------------*/
|
|
/** Only constructor is private so no instances can be created. */
|
private emit() { }
|
|
/*-----------------------------------------------------------*/
|
/*--- Static (Class) Variables ------------------------------*/
|
/*-----------------------------------------------------------*/
|
|
public static String input_file_name;
|
|
/** The prefix placed on names that pollute someone else's name space. */
|
public static String prefix = "CUP$";
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Package that the resulting code goes into (null is used for unnamed). */
|
public static String package_name = null;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Name of the generated class for symbol constants. */
|
public static String symbol_const_class_name = "sym";
|
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Name of the generated parser class. */
|
public static String parser_class_name = "parser";
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** User declarations for direct inclusion in user action class. */
|
public static String action_code = null;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** User declarations for direct inclusion in parser class. */
|
public static String parser_code = null;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** User code for user_init() which is called during parser initialization. */
|
public static String init_code = null;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** User code for scan() which is called to get the next token. */
|
public static String scan_code = null;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** The start production of the grammar. */
|
public static production start_production = null;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** List of imports (Strings containing class names) to go with actions. */
|
public static Stack import_list = new Stack();
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Number of conflict found while building tables. */
|
public static int num_conflicts = 0;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Do we skip warnings? */
|
public static boolean nowarn = false;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Count of the number on non-reduced productions found. */
|
public static int not_reduced = 0;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Count of unused terminals. */
|
public static int unused_term = 0;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Count of unused non terminals. */
|
public static int unused_non_term = 0;
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/* Timing values used to produce timing report in main.*/
|
|
/** Time to produce symbol constant class. */
|
public static long symbols_time = 0;
|
|
/** Time to produce parser class. */
|
public static long parser_time = 0;
|
|
/** Time to produce action code class. */
|
public static long action_code_time = 0;
|
|
/** Time to produce the production table. */
|
public static long production_table_time = 0;
|
|
/** Time to produce the action table. */
|
public static long action_table_time = 0;
|
|
/** Time to produce the reduce-goto table. */
|
public static long goto_table_time = 0;
|
|
/** Do we produce calls debug_gammar in generated parser? */
|
public static String debug_grammar = null;
|
|
/*-----------------------------------------------------------*/
|
/*--- General Methods ---------------------------------------*/
|
/*-----------------------------------------------------------*/
|
|
/** Build a string with the standard prefix.
|
* @param str string to prefix.
|
*/
|
protected static String pre(String str) {return prefix + str;}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit a package spec if the user wants one.
|
* @param out stream to produce output on.
|
*/
|
protected static void emit_package(PrintStream out)
|
{
|
/* generate a package spec if we have a name for one */
|
if (package_name != null)
|
out.println("package " + package_name + ";\n");
|
|
}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit code for the symbol constant class, optionally including non terms,
|
* if they have been requested.
|
* @param out stream to produce output on.
|
* @param emit_non_terms do we emit constants for non terminals?
|
*/
|
public static void symbols(PrintStream out, boolean emit_non_terms)
|
{
|
terminal term;
|
non_terminal nt;
|
|
long start_time = System.currentTimeMillis();
|
|
/* top of file */
|
out.println();
|
out.println("//----------------------------------------------------");
|
out.println("// The following code was generated by " +
|
version.title_str);
|
out.println("// " + new Date());
|
out.println("//----------------------------------------------------");
|
out.println();
|
emit_package(out);
|
|
/* class header */
|
out.println(
|
"/** JavaCup generated class containing symbol constants. */");
|
out.println("public class " + symbol_const_class_name + " {");
|
|
out.println(" /* terminals */");
|
|
/* walk over the terminals */ /* later might sort these */
|
for (Enumeration e = terminal.all(); e.hasMoreElements(); )
|
{
|
term = (terminal)e.nextElement();
|
|
/* output a constant decl for the terminal */
|
out.println(" static final int " + term.name() + " = " +
|
term.index() + ";");
|
}
|
|
/* do the non terminals if they want them (parser doesn't need them) */
|
if (emit_non_terms)
|
{
|
out.println("\n /* non terminals */");
|
|
/* walk over the non terminals */ /* later might sort these */
|
for (Enumeration e = non_terminal.all(); e.hasMoreElements(); )
|
{
|
nt = (non_terminal)e.nextElement();
|
|
/* output a constant decl for the terminal */
|
out.println(" static final int " + nt.name() + " = " +
|
nt.index() + ";");
|
}
|
}
|
|
/* end of class */
|
out.println("};\n");
|
|
symbols_time = System.currentTimeMillis() - start_time;
|
}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit code for the non-public class holding the actual action code.
|
* @param out stream to produce output on.
|
* @param start_prod the start production of the grammar.
|
*/
|
protected static void emit_action_code(PrintStream out, production start_prod)
|
throws internal_error
|
{
|
production prod;
|
|
long start_time = System.currentTimeMillis();
|
|
/* class header */
|
out.println();
|
out.println(
|
"/** JavaCup generated class to encapsulate user supplied action code.*/"
|
);
|
out.println("class " + pre("actions") + " {");
|
|
/* user supplied code */
|
if (action_code != null)
|
{
|
out.println();
|
out.println(action_code);
|
}
|
|
/* constructor */
|
out.println();
|
out.println(" /** Constructor */");
|
out.println(" " + pre("actions") + "() { }");
|
|
/* action method head */
|
out.println();
|
out.println(" /** Method with the actual generated action code. */");
|
out.println(" public final java_cup.runtime.symbol " +
|
pre("do_action") + "(");
|
out.println(" int " + pre("act_num,"));
|
out.println(" java_cup.runtime.lr_parser " + pre("parser,"));
|
out.println(" java.util.Stack " + pre("stack,"));
|
out.println(" int " + pre("top)"));
|
out.println(" throws java.lang.Exception");
|
out.println(" {");
|
|
/* declaration of result symbol */
|
out.println(" /* object for return from actions */");
|
out.println(" java_cup.runtime.symbol " + pre("result") + ";");
|
out.println();
|
|
/* switch top */
|
out.println(" /* select the action based on the action number */");
|
out.println(" switch (" + pre("act_num") + ")");
|
out.println(" {");
|
|
/* emit action code for each production as a separate case */
|
for (Enumeration p = production.all(); p.hasMoreElements(); )
|
{
|
prod = (production)p.nextElement();
|
|
/* case label */
|
out.println(" /*. . . . . . . . . . . . . . . . . . . .*/");
|
out.println(" case " + prod.index() + ": // " +
|
prod.to_simple_string());
|
|
/* give them their own block to work in */
|
out.println(" {");
|
|
/* user supplied code for debug_grammar() */
|
if (debug_grammar != null)
|
out.println(" " +debug_grammar+ "(\"" +
|
prod.to_simple_string() + "\");");
|
|
/* create the result symbol */
|
out.println(" " + pre("result") + " = new " +
|
prod.lhs().the_symbol().stack_type() + "(/*" +
|
prod.lhs().the_symbol().name() + "*/" +
|
prod.lhs().the_symbol().index() + ");");
|
|
/* if there is an action string, emit it */
|
if (prod.action() != null && prod.action().code_string() != null &&
|
!prod.action().equals(""))
|
out.println(" " + prod.action().code_string());
|
|
/* end of their block */
|
out.println(" }");
|
|
/* if this was the start production, do action for accept */
|
if (prod == start_prod)
|
{
|
out.println(" /* ACCEPT */");
|
out.println(" " + pre("parser") + ".done_parsing();");
|
}
|
|
/* code to return lhs symbol */
|
out.println(" return " + pre("result") + ";");
|
out.println();
|
}
|
|
/* end of switch */
|
out.println(" /* . . . . . .*/");
|
out.println(" default:");
|
out.println(" throw new Exception(");
|
out.println(" \"Invalid action number found in " +
|
"internal parse table\");");
|
out.println();
|
out.println(" }");
|
|
/* end of method */
|
out.println(" }");
|
|
/* end of class */
|
out.println("};\n");
|
|
action_code_time = System.currentTimeMillis() - start_time;
|
}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit the production table.
|
* @param out stream to produce output on.
|
*/
|
protected static void emit_production_table(PrintStream out)
|
{
|
production all_prods[];
|
production prod;
|
|
long start_time = System.currentTimeMillis();
|
|
/* do the top of the table */
|
out.println();
|
out.println(" /** production table */");
|
out.println(" protected static final short _production_table[][] = {");
|
|
/* collect up the productions in order */
|
all_prods = new production[production.number()];
|
for (Enumeration p = production.all(); p.hasMoreElements(); )
|
{
|
prod = (production)p.nextElement();
|
all_prods[prod.index()] = prod;
|
}
|
|
/* do one entry per production */
|
out.print(" ");
|
for (int i = 0; i<production.number(); i++)
|
{
|
prod = all_prods[i];
|
|
/* make the table entry */
|
out.print(" {");
|
out.print(/* lhs symbol # */ prod.lhs().the_symbol().index() + ", ");
|
out.print(/* rhs size */ prod.rhs_length() + "}");
|
|
/* put in a comma if we aren't at the end */
|
if (i < production.number()-1) out.print(", ");
|
|
/* 5 entries per line */
|
if ((i+1) % 5 == 0)
|
{
|
out.println();
|
out.print(" ");
|
}
|
}
|
|
/* finish off the table initializer */
|
out.println(" };");
|
|
/* do the public accessor method */
|
out.println();
|
out.println(" /** access to production table */");
|
out.println(" public short[][] production_table() " +
|
"{return _production_table;}");
|
|
production_table_time = System.currentTimeMillis() - start_time;
|
}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit the action table.
|
* @param out stream to produce output on.
|
* @param act_tab the internal representation of the action table.
|
* @param compact_reduces do we use the most frequent reduce as default?
|
*/
|
protected static void do_action_table(
|
PrintStream out,
|
parse_action_table act_tab,
|
boolean compact_reduces)
|
throws internal_error
|
{
|
parse_action_row row;
|
parse_action act;
|
int red;
|
|
long start_time = System.currentTimeMillis();
|
|
out.println();
|
out.println(" /** parse action table */");
|
out.println(" protected static final short[][] _action_table = {");
|
|
/* do each state (row) of the action table */
|
for (int i = 0; i < act_tab.num_states(); i++)
|
{
|
/* get the row */
|
row = act_tab.under_state[i];
|
|
/* determine the default for the row */
|
if (compact_reduces)
|
row.compute_default();
|
else
|
row.default_reduce = -1;
|
|
out.print(" /*" + i + "*/{");
|
|
/* do each column */
|
for (int j = 0; j < row.size(); j++)
|
{
|
/* extract the action from the table */
|
act = row.under_term[j];
|
|
/* skip error entries these are all defaulted out */
|
if (act.kind() != parse_action.ERROR)
|
{
|
/* first put in the symbol index, then the actual entry */
|
|
/* shifts get positive entries of state number + 1 */
|
if (act.kind() == parse_action.SHIFT)
|
{
|
out.print(j + "," +
|
(((shift_action)act).shift_to().index() + 1) + ",");
|
}
|
|
/* reduce actions get negated entries of production# + 1 */
|
else if (act.kind() == parse_action.REDUCE)
|
{
|
/* if its the default entry let it get defaulted out */
|
red = ((reduce_action)act).reduce_with().index();
|
if (red != row.default_reduce)
|
out.print(j + "," + (-(red+1)) + ",");
|
}
|
|
/* shouldn't be anything else */
|
else
|
throw new internal_error("Unrecognized action code " +
|
act.kind() + " found in parse table");
|
}
|
}
|
|
/* finish off the row with a default entry */
|
if (row.default_reduce != -1)
|
out.println("-1," + (-(row.default_reduce+1)) + "},");
|
else
|
out.println("-1,0},");
|
}
|
|
/* finish off the init of the table */
|
out.println(" };");
|
|
/* do the public accessor method */
|
out.println();
|
out.println(" /** access to parse action table */");
|
out.println(" public short[][] action_table() {return _action_table;}");
|
|
action_table_time = System.currentTimeMillis() - start_time;
|
}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit the reduce-goto table.
|
* @param out stream to produce output on.
|
* @param red_tab the internal representation of the reduce-goto table.
|
*/
|
protected static void do_reduce_table(
|
PrintStream out,
|
parse_reduce_table red_tab)
|
{
|
lalr_state goto_st;
|
parse_action act;
|
|
long start_time = System.currentTimeMillis();
|
|
out.println();
|
out.println(" /** reduce_goto table */");
|
out.println(" protected static final short[][] _reduce_table = {");
|
|
/* do each row of the reduce-goto table */
|
for (int i=0; i<red_tab.num_states(); i++)
|
{
|
out.print(" /*" + i + "*/{");
|
|
/* do each entry in the row */
|
for (int j=0; j<red_tab.under_state[i].size(); j++)
|
{
|
/* get the entry */
|
goto_st = red_tab.under_state[i].under_non_term[j];
|
|
/* if we have none, skip it */
|
if (goto_st != null)
|
{
|
/* make entries for the index and the value */
|
out.print(j + "," + goto_st.index() + ",");
|
}
|
}
|
|
/* end row with default value */
|
out.println("-1,-1},");
|
}
|
|
/* finish off the init of the table */
|
out.println(" };");
|
|
/* do the public accessor method */
|
out.println();
|
out.println(" /** access to reduce_goto table */");
|
out.println(" public short[][] reduce_table() {return _reduce_table;}");
|
out.println();
|
|
goto_table_time = System.currentTimeMillis() - start_time;
|
}
|
|
/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
|
|
/** Emit the parser subclass with embedded tables.
|
* @param out stream to produce output on.
|
* @param action_table internal representation of the action table.
|
* @param reduce_table internal representation of the reduce-goto table.
|
* @param start_st start state of the parse machine.
|
* @param start_prod start production of the grammar.
|
* @param compact_reduces do we use most frequent reduce as default?
|
*/
|
public static void parser(
|
PrintStream out,
|
parse_action_table action_table,
|
parse_reduce_table reduce_table,
|
int start_st,
|
production start_prod,
|
boolean compact_reduces)
|
throws internal_error
|
{
|
long start_time = System.currentTimeMillis();
|
|
/* top of file */
|
out.println();
|
out.println("//----------------------------------------------------");
|
out.println("// The following code was generated by " +
|
version.title_str);
|
out.println("// " + new Date());
|
out.println("//----------------------------------------------------");
|
out.println();
|
emit_package(out);
|
|
/* user supplied imports */
|
for (int i = 0; i < import_list.size(); i++)
|
out.println("import " + import_list.elementAt(i) + ";");
|
|
/* class header */
|
out.println();
|
out.println("public class " + parser_class_name +
|
" extends java_cup.runtime.lr_parser {");
|
|
/* constructor */
|
out.println();
|
out.println(" /** constructor */");
|
out.println(" public " + parser_class_name + "() {super();}");
|
|
/* emit the various tables */
|
emit_production_table(out);
|
do_action_table(out, action_table, compact_reduces);
|
do_reduce_table(out, reduce_table);
|
|
/* instance of the action encapsulation class */
|
out.println(" /** instance of action encapsulation class */");
|
out.println(" protected " + pre("actions") + " action_obj;");
|
out.println();
|
|
/* action object initializer */
|
out.println(" /** action encapsulation object initializer */");
|
out.println(" protected void init_actions()");
|
out.println(" {");
|
out.println(" action_obj = new " + pre("actions") + "();");
|
out.println(" }");
|
out.println();
|
|
/* access to action code */
|
out.println(" /** invoke a user supplied parse action */");
|
out.println(" public java_cup.runtime.symbol do_action(");
|
out.println(" int act_num,");
|
out.println(" java_cup.runtime.lr_parser parser,");
|
out.println(" java.util.Stack stack,");
|
out.println(" int top)");
|
out.println(" throws java.lang.Exception");
|
out.println(" {");
|
out.println(" /* call code in generated class */");
|
out.println(" return action_obj." + pre("do_action(") +
|
"act_num, parser, stack, top);");
|
out.println(" }");
|
out.println("");
|
|
|
/* method to tell the parser about the start state */
|
out.println(" /** start state */");
|
out.println(" public int start_state() {return " + start_st + ";}");
|
|
/* method to indicate start production */
|
out.println(" /** start production */");
|
out.println(" public int start_production() {return " +
|
start_production.index() + ";}");
|
out.println();
|
|
/* methods to indicate EOF and error symbol indexes */
|
out.println(" /** EOF symbol index */");
|
out.println(" public int EOF_sym() {return " + terminal.EOF.index() +
|
";}");
|
out.println();
|
out.println(" /** error symbol index */");
|
out.println(" public int error_sym() {return " + terminal.error.index() +
|
";}");
|
out.println();
|
|
/* user supplied code for user_init() */
|
if (init_code != null)
|
{
|
out.println();
|
out.println(" /** user initialization */");
|
out.println(" public void user_init() throws java.lang.Exception");
|
out.println(" {");
|
out.println(init_code);
|
out.println(" }");
|
}
|
|
/* user supplied code for scan */
|
if (scan_code != null)
|
{
|
out.println();
|
out.println(" /** scan to get the next token */");
|
out.println(" public java_cup.runtime.token scan()");
|
out.println(" throws java.lang.Exception");
|
out.println(" {");
|
out.println(scan_code);
|
out.println(" }");
|
}
|
|
/* user supplied code */
|
if (parser_code != null)
|
{
|
out.println();
|
out.println(parser_code);
|
}
|
|
/* end of class */
|
out.println("};");
|
|
/* put out the action code class */
|
emit_action_code(out, start_prod);
|
|
parser_time = System.currentTimeMillis() - start_time;
|
}
|
|
/*-----------------------------------------------------------*/
|
};
|