--- /dev/null
+
+package java_cup;
+
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/** 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();
+ }
+ /*-----------------------------------------------------------*/
+}
+