• 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.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