package java_cup ;
import java.util.Enumeration ;
import java.util.Hashtable ;
/**
* This class represents a set of LALR items. For purposes of building these
* sets, items are considered unique only if they have unique cores (i.e.,
* ignoring differences in their lookahead sets).
* <p>
* This class provides fairly conventional set oriented operations (union,
* sub/super-set tests, etc.), as well as an LALR "closure" operation (see
* compute_closure()).
*
* @see java_cup.lalr_item
* @see java_cup.lalr_state
* @version last updated: 3/6/96
* @author Scott Hudson
*/
public class lalr_item_set
{
/*-----------------------------------------------------------*/
/*--- Constructor(s) ----------------------------------------*/
/*-----------------------------------------------------------*/
/** Constructor for an empty set. */
public lalr_item_set ( )
{
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Constructor for cloning from another set.
*
* @param other indicates set we should copy from.
*/
public lalr_item_set ( lalr_item_set other ) throws internal_error
{
not_null ( other ) ;
_all = ( Hashtable ) other._all.clone ( ) ;
}
/*-----------------------------------------------------------*/
/*--- (Access to) Instance Variables ------------------------*/
/*-----------------------------------------------------------*/
/**
* A hash table to implement the set. We store the items using themselves as
* keys.
*/
protected Hashtable _all = new Hashtable ( 11 ) ;
/** Access to all elements of the set. */
public Enumeration all ( )
{
return _all.elements ( ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Cached hashcode for this set. */
protected Integer hashcode_cache = null ;
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Size of the set */
public int size ( )
{
return _all.size ( ) ;
}
/*-----------------------------------------------------------*/
/*--- Set Operation Methods ---------------------------------*/
/*-----------------------------------------------------------*/
/**
* Does the set contain a particular item?
*
* @param itm the item in question.
*/
public boolean contains ( lalr_item itm )
{
return _all.containsKey ( itm ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Return the item in the set matching a particular item (or null if not
* found)
*
* @param itm the item we are looking for.
*/
public lalr_item find ( lalr_item itm )
{
return ( lalr_item ) _all.get ( itm ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Is this set an (improper) subset of another?
*
* @param other the other set in question.
*/
public boolean is_subset_of ( lalr_item_set other ) throws internal_error
{
not_null ( other ) ;
/* walk down our set and make sure every element is in the other */
for ( Enumeration e = all ( ) ; e.hasMoreElements ( ) ; )
if ( ! other.contains ( ( lalr_item ) e.nextElement ( ) ) ) return false ;
/* they were all there */
return true ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Is this set an (improper) superset of another?
*
* @param other the other set in question.
*/
public boolean is_superset_of ( lalr_item_set other ) throws internal_error
{
not_null ( other ) ;
return other.is_subset_of ( this ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Add a singleton item, merging lookahead sets if the item is already part of
* the set. returns the element of the set that was added or merged into.
*
* @param itm the item being added.
*/
public lalr_item add ( lalr_item itm ) throws internal_error
{
lalr_item other ;
not_null ( itm ) ;
/* see if an item with a matching core is already there */
other = ( lalr_item ) _all.get ( itm ) ;
/* if so, merge this lookahead into the original and leave it */
if ( other != null )
{
other.lookahead ( ).add ( itm.lookahead ( ) ) ;
return other ;
}
/* otherwise we just go in the set */
else
{
/* invalidate cached hashcode */
hashcode_cache = null ;
_all.put ( itm , itm ) ;
return itm ;
}
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Remove a single item if it is in the set.
*
* @param itm the item to remove.
*/
public void remove ( lalr_item itm ) throws internal_error
{
not_null ( itm ) ;
/* invalidate cached hashcode */
hashcode_cache = null ;
/* remove it from hash table implementing set */
_all.remove ( itm ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Add a complete set, merging lookaheads where items are already in the set
*
* @param other the set to be added.
*/
public void add ( lalr_item_set other ) throws internal_error
{
not_null ( other ) ;
/* walk down the other set and do the adds individually */
for ( Enumeration e = other.all ( ) ; e.hasMoreElements ( ) ; )
add ( ( lalr_item ) e.nextElement ( ) ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Remove (set subtract) a complete set.
*
* @param other the set to remove.
*/
public void remove ( lalr_item_set other ) throws internal_error
{
not_null ( other ) ;
/* walk down the other set and do the removes individually */
for ( Enumeration e = other.all ( ) ; e.hasMoreElements ( ) ; )
remove ( ( lalr_item ) e.nextElement ( ) ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Remove and return one item from the set (done in hash order). */
public lalr_item get_one ( ) throws internal_error
{
Enumeration the_set ;
lalr_item result ;
the_set = all ( ) ;
if ( the_set.hasMoreElements ( ) )
{
result = ( lalr_item ) the_set.nextElement ( ) ;
remove ( result ) ;
return result ;
}
else return null ;
}
/*-----------------------------------------------------------*/
/*--- General Methods ---------------------------------------*/
/*-----------------------------------------------------------*/
/**
* Helper function for null test. Throws an interal_error exception if its
* parameter is null.
*
* @param obj the object we are testing.
*/
protected void not_null ( Object obj ) throws internal_error
{
if ( obj == null )
throw new internal_error ( "Null object used in set operation" ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/**
* Compute the closure of the set using the LALR closure rules. Basically for
* every item of the form:
*
* <pre>
* [L ::= a *N alpha, l]
* </pre>
*
* (where N is a a non terminal and alpha is a string of symbols) make sure
* there are also items of the form:
*
* <pre>
* [N ::= *beta, first(alpha l)]
* </pre>
*
* corresponding to each production of N. Items with identical cores but
* differing lookahead sets are merged by creating a new item with the same
* core and the union of the lookahead sets (the LA in LALR stands for
* "lookahead merged" and this is where the merger is). This routine assumes
* that nullability and first sets have been computed for all productions
* before it is called.
*/
public void compute_closure ( ) throws internal_error
{
lalr_item_set consider ;
lalr_item itm , new_itm , add_itm ;
non_terminal nt ;
terminal_set new_lookaheads ;
Enumeration p ;
production prod ;
boolean need_prop ;
/* invalidate cached hashcode */
hashcode_cache = null ;
/* each current element needs to be considered */
consider = new lalr_item_set ( this ) ;
/* repeat this until there is nothing else to consider */
while ( consider.size ( ) > 0 )
{
/* get one item to consider */
itm = consider.get_one ( ) ;
/* do we have a dot before a non terminal */
nt = itm.dot_before_nt ( ) ;
if ( nt != null )
{
/* create the lookahead set based on first after dot */
new_lookaheads = itm.calc_lookahead ( itm.lookahead ( ) ) ;
/* are we going to need to propagate our lookahead to new item */
need_prop = itm.lookahead_visible ( ) ;
/* create items for each production of that non term */
for ( p = nt.productions ( ) ; p.hasMoreElements ( ) ; )
{
prod = ( production ) p.nextElement ( ) ;
/* create new item with dot at start and that lookahead */
new_itm = new lalr_item ( prod , new terminal_set ( new_lookaheads ) ) ;
/* add/merge item into the set */
add_itm = add ( new_itm ) ;
/* if propagation is needed link to that item */
if ( need_prop ) itm.add_propagate ( add_itm ) ;
/* was this was a new item */
if ( add_itm == new_itm )
{
/* that may need further closure, consider it also */
consider.add ( new_itm ) ;
}
}
}
}
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Equality comparison. */
public boolean equals ( lalr_item_set other )
{
if ( other == null || other.size ( ) != size ( ) ) return false ;
/* once we know they are the same size, then improper subset does test */
try
{
return is_subset_of ( other ) ;
}
catch ( internal_error e )
{
/* can't throw error from here (because superclass doesn't) so crash */
e.crash ( ) ;
return false ;
}
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Generic equality comparison. */
public boolean equals ( Object other )
{
if ( ! ( other instanceof lalr_item_set ) )
return false ;
else return equals ( ( lalr_item_set ) other ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Return hash code. */
public int hashCode ( )
{
int result = 0 ;
Enumeration e ;
int cnt ;
/* only compute a new one if we don't have it cached */
if ( hashcode_cache == null )
{
/* hash together codes from at most first 5 elements */
// CSA fix! we'd *like* to hash just a few elements, but
// that means equal sets will have inequal hashcodes, which
// we're not allowed (by contract) to do. So hash them all.
for ( e = all ( ) , cnt = 0 ; e.hasMoreElements ( ) /* && cnt<5 */; cnt ++ )
result ^= ( ( lalr_item ) e.nextElement ( ) ).hashCode ( ) ;
hashcode_cache = new Integer ( result ) ;
}
return hashcode_cache.intValue ( ) ;
}
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
/** Convert to string. */
public String toString ( )
{
StringBuffer result = new StringBuffer ( ) ;
result.append ( "{\n" ) ;
for ( Enumeration e = all ( ) ; e.hasMoreElements ( ) ; )
{
result.append ( " " + ( lalr_item ) e.nextElement ( ) + "\n" ) ;
}
result.append ( "}" ) ;
return result.toString ( ) ;
}
/*-----------------------------------------------------------*/
}