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 */ @SuppressWarnings ( { "all", "unchecked" } ) 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 (); } /*-----------------------------------------------------------*/ }