• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * [The "BSD license"]
3  *  Copyright (c) 2010 Terence Parr
4  *  All rights reserved.
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions
8  *  are met:
9  *  1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *  2. Redistributions in binary form must reproduce the above copyright
12  *      notice, this list of conditions and the following disclaimer in the
13  *      documentation and/or other materials provided with the distribution.
14  *  3. The name of the author may not be used to endorse or promote products
15  *      derived from this software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 package org.antlr.tool;
29 
30 import org.antlr.analysis.NFAState;
31 import org.antlr.codegen.CodeGenerator;
32 import org.antlr.grammar.v3.ANTLRParser;
33 import org.antlr.runtime.CommonToken;
34 import org.antlr.runtime.Token;
35 
36 import java.util.*;
37 
38 /** Combine the info associated with a rule. */
39 public class Rule {
40 	public String name;
41 	public int index;
42 	public String modifier;
43 	public NFAState startState;
44 	public NFAState stopState;
45 
46 	/** This rule's options */
47 	protected Map options;
48 
49 	public static final Set legalOptions =
50 			new HashSet() {
51                 {
52                     add("k"); add("greedy"); add("memoize");
53                     add("backtrack");
54                 }
55             };
56 
57 	/** The AST representing the whole rule */
58 	public GrammarAST tree;
59 
60 	/** To which grammar does this belong? */
61 	public Grammar grammar;
62 
63 	/** For convenience, track the argument def AST action node if any */
64 	public GrammarAST argActionAST;
65 
66 	public GrammarAST EORNode;
67 
68 	/** The return values of a rule and predefined rule attributes */
69 	public AttributeScope returnScope;
70 
71 	public AttributeScope parameterScope;
72 
73 	/** the attributes defined with "scope {...}" inside a rule */
74 	public AttributeScope ruleScope;
75 
76 	/** A list of scope names (String) used by this rule */
77 	public List useScopes;
78 
79     /** Exceptions that this rule can throw */
80     public Set<String> throwsSpec;
81 
82     /** A list of all LabelElementPair attached to tokens like id=ID */
83     public LinkedHashMap tokenLabels;
84 
85     /** A list of all LabelElementPair attached to tokens like x=. in tree grammar */
86     public LinkedHashMap wildcardTreeLabels;
87 
88     /** A list of all LabelElementPair attached to tokens like x+=. in tree grammar */
89     public LinkedHashMap wildcardTreeListLabels;
90 
91 	/** A list of all LabelElementPair attached to single char literals like x='a' */
92 	public LinkedHashMap charLabels;
93 
94 	/** A list of all LabelElementPair attached to rule references like f=field */
95 	public LinkedHashMap ruleLabels;
96 
97 	/** A list of all Token list LabelElementPair like ids+=ID */
98 	public LinkedHashMap tokenListLabels;
99 
100 	/** A list of all rule ref list LabelElementPair like ids+=expr */
101 	public LinkedHashMap ruleListLabels;
102 
103 	/** All labels go in here (plus being split per the above lists) to
104 	 *  catch dup label and label type mismatches.
105 	 */
106 	protected Map<String, Grammar.LabelElementPair> labelNameSpace =
107 		new HashMap<String, Grammar.LabelElementPair>();
108 
109 	/** Map a name to an action for this rule.  Currently init is only
110 	 *  one we use, but we can add more in future.
111 	 *  The code generator will use this to fill holes in the rule template.
112 	 *  I track the AST node for the action in case I need the line number
113 	 *  for errors.  A better name is probably namedActions, but I don't
114 	 *  want everyone to have to change their code gen templates now.
115 	 */
116 	protected Map<String, GrammarAST> actions =
117 		new HashMap<String, GrammarAST>();
118 
119 	/** Track all executable actions other than named actions like @init.
120 	 *  Also tracks exception handlers, predicates, and rewrite rewrites.
121 	 *  We need to examine these actions before code generation so
122 	 *  that we can detect refs to $rule.attr etc...
123 	 */
124 	protected List<GrammarAST> inlineActions = new ArrayList<GrammarAST>();
125 
126 	public int numberOfAlts;
127 
128 	/** Each alt has a Map<tokenRefName,List<tokenRefAST>>; range 1..numberOfAlts.
129 	 *  So, if there are 3 ID refs in a rule's alt number 2, you'll have
130 	 *  altToTokenRef[2].get("ID").size()==3.  This is used to see if $ID is ok.
131 	 *  There must be only one ID reference in the alt for $ID to be ok in
132 	 *  an action--must be unique.
133 	 *
134 	 *  This also tracks '+' and "int" literal token references
135 	 *  (if not in LEXER).
136 	 *
137 	 *  Rewrite rules force tracking of all tokens.
138 	 */
139 	protected Map<String, List<GrammarAST>>[] altToTokenRefMap;
140 
141 	/** Each alt has a Map<ruleRefName,List<ruleRefAST>>; range 1..numberOfAlts
142 	 *  So, if there are 3 expr refs in a rule's alt number 2, you'll have
143 	 *  altToRuleRef[2].get("expr").size()==3.  This is used to see if $expr is ok.
144 	 *  There must be only one expr reference in the alt for $expr to be ok in
145 	 *  an action--must be unique.
146 	 *
147 	 *  Rewrite rules force tracking of all rule result ASTs. 1..n
148 	 */
149 	protected Map<String, List<GrammarAST>>[] altToRuleRefMap;
150 
151 	/** Do not generate start, stop etc... in a return value struct unless
152 	 *  somebody references $r.start somewhere.
153 	 */
154 	public boolean referencedPredefinedRuleAttributes = false;
155 
156 	public boolean isSynPred = false;
157 
158 	public boolean imported = false;
159 
Rule(Grammar grammar, String ruleName, int ruleIndex, int numberOfAlts)160 	public Rule(Grammar grammar,
161 				String ruleName,
162 				int ruleIndex,
163 				int numberOfAlts)
164 	{
165 		this.name = ruleName;
166 		this.index = ruleIndex;
167 		this.numberOfAlts = numberOfAlts;
168 		this.grammar = grammar;
169 		throwsSpec = new HashSet<String>();
170 		altToTokenRefMap = new Map[numberOfAlts+1];
171 		altToRuleRefMap = new Map[numberOfAlts+1];
172 		for (int alt=1; alt<=numberOfAlts; alt++) {
173 			altToTokenRefMap[alt] = new HashMap<String, List<GrammarAST>>();
174 			altToRuleRefMap[alt] = new HashMap<String, List<GrammarAST>>();
175 		}
176 	}
177 
getRuleType(String ruleName)178 	public static int getRuleType(String ruleName){
179 		if (ruleName == null || ruleName.length() == 0)
180 			throw new IllegalArgumentException("The specified rule name is not valid.");
181 		return Character.isUpperCase(ruleName.charAt(0)) ? Grammar.LEXER : Grammar.PARSER;
182 	}
183 
defineLabel(Token label, GrammarAST elementRef, int type)184 	public void defineLabel(Token label, GrammarAST elementRef, int type) {
185 		Grammar.LabelElementPair pair = grammar.new LabelElementPair(label,elementRef);
186 		pair.type = type;
187 		labelNameSpace.put(label.getText(), pair);
188 		switch ( type ) {
189             case Grammar.TOKEN_LABEL :
190                 if ( tokenLabels==null ) tokenLabels = new LinkedHashMap();
191                 tokenLabels.put(label.getText(), pair);
192                 break;
193             case Grammar.WILDCARD_TREE_LABEL :
194                 if ( wildcardTreeLabels==null ) wildcardTreeLabels = new LinkedHashMap();
195                 wildcardTreeLabels.put(label.getText(), pair);
196                 break;
197             case Grammar.WILDCARD_TREE_LIST_LABEL :
198                 if ( wildcardTreeListLabels==null ) wildcardTreeListLabels = new LinkedHashMap();
199                 wildcardTreeListLabels.put(label.getText(), pair);
200                 break;
201 			case Grammar.RULE_LABEL :
202 				if ( ruleLabels==null ) ruleLabels = new LinkedHashMap();
203 				ruleLabels.put(label.getText(), pair);
204 				break;
205 			case Grammar.TOKEN_LIST_LABEL :
206 				if ( tokenListLabels==null ) tokenListLabels = new LinkedHashMap();
207 				tokenListLabels.put(label.getText(), pair);
208 				break;
209 			case Grammar.RULE_LIST_LABEL :
210 				if ( ruleListLabels==null ) ruleListLabels = new LinkedHashMap();
211 				ruleListLabels.put(label.getText(), pair);
212 				break;
213 			case Grammar.CHAR_LABEL :
214 				if ( charLabels==null ) charLabels = new LinkedHashMap();
215 				charLabels.put(label.getText(), pair);
216 				break;
217 		}
218 	}
219 
getLabel(String name)220 	public Grammar.LabelElementPair getLabel(String name) {
221 		return (Grammar.LabelElementPair)labelNameSpace.get(name);
222 	}
223 
getTokenLabel(String name)224 	public Grammar.LabelElementPair getTokenLabel(String name) {
225 		Grammar.LabelElementPair pair = null;
226 		if ( tokenLabels!=null ) {
227 			return (Grammar.LabelElementPair)tokenLabels.get(name);
228 		}
229 		return pair;
230 	}
231 
getRuleLabels()232 	public Map getRuleLabels() {
233 		return ruleLabels;
234 	}
235 
getRuleListLabels()236 	public Map getRuleListLabels() {
237 		return ruleListLabels;
238 	}
239 
getRuleLabel(String name)240 	public Grammar.LabelElementPair getRuleLabel(String name) {
241 		Grammar.LabelElementPair pair = null;
242 		if ( ruleLabels!=null ) {
243 			return (Grammar.LabelElementPair)ruleLabels.get(name);
244 		}
245 		return pair;
246 	}
247 
getTokenListLabel(String name)248 	public Grammar.LabelElementPair getTokenListLabel(String name) {
249 		Grammar.LabelElementPair pair = null;
250 		if ( tokenListLabels!=null ) {
251 			return (Grammar.LabelElementPair)tokenListLabels.get(name);
252 		}
253 		return pair;
254 	}
255 
getRuleListLabel(String name)256 	public Grammar.LabelElementPair getRuleListLabel(String name) {
257 		Grammar.LabelElementPair pair = null;
258 		if ( ruleListLabels!=null ) {
259 			return (Grammar.LabelElementPair)ruleListLabels.get(name);
260 		}
261 		return pair;
262 	}
263 
264 	/** Track a token ID or literal like '+' and "void" as having been referenced
265 	 *  somewhere within the alts (not rewrite sections) of a rule.
266 	 *
267 	 *  This differs from Grammar.altReferencesTokenID(), which tracks all
268 	 *  token IDs to check for token IDs without corresponding lexer rules.
269 	 */
trackTokenReferenceInAlt(GrammarAST refAST, int outerAltNum)270 	public void trackTokenReferenceInAlt(GrammarAST refAST, int outerAltNum) {
271 		List refs = (List)altToTokenRefMap[outerAltNum].get(refAST.getText());
272 		if ( refs==null ) {
273 			refs = new ArrayList();
274 			altToTokenRefMap[outerAltNum].put(refAST.getText(), refs);
275 		}
276 		refs.add(refAST);
277 	}
278 
getTokenRefsInAlt(String ref, int outerAltNum)279 	public List getTokenRefsInAlt(String ref, int outerAltNum) {
280 		if ( altToTokenRefMap[outerAltNum]!=null ) {
281 			List tokenRefASTs = (List)altToTokenRefMap[outerAltNum].get(ref);
282 			return tokenRefASTs;
283 		}
284 		return null;
285 	}
286 
trackRuleReferenceInAlt(GrammarAST refAST, int outerAltNum)287 	public void trackRuleReferenceInAlt(GrammarAST refAST, int outerAltNum) {
288 		List refs = (List)altToRuleRefMap[outerAltNum].get(refAST.getText());
289 		if ( refs==null ) {
290 			refs = new ArrayList();
291 			altToRuleRefMap[outerAltNum].put(refAST.getText(), refs);
292 		}
293 		refs.add(refAST);
294 	}
295 
getRuleRefsInAlt(String ref, int outerAltNum)296 	public List getRuleRefsInAlt(String ref, int outerAltNum) {
297 		if ( altToRuleRefMap[outerAltNum]!=null ) {
298 			List ruleRefASTs = (List)altToRuleRefMap[outerAltNum].get(ref);
299 			return ruleRefASTs;
300 		}
301 		return null;
302 	}
303 
getTokenRefsInAlt(int altNum)304 	public Set getTokenRefsInAlt(int altNum) {
305 		return altToTokenRefMap[altNum].keySet();
306 	}
307 
308 	/** For use with rewrite rules, we must track all tokens matched on the
309 	 *  left-hand-side; so we need Lists.  This is a unique list of all
310 	 *  token types for which the rule needs a list of tokens.  This
311 	 *  is called from the rule template not directly by the code generator.
312 	 */
getAllTokenRefsInAltsWithRewrites()313 	public Set getAllTokenRefsInAltsWithRewrites() {
314 		String output = (String)grammar.getOption("output");
315 		Set<String> tokens = new HashSet<String>();
316 		if ( output==null || !output.equals("AST") ) {
317 			// return nothing if not generating trees; i.e., don't do for templates
318 			return tokens;
319 		}
320 		//System.out.println("blk "+tree.findFirstType(ANTLRParser.BLOCK).toStringTree());
321 		for (int i = 1; i <= numberOfAlts; i++) {
322 			if ( hasRewrite(i) ) {
323 				Map<String, List<GrammarAST>> m = altToTokenRefMap[i];
324 				for (String tokenName : m.keySet()) {
325 					// convert token name like ID to ID, "void" to 31
326 					int ttype = grammar.getTokenType(tokenName);
327 					String label = grammar.generator.getTokenTypeAsTargetLabel(ttype);
328 					tokens.add(label);
329 				}
330 			}
331 		}
332 		return tokens;
333 	}
334 
getRuleRefsInAlt(int outerAltNum)335 	public Set getRuleRefsInAlt(int outerAltNum) {
336 		return altToRuleRefMap[outerAltNum].keySet();
337 	}
338 
339 	/** For use with rewrite rules, we must track all rule AST results on the
340 	 *  left-hand-side; so we need Lists.  This is a unique list of all
341 	 *  rule results for which the rule needs a list of results.
342 	 */
getAllRuleRefsInAltsWithRewrites()343 	public Set getAllRuleRefsInAltsWithRewrites() {
344 		Set rules = new HashSet();
345 		for (int i = 1; i <= numberOfAlts; i++) {
346 			if ( hasRewrite(i) ) {
347 				Map m = altToRuleRefMap[i];
348 				rules.addAll(m.keySet());
349 			}
350 		}
351 		return rules;
352 	}
353 
getInlineActions()354 	public List<GrammarAST> getInlineActions() {
355 		return inlineActions;
356 	}
357 
hasRewrite(int i)358 	public boolean hasRewrite(int i) {
359 		GrammarAST blk = tree.findFirstType(ANTLRParser.BLOCK);
360 		GrammarAST alt = blk.getBlockALT(i);
361 		GrammarAST rew = (GrammarAST)alt.getNextSibling();
362 		if ( rew!=null && rew.getType()==ANTLRParser.REWRITES ) return true;
363 		if ( alt.findFirstType(ANTLRParser.REWRITES)!=null ) return true;
364 		return false;
365 	}
366 
367 	/** Return the scope containing name */
getAttributeScope(String name)368 	public AttributeScope getAttributeScope(String name) {
369 		AttributeScope scope = getLocalAttributeScope(name);
370 		if ( scope!=null ) {
371 			return scope;
372 		}
373 		if ( ruleScope!=null && ruleScope.getAttribute(name)!=null ) {
374 			scope = ruleScope;
375 		}
376 		return scope;
377 	}
378 
379 	/** Get the arg, return value, or predefined property for this rule */
getLocalAttributeScope(String name)380 	public AttributeScope getLocalAttributeScope(String name) {
381 		AttributeScope scope = null;
382 		if ( returnScope!=null && returnScope.getAttribute(name)!=null ) {
383 			scope = returnScope;
384 		}
385 		else if ( parameterScope!=null && parameterScope.getAttribute(name)!=null ) {
386 			scope = parameterScope;
387 		}
388 		else {
389 			AttributeScope rulePropertiesScope =
390 				RuleLabelScope.grammarTypeToRulePropertiesScope[grammar.type];
391 			if ( rulePropertiesScope.getAttribute(name)!=null ) {
392 				scope = rulePropertiesScope;
393 			}
394 		}
395 		return scope;
396 	}
397 
398 	/** For references to tokens rather than by label such as $ID, we
399 	 *  need to get the existing label for the ID ref or create a new
400 	 *  one.
401 	 */
getElementLabel(String refdSymbol, int outerAltNum, CodeGenerator generator)402 	public String getElementLabel(String refdSymbol,
403 								  int outerAltNum,
404 								  CodeGenerator generator)
405 	{
406 		GrammarAST uniqueRefAST;
407 		if ( grammar.type != Grammar.LEXER &&
408 			 Character.isUpperCase(refdSymbol.charAt(0)) )
409 		{
410 			// symbol is a token
411 			List tokenRefs = getTokenRefsInAlt(refdSymbol, outerAltNum);
412 			uniqueRefAST = (GrammarAST)tokenRefs.get(0);
413 		}
414 		else {
415 			// symbol is a rule
416 			List ruleRefs = getRuleRefsInAlt(refdSymbol, outerAltNum);
417 			uniqueRefAST = (GrammarAST)ruleRefs.get(0);
418 		}
419 		if ( uniqueRefAST.code==null ) {
420 			// no code?  must not have gen'd yet; forward ref
421 			return null;
422 		}
423 		String labelName = null;
424 		String existingLabelName =
425 			(String)uniqueRefAST.code.getAttribute("label");
426 		// reuse any label or list label if it exists
427 		if ( existingLabelName!=null ) {
428 			labelName = existingLabelName;
429 		}
430 		else {
431 			// else create new label
432 			labelName = generator.createUniqueLabel(refdSymbol);
433 			CommonToken label = new CommonToken(ANTLRParser.ID, labelName);
434 			if ( grammar.type != Grammar.LEXER &&
435 				 Character.isUpperCase(refdSymbol.charAt(0)) )
436 			{
437 				grammar.defineTokenRefLabel(name, label, uniqueRefAST);
438 			}
439 			else {
440 				grammar.defineRuleRefLabel(name, label, uniqueRefAST);
441 			}
442 			uniqueRefAST.code.add("label", labelName);
443 		}
444 		return labelName;
445 	}
446 
447 	/** If a rule has no user-defined return values and nobody references
448 	 *  it's start/stop (predefined attributes), then there is no need to
449 	 *  define a struct; otherwise for now we assume a struct.  A rule also
450 	 *  has multiple return values if you are building trees or templates.
451 	 */
getHasMultipleReturnValues()452 	public boolean getHasMultipleReturnValues() {
453 		return
454 			referencedPredefinedRuleAttributes || grammar.buildAST() ||
455 			grammar.buildTemplate() ||
456 			(returnScope!=null && returnScope.attributes.size()>1);
457 	}
458 
getHasSingleReturnValue()459 	public boolean getHasSingleReturnValue() {
460 		return
461 			!(referencedPredefinedRuleAttributes || grammar.buildAST() ||
462 			  grammar.buildTemplate()) &&
463 									   (returnScope!=null && returnScope.attributes.size()==1);
464 	}
465 
getHasReturnValue()466 	public boolean getHasReturnValue() {
467 		return
468 			referencedPredefinedRuleAttributes || grammar.buildAST() ||
469 			grammar.buildTemplate() ||
470 			(returnScope!=null && returnScope.attributes.size()>0);
471 	}
472 
getSingleValueReturnType()473 	public String getSingleValueReturnType() {
474 		if ( returnScope!=null && returnScope.attributes.size()==1 ) {
475 			Collection retvalAttrs = returnScope.attributes.values();
476 			Object[] javaSucks = retvalAttrs.toArray();
477 			return ((Attribute)javaSucks[0]).type;
478 		}
479 		return null;
480 	}
481 
getSingleValueReturnName()482 	public String getSingleValueReturnName() {
483 		if ( returnScope!=null && returnScope.attributes.size()==1 ) {
484 			Collection retvalAttrs = returnScope.attributes.values();
485 			Object[] javaSucks = retvalAttrs.toArray();
486 			return ((Attribute)javaSucks[0]).name;
487 		}
488 		return null;
489 	}
490 
491 	/** Given @scope::name {action} define it for this grammar.  Later,
492 	 *  the code generator will ask for the actions table.
493 	 */
defineNamedAction(GrammarAST ampersandAST, GrammarAST nameAST, GrammarAST actionAST)494 	public void defineNamedAction(GrammarAST ampersandAST,
495 								  GrammarAST nameAST,
496 								  GrammarAST actionAST)
497 	{
498 		//System.out.println("rule @"+nameAST.getText()+"{"+actionAST.getText()+"}");
499 		String actionName = nameAST.getText();
500 		GrammarAST a = (GrammarAST)actions.get(actionName);
501 		if ( a!=null ) {
502 			ErrorManager.grammarError(
503 				ErrorManager.MSG_ACTION_REDEFINITION,grammar,
504 				nameAST.getToken(),nameAST.getText());
505 		}
506 		else {
507 			actions.put(actionName,actionAST);
508 		}
509 	}
510 
trackInlineAction(GrammarAST actionAST)511 	public void trackInlineAction(GrammarAST actionAST) {
512 		inlineActions.add(actionAST);
513 	}
514 
getActions()515 	public Map<String, GrammarAST> getActions() {
516 		return actions;
517 	}
518 
setActions(Map<String, GrammarAST> actions)519 	public void setActions(Map<String, GrammarAST> actions) {
520 		this.actions = actions;
521 	}
522 
523 	/** Save the option key/value pair and process it; return the key
524 	 *  or null if invalid option.
525 	 */
setOption(String key, Object value, Token optionsStartToken)526 	public String setOption(String key, Object value, Token optionsStartToken) {
527 		if ( !legalOptions.contains(key) ) {
528 			ErrorManager.grammarError(ErrorManager.MSG_ILLEGAL_OPTION,
529 									  grammar,
530 									  optionsStartToken,
531 									  key);
532 			return null;
533 		}
534 		if ( options==null ) {
535 			options = new HashMap();
536 		}
537         if ( key.equals("memoize") && value.toString().equals("true") ) {
538             grammar.atLeastOneRuleMemoizes = true;
539         }
540         if ( key.equals("backtrack") && value.toString().equals("true") ) {
541             grammar.composite.getRootGrammar().atLeastOneBacktrackOption = true;
542         }
543 		if ( key.equals("k") ) {
544 			grammar.numberOfManualLookaheadOptions++;
545 		}
546 		 options.put(key, value);
547 		return key;
548 	}
549 
setOptions(Map options, Token optionsStartToken)550 	public void setOptions(Map options, Token optionsStartToken) {
551 		if ( options==null ) {
552 			this.options = null;
553 			return;
554 		}
555 		Set keys = options.keySet();
556 		for (Iterator it = keys.iterator(); it.hasNext();) {
557 			String optionName = (String) it.next();
558 			Object optionValue = options.get(optionName);
559 			String stored=setOption(optionName, optionValue, optionsStartToken);
560 			if ( stored==null ) {
561 				it.remove();
562 			}
563 		}
564 	}
565 
566 	/** Used during grammar imports to see if sets of rules intersect... This
567 	 *  method and hashCode use the String name as the key for Rule objects.
568 	public boolean equals(Object other) {
569 		return this.name.equals(((Rule)other).name);
570 	}
571 	 */
572 
573 	/** Used during grammar imports to see if sets of rules intersect...
574 	public int hashCode() {
575 		return name.hashCode();
576 	}
577 	 * */
578 
toString()579 	public String toString() { // used for testing
580 		return "["+grammar.name+"."+name+",index="+index+",line="+tree.getToken().getLine()+"]";
581 	}
582 }
583