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 */
28package org.antlr.tool;
29
30import org.antlr.analysis.Label;
31import org.antlr.runtime.Token;
32
33import java.util.Iterator;
34import java.util.List;
35import java.util.Set;
36
37public class NameSpaceChecker {
38	protected Grammar grammar;
39
40	public NameSpaceChecker(Grammar grammar) {
41		this.grammar = grammar;
42	}
43
44	public void checkConflicts() {
45		for (int i = CompositeGrammar.MIN_RULE_INDEX; i < grammar.composite.ruleIndexToRuleList.size(); i++) {
46			Rule r = grammar.composite.ruleIndexToRuleList.elementAt(i);
47			if ( r==null ) {
48				continue;
49			}
50			// walk all labels for Rule r
51			if ( r.labelNameSpace!=null ) {
52				Iterator it = r.labelNameSpace.values().iterator();
53				while ( it.hasNext() ) {
54					Grammar.LabelElementPair pair = (Grammar.LabelElementPair) it.next();
55					checkForLabelConflict(r, pair.label);
56				}
57			}
58			// walk rule scope attributes for Rule r
59			if ( r.ruleScope!=null ) {
60				List attributes = r.ruleScope.getAttributes();
61				for (int j = 0; j < attributes.size(); j++) {
62					Attribute attribute = (Attribute) attributes.get(j);
63					checkForRuleScopeAttributeConflict(r, attribute);
64				}
65			}
66			checkForRuleDefinitionProblems(r);
67			checkForRuleArgumentAndReturnValueConflicts(r);
68		}
69		// check all global scopes against tokens
70		Iterator it = grammar.getGlobalScopes().values().iterator();
71		while (it.hasNext()) {
72			AttributeScope scope = (AttributeScope) it.next();
73			checkForGlobalScopeTokenConflict(scope);
74		}
75		// check for missing rule, tokens
76		lookForReferencesToUndefinedSymbols();
77	}
78
79	protected void checkForRuleArgumentAndReturnValueConflicts(Rule r) {
80		if ( r.returnScope!=null ) {
81			Set conflictingKeys = r.returnScope.intersection(r.parameterScope);
82			if (conflictingKeys!=null) {
83				for (Iterator it = conflictingKeys.iterator(); it.hasNext();) {
84					String key = (String) it.next();
85					ErrorManager.grammarError(
86						ErrorManager.MSG_ARG_RETVAL_CONFLICT,
87						grammar,
88						r.tree.getToken(),
89						key,
90						r.name);
91				}
92			}
93		}
94	}
95
96	protected void checkForRuleDefinitionProblems(Rule r) {
97		String ruleName = r.name;
98		Token ruleToken = r.tree.getToken();
99		int msgID = 0;
100		if ( (grammar.type==Grammar.PARSER||grammar.type==Grammar.TREE_PARSER) &&
101			 Character.isUpperCase(ruleName.charAt(0)) )
102		{
103			msgID = ErrorManager.MSG_LEXER_RULES_NOT_ALLOWED;
104        }
105        else if ( grammar.type==Grammar.LEXER &&
106			      Character.isLowerCase(ruleName.charAt(0)) &&
107			      !r.isSynPred )
108		{
109			msgID = ErrorManager.MSG_PARSER_RULES_NOT_ALLOWED;
110        }
111		else if ( grammar.getGlobalScope(ruleName)!=null ) {
112			msgID = ErrorManager.MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE;
113		}
114		if ( msgID!=0 ) {
115			ErrorManager.grammarError(msgID, grammar, ruleToken, ruleName);
116		}
117	}
118
119	/** If ref to undefined rule, give error at first occurrence.
120	 *
121	 *  Give error if you cannot find the scope override on a rule reference.
122	 *
123	 *  If you ref ID in a combined grammar and don't define ID as a lexer rule
124	 *  it is an error.
125	 */
126	protected void lookForReferencesToUndefinedSymbols() {
127		// for each rule ref, ask if there is a rule definition
128		for (Iterator iter = grammar.ruleRefs.iterator(); iter.hasNext();) {
129			GrammarAST refAST = (GrammarAST)iter.next();
130			Token tok = refAST.token;
131			String ruleName = tok.getText();
132			Rule localRule = grammar.getLocallyDefinedRule(ruleName);
133			Rule rule = grammar.getRule(ruleName);
134			if ( localRule==null && rule!=null ) { // imported rule?
135				grammar.delegatedRuleReferences.add(rule);
136				rule.imported = true;
137			}
138			if ( rule==null && grammar.getTokenType(ruleName)!=Label.EOF ) {
139				ErrorManager.grammarError(ErrorManager.MSG_UNDEFINED_RULE_REF,
140										  grammar,
141										  tok,
142										  ruleName);
143			}
144        }
145		if ( grammar.type==Grammar.COMBINED ) {
146			// if we're a combined grammar, we know which token IDs have no
147			// associated lexer rule.
148			for (Iterator iter = grammar.tokenIDRefs.iterator(); iter.hasNext();) {
149				Token tok = (Token) iter.next();
150				String tokenID = tok.getText();
151				if ( !grammar.composite.lexerRules.contains(tokenID) &&
152					 grammar.getTokenType(tokenID)!=Label.EOF )
153				{
154					ErrorManager.grammarWarning(ErrorManager.MSG_NO_TOKEN_DEFINITION,
155												grammar,
156												tok,
157												tokenID);
158				}
159			}
160		}
161		// check scopes and scoped rule refs
162		for (Iterator it = grammar.scopedRuleRefs.iterator(); it.hasNext();) {
163			GrammarAST scopeAST = (GrammarAST)it.next(); // ^(DOT ID atom)
164			Grammar scopeG = grammar.composite.getGrammar(scopeAST.getText());
165			GrammarAST refAST = (GrammarAST)scopeAST.getChild(1);
166			String ruleName = refAST.getText();
167			if ( scopeG==null ) {
168				ErrorManager.grammarError(ErrorManager.MSG_NO_SUCH_GRAMMAR_SCOPE,
169										  grammar,
170										  scopeAST.getToken(),
171										  scopeAST.getText(),
172										  ruleName);
173			}
174			else {
175				Rule rule = grammar.getRule(scopeG.name, ruleName);
176				if ( rule==null ) {
177					ErrorManager.grammarError(ErrorManager.MSG_NO_SUCH_RULE_IN_SCOPE,
178											  grammar,
179											  scopeAST.getToken(),
180											  scopeAST.getText(),
181											  ruleName);
182				}
183			}
184		}
185	}
186
187	protected void checkForGlobalScopeTokenConflict(AttributeScope scope) {
188		if ( grammar.getTokenType(scope.getName())!=Label.INVALID ) {
189			ErrorManager.grammarError(ErrorManager.MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE,
190									  grammar, null, scope.getName());
191		}
192	}
193
194	/** Check for collision of a rule-scope dynamic attribute with:
195	 *  arg, return value, rule name itself.  Labels are checked elsewhere.
196	 */
197	public void checkForRuleScopeAttributeConflict(Rule r, Attribute attribute) {
198		int msgID = 0;
199		Object arg2 = null;
200		String attrName = attribute.name;
201		if ( r.name.equals(attrName) ) {
202			msgID = ErrorManager.MSG_ATTRIBUTE_CONFLICTS_WITH_RULE;
203			arg2 = r.name;
204		}
205		else if ( (r.returnScope!=null&&r.returnScope.getAttribute(attrName)!=null) ||
206				  (r.parameterScope!=null&&r.parameterScope.getAttribute(attrName)!=null) )
207		{
208			msgID = ErrorManager.MSG_ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL;
209			arg2 = r.name;
210		}
211		if ( msgID!=0 ) {
212			ErrorManager.grammarError(msgID,grammar,r.tree.getToken(),attrName,arg2);
213		}
214	}
215
216	/** Make sure a label doesn't conflict with another symbol.
217	 *  Labels must not conflict with: rules, tokens, scope names,
218	 *  return values, parameters, and rule-scope dynamic attributes
219	 *  defined in surrounding rule.
220	 */
221	protected void checkForLabelConflict(Rule r, Token label) {
222		int msgID = 0;
223		Object arg2 = null;
224		if ( grammar.getGlobalScope(label.getText())!=null ) {
225			msgID = ErrorManager.MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE;
226		}
227		else if ( grammar.getRule(label.getText())!=null ) {
228			msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_RULE;
229		}
230		else if ( grammar.getTokenType(label.getText())!=Label.INVALID ) {
231			msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_TOKEN;
232		}
233		else if ( r.ruleScope!=null && r.ruleScope.getAttribute(label.getText())!=null ) {
234			msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE;
235			arg2 = r.name;
236		}
237		else if ( (r.returnScope!=null&&r.returnScope.getAttribute(label.getText())!=null) ||
238				  (r.parameterScope!=null&&r.parameterScope.getAttribute(label.getText())!=null) )
239		{
240			msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL;
241			arg2 = r.name;
242		}
243		if ( msgID!=0 ) {
244			ErrorManager.grammarError(msgID,grammar,label,label.getText(),arg2);
245		}
246	}
247
248	/** If type of previous label differs from new label's type, that's an error.
249	 */
250	public boolean checkForLabelTypeMismatch(Rule r, Token label, int type) {
251		Grammar.LabelElementPair prevLabelPair =
252			(Grammar.LabelElementPair)r.labelNameSpace.get(label.getText());
253		if ( prevLabelPair!=null ) {
254			// label already defined; if same type, no problem
255			if ( prevLabelPair.type != type ) {
256				String typeMismatchExpr =
257					Grammar.LabelTypeToString[type]+"!="+
258					Grammar.LabelTypeToString[prevLabelPair.type];
259				ErrorManager.grammarError(
260					ErrorManager.MSG_LABEL_TYPE_CONFLICT,
261					grammar,
262					label,
263					label.getText(),
264					typeMismatchExpr);
265				return true;
266			}
267		}
268		return false;
269	}
270}
271