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