package java_cup ;
import java.util.Enumeration ;
import java.util.Hashtable ;
/**
* This class represents a production in the grammar. It contains a LHS non
* terminal, and an array of RHS symbols. As various transformations are done on
* the RHS of the production, it may shrink. As a result a separate length is
* always maintained to indicate how much of the RHS array is still valid.
* <p>
* I addition to construction and manipulation operations, productions provide
* methods for factoring out actions (see remove_embedded_actions()), for
* computing the nullability of the production (i.e., can it derive the empty
* string, see check_nullable()), and operations for computing its first set
* (i.e., the set of terminals that could appear at the beginning of some string
* derived from the production, see check_first_set()).
*
* @see java_cup.production_part
* @see java_cup.symbol_part
* @see java_cup.action_part
* @version last updated: 7/3/96
* @author Frank Flannery
*/
public class production
{
/*-----------------------------------------------------------*/
/*--- Constructor(s) ----------------------------------------*/
/*-----------------------------------------------------------*/
/**
* Full constructor. This constructor accepts a LHS non terminal, an array of
* RHS parts (including terminals, non terminals, and actions), and a string
* for a final reduce action. It does several manipulations in the process of
* creating a production object. After some validity checking it translates
* labels that appear in actions into code for accessing objects on the
* runtime parse stack. It them merges adjacent actions if they appear and
* moves any trailing action into the final reduce actions string. Next it
* removes any embedded actions by factoring them out with new action
* productions. Finally it assigns a unique index to the production.
* <p>
* Factoring out of actions is accomplished by creating new "hidden" non
* terminals. For example if the production was originally:
*
* <pre>
* A ::= B {action} C D
* </pre>
*
* then it is factored into two productions:
*
* <pre>
* A ::= B X C D
* X ::= {action}
* </pre>
*
* (where X is a unique new non terminal). This has the effect of placing all
* actions at the end where they can be handled as part of a reduce by the
* parser.
*/
public production ( non_terminal lhs_sym , production_part rhs_parts[] ,
int rhs_l , String action_str ) throws internal_error
{
int i ;
action_part tail_action ;
String declare_str ;
int rightlen = rhs_l ;
/* remember the length */
if ( rhs_l >= 0 )
_rhs_length = rhs_l ;
else if ( rhs_parts != null )
_rhs_length = rhs_parts.length ;
else _rhs_length = 0 ;
/* make sure we have a valid left-hand-side */
if ( lhs_sym == null )
throw new internal_error (
"Attempt to construct a production with a null LHS" ) ;
/*
* I'm not translating labels anymore, I'm adding code to declare labels as
* valid variables. This way, the users code string is untouched 6/96 frankf
*/
/*
* check if the last part of the right hand side is an action. If it is, it
* won't be on the stack, so we don't want to count it in the rightlen. Then
* when we search down the stack for a Symbol, we don't try to search past
* action
*/
if ( rhs_l > 0 )
{
if ( rhs_parts [ rhs_l - 1 ].is_action ( ) )
{
rightlen = rhs_l - 1 ;
}
else
{
rightlen = rhs_l ;
}
}
/* get the generated declaration code for the necessary labels. */
declare_str = declare_labels ( rhs_parts , rightlen , action_str ) ;
if ( action_str == null )
action_str = declare_str ;
else action_str = declare_str + action_str ;
/* count use of lhs */
lhs_sym.note_use ( ) ;
/* create the part for left-hand-side */
_lhs = new symbol_part ( lhs_sym ) ;
/* merge adjacent actions (if any) */
_rhs_length = merge_adjacent_actions ( rhs_parts , _rhs_length ) ;
/* strip off any trailing action */
tail_action = strip_trailing_action ( rhs_parts , _rhs_length ) ;
if ( tail_action != null ) _rhs_length -- ;
/*
* Why does this run through the right hand side happen over and over? here
* a quick combination of two prior runs plus one I wanted of my own frankf
* 6/25/96
*/
/* allocate and copy over the right-hand-side */
/* count use of each rhs symbol */
_rhs = new production_part [ _rhs_length ] ;
for ( i = 0 ; i < _rhs_length ; i ++ )
{
_rhs [ i ] = rhs_parts [ i ] ;
if ( ! _rhs [ i ].is_action ( ) )
{
( ( symbol_part ) _rhs [ i ] ).the_symbol ( ).note_use ( ) ;
if ( ( ( symbol_part ) _rhs [ i ] ).the_symbol ( ) instanceof terminal )
{
_rhs_prec = ( ( terminal ) ( ( symbol_part ) _rhs [ i ] )
.the_symbol ( ) ).precedence_num ( ) ;
_rhs_assoc = ( ( terminal ) ( ( symbol_part ) _rhs [ i ] )
.the_symbol ( ) ).precedence_side ( ) ;
}
}
}
/*
* now action string is really declaration string, so put it in front!
* 6/14/96 frankf
*/
if ( action_str == null ) action_str = "" ;
if ( tail_action != null && tail_action.code_string ( ) != null )
action_str = action_str + "\t\t" + tail_action.code_string ( ) ;
/* stash the action */
_action = new action_part ( action_str ) ;
/* rewrite production to remove any embedded actions */
remove_embedded_actions ( ) ;
/* assign an index */
_index = next_index ++ ;
/* put us in the global collection of productions */
_all.put ( new Integer ( _index ) , this ) ;
/* put us in the production list of the lhs non terminal */
lhs_sym.add_production ( this ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Constructor with no action string. */
public production ( non_terminal lhs_sym , production_part rhs_parts[] ,
int rhs_l ) throws internal_error
{
this ( lhs_sym , rhs_parts , rhs_l , null ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/*
* Constructor with precedence and associativity of production contextually
* define
*/
public production ( non_terminal lhs_sym , production_part rhs_parts[] ,
int rhs_l , String action_str , int prec_num , int prec_side )
throws internal_error
{
this ( lhs_sym , rhs_parts , rhs_l , action_str ) ;
/* set the precedence */
set_precedence_num ( prec_num ) ;
set_precedence_side ( prec_side ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/*
* Constructor w/ no action string and contextual precedence defined
*/
public production ( non_terminal lhs_sym , production_part rhs_parts[] ,
int rhs_l , int prec_num , int prec_side ) throws internal_error
{
this ( lhs_sym , rhs_parts , rhs_l , null ) ;
/* set the precedence */
set_precedence_num ( prec_num ) ;
set_precedence_side ( prec_side ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/*-----------------------------------------------------------*/
/*--- (Access to) Static (Class) Variables ------------------*/
/*-----------------------------------------------------------*/
/**
* Table of all productions. Elements are stored using their index as the key.
*/
protected static Hashtable _all = new Hashtable ( ) ;
/** Access to all productions. */
public static Enumeration all ( )
{
return _all.elements ( ) ;
}
/** Lookup a production by index. */
public static production find ( int indx )
{
return ( production ) _all.get ( new Integer ( indx ) ) ;
}
// Hm Added clear to clear all static fields
public static void clear ( )
{
_all.clear ( ) ;
next_index = 0 ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Total number of productions. */
public static int number ( )
{
return _all.size ( ) ;
}
/** Static counter for assigning unique index numbers. */
protected static int next_index ;
/*-----------------------------------------------------------*/
/*--- (Access to) Instance Variables ------------------------*/
/*-----------------------------------------------------------*/
/** The left hand side non-terminal. */
protected symbol_part _lhs ;
/** The left hand side non-terminal. */
public symbol_part lhs ( )
{
return _lhs ;
}
/** The precedence of the rule */
protected int _rhs_prec = - 1 ;
protected int _rhs_assoc = - 1 ;
/** Access to the precedence of the rule */
public int precedence_num ( )
{
return _rhs_prec ;
}
public int precedence_side ( )
{
return _rhs_assoc ;
}
/** Setting the precedence of a rule */
public void set_precedence_num ( int prec_num )
{
_rhs_prec = prec_num ;
}
public void set_precedence_side ( int prec_side )
{
_rhs_assoc = prec_side ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** A collection of parts for the right hand side. */
protected production_part _rhs[] ;
/** Access to the collection of parts for the right hand side. */
public production_part rhs ( int indx ) throws internal_error
{
if ( indx >= 0 && indx < _rhs_length )
return _rhs [ indx ] ;
else throw new internal_error (
"Index out of range for right hand side of production" ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** How much of the right hand side array we are presently using. */
protected int _rhs_length ;
/** How much of the right hand side array we are presently using. */
public int rhs_length ( )
{
return _rhs_length ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* An action_part containing code for the action to be performed when we
* reduce with this production.
*/
protected action_part _action ;
/**
* An action_part containing code for the action to be performed when we
* reduce with this production.
*/
public action_part action ( )
{
return _action ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Index number of the production. */
protected int _index ;
/** Index number of the production. */
public int index ( )
{
return _index ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Count of number of reductions using this production. */
protected int _num_reductions = 0 ;
/** Count of number of reductions using this production. */
public int num_reductions ( )
{
return _num_reductions ;
}
/** Increment the count of reductions with this non-terminal */
public void note_reduction_use ( )
{
_num_reductions ++ ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Is the nullability of the production known or unknown? */
protected boolean _nullable_known = false ;
/** Is the nullability of the production known or unknown? */
public boolean nullable_known ( )
{
return _nullable_known ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Nullability of the production (can it derive the empty string). */
protected boolean _nullable = false ;
/** Nullability of the production (can it derive the empty string). */
public boolean nullable ( )
{
return _nullable ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* First set of the production. This is the set of terminals that could appear
* at the front of some string derived from this production.
*/
protected terminal_set _first_set = new terminal_set ( ) ;
/**
* First set of the production. This is the set of terminals that could appear
* at the front of some string derived from this production.
*/
public terminal_set first_set ( )
{
return _first_set ;
}
/*-----------------------------------------------------------*/
/*--- Static Methods ----------------------------------------*/
/*-----------------------------------------------------------*/
/**
* Determine if a given character can be a label id starter.
*
* @param c the character in question.
*/
protected static boolean is_id_start ( char c )
{
return ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c == '_' ) ;
// later need to handle non-8-bit chars here
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Determine if a character can be in a label id.
*
* @param c the character in question.
*/
protected static boolean is_id_char ( char c )
{
return is_id_start ( c ) || ( c >= '0' && c <= '9' ) ;
}
/*-----------------------------------------------------------*/
/*--- General Methods ---------------------------------------*/
/*-----------------------------------------------------------*/
/**
* Return label declaration code
*
* @param labelname the label name
* @param stack_type the stack type of label?
* @author frankf
*/
protected String make_declaration ( String labelname , String stack_type ,
int offset )
{
String ret ;
/* Put in the left/right value labels */
if ( emit.lr_values ( ) )
ret = "\t\tint "
+ labelname
+ "left = ((java_cup.runtime.Symbol)"
+ emit.pre ( "stack" )
+
// TUM 20050917
( ( offset == 0 ) ? ".peek()" : ( ".elementAt(" + emit.pre ( "top" )
+ "-" + offset + ")" ) ) + ").left;\n"
+ "\t\tint "
+ labelname
+ "right = ((java_cup.runtime.Symbol)"
+ emit.pre ( "stack" )
+
// TUM 20050917
( ( offset == 0 ) ? ".peek()" : ( ".elementAt(" + emit.pre ( "top" )
+ "-" + offset + ")" ) ) + ").right;\n" ;
else ret = "" ;
/* otherwise, just declare label. */
return ret + "\t\t"
+ stack_type
+ " "
+ labelname
+ " = ("
+ stack_type
+ ")(("
+ "java_cup.runtime.Symbol) "
+ emit.pre ( "stack" )
+
// TUM 20050917
( ( offset == 0 ) ? ".peek()" : ( ".elementAt(" + emit.pre ( "top" )
+ "-" + offset + ")" ) ) + ").value;\n" ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Declare label names as valid variables within the action string
*
* @param rhs array of RHS parts.
* @param rhs_len how much of rhs to consider valid.
* @param final_action the final action string of the production.
* @param lhs_type the object type associated with the LHS symbol.
*/
protected String declare_labels ( production_part rhs[] , int rhs_len ,
String final_action )
{
String declaration = "" ;
symbol_part part ;
action_part act_part ;
int pos ;
/* walk down the parts and extract the labels */
for ( pos = 0 ; pos < rhs_len ; pos ++ )
{
if ( ! rhs [ pos ].is_action ( ) )
{
part = ( symbol_part ) rhs [ pos ] ;
/* if it has a label, make declaration! */
if ( part.label ( ) != null )
{
declaration = declaration
+ make_declaration ( part.label ( ) , part.the_symbol ( )
.stack_type ( ) , rhs_len - pos - 1 ) ;
}
}
}
return declaration ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Helper routine to merge adjacent actions in a set of RHS parts
*
* @param rhs_parts array of RHS parts.
* @param len amount of that array that is valid.
* @return remaining valid length.
*/
protected int merge_adjacent_actions ( production_part rhs_parts[] , int len )
{
int from_loc , to_loc , merge_cnt ;
/* bail out early if we have no work to do */
if ( rhs_parts == null || len == 0 ) return 0 ;
merge_cnt = 0 ;
to_loc = - 1 ;
for ( from_loc = 0 ; from_loc < len ; from_loc ++ )
{
/* do we go in the current position or one further */
if ( to_loc < 0 || ! rhs_parts [ to_loc ].is_action ( )
|| ! rhs_parts [ from_loc ].is_action ( ) )
{
/* next one */
to_loc ++ ;
/* clear the way for it */
if ( to_loc != from_loc ) rhs_parts [ to_loc ] = null ;
}
/* if this is not trivial? */
if ( to_loc != from_loc )
{
/* do we merge or copy? */
if ( rhs_parts [ to_loc ] != null && rhs_parts [ to_loc ].is_action ( )
&& rhs_parts [ from_loc ].is_action ( ) )
{
/* merge */
rhs_parts [ to_loc ] = new action_part (
( ( action_part ) rhs_parts [ to_loc ] ).code_string ( )
+ ( ( action_part ) rhs_parts [ from_loc ] ).code_string ( ) ) ;
merge_cnt ++ ;
}
else
{
/* copy */
rhs_parts [ to_loc ] = rhs_parts [ from_loc ] ;
}
}
}
/* return the used length */
return len - merge_cnt ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Helper routine to strip a trailing action off rhs and return it
*
* @param rhs_parts array of RHS parts.
* @param len how many of those are valid.
* @return the removed action part.
*/
protected action_part strip_trailing_action ( production_part rhs_parts[] ,
int len )
{
action_part result ;
/* bail out early if we have nothing to do */
if ( rhs_parts == null || len == 0 ) return null ;
/* see if we have a trailing action */
if ( rhs_parts [ len - 1 ].is_action ( ) )
{
/* snip it out and return it */
result = ( action_part ) rhs_parts [ len - 1 ] ;
rhs_parts [ len - 1 ] = null ;
return result ;
}
else return null ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Remove all embedded actions from a production by factoring them out into
* individual action production using new non terminals. if the original
* production was:
*
* <pre>
* A ::= B {action1} C {action2} D
* </pre>
*
* then it will be factored into:
*
* <pre>
* A ::= B NT$1 C NT$2 D
* NT$1 ::= {action1}
* NT$2 ::= {action2}
* </pre>
*
* where NT$1 and NT$2 are new system created non terminals.
*/
/*
* the declarations added to the parent production are also passed along, as
* they should be perfectly valid in this code string, since it was originally
* a code string in the parent, not on its own. frank 6/20/96
*/
protected void remove_embedded_actions ( ) throws internal_error
{
non_terminal new_nt ;
production new_prod ;
String declare_str ;
int lastLocation = - 1 ;
/* walk over the production and process each action */
for ( int act_loc = 0 ; act_loc < rhs_length ( ) ; act_loc ++ )
if ( rhs ( act_loc ).is_action ( ) )
{
declare_str = declare_labels ( _rhs , act_loc , "" ) ;
/* create a new non terminal for the action production */
new_nt = non_terminal.create_new ( null , lhs ( ).the_symbol ( )
.stack_type ( ) ) ; // TUM 20060608 embedded actions patch
new_nt.is_embedded_action = true ; /* 24-Mar-1998, CSA */
/* create a new production with just the action */
new_prod = new action_production ( this , new_nt , null , 0 ,
declare_str + ( ( action_part ) rhs ( act_loc ) ).code_string ( ) ,
( lastLocation == - 1 ) ? - 1 : ( act_loc - lastLocation ) ) ;
/* replace the action with the generated non terminal */
_rhs [ act_loc ] = new symbol_part ( new_nt ) ;
lastLocation = act_loc ;
}
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Check to see if the production (now) appears to be nullable. A production
* is nullable if its RHS could derive the empty string. This results when the
* RHS is empty or contains only non terminals which themselves are nullable.
*/
public boolean check_nullable ( ) throws internal_error
{
production_part part ;
symbol sym ;
int pos ;
/* if we already know bail out early */
if ( nullable_known ( ) ) return nullable ( ) ;
/* if we have a zero size RHS we are directly nullable */
if ( rhs_length ( ) == 0 )
{
/* stash and return the result */
return set_nullable ( true ) ;
}
/* otherwise we need to test all of our parts */
for ( pos = 0 ; pos < rhs_length ( ) ; pos ++ )
{
part = rhs ( pos ) ;
/* only look at non-actions */
if ( ! part.is_action ( ) )
{
sym = ( ( symbol_part ) part ).the_symbol ( ) ;
/* if its a terminal we are definitely not nullable */
if ( ! sym.is_non_term ( ) )
return set_nullable ( false ) ;
/* its a non-term, is it marked nullable */
else if ( ! ( ( non_terminal ) sym ).nullable ( ) )
/* this one not (yet) nullable, so we aren't */
return false ;
}
}
/* if we make it here all parts are nullable */
return set_nullable ( true ) ;
}
/** set (and return) nullability */
boolean set_nullable ( boolean v )
{
_nullable_known = true ;
_nullable = v ;
return v ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Update (and return) the first set based on current NT firsts. This assumes
* that nullability has already been computed for all non terminals and
* productions.
*/
public terminal_set check_first_set ( ) throws internal_error
{
int part ;
symbol sym ;
/* walk down the right hand side till we get past all nullables */
for ( part = 0 ; part < rhs_length ( ) ; part ++ )
{
/* only look at non-actions */
if ( ! rhs ( part ).is_action ( ) )
{
sym = ( ( symbol_part ) rhs ( part ) ).the_symbol ( ) ;
/* is it a non-terminal? */
if ( sym.is_non_term ( ) )
{
/* add in current firsts from that NT */
_first_set.add ( ( ( non_terminal ) sym ).first_set ( ) ) ;
/* if its not nullable, we are done */
if ( ! ( ( non_terminal ) sym ).nullable ( ) ) break ;
}
else
{
/* its a terminal -- add that to the set */
_first_set.add ( ( terminal ) sym ) ;
/* we are done */
break ;
}
}
}
/* return our updated first set */
return first_set ( ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Equality comparison. */
public boolean equals ( production other )
{
if ( other == null ) return false ;
return other._index == _index ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Generic equality comparison. */
public boolean equals ( Object other )
{
if ( ! ( other instanceof production ) )
return false ;
else return equals ( ( production ) other ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Produce a hash code. */
public int hashCode ( )
{
/* just use a simple function of the index */
return _index * 13 ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Convert to a string. */
public String toString ( )
{
String result ;
/* catch any internal errors */
try
{
result = "production [" + index ( ) + "]: " ;
result += ( ( lhs ( ) != null ) ? lhs ( ).toString ( ) : "$$NULL-LHS$$" ) ;
result += " :: = " ;
for ( int i = 0 ; i < rhs_length ( ) ; i ++ )
result += rhs ( i ) + " " ;
result += ";" ;
if ( action ( ) != null && action ( ).code_string ( ) != null )
result += " {" + action ( ).code_string ( ) + "}" ;
if ( nullable_known ( ) ) if ( nullable ( ) )
result += "[NULLABLE]" ;
else result += "[NOT NULLABLE]" ;
}
catch ( internal_error e )
{
/*
* crash on internal error since we can't throw it from here (because
* superclass does not throw anything.
*/
e.crash ( ) ;
result = null ;
}
return result ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Convert to a simpler string. */
public String to_simple_string ( ) throws internal_error
{
String result ;
result = ( ( lhs ( ) != null ) ? lhs ( ).the_symbol ( ).name ( )
: "NULL_LHS" ) ;
result += " ::= " ;
for ( int i = 0 ; i < rhs_length ( ) ; i ++ )
if ( ! rhs ( i ).is_action ( ) )
result += ( ( symbol_part ) rhs ( i ) ).the_symbol ( ).name ( ) + " " ;
return result ;
}
/*-----------------------------------------------------------*/
}