Home | History | Annotate | Download | only in java_cup
      1 
      2 package java_cup;
      3 
      4 import java.util.Enumeration;
      5 import java.util.Hashtable;
      6 
      7 /** This class represents a set of LALR items.  For purposes of building
      8  *  these sets, items are considered unique only if they have unique cores
      9  *  (i.e., ignoring differences in their lookahead sets).<p>
     10  *
     11  *  This class provides fairly conventional set oriented operations (union,
     12  *  sub/super-set tests, etc.), as well as an LALR "closure" operation (see
     13  *  compute_closure()).
     14  *
     15  * @see     java_cup.lalr_item
     16  * @see     java_cup.lalr_state
     17  * @version last updated: 11/25/95
     18  * @author  Scott Hudson
     19  */
     20 
     21 public class lalr_item_set {
     22 
     23   /*-----------------------------------------------------------*/
     24   /*--- Constructor(s) ----------------------------------------*/
     25   /*-----------------------------------------------------------*/
     26 
     27   /** Constructor for an empty set. */
     28   public lalr_item_set() { }
     29 
     30   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
     31 
     32   /** Constructor for cloning from another set.
     33    * @param other indicates set we should copy from.
     34    */
     35   public lalr_item_set(lalr_item_set other)
     36     throws internal_error
     37     {
     38       not_null(other);
     39       _all = (Hashtable)other._all.clone();
     40     }
     41 
     42   /*-----------------------------------------------------------*/
     43   /*--- (Access to) Instance Variables ------------------------*/
     44   /*-----------------------------------------------------------*/
     45 
     46   /** A hash table to implement the set.  We store the items using themselves
     47    *  as keys.
     48    */
     49   protected Hashtable _all = new Hashtable(11);
     50 
     51   /** Access to all elements of the set. */
     52   public Enumeration all() {return _all.elements();}
     53 
     54   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
     55 
     56   /** Cached hashcode for this set. */
     57   protected Integer hashcode_cache = null;
     58 
     59   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
     60 
     61   /** Size of the set */
     62   public int size() {return _all.size();}
     63 
     64   /*-----------------------------------------------------------*/
     65   /*--- Set Operation Methods ---------------------------------*/
     66   /*-----------------------------------------------------------*/
     67 
     68   /** Does the set contain a particular item?
     69    * @param itm the item in question.
     70    */
     71   public boolean contains(lalr_item itm) {return _all.containsKey(itm);}
     72 
     73   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
     74 
     75   /** Return the item in the set matching a particular item (or null if not
     76    *  found)
     77    *  @param itm the item we are looking for.
     78    */
     79   public lalr_item find(lalr_item itm) {return (lalr_item)_all.get(itm);}
     80 
     81   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
     82 
     83   /** Is this set an (improper) subset of another?
     84    * @param other the other set in question.
     85    */
     86   public boolean is_subset_of(lalr_item_set other) throws internal_error
     87     {
     88       not_null(other);
     89 
     90       /* walk down our set and make sure every element is in the other */
     91       for (Enumeration e = all(); e.hasMoreElements(); )
     92     if (!other.contains((lalr_item)e.nextElement()))
     93       return false;
     94 
     95       /* they were all there */
     96       return true;
     97     }
     98 
     99   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    100 
    101   /** Is this set an (improper) superset of another?
    102    * @param other the other set in question.
    103    */
    104   public boolean is_superset_of(lalr_item_set other) throws internal_error
    105     {
    106       not_null(other);
    107       return other.is_subset_of(this);
    108     }
    109 
    110   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    111 
    112   /** Add a singleton item, merging lookahead sets if the item is already
    113    *  part of the set.  returns the element of the set that was added or
    114    *  merged into.
    115    * @param itm the item being added.
    116    */
    117   public lalr_item add(lalr_item itm) throws internal_error
    118     {
    119       lalr_item other;
    120 
    121       not_null(itm);
    122 
    123       /* see if an item with a matching core is already there */
    124       other = (lalr_item)_all.get(itm);
    125 
    126       /* if so, merge this lookahead into the original and leave it */
    127       if (other != null)
    128     {
    129       other.lookahead().add(itm.lookahead());
    130       return other;
    131     }
    132       /* otherwise we just go in the set */
    133       else
    134     {
    135           /* invalidate cached hashcode */
    136           hashcode_cache = null;
    137 
    138           _all.put(itm,itm);
    139       return itm;
    140     }
    141     };
    142 
    143   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    144 
    145   /** Remove a single item if it is in the set.
    146    * @param itm the item to remove.
    147    */
    148   public void remove(lalr_item itm) throws internal_error
    149     {
    150       not_null(itm);
    151 
    152       /* invalidate cached hashcode */
    153       hashcode_cache = null;
    154 
    155       /* remove it from hash table implementing set */
    156       _all.remove(itm);
    157     };
    158 
    159   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    160 
    161   /** Add a complete set, merging lookaheads where items are already in
    162    *  the set
    163    * @param other the set to be added.
    164    */
    165   public void add(lalr_item_set other) throws internal_error
    166     {
    167       not_null(other);
    168 
    169       /* walk down the other set and do the adds individually */
    170       for (Enumeration e = other.all(); e.hasMoreElements(); )
    171     add((lalr_item)e.nextElement());
    172     }
    173 
    174   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    175 
    176   /** Remove (set subtract) a complete set.
    177    * @param other the set to remove.
    178    */
    179   public void remove(lalr_item_set other) throws internal_error
    180     {
    181       not_null(other);
    182 
    183       /* walk down the other set and do the removes individually */
    184       for (Enumeration e = other.all(); e.hasMoreElements(); )
    185     remove((lalr_item)e.nextElement());
    186     }
    187 
    188   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    189 
    190   /** Remove and return one item from the set (done in hash order). */
    191   public lalr_item get_one() throws internal_error
    192     {
    193       Enumeration the_set;
    194       lalr_item result;
    195 
    196       the_set = all();
    197       if (the_set.hasMoreElements())
    198     {
    199           result = (lalr_item)the_set.nextElement();
    200           remove(result);
    201       return result;
    202     }
    203       else
    204     return null;
    205     }
    206 
    207   /*-----------------------------------------------------------*/
    208   /*--- General Methods ---------------------------------------*/
    209   /*-----------------------------------------------------------*/
    210 
    211   /** Helper function for null test.  Throws an interal_error exception if its
    212    *  parameter is null.
    213    *  @param obj the object we are testing.
    214    */
    215   protected void not_null(Object obj) throws internal_error
    216     {
    217       if (obj == null)
    218     throw new internal_error("Null object used in set operation");
    219     }
    220 
    221   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    222 
    223   /** Compute the closure of the set using the LALR closure rules.  Basically
    224    *  for every item of the form: <pre>
    225    *    [L ::= a *N alpha, l]
    226    *  </pre>
    227    *  (where N is a a non terminal and alpha is a string of symbols) make
    228    *  sure there are also items of the form:  <pre>
    229    *    [N ::= *beta, first(alpha l)]
    230    *  </pre>
    231    *  corresponding to each production of N.  Items with identical cores but
    232    *  differing lookahead sets are merged by creating a new item with the same
    233    *  core and the union of the lookahead sets (the LA in LALR stands for
    234    *  "lookahead merged" and this is where the merger is).  This routine
    235    *  assumes that nullability and first sets have been computed for all
    236    *  productions before it is called.
    237    */
    238   public void compute_closure()
    239     throws internal_error
    240     {
    241       lalr_item_set consider;
    242       lalr_item     itm, new_itm, add_itm;
    243       non_terminal  nt;
    244       terminal_set  new_lookaheads;
    245       Enumeration   p;
    246       production    prod;
    247       boolean       need_prop;
    248 
    249       /* invalidate cached hashcode */
    250       hashcode_cache = null;
    251 
    252       /* each current element needs to be considered */
    253       consider = new lalr_item_set(this);
    254 
    255       /* repeat this until there is nothing else to consider */
    256       while (consider.size() > 0)
    257     {
    258       /* get one item to consider */
    259       itm = consider.get_one();
    260 
    261       /* do we have a dot before a non terminal */
    262       nt = itm.dot_before_nt();
    263       if (nt != null)
    264         {
    265           /* create the lookahead set based on first after dot */
    266           new_lookaheads = itm.calc_lookahead(itm.lookahead());
    267 
    268           /* are we going to need to propagate our lookahead to new item */
    269           need_prop = itm.lookahead_visible();
    270 
    271           /* create items for each production of that non term */
    272           for (p = nt.productions(); p.hasMoreElements(); )
    273         {
    274           prod = (production)p.nextElement();
    275 
    276           /* create new item with dot at start and that lookahead */
    277           new_itm = new lalr_item(prod,new_lookaheads);
    278 
    279           /* add/merge item into the set */
    280           add_itm = add(new_itm);
    281 
    282           /* if propagation is needed link to that item */
    283           if (need_prop)
    284             itm.add_propagate(add_itm);
    285 
    286           /* was this was a new item*/
    287           if (add_itm == new_itm)
    288             {
    289               /* that may need further closure, consider it also */
    290               consider.add(new_itm);
    291             }
    292         }
    293         }
    294     }
    295     }
    296 
    297   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    298 
    299   /** Equality comparison. */
    300   public boolean equals(lalr_item_set other)
    301     {
    302       if (other == null || other.size() != size()) return false;
    303 
    304       /* once we know they are the same size, then improper subset does test */
    305       try {
    306         return is_subset_of(other);
    307       } catch (internal_error e) {
    308     /* can't throw error from here (because superclass doesn't) so crash */
    309     e.crash();
    310     return false;
    311       }
    312 
    313     }
    314 
    315   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    316 
    317   /** Generic equality comparison. */
    318   public boolean equals(Object other)
    319     {
    320       if (!(other instanceof lalr_item_set))
    321     return false;
    322       else
    323     return equals((lalr_item_set)other);
    324     }
    325 
    326   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    327 
    328   /** Return hash code. */
    329   public int hashCode()
    330     {
    331       int result = 0;
    332       Enumeration e;
    333       int cnt;
    334 
    335       /* only compute a new one if we don't have it cached */
    336       if (hashcode_cache == null)
    337     {
    338           /* hash together codes from at most first 5 elements */
    339           for (e = all(), cnt=0 ; e.hasMoreElements() && cnt<5; cnt++)
    340         result ^= ((lalr_item)e.nextElement()).hashCode();
    341 
    342       hashcode_cache = new Integer(result);
    343     }
    344 
    345       return hashcode_cache.intValue();
    346     }
    347 
    348   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    349 
    350   /** Convert to string. */
    351   public String toString()
    352     {
    353       StringBuffer result = new StringBuffer();
    354 
    355       result.append("{\n");
    356       for (Enumeration e=all(); e.hasMoreElements(); )
    357      {
    358        result.append("  " + (lalr_item)e.nextElement() + "\n");
    359      }
    360        result.append("}");
    361 
    362        return result.toString();
    363     }
    364     /*-----------------------------------------------------------*/
    365 };
    366 
    367