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