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