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