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: 11/25/95 * @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_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 */ 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(); } /*-----------------------------------------------------------*/ };