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