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