• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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