| /* |
| * [The "BSD license"] |
| * Copyright (c) 2010 Terence Parr |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.antlr.tool; |
| |
| import org.antlr.analysis.Label; |
| import org.antlr.runtime.Token; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| public class NameSpaceChecker { |
| protected Grammar grammar; |
| |
| public NameSpaceChecker(Grammar grammar) { |
| this.grammar = grammar; |
| } |
| |
| public void checkConflicts() { |
| for (int i = CompositeGrammar.MIN_RULE_INDEX; i < grammar.composite.ruleIndexToRuleList.size(); i++) { |
| Rule r = grammar.composite.ruleIndexToRuleList.elementAt(i); |
| if ( r==null ) { |
| continue; |
| } |
| // walk all labels for Rule r |
| if ( r.labelNameSpace!=null ) { |
| Iterator it = r.labelNameSpace.values().iterator(); |
| while ( it.hasNext() ) { |
| Grammar.LabelElementPair pair = (Grammar.LabelElementPair) it.next(); |
| checkForLabelConflict(r, pair.label); |
| } |
| } |
| // walk rule scope attributes for Rule r |
| if ( r.ruleScope!=null ) { |
| List attributes = r.ruleScope.getAttributes(); |
| for (int j = 0; j < attributes.size(); j++) { |
| Attribute attribute = (Attribute) attributes.get(j); |
| checkForRuleScopeAttributeConflict(r, attribute); |
| } |
| } |
| checkForRuleDefinitionProblems(r); |
| checkForRuleArgumentAndReturnValueConflicts(r); |
| } |
| // check all global scopes against tokens |
| Iterator it = grammar.getGlobalScopes().values().iterator(); |
| while (it.hasNext()) { |
| AttributeScope scope = (AttributeScope) it.next(); |
| checkForGlobalScopeTokenConflict(scope); |
| } |
| // check for missing rule, tokens |
| lookForReferencesToUndefinedSymbols(); |
| } |
| |
| protected void checkForRuleArgumentAndReturnValueConflicts(Rule r) { |
| if ( r.returnScope!=null ) { |
| Set conflictingKeys = r.returnScope.intersection(r.parameterScope); |
| if (conflictingKeys!=null) { |
| for (Iterator it = conflictingKeys.iterator(); it.hasNext();) { |
| String key = (String) it.next(); |
| ErrorManager.grammarError( |
| ErrorManager.MSG_ARG_RETVAL_CONFLICT, |
| grammar, |
| r.tree.getToken(), |
| key, |
| r.name); |
| } |
| } |
| } |
| } |
| |
| protected void checkForRuleDefinitionProblems(Rule r) { |
| String ruleName = r.name; |
| Token ruleToken = r.tree.getToken(); |
| int msgID = 0; |
| if ( (grammar.type==Grammar.PARSER||grammar.type==Grammar.TREE_PARSER) && |
| Character.isUpperCase(ruleName.charAt(0)) ) |
| { |
| msgID = ErrorManager.MSG_LEXER_RULES_NOT_ALLOWED; |
| } |
| else if ( grammar.type==Grammar.LEXER && |
| Character.isLowerCase(ruleName.charAt(0)) && |
| !r.isSynPred ) |
| { |
| msgID = ErrorManager.MSG_PARSER_RULES_NOT_ALLOWED; |
| } |
| else if ( grammar.getGlobalScope(ruleName)!=null ) { |
| msgID = ErrorManager.MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE; |
| } |
| if ( msgID!=0 ) { |
| ErrorManager.grammarError(msgID, grammar, ruleToken, ruleName); |
| } |
| } |
| |
| /** If ref to undefined rule, give error at first occurrence. |
| * |
| * Give error if you cannot find the scope override on a rule reference. |
| * |
| * If you ref ID in a combined grammar and don't define ID as a lexer rule |
| * it is an error. |
| */ |
| protected void lookForReferencesToUndefinedSymbols() { |
| // for each rule ref, ask if there is a rule definition |
| for (Iterator iter = grammar.ruleRefs.iterator(); iter.hasNext();) { |
| GrammarAST refAST = (GrammarAST)iter.next(); |
| Token tok = refAST.token; |
| String ruleName = tok.getText(); |
| Rule localRule = grammar.getLocallyDefinedRule(ruleName); |
| Rule rule = grammar.getRule(ruleName); |
| if ( localRule==null && rule!=null ) { // imported rule? |
| grammar.delegatedRuleReferences.add(rule); |
| rule.imported = true; |
| } |
| if ( rule==null && grammar.getTokenType(ruleName)!=Label.EOF ) { |
| ErrorManager.grammarError(ErrorManager.MSG_UNDEFINED_RULE_REF, |
| grammar, |
| tok, |
| ruleName); |
| } |
| } |
| if ( grammar.type==Grammar.COMBINED ) { |
| // if we're a combined grammar, we know which token IDs have no |
| // associated lexer rule. |
| for (Iterator iter = grammar.tokenIDRefs.iterator(); iter.hasNext();) { |
| Token tok = (Token) iter.next(); |
| String tokenID = tok.getText(); |
| if ( !grammar.composite.lexerRules.contains(tokenID) && |
| grammar.getTokenType(tokenID)!=Label.EOF ) |
| { |
| ErrorManager.grammarWarning(ErrorManager.MSG_NO_TOKEN_DEFINITION, |
| grammar, |
| tok, |
| tokenID); |
| } |
| } |
| } |
| // check scopes and scoped rule refs |
| for (Iterator it = grammar.scopedRuleRefs.iterator(); it.hasNext();) { |
| GrammarAST scopeAST = (GrammarAST)it.next(); // ^(DOT ID atom) |
| Grammar scopeG = grammar.composite.getGrammar(scopeAST.getText()); |
| GrammarAST refAST = (GrammarAST)scopeAST.getChild(1); |
| String ruleName = refAST.getText(); |
| if ( scopeG==null ) { |
| ErrorManager.grammarError(ErrorManager.MSG_NO_SUCH_GRAMMAR_SCOPE, |
| grammar, |
| scopeAST.getToken(), |
| scopeAST.getText(), |
| ruleName); |
| } |
| else { |
| Rule rule = grammar.getRule(scopeG.name, ruleName); |
| if ( rule==null ) { |
| ErrorManager.grammarError(ErrorManager.MSG_NO_SUCH_RULE_IN_SCOPE, |
| grammar, |
| scopeAST.getToken(), |
| scopeAST.getText(), |
| ruleName); |
| } |
| } |
| } |
| } |
| |
| protected void checkForGlobalScopeTokenConflict(AttributeScope scope) { |
| if ( grammar.getTokenType(scope.getName())!=Label.INVALID ) { |
| ErrorManager.grammarError(ErrorManager.MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE, |
| grammar, null, scope.getName()); |
| } |
| } |
| |
| /** Check for collision of a rule-scope dynamic attribute with: |
| * arg, return value, rule name itself. Labels are checked elsewhere. |
| */ |
| public void checkForRuleScopeAttributeConflict(Rule r, Attribute attribute) { |
| int msgID = 0; |
| Object arg2 = null; |
| String attrName = attribute.name; |
| if ( r.name.equals(attrName) ) { |
| msgID = ErrorManager.MSG_ATTRIBUTE_CONFLICTS_WITH_RULE; |
| arg2 = r.name; |
| } |
| else if ( (r.returnScope!=null&&r.returnScope.getAttribute(attrName)!=null) || |
| (r.parameterScope!=null&&r.parameterScope.getAttribute(attrName)!=null) ) |
| { |
| msgID = ErrorManager.MSG_ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL; |
| arg2 = r.name; |
| } |
| if ( msgID!=0 ) { |
| ErrorManager.grammarError(msgID,grammar,r.tree.getToken(),attrName,arg2); |
| } |
| } |
| |
| /** Make sure a label doesn't conflict with another symbol. |
| * Labels must not conflict with: rules, tokens, scope names, |
| * return values, parameters, and rule-scope dynamic attributes |
| * defined in surrounding rule. |
| */ |
| protected void checkForLabelConflict(Rule r, Token label) { |
| int msgID = 0; |
| Object arg2 = null; |
| if ( grammar.getGlobalScope(label.getText())!=null ) { |
| msgID = ErrorManager.MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE; |
| } |
| else if ( grammar.getRule(label.getText())!=null ) { |
| msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_RULE; |
| } |
| else if ( grammar.getTokenType(label.getText())!=Label.INVALID ) { |
| msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_TOKEN; |
| } |
| else if ( r.ruleScope!=null && r.ruleScope.getAttribute(label.getText())!=null ) { |
| msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE; |
| arg2 = r.name; |
| } |
| else if ( (r.returnScope!=null&&r.returnScope.getAttribute(label.getText())!=null) || |
| (r.parameterScope!=null&&r.parameterScope.getAttribute(label.getText())!=null) ) |
| { |
| msgID = ErrorManager.MSG_LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL; |
| arg2 = r.name; |
| } |
| if ( msgID!=0 ) { |
| ErrorManager.grammarError(msgID,grammar,label,label.getText(),arg2); |
| } |
| } |
| |
| /** If type of previous label differs from new label's type, that's an error. |
| */ |
| public boolean checkForLabelTypeMismatch(Rule r, Token label, int type) { |
| Grammar.LabelElementPair prevLabelPair = |
| (Grammar.LabelElementPair)r.labelNameSpace.get(label.getText()); |
| if ( prevLabelPair!=null ) { |
| // label already defined; if same type, no problem |
| if ( prevLabelPair.type != type ) { |
| String typeMismatchExpr = |
| Grammar.LabelTypeToString[type]+"!="+ |
| Grammar.LabelTypeToString[prevLabelPair.type]; |
| ErrorManager.grammarError( |
| ErrorManager.MSG_LABEL_TYPE_CONFLICT, |
| grammar, |
| label, |
| label.getText(), |
| typeMismatchExpr); |
| return true; |
| } |
| } |
| return false; |
| } |
| } |