| |
| 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(); |
| } |
| /*-----------------------------------------------------------*/ |
| }; |
| |