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.Tool; 31 import org.antlr.analysis.DFAState; 32 import org.antlr.analysis.DecisionProbe; 33 import org.antlr.analysis.NFAState; 34 import org.antlr.misc.BitSet; 35 import org.antlr.runtime.RecognitionException; 36 import org.antlr.runtime.Token; 37 import org.stringtemplate.v4.ST; 38 import org.stringtemplate.v4.STErrorListener; 39 import org.stringtemplate.v4.STGroup; 40 import org.stringtemplate.v4.STGroupFile; 41 import org.stringtemplate.v4.misc.STMessage; 42 43 import java.lang.reflect.Field; 44 import java.util.Collection; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Locale; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.WeakHashMap; 52 53 /** Defines all the errors ANTLR can generator for both the tool and for 54 * issues with a grammar. 55 * 56 * Here is a list of language names: 57 * 58 * http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt 59 * 60 * Here is a list of country names: 61 * 62 * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html 63 * 64 * I use constants not strings to identify messages as the compiler will 65 * find any errors/mismatches rather than leaving a mistyped string in 66 * the code to be found randomly in the future. Further, Intellij can 67 * do field name expansion to save me some typing. I have to map 68 * int constants to template names, however, which could introduce a mismatch. 69 * Someone could provide a .stg file that had a template name wrong. When 70 * I load the group, then, I must verify that all messages are there. 71 * 72 * This is essentially the functionality of the resource bundle stuff Java 73 * has, but I don't want to load a property file--I want to load a template 74 * group file and this is so simple, why mess with their junk. 75 * 76 * I use the default Locale as defined by java to compute a group file name 77 * in the org/antlr/tool/templates/messages dir called en_US.stg and so on. 78 * 79 * Normally we want to use the default locale, but often a message file will 80 * not exist for it so we must fall back on the US local. 81 * 82 * During initialization of this class, all errors go straight to System.err. 83 * There is no way around this. If I have not set up the error system, how 84 * can I do errors properly? For example, if the string template group file 85 * full of messages has an error, how could I print to anything but System.err? 86 * 87 * TODO: how to map locale to a file encoding for the stringtemplate group file? 88 * ST knows how to pay attention to the default encoding so it 89 * should probably just work unless a GUI sets the local to some chinese 90 * variation but System.getProperty("file.encoding") is US. Hmm... 91 * 92 * TODO: get antlr.g etc.. parsing errors to come here. 93 */ 94 public class ErrorManager { 95 // TOOL ERRORS 96 // file errors 97 public static final int MSG_CANNOT_WRITE_FILE = 1; 98 public static final int MSG_CANNOT_CLOSE_FILE = 2; 99 public static final int MSG_CANNOT_FIND_TOKENS_FILE = 3; 100 public static final int MSG_ERROR_READING_TOKENS_FILE = 4; 101 public static final int MSG_DIR_NOT_FOUND = 5; 102 public static final int MSG_OUTPUT_DIR_IS_FILE = 6; 103 public static final int MSG_CANNOT_OPEN_FILE = 7; 104 public static final int MSG_FILE_AND_GRAMMAR_NAME_DIFFER = 8; 105 public static final int MSG_FILENAME_EXTENSION_ERROR = 9; 106 107 public static final int MSG_INTERNAL_ERROR = 10; 108 public static final int MSG_INTERNAL_WARNING = 11; 109 public static final int MSG_ERROR_CREATING_ARTIFICIAL_RULE = 12; 110 public static final int MSG_TOKENS_FILE_SYNTAX_ERROR = 13; 111 public static final int MSG_CANNOT_GEN_DOT_FILE = 14; 112 public static final int MSG_BAD_AST_STRUCTURE = 15; 113 public static final int MSG_BAD_ACTION_AST_STRUCTURE = 16; 114 115 // code gen errors 116 public static final int MSG_MISSING_CODE_GEN_TEMPLATES = 20; 117 public static final int MSG_MISSING_CYCLIC_DFA_CODE_GEN_TEMPLATES = 21; 118 public static final int MSG_CODE_GEN_TEMPLATES_INCOMPLETE = 22; 119 public static final int MSG_CANNOT_CREATE_TARGET_GENERATOR = 23; 120 //public static final int MSG_CANNOT_COMPUTE_SAMPLE_INPUT_SEQ = 24; 121 public static final int MSG_STRING_TEMPLATE_ERROR = 24; 122 123 // GRAMMAR ERRORS 124 public static final int MSG_SYNTAX_ERROR = 100; 125 public static final int MSG_RULE_REDEFINITION = 101; 126 public static final int MSG_LEXER_RULES_NOT_ALLOWED = 102; 127 public static final int MSG_PARSER_RULES_NOT_ALLOWED = 103; 128 public static final int MSG_CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL = 104; 129 public static final int MSG_NO_TOKEN_DEFINITION = 105; 130 public static final int MSG_UNDEFINED_RULE_REF = 106; 131 public static final int MSG_LITERAL_NOT_ASSOCIATED_WITH_LEXER_RULE = 107; 132 public static final int MSG_CANNOT_ALIAS_TOKENS_IN_LEXER = 108; 133 public static final int MSG_ATTRIBUTE_REF_NOT_IN_RULE = 111; 134 public static final int MSG_INVALID_RULE_SCOPE_ATTRIBUTE_REF = 112; 135 public static final int MSG_UNKNOWN_ATTRIBUTE_IN_SCOPE = 113; 136 public static final int MSG_UNKNOWN_SIMPLE_ATTRIBUTE = 114; 137 public static final int MSG_INVALID_RULE_PARAMETER_REF = 115; 138 public static final int MSG_UNKNOWN_RULE_ATTRIBUTE = 116; 139 public static final int MSG_ISOLATED_RULE_SCOPE = 117; 140 public static final int MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE = 118; 141 public static final int MSG_LABEL_CONFLICTS_WITH_RULE = 119; 142 public static final int MSG_LABEL_CONFLICTS_WITH_TOKEN = 120; 143 public static final int MSG_LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE = 121; 144 public static final int MSG_LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL = 122; 145 public static final int MSG_ATTRIBUTE_CONFLICTS_WITH_RULE = 123; 146 public static final int MSG_ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL = 124; 147 public static final int MSG_LABEL_TYPE_CONFLICT = 125; 148 public static final int MSG_ARG_RETVAL_CONFLICT = 126; 149 public static final int MSG_NONUNIQUE_REF = 127; 150 public static final int MSG_FORWARD_ELEMENT_REF = 128; 151 public static final int MSG_MISSING_RULE_ARGS = 129; 152 public static final int MSG_RULE_HAS_NO_ARGS = 130; 153 public static final int MSG_ARGS_ON_TOKEN_REF = 131; 154 public static final int MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT = 132; 155 public static final int MSG_ILLEGAL_OPTION = 133; 156 public static final int MSG_LIST_LABEL_INVALID_UNLESS_RETVAL_STRUCT = 134; 157 public static final int MSG_UNDEFINED_TOKEN_REF_IN_REWRITE = 135; 158 public static final int MSG_REWRITE_ELEMENT_NOT_PRESENT_ON_LHS = 136; 159 public static final int MSG_UNDEFINED_LABEL_REF_IN_REWRITE = 137; 160 public static final int MSG_NO_GRAMMAR_START_RULE = 138; 161 public static final int MSG_EMPTY_COMPLEMENT = 139; 162 public static final int MSG_UNKNOWN_DYNAMIC_SCOPE = 140; 163 public static final int MSG_UNKNOWN_DYNAMIC_SCOPE_ATTRIBUTE = 141; 164 public static final int MSG_ISOLATED_RULE_ATTRIBUTE = 142; 165 public static final int MSG_INVALID_ACTION_SCOPE = 143; 166 public static final int MSG_ACTION_REDEFINITION = 144; 167 public static final int MSG_DOUBLE_QUOTES_ILLEGAL = 145; 168 public static final int MSG_INVALID_TEMPLATE_ACTION = 146; 169 public static final int MSG_MISSING_ATTRIBUTE_NAME = 147; 170 public static final int MSG_ARG_INIT_VALUES_ILLEGAL = 148; 171 public static final int MSG_REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION = 149; 172 public static final int MSG_NO_RULES = 150; 173 public static final int MSG_WRITE_TO_READONLY_ATTR = 151; 174 public static final int MSG_MISSING_AST_TYPE_IN_TREE_GRAMMAR = 152; 175 public static final int MSG_REWRITE_FOR_MULTI_ELEMENT_ALT = 153; 176 public static final int MSG_RULE_INVALID_SET = 154; 177 public static final int MSG_HETERO_ILLEGAL_IN_REWRITE_ALT = 155; 178 public static final int MSG_NO_SUCH_GRAMMAR_SCOPE = 156; 179 public static final int MSG_NO_SUCH_RULE_IN_SCOPE = 157; 180 public static final int MSG_TOKEN_ALIAS_CONFLICT = 158; 181 public static final int MSG_TOKEN_ALIAS_REASSIGNMENT = 159; 182 public static final int MSG_TOKEN_VOCAB_IN_DELEGATE = 160; 183 public static final int MSG_INVALID_IMPORT = 161; 184 public static final int MSG_IMPORTED_TOKENS_RULE_EMPTY = 162; 185 public static final int MSG_IMPORT_NAME_CLASH = 163; 186 public static final int MSG_AST_OP_WITH_NON_AST_OUTPUT_OPTION = 164; 187 public static final int MSG_AST_OP_IN_ALT_WITH_REWRITE = 165; 188 public static final int MSG_WILDCARD_AS_ROOT = 166; 189 public static final int MSG_CONFLICTING_OPTION_IN_TREE_FILTER = 167; 190 public static final int MSG_ILLEGAL_OPTION_VALUE = 168; 191 public static final int MSG_ALL_OPS_NEED_SAME_ASSOC = 169; 192 public static final int MSG_RANGE_OP_ILLEGAL = 170; 193 194 // GRAMMAR WARNINGS 195 public static final int MSG_GRAMMAR_NONDETERMINISM = 200; // A predicts alts 1,2 196 public static final int MSG_UNREACHABLE_ALTS = 201; // nothing predicts alt i 197 public static final int MSG_DANGLING_STATE = 202; // no edges out of state 198 public static final int MSG_INSUFFICIENT_PREDICATES = 203; 199 public static final int MSG_DUPLICATE_SET_ENTRY = 204; // (A|A) 200 public static final int MSG_ANALYSIS_ABORTED = 205; 201 public static final int MSG_RECURSION_OVERLOW = 206; 202 public static final int MSG_LEFT_RECURSION = 207; 203 public static final int MSG_UNREACHABLE_TOKENS = 208; // nothing predicts token 204 public static final int MSG_TOKEN_NONDETERMINISM = 209; // alts of Tokens rule 205 public static final int MSG_LEFT_RECURSION_CYCLES = 210; 206 public static final int MSG_NONREGULAR_DECISION = 211; 207 208 209 // Dependency sorting errors 210 // 211 public static final int MSG_CIRCULAR_DEPENDENCY = 212; // t1.g -> t2.g -> t3.g ->t1.g 212 213 public static final int MAX_MESSAGE_NUMBER = 212; 214 215 /** Do not do perform analysis if one of these happens */ 216 public static final BitSet ERRORS_FORCING_NO_ANALYSIS = new BitSet() { 217 { 218 add(MSG_RULE_REDEFINITION); 219 add(MSG_UNDEFINED_RULE_REF); 220 add(MSG_LEFT_RECURSION_CYCLES); 221 add(MSG_REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION); 222 add(MSG_NO_RULES); 223 add(MSG_NO_SUCH_GRAMMAR_SCOPE); 224 add(MSG_NO_SUCH_RULE_IN_SCOPE); 225 add(MSG_LEXER_RULES_NOT_ALLOWED); 226 add(MSG_WILDCARD_AS_ROOT); 227 add(MSG_CIRCULAR_DEPENDENCY); 228 // TODO: ... 229 } 230 }; 231 232 /** Do not do code gen if one of these happens */ 233 public static final BitSet ERRORS_FORCING_NO_CODEGEN = new BitSet() { 234 { 235 add(MSG_NONREGULAR_DECISION); 236 add(MSG_RECURSION_OVERLOW); 237 add(MSG_UNREACHABLE_ALTS); 238 add(MSG_FILE_AND_GRAMMAR_NAME_DIFFER); 239 add(MSG_INVALID_IMPORT); 240 add(MSG_AST_OP_WITH_NON_AST_OUTPUT_OPTION); 241 add(MSG_CIRCULAR_DEPENDENCY); 242 // TODO: ... 243 } 244 }; 245 246 /** Only one error can be emitted for any entry in this table. 247 * Map<String,Set> where the key is a method name like danglingState. 248 * The set is whatever that method accepts or derives like a DFA. 249 */ 250 public static final Map<String, Set<String>> emitSingleError = new HashMap<String, Set<String>>() { 251 { 252 put("danglingState", new HashSet<String>()); 253 } 254 }; 255 256 /** Messages should be sensitive to the locale. */ 257 private static Locale locale; 258 private static String formatName; 259 260 /** Each thread might need it's own error listener; e.g., a GUI with 261 * multiple window frames holding multiple grammars. 262 */ 263 private static final Map<Thread, ANTLRErrorListener> threadToListenerMap = new WeakHashMap<Thread, ANTLRErrorListener>(); 264 265 public static class ErrorState { 266 public int errors; 267 public int warnings; 268 public int infos; 269 /** Track all msgIDs; we use to abort later if necessary 270 * also used in Message to find out what type of message it is via getMessageType() 271 */ 272 public BitSet errorMsgIDs = new BitSet(); 273 public BitSet warningMsgIDs = new BitSet(); 274 // TODO: figure out how to do info messages. these do not have IDs...kr 275 //public BitSet infoMsgIDs = new BitSet(); 276 } 277 278 /** Track the number of errors regardless of the listener but track 279 * per thread. 280 */ 281 private static final Map<Thread, ErrorState> threadToErrorStateMap = new WeakHashMap<Thread, ErrorState>(); 282 283 /** Each thread has its own ptr to a Tool object, which knows how 284 * to panic, for example. In a GUI, the thread might just throw an Error 285 * to exit rather than the suicide System.exit. 286 */ 287 private static final Map<Thread, Tool> threadToToolMap = new WeakHashMap<Thread, Tool>(); 288 289 /** The group of templates that represent all possible ANTLR errors. */ 290 private static STGroup messages; 291 /** The group of templates that represent the current message format. */ 292 private static STGroup format; 293 294 /** From a msgID how can I get the name of the template that describes 295 * the error or warning? 296 */ 297 private static final String[] idToMessageTemplateName = new String[MAX_MESSAGE_NUMBER+1]; 298 299 static ANTLRErrorListener theDefaultErrorListener = new ANTLRErrorListener() { 300 @Override 301 public void info(String msg) { 302 if (formatWantsSingleLineMessage()) { 303 msg = msg.replaceAll("\n", " "); 304 } 305 System.err.println(msg); 306 } 307 308 @Override 309 public void error(Message msg) { 310 String outputMsg = msg.toString(); 311 if (formatWantsSingleLineMessage()) { 312 outputMsg = outputMsg.replaceAll("\n", " "); 313 } 314 System.err.println(outputMsg); 315 } 316 317 @Override 318 public void warning(Message msg) { 319 String outputMsg = msg.toString(); 320 if (formatWantsSingleLineMessage()) { 321 outputMsg = outputMsg.replaceAll("\n", " "); 322 } 323 System.err.println(outputMsg); 324 } 325 326 @Override 327 public void error(ToolMessage msg) { 328 String outputMsg = msg.toString(); 329 if (formatWantsSingleLineMessage()) { 330 outputMsg = outputMsg.replaceAll("\n", " "); 331 } 332 System.err.println(outputMsg); 333 } 334 }; 335 336 /** Handle all ST error listeners here (code gen, Grammar, and this class 337 * use templates. 338 */ 339 static STErrorListener initSTListener = 340 new STErrorListener() { 341 @Override 342 public void compileTimeError(STMessage msg) { 343 System.err.println("ErrorManager init error: "+msg); 344 } 345 346 @Override 347 public void runTimeError(STMessage msg) { 348 System.err.println("ErrorManager init error: "+msg); 349 } 350 351 @Override 352 public void IOError(STMessage msg) { 353 System.err.println("ErrorManager init error: "+msg); 354 } 355 356 @Override 357 public void internalError(STMessage msg) { 358 System.err.println("ErrorManager init error: "+msg); 359 } 360 361 }; 362 363 /** During verification of the messages group file, don't gen errors. 364 * I'll handle them here. This is used only after file has loaded ok 365 * and only for the messages STG. 366 */ 367 static STErrorListener blankSTListener = 368 new STErrorListener() { 369 @Override public void compileTimeError(STMessage msg) { } 370 @Override public void runTimeError(STMessage msg) { } 371 @Override public void IOError(STMessage msg) { } 372 @Override public void internalError(STMessage msg) { } 373 }; 374 375 /** Errors during initialization related to ST must all go to System.err. 376 */ 377 static STErrorListener theDefaultSTListener = 378 new STErrorListener() { 379 @Override 380 public void compileTimeError(STMessage msg) { 381 ErrorManager.error(ErrorManager.MSG_STRING_TEMPLATE_ERROR, msg.toString(), msg.cause); 382 } 383 384 @Override 385 public void runTimeError(STMessage msg) { 386 switch (msg.error) { 387 case NO_SUCH_ATTRIBUTE: 388 case NO_SUCH_ATTRIBUTE_PASS_THROUGH: 389 case NO_SUCH_PROPERTY: 390 ErrorManager.warning(ErrorManager.MSG_STRING_TEMPLATE_ERROR, msg.toString()); 391 return; 392 393 default: 394 ErrorManager.error(ErrorManager.MSG_STRING_TEMPLATE_ERROR, msg.toString(), msg.cause); 395 return; 396 } 397 } 398 399 @Override 400 public void IOError(STMessage msg) { 401 ErrorManager.error(ErrorManager.MSG_STRING_TEMPLATE_ERROR, msg.toString(), msg.cause); 402 } 403 404 @Override 405 public void internalError(STMessage msg) { 406 ErrorManager.error(ErrorManager.MSG_STRING_TEMPLATE_ERROR, msg.toString(), msg.cause); 407 } 408 }; 409 410 // make sure that this class is ready to use after loading 411 static { initIdToMessageNameMapping()412 initIdToMessageNameMapping(); 413 // it is inefficient to set the default locale here if another 414 // piece of code is going to set the locale, but that would 415 // require that a user call an init() function or something. I prefer 416 // that this class be ready to go when loaded as I'm absentminded ;) Locale.getDefault()417 setLocale(Locale.getDefault()); 418 // try to load the message format group 419 // the user might have specified one on the command line 420 // if not, or if the user has given an illegal value, we will fall back to "antlr" 421 setFormat("antlr"); 422 } 423 getSTErrorListener()424 public static STErrorListener getSTErrorListener() { 425 return theDefaultSTListener; 426 } 427 428 /** We really only need a single locale for entire running ANTLR code 429 * in a single VM. Only pay attention to the language, not the country 430 * so that French Canadians and French Frenchies all get the same 431 * template file, fr.stg. Just easier this way. 432 */ setLocale(Locale locale)433 public static void setLocale(Locale locale) { 434 ErrorManager.locale = locale; 435 String language = locale.getLanguage(); 436 String fileName = "org/antlr/tool/templates/messages/languages/"+language+".stg"; 437 try { 438 messages = new STGroupFile(fileName); 439 } 440 catch (IllegalArgumentException iae) { 441 if ( language.equals(Locale.US.getLanguage()) ) { 442 rawError("ANTLR installation corrupted; cannot find English messages file "+fileName); 443 panic(); 444 } 445 else { 446 setLocale(Locale.US); // recurse on this rule, trying the US locale 447 } 448 } 449 450 messages.setListener(blankSTListener); 451 boolean messagesOK = verifyMessages(); 452 if ( !messagesOK && language.equals(Locale.US.getLanguage()) ) { 453 rawError("ANTLR installation corrupted; English messages file "+language+".stg incomplete"); 454 panic(); 455 } 456 else if ( !messagesOK ) { 457 setLocale(Locale.US); // try US to see if that will work 458 } 459 } 460 461 /** The format gets reset either from the Tool if the user supplied a command line option to that effect 462 * Otherwise we just use the default "antlr". 463 */ setFormat(String formatName)464 public static void setFormat(String formatName) { 465 ErrorManager.formatName = formatName; 466 String fileName = "org/antlr/tool/templates/messages/formats/"+formatName+".stg"; 467 format = new STGroupFile(fileName); 468 format.setListener(initSTListener); 469 if ( !format.isDefined("message") ) { // pick random msg to load 470 if ( formatName.equals("antlr") ) { 471 rawError("no such message format file "+fileName+" retrying with default ANTLR format"); 472 setFormat("antlr"); // recurse on this rule, trying the default message format 473 return; 474 } 475 else { 476 setFormat("antlr"); // recurse on this rule, trying the default message format 477 } 478 } 479 480 format.setListener(blankSTListener); 481 boolean formatOK = verifyFormat(); 482 if ( !formatOK && formatName.equals("antlr") ) { 483 rawError("ANTLR installation corrupted; ANTLR messages format file "+formatName+".stg incomplete"); 484 panic(); 485 } 486 else if ( !formatOK ) { 487 setFormat("antlr"); // recurse on this rule, trying the default message format 488 } 489 } 490 491 /** Encodes the error handling found in setLocale, but does not trigger 492 * panics, which would make GUI tools die if ANTLR's installation was 493 * a bit screwy. Duplicated code...ick. 494 public static Locale getLocaleForValidMessages(Locale locale) { 495 ErrorManager.locale = locale; 496 String language = locale.getLanguage(); 497 String fileName = "org/antlr/tool/templates/messages/"+language+".stg"; 498 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 499 InputStream is = cl.getResourceAsStream(fileName); 500 if ( is==null && language.equals(Locale.US.getLanguage()) ) { 501 return null; 502 } 503 else if ( is==null ) { 504 return getLocaleForValidMessages(Locale.US); // recurse on this rule, trying the US locale 505 } 506 507 boolean messagesOK = verifyMessages(); 508 if ( !messagesOK && language.equals(Locale.US.getLanguage()) ) { 509 return null; 510 } 511 else if ( !messagesOK ) { 512 return getLocaleForValidMessages(Locale.US); // try US to see if that will work 513 } 514 return true; 515 } 516 */ 517 518 /** In general, you'll want all errors to go to a single spot. 519 * However, in a GUI, you might have two frames up with two 520 * different grammars. Two threads might launch to process the 521 * grammars--you would want errors to go to different objects 522 * depending on the thread. I store a single listener per 523 * thread. 524 */ setErrorListener(ANTLRErrorListener listener)525 public static void setErrorListener(ANTLRErrorListener listener) { 526 threadToListenerMap.put(Thread.currentThread(), listener); 527 } 528 removeErrorListener()529 public static void removeErrorListener() { 530 threadToListenerMap.remove(Thread.currentThread()); 531 } 532 setTool(Tool tool)533 public static void setTool(Tool tool) { 534 threadToToolMap.put(Thread.currentThread(), tool); 535 } 536 537 /** Given a message ID, return a ST that somebody can fill 538 * with data. We need to convert the int ID to the name of a template 539 * in the messages ST group. 540 */ getMessage(int msgID)541 public static ST getMessage(int msgID) { 542 String msgName = idToMessageTemplateName[msgID]; 543 return messages.getInstanceOf(msgName); 544 } getMessageType(int msgID)545 public static String getMessageType(int msgID) { 546 if (getErrorState().warningMsgIDs.member(msgID)) { 547 return messages.getInstanceOf("warning").render(); 548 } 549 else if (getErrorState().errorMsgIDs.member(msgID)) { 550 return messages.getInstanceOf("error").render(); 551 } 552 assertTrue(false, "Assertion failed! Message ID " + msgID + " created but is not present in errorMsgIDs or warningMsgIDs."); 553 return ""; 554 } 555 556 /** Return a ST that refers to the current format used for 557 * emitting messages. 558 */ getLocationFormat()559 public static ST getLocationFormat() { 560 return format.getInstanceOf("location"); 561 } getReportFormat()562 public static ST getReportFormat() { 563 return format.getInstanceOf("report"); 564 } getMessageFormat()565 public static ST getMessageFormat() { 566 return format.getInstanceOf("message"); 567 } formatWantsSingleLineMessage()568 public static boolean formatWantsSingleLineMessage() { 569 return format.getInstanceOf("wantsSingleLineMessage").render().equals("true"); 570 } 571 getErrorListener()572 public static ANTLRErrorListener getErrorListener() { 573 ANTLRErrorListener el = 574 threadToListenerMap.get(Thread.currentThread()); 575 if ( el==null ) { 576 return theDefaultErrorListener; 577 } 578 return el; 579 } 580 getErrorState()581 public static ErrorState getErrorState() { 582 ErrorState ec = 583 threadToErrorStateMap.get(Thread.currentThread()); 584 if ( ec==null ) { 585 ec = new ErrorState(); 586 threadToErrorStateMap.put(Thread.currentThread(), ec); 587 } 588 return ec; 589 } 590 getNumErrors()591 public static int getNumErrors() { 592 return getErrorState().errors; 593 } 594 resetErrorState()595 public static void resetErrorState() { 596 threadToListenerMap.clear(); 597 ErrorState ec = new ErrorState(); 598 threadToErrorStateMap.put(Thread.currentThread(), ec); 599 } 600 info(String msg)601 public static void info(String msg) { 602 getErrorState().infos++; 603 getErrorListener().info(msg); 604 } 605 error(int msgID)606 public static void error(int msgID) { 607 getErrorState().errors++; 608 getErrorState().errorMsgIDs.add(msgID); 609 getErrorListener().error(new ToolMessage(msgID)); 610 } 611 error(int msgID, Throwable e)612 public static void error(int msgID, Throwable e) { 613 getErrorState().errors++; 614 getErrorState().errorMsgIDs.add(msgID); 615 getErrorListener().error(new ToolMessage(msgID,e)); 616 } 617 error(int msgID, Object arg)618 public static void error(int msgID, Object arg) { 619 getErrorState().errors++; 620 getErrorState().errorMsgIDs.add(msgID); 621 getErrorListener().error(new ToolMessage(msgID, arg)); 622 } 623 error(int msgID, Object arg, Object arg2)624 public static void error(int msgID, Object arg, Object arg2) { 625 getErrorState().errors++; 626 getErrorState().errorMsgIDs.add(msgID); 627 getErrorListener().error(new ToolMessage(msgID, arg, arg2)); 628 } 629 error(int msgID, Object arg, Throwable e)630 public static void error(int msgID, Object arg, Throwable e) { 631 getErrorState().errors++; 632 getErrorState().errorMsgIDs.add(msgID); 633 getErrorListener().error(new ToolMessage(msgID, arg, e)); 634 } 635 warning(int msgID, Object arg)636 public static void warning(int msgID, Object arg) { 637 getErrorState().warnings++; 638 getErrorState().warningMsgIDs.add(msgID); 639 getErrorListener().warning(new ToolMessage(msgID, arg)); 640 } 641 nondeterminism(DecisionProbe probe, DFAState d)642 public static void nondeterminism(DecisionProbe probe, 643 DFAState d) 644 { 645 getErrorState().warnings++; 646 Message msg = new GrammarNonDeterminismMessage(probe,d); 647 getErrorState().warningMsgIDs.add(msg.msgID); 648 getErrorListener().warning(msg); 649 } 650 danglingState(DecisionProbe probe, DFAState d)651 public static void danglingState(DecisionProbe probe, 652 DFAState d) 653 { 654 getErrorState().errors++; 655 Message msg = new GrammarDanglingStateMessage(probe,d); 656 getErrorState().errorMsgIDs.add(msg.msgID); 657 Set<String> seen = emitSingleError.get("danglingState"); 658 if ( !seen.contains(d.dfa.decisionNumber+"|"+d.getAltSet()) ) { 659 getErrorListener().error(msg); 660 // we've seen this decision and this alt set; never again 661 seen.add(d.dfa.decisionNumber+"|"+d.getAltSet()); 662 } 663 } 664 analysisAborted(DecisionProbe probe)665 public static void analysisAborted(DecisionProbe probe) 666 { 667 getErrorState().warnings++; 668 Message msg = new GrammarAnalysisAbortedMessage(probe); 669 getErrorState().warningMsgIDs.add(msg.msgID); 670 getErrorListener().warning(msg); 671 } 672 unreachableAlts(DecisionProbe probe, List<Integer> alts)673 public static void unreachableAlts(DecisionProbe probe, 674 List<Integer> alts) 675 { 676 getErrorState().errors++; 677 Message msg = new GrammarUnreachableAltsMessage(probe,alts); 678 getErrorState().errorMsgIDs.add(msg.msgID); 679 getErrorListener().error(msg); 680 } 681 insufficientPredicates(DecisionProbe probe, DFAState d, Map<Integer, Set<Token>> altToUncoveredLocations)682 public static void insufficientPredicates(DecisionProbe probe, 683 DFAState d, 684 Map<Integer, Set<Token>> altToUncoveredLocations) 685 { 686 getErrorState().warnings++; 687 Message msg = new GrammarInsufficientPredicatesMessage(probe,d,altToUncoveredLocations); 688 getErrorState().warningMsgIDs.add(msg.msgID); 689 getErrorListener().warning(msg); 690 } 691 nonLLStarDecision(DecisionProbe probe)692 public static void nonLLStarDecision(DecisionProbe probe) { 693 getErrorState().errors++; 694 Message msg = new NonRegularDecisionMessage(probe, probe.getNonDeterministicAlts()); 695 getErrorState().errorMsgIDs.add(msg.msgID); 696 getErrorListener().error(msg); 697 } 698 recursionOverflow(DecisionProbe probe, DFAState sampleBadState, int alt, Collection<String> targetRules, Collection<? extends Collection<? extends NFAState>> callSiteStates)699 public static void recursionOverflow(DecisionProbe probe, 700 DFAState sampleBadState, 701 int alt, 702 Collection<String> targetRules, 703 Collection<? extends Collection<? extends NFAState>> callSiteStates) 704 { 705 getErrorState().errors++; 706 Message msg = new RecursionOverflowMessage(probe,sampleBadState, alt, 707 targetRules, callSiteStates); 708 getErrorState().errorMsgIDs.add(msg.msgID); 709 getErrorListener().error(msg); 710 } 711 712 /* 713 // TODO: we can remove I think. All detected now with cycles check. 714 public static void leftRecursion(DecisionProbe probe, 715 int alt, 716 Collection targetRules, 717 Collection callSiteStates) 718 { 719 getErrorState().warnings++; 720 Message msg = new LeftRecursionMessage(probe, alt, targetRules, callSiteStates); 721 getErrorState().warningMsgIDs.add(msg.msgID); 722 getErrorListener().warning(msg); 723 } 724 */ 725 leftRecursionCycles(Collection<? extends Set<? extends Rule>> cycles)726 public static void leftRecursionCycles(Collection<? extends Set<? extends Rule>> cycles) { 727 getErrorState().errors++; 728 Message msg = new LeftRecursionCyclesMessage(cycles); 729 getErrorState().errorMsgIDs.add(msg.msgID); 730 getErrorListener().error(msg); 731 } 732 grammarError(int msgID, Grammar g, Token token, Object arg, Object arg2)733 public static void grammarError(int msgID, 734 Grammar g, 735 Token token, 736 Object arg, 737 Object arg2) 738 { 739 getErrorState().errors++; 740 Message msg = new GrammarSemanticsMessage(msgID,g,token,arg,arg2); 741 getErrorState().errorMsgIDs.add(msgID); 742 getErrorListener().error(msg); 743 } 744 grammarError(int msgID, Grammar g, Token token, Object arg)745 public static void grammarError(int msgID, 746 Grammar g, 747 Token token, 748 Object arg) 749 { 750 grammarError(msgID,g,token,arg,null); 751 } 752 grammarError(int msgID, Grammar g, Token token)753 public static void grammarError(int msgID, 754 Grammar g, 755 Token token) 756 { 757 grammarError(msgID,g,token,null,null); 758 } 759 grammarWarning(int msgID, Grammar g, Token token, Object arg, Object arg2)760 public static void grammarWarning(int msgID, 761 Grammar g, 762 Token token, 763 Object arg, 764 Object arg2) 765 { 766 getErrorState().warnings++; 767 Message msg = new GrammarSemanticsMessage(msgID,g,token,arg,arg2); 768 getErrorState().warningMsgIDs.add(msgID); 769 getErrorListener().warning(msg); 770 } 771 grammarWarning(int msgID, Grammar g, Token token, Object arg)772 public static void grammarWarning(int msgID, 773 Grammar g, 774 Token token, 775 Object arg) 776 { 777 grammarWarning(msgID,g,token,arg,null); 778 } 779 grammarWarning(int msgID, Grammar g, Token token)780 public static void grammarWarning(int msgID, 781 Grammar g, 782 Token token) 783 { 784 grammarWarning(msgID,g,token,null,null); 785 } 786 syntaxError(int msgID, Grammar grammar, Token token, Object arg, RecognitionException re)787 public static void syntaxError(int msgID, 788 Grammar grammar, 789 Token token, 790 Object arg, 791 RecognitionException re) 792 { 793 getErrorState().errors++; 794 getErrorState().errorMsgIDs.add(msgID); 795 getErrorListener().error( 796 new GrammarSyntaxMessage(msgID,grammar,token,arg,re) 797 ); 798 } 799 internalError(Object error, Throwable e)800 public static void internalError(Object error, Throwable e) { 801 StackTraceElement location = getLastNonErrorManagerCodeLocation(e); 802 String msg = "Exception "+e+"@"+location+": "+error; 803 error(MSG_INTERNAL_ERROR, msg); 804 } 805 internalError(Object error)806 public static void internalError(Object error) { 807 StackTraceElement location = 808 getLastNonErrorManagerCodeLocation(new Exception()); 809 String msg = location+": "+error; 810 error(MSG_INTERNAL_ERROR, msg); 811 } 812 doNotAttemptAnalysis()813 public static boolean doNotAttemptAnalysis() { 814 return !getErrorState().errorMsgIDs.and(ERRORS_FORCING_NO_ANALYSIS).isNil(); 815 } 816 doNotAttemptCodeGen()817 public static boolean doNotAttemptCodeGen() { 818 return doNotAttemptAnalysis() || 819 !getErrorState().errorMsgIDs.and(ERRORS_FORCING_NO_CODEGEN).isNil(); 820 } 821 822 /** Return first non ErrorManager code location for generating messages */ getLastNonErrorManagerCodeLocation(Throwable e)823 private static StackTraceElement getLastNonErrorManagerCodeLocation(Throwable e) { 824 StackTraceElement[] stack = e.getStackTrace(); 825 int i = 0; 826 for (; i < stack.length; i++) { 827 StackTraceElement t = stack[i]; 828 if ( t.toString().indexOf("ErrorManager")<0 ) { 829 break; 830 } 831 } 832 StackTraceElement location = stack[i]; 833 return location; 834 } 835 836 // A S S E R T I O N C O D E 837 assertTrue(boolean condition, String message)838 public static void assertTrue(boolean condition, String message) { 839 if ( !condition ) { 840 internalError(message); 841 } 842 } 843 844 // S U P P O R T C O D E 845 initIdToMessageNameMapping()846 protected static boolean initIdToMessageNameMapping() { 847 // make sure a message exists, even if it's just to indicate a problem 848 for (int i = 0; i < idToMessageTemplateName.length; i++) { 849 idToMessageTemplateName[i] = "INVALID MESSAGE ID: "+i; 850 } 851 // get list of fields and use it to fill in idToMessageTemplateName mapping 852 Field[] fields = ErrorManager.class.getFields(); 853 for (int i = 0; i < fields.length; i++) { 854 Field f = fields[i]; 855 String fieldName = f.getName(); 856 if ( !fieldName.startsWith("MSG_") ) { 857 continue; 858 } 859 String templateName = 860 fieldName.substring("MSG_".length(),fieldName.length()); 861 int msgID; 862 try { 863 // get the constant value from this class object 864 msgID = f.getInt(ErrorManager.class); 865 } 866 catch (IllegalAccessException iae) { 867 System.err.println("cannot get const value for "+f.getName()); 868 continue; 869 } 870 if ( fieldName.startsWith("MSG_") ) { 871 idToMessageTemplateName[msgID] = templateName; 872 } 873 } 874 return true; 875 } 876 877 /** Use reflection to find list of MSG_ fields and then verify a 878 * template exists for each one from the locale's group. 879 */ verifyMessages()880 protected static boolean verifyMessages() { 881 boolean ok = true; 882 Field[] fields = ErrorManager.class.getFields(); 883 for (int i = 0; i < fields.length; i++) { 884 Field f = fields[i]; 885 String fieldName = f.getName(); 886 String templateName = 887 fieldName.substring("MSG_".length(),fieldName.length()); 888 if ( fieldName.startsWith("MSG_") ) { 889 if ( !messages.isDefined(templateName) ) { 890 System.err.println("Message "+templateName+" in locale "+ 891 locale+" not found"); 892 ok = false; 893 } 894 } 895 } 896 // check for special templates 897 if (!messages.isDefined("warning")) { 898 System.err.println("Message template 'warning' not found in locale "+ locale); 899 ok = false; 900 } 901 if (!messages.isDefined("error")) { 902 System.err.println("Message template 'error' not found in locale "+ locale); 903 ok = false; 904 } 905 return ok; 906 } 907 908 /** Verify the message format template group */ verifyFormat()909 protected static boolean verifyFormat() { 910 boolean ok = true; 911 if (!format.isDefined("location")) { 912 System.err.println("Format template 'location' not found in " + formatName); 913 ok = false; 914 } 915 if (!format.isDefined("message")) { 916 System.err.println("Format template 'message' not found in " + formatName); 917 ok = false; 918 } 919 if (!format.isDefined("report")) { 920 System.err.println("Format template 'report' not found in " + formatName); 921 ok = false; 922 } 923 return ok; 924 } 925 926 /** If there are errors during ErrorManager init, we have no choice 927 * but to go to System.err. 928 */ rawError(String msg)929 static void rawError(String msg) { 930 System.err.println(msg); 931 } 932 rawError(String msg, Throwable e)933 static void rawError(String msg, Throwable e) { 934 rawError(msg); 935 e.printStackTrace(System.err); 936 } 937 938 /** I *think* this will allow Tool subclasses to exit gracefully 939 * for GUIs etc... 940 */ panic()941 public static void panic() { 942 Tool tool = threadToToolMap.get(Thread.currentThread()); 943 if ( tool==null ) { 944 // no tool registered, exit 945 throw new Error("ANTLR ErrorManager panic"); 946 } 947 else { 948 tool.panic(); 949 } 950 } 951 } 952