| /* |
| * [The "BSD license"] |
| * Copyright (c) 2010 Terence Parr |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.antlr.tool; |
| |
| import org.antlr.Tool; |
| import org.antlr.analysis.DFAState; |
| import org.antlr.analysis.DecisionProbe; |
| import org.antlr.misc.BitSet; |
| import org.antlr.runtime.RecognitionException; |
| import org.antlr.runtime.Token; |
| import org.stringtemplate.v4.ST; |
| import org.stringtemplate.v4.STErrorListener; |
| import org.stringtemplate.v4.STGroup; |
| import org.stringtemplate.v4.STGroupFile; |
| import org.stringtemplate.v4.misc.STMessage; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.*; |
| |
| /** Defines all the errors ANTLR can generator for both the tool and for |
| * issues with a grammar. |
| * |
| * Here is a list of language names: |
| * |
| * http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt |
| * |
| * Here is a list of country names: |
| * |
| * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html |
| * |
| * I use constants not strings to identify messages as the compiler will |
| * find any errors/mismatches rather than leaving a mistyped string in |
| * the code to be found randomly in the future. Further, Intellij can |
| * do field name expansion to save me some typing. I have to map |
| * int constants to template names, however, which could introduce a mismatch. |
| * Someone could provide a .stg file that had a template name wrong. When |
| * I load the group, then, I must verify that all messages are there. |
| * |
| * This is essentially the functionality of the resource bundle stuff Java |
| * has, but I don't want to load a property file--I want to load a template |
| * group file and this is so simple, why mess with their junk. |
| * |
| * I use the default Locale as defined by java to compute a group file name |
| * in the org/antlr/tool/templates/messages dir called en_US.stg and so on. |
| * |
| * Normally we want to use the default locale, but often a message file will |
| * not exist for it so we must fall back on the US local. |
| * |
| * During initialization of this class, all errors go straight to System.err. |
| * There is no way around this. If I have not set up the error system, how |
| * can I do errors properly? For example, if the string template group file |
| * full of messages has an error, how could I print to anything but System.err? |
| * |
| * TODO: how to map locale to a file encoding for the stringtemplate group file? |
| * ST knows how to pay attention to the default encoding so it |
| * should probably just work unless a GUI sets the local to some chinese |
| * variation but System.getProperty("file.encoding") is US. Hmm... |
| * |
| * TODO: get antlr.g etc.. parsing errors to come here. |
| */ |
| public class ErrorManager { |
| // TOOL ERRORS |
| // file errors |
| public static final int MSG_CANNOT_WRITE_FILE = 1; |
| public static final int MSG_CANNOT_CLOSE_FILE = 2; |
| public static final int MSG_CANNOT_FIND_TOKENS_FILE = 3; |
| public static final int MSG_ERROR_READING_TOKENS_FILE = 4; |
| public static final int MSG_DIR_NOT_FOUND = 5; |
| public static final int MSG_OUTPUT_DIR_IS_FILE = 6; |
| public static final int MSG_CANNOT_OPEN_FILE = 7; |
| public static final int MSG_FILE_AND_GRAMMAR_NAME_DIFFER = 8; |
| public static final int MSG_FILENAME_EXTENSION_ERROR = 9; |
| |
| public static final int MSG_INTERNAL_ERROR = 10; |
| public static final int MSG_INTERNAL_WARNING = 11; |
| public static final int MSG_ERROR_CREATING_ARTIFICIAL_RULE = 12; |
| public static final int MSG_TOKENS_FILE_SYNTAX_ERROR = 13; |
| public static final int MSG_CANNOT_GEN_DOT_FILE = 14; |
| public static final int MSG_BAD_AST_STRUCTURE = 15; |
| public static final int MSG_BAD_ACTION_AST_STRUCTURE = 16; |
| |
| // code gen errors |
| public static final int MSG_MISSING_CODE_GEN_TEMPLATES = 20; |
| public static final int MSG_MISSING_CYCLIC_DFA_CODE_GEN_TEMPLATES = 21; |
| public static final int MSG_CODE_GEN_TEMPLATES_INCOMPLETE = 22; |
| public static final int MSG_CANNOT_CREATE_TARGET_GENERATOR = 23; |
| //public static final int MSG_CANNOT_COMPUTE_SAMPLE_INPUT_SEQ = 24; |
| |
| // GRAMMAR ERRORS |
| public static final int MSG_SYNTAX_ERROR = 100; |
| public static final int MSG_RULE_REDEFINITION = 101; |
| public static final int MSG_LEXER_RULES_NOT_ALLOWED = 102; |
| public static final int MSG_PARSER_RULES_NOT_ALLOWED = 103; |
| public static final int MSG_CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL = 104; |
| public static final int MSG_NO_TOKEN_DEFINITION = 105; |
| public static final int MSG_UNDEFINED_RULE_REF = 106; |
| public static final int MSG_LITERAL_NOT_ASSOCIATED_WITH_LEXER_RULE = 107; |
| public static final int MSG_CANNOT_ALIAS_TOKENS_IN_LEXER = 108; |
| public static final int MSG_ATTRIBUTE_REF_NOT_IN_RULE = 111; |
| public static final int MSG_INVALID_RULE_SCOPE_ATTRIBUTE_REF = 112; |
| public static final int MSG_UNKNOWN_ATTRIBUTE_IN_SCOPE = 113; |
| public static final int MSG_UNKNOWN_SIMPLE_ATTRIBUTE = 114; |
| public static final int MSG_INVALID_RULE_PARAMETER_REF = 115; |
| public static final int MSG_UNKNOWN_RULE_ATTRIBUTE = 116; |
| public static final int MSG_ISOLATED_RULE_SCOPE = 117; |
| public static final int MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE = 118; |
| public static final int MSG_LABEL_CONFLICTS_WITH_RULE = 119; |
| public static final int MSG_LABEL_CONFLICTS_WITH_TOKEN = 120; |
| public static final int MSG_LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE = 121; |
| public static final int MSG_LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL = 122; |
| public static final int MSG_ATTRIBUTE_CONFLICTS_WITH_RULE = 123; |
| public static final int MSG_ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL = 124; |
| public static final int MSG_LABEL_TYPE_CONFLICT = 125; |
| public static final int MSG_ARG_RETVAL_CONFLICT = 126; |
| public static final int MSG_NONUNIQUE_REF = 127; |
| public static final int MSG_FORWARD_ELEMENT_REF = 128; |
| public static final int MSG_MISSING_RULE_ARGS = 129; |
| public static final int MSG_RULE_HAS_NO_ARGS = 130; |
| public static final int MSG_ARGS_ON_TOKEN_REF = 131; |
| public static final int MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT = 132; |
| public static final int MSG_ILLEGAL_OPTION = 133; |
| public static final int MSG_LIST_LABEL_INVALID_UNLESS_RETVAL_STRUCT = 134; |
| public static final int MSG_UNDEFINED_TOKEN_REF_IN_REWRITE = 135; |
| public static final int MSG_REWRITE_ELEMENT_NOT_PRESENT_ON_LHS = 136; |
| public static final int MSG_UNDEFINED_LABEL_REF_IN_REWRITE = 137; |
| public static final int MSG_NO_GRAMMAR_START_RULE = 138; |
| public static final int MSG_EMPTY_COMPLEMENT = 139; |
| public static final int MSG_UNKNOWN_DYNAMIC_SCOPE = 140; |
| public static final int MSG_UNKNOWN_DYNAMIC_SCOPE_ATTRIBUTE = 141; |
| public static final int MSG_ISOLATED_RULE_ATTRIBUTE = 142; |
| public static final int MSG_INVALID_ACTION_SCOPE = 143; |
| public static final int MSG_ACTION_REDEFINITION = 144; |
| public static final int MSG_DOUBLE_QUOTES_ILLEGAL = 145; |
| public static final int MSG_INVALID_TEMPLATE_ACTION = 146; |
| public static final int MSG_MISSING_ATTRIBUTE_NAME = 147; |
| public static final int MSG_ARG_INIT_VALUES_ILLEGAL = 148; |
| public static final int MSG_REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION = 149; |
| public static final int MSG_NO_RULES = 150; |
| public static final int MSG_WRITE_TO_READONLY_ATTR = 151; |
| public static final int MSG_MISSING_AST_TYPE_IN_TREE_GRAMMAR = 152; |
| public static final int MSG_REWRITE_FOR_MULTI_ELEMENT_ALT = 153; |
| public static final int MSG_RULE_INVALID_SET = 154; |
| public static final int MSG_HETERO_ILLEGAL_IN_REWRITE_ALT = 155; |
| public static final int MSG_NO_SUCH_GRAMMAR_SCOPE = 156; |
| public static final int MSG_NO_SUCH_RULE_IN_SCOPE = 157; |
| public static final int MSG_TOKEN_ALIAS_CONFLICT = 158; |
| public static final int MSG_TOKEN_ALIAS_REASSIGNMENT = 159; |
| public static final int MSG_TOKEN_VOCAB_IN_DELEGATE = 160; |
| public static final int MSG_INVALID_IMPORT = 161; |
| public static final int MSG_IMPORTED_TOKENS_RULE_EMPTY = 162; |
| public static final int MSG_IMPORT_NAME_CLASH = 163; |
| public static final int MSG_AST_OP_WITH_NON_AST_OUTPUT_OPTION = 164; |
| public static final int MSG_AST_OP_IN_ALT_WITH_REWRITE = 165; |
| public static final int MSG_WILDCARD_AS_ROOT = 166; |
| public static final int MSG_CONFLICTING_OPTION_IN_TREE_FILTER = 167; |
| public static final int MSG_ILLEGAL_OPTION_VALUE = 168; |
| public static final int MSG_ALL_OPS_NEED_SAME_ASSOC = 169; |
| |
| // GRAMMAR WARNINGS |
| public static final int MSG_GRAMMAR_NONDETERMINISM = 200; // A predicts alts 1,2 |
| public static final int MSG_UNREACHABLE_ALTS = 201; // nothing predicts alt i |
| public static final int MSG_DANGLING_STATE = 202; // no edges out of state |
| public static final int MSG_INSUFFICIENT_PREDICATES = 203; |
| public static final int MSG_DUPLICATE_SET_ENTRY = 204; // (A|A) |
| public static final int MSG_ANALYSIS_ABORTED = 205; |
| public static final int MSG_RECURSION_OVERLOW = 206; |
| public static final int MSG_LEFT_RECURSION = 207; |
| public static final int MSG_UNREACHABLE_TOKENS = 208; // nothing predicts token |
| public static final int MSG_TOKEN_NONDETERMINISM = 209; // alts of Tokens rule |
| public static final int MSG_LEFT_RECURSION_CYCLES = 210; |
| public static final int MSG_NONREGULAR_DECISION = 211; |
| |
| |
| // Dependency sorting errors |
| // |
| public static final int MSG_CIRCULAR_DEPENDENCY = 212; // t1.g -> t2.g -> t3.g ->t1.g |
| |
| public static final int MAX_MESSAGE_NUMBER = 212; |
| |
| /** Do not do perform analysis if one of these happens */ |
| public static final BitSet ERRORS_FORCING_NO_ANALYSIS = new BitSet() { |
| { |
| add(MSG_RULE_REDEFINITION); |
| add(MSG_UNDEFINED_RULE_REF); |
| add(MSG_LEFT_RECURSION_CYCLES); |
| add(MSG_REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION); |
| add(MSG_NO_RULES); |
| add(MSG_NO_SUCH_GRAMMAR_SCOPE); |
| add(MSG_NO_SUCH_RULE_IN_SCOPE); |
| add(MSG_LEXER_RULES_NOT_ALLOWED); |
| add(MSG_WILDCARD_AS_ROOT); |
| add(MSG_CIRCULAR_DEPENDENCY); |
| // TODO: ... |
| } |
| }; |
| |
| /** Do not do code gen if one of these happens */ |
| public static final BitSet ERRORS_FORCING_NO_CODEGEN = new BitSet() { |
| { |
| add(MSG_NONREGULAR_DECISION); |
| add(MSG_RECURSION_OVERLOW); |
| add(MSG_UNREACHABLE_ALTS); |
| add(MSG_FILE_AND_GRAMMAR_NAME_DIFFER); |
| add(MSG_INVALID_IMPORT); |
| add(MSG_AST_OP_WITH_NON_AST_OUTPUT_OPTION); |
| add(MSG_CIRCULAR_DEPENDENCY); |
| // TODO: ... |
| } |
| }; |
| |
| /** Only one error can be emitted for any entry in this table. |
| * Map<String,Set> where the key is a method name like danglingState. |
| * The set is whatever that method accepts or derives like a DFA. |
| */ |
| public static final Map emitSingleError = new HashMap() { |
| { |
| put("danglingState", new HashSet()); |
| } |
| }; |
| |
| /** Messages should be sensitive to the locale. */ |
| private static Locale locale; |
| private static String formatName; |
| |
| /** Each thread might need it's own error listener; e.g., a GUI with |
| * multiple window frames holding multiple grammars. |
| */ |
| private static Map threadToListenerMap = new HashMap(); |
| |
| static class ErrorState { |
| public int errors; |
| public int warnings; |
| public int infos; |
| /** Track all msgIDs; we use to abort later if necessary |
| * also used in Message to find out what type of message it is via getMessageType() |
| */ |
| public BitSet errorMsgIDs = new BitSet(); |
| public BitSet warningMsgIDs = new BitSet(); |
| // TODO: figure out how to do info messages. these do not have IDs...kr |
| //public BitSet infoMsgIDs = new BitSet(); |
| } |
| |
| /** Track the number of errors regardless of the listener but track |
| * per thread. |
| */ |
| private static Map threadToErrorStateMap = new HashMap(); |
| |
| /** Each thread has its own ptr to a Tool object, which knows how |
| * to panic, for example. In a GUI, the thread might just throw an Error |
| * to exit rather than the suicide System.exit. |
| */ |
| private static Map threadToToolMap = new HashMap(); |
| |
| /** The group of templates that represent all possible ANTLR errors. */ |
| private static STGroup messages; |
| /** The group of templates that represent the current message format. */ |
| private static STGroup format; |
| |
| /** From a msgID how can I get the name of the template that describes |
| * the error or warning? |
| */ |
| private static String[] idToMessageTemplateName = new String[MAX_MESSAGE_NUMBER+1]; |
| |
| static ANTLRErrorListener theDefaultErrorListener = new ANTLRErrorListener() { |
| public void info(String msg) { |
| if (formatWantsSingleLineMessage()) { |
| msg = msg.replaceAll("\n", " "); |
| } |
| System.err.println(msg); |
| } |
| |
| public void error(Message msg) { |
| String outputMsg = msg.toString(); |
| if (formatWantsSingleLineMessage()) { |
| outputMsg = outputMsg.replaceAll("\n", " "); |
| } |
| System.err.println(outputMsg); |
| } |
| |
| public void warning(Message msg) { |
| String outputMsg = msg.toString(); |
| if (formatWantsSingleLineMessage()) { |
| outputMsg = outputMsg.replaceAll("\n", " "); |
| } |
| System.err.println(outputMsg); |
| } |
| |
| public void error(ToolMessage msg) { |
| String outputMsg = msg.toString(); |
| if (formatWantsSingleLineMessage()) { |
| outputMsg = outputMsg.replaceAll("\n", " "); |
| } |
| System.err.println(outputMsg); |
| } |
| }; |
| |
| /** Handle all ST error listeners here (code gen, Grammar, and this class |
| * use templates. |
| */ |
| static STErrorListener initSTListener = |
| new STErrorListener() { |
| public void compileTimeError(STMessage msg) { |
| System.err.println("ErrorManager init error: "+msg); |
| } |
| |
| public void runTimeError(STMessage msg) { |
| System.err.println("ErrorManager init error: "+msg); |
| } |
| |
| public void IOError(STMessage msg) { |
| System.err.println("ErrorManager init error: "+msg); |
| } |
| |
| public void internalError(STMessage msg) { |
| System.err.println("ErrorManager init error: "+msg); |
| } |
| |
| }; |
| |
| /** During verification of the messages group file, don't gen errors. |
| * I'll handle them here. This is used only after file has loaded ok |
| * and only for the messages STG. |
| */ |
| static STErrorListener blankSTListener = |
| new STErrorListener() { |
| public void compileTimeError(STMessage msg) { } |
| public void runTimeError(STMessage msg) { } |
| public void IOError(STMessage msg) { } |
| public void internalError(STMessage msg) { } |
| }; |
| |
| /** Errors during initialization related to ST must all go to System.err. |
| */ |
| static STErrorListener theDefaultSTListener = |
| new STErrorListener() { |
| public void compileTimeError(STMessage msg) { |
| ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause); |
| } |
| |
| public void runTimeError(STMessage msg) { |
| ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause); |
| } |
| |
| public void IOError(STMessage msg) { |
| ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause); |
| } |
| |
| public void internalError(STMessage msg) { |
| ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause); |
| } |
| }; |
| |
| // make sure that this class is ready to use after loading |
| static { |
| initIdToMessageNameMapping(); |
| // it is inefficient to set the default locale here if another |
| // piece of code is going to set the locale, but that would |
| // require that a user call an init() function or something. I prefer |
| // that this class be ready to go when loaded as I'm absentminded ;) |
| setLocale(Locale.getDefault()); |
| // try to load the message format group |
| // the user might have specified one on the command line |
| // if not, or if the user has given an illegal value, we will fall back to "antlr" |
| setFormat("antlr"); |
| } |
| |
| public static STErrorListener getSTErrorListener() { |
| return theDefaultSTListener; |
| } |
| |
| /** We really only need a single locale for entire running ANTLR code |
| * in a single VM. Only pay attention to the language, not the country |
| * so that French Canadians and French Frenchies all get the same |
| * template file, fr.stg. Just easier this way. |
| */ |
| public static void setLocale(Locale locale) { |
| ErrorManager.locale = locale; |
| String language = locale.getLanguage(); |
| String fileName = "org/antlr/tool/templates/messages/languages/"+language+".stg"; |
| try { |
| messages = new STGroupFile(fileName); |
| } |
| catch (IllegalArgumentException iae) { |
| if ( language.equals(Locale.US.getLanguage()) ) { |
| rawError("ANTLR installation corrupted; cannot find English messages file "+fileName); |
| panic(); |
| } |
| else { |
| setLocale(Locale.US); // recurse on this rule, trying the US locale |
| } |
| } |
| |
| messages.setListener(blankSTListener); |
| boolean messagesOK = verifyMessages(); |
| if ( !messagesOK && language.equals(Locale.US.getLanguage()) ) { |
| rawError("ANTLR installation corrupted; English messages file "+language+".stg incomplete"); |
| panic(); |
| } |
| else if ( !messagesOK ) { |
| setLocale(Locale.US); // try US to see if that will work |
| } |
| } |
| |
| /** The format gets reset either from the Tool if the user supplied a command line option to that effect |
| * Otherwise we just use the default "antlr". |
| */ |
| public static void setFormat(String formatName) { |
| ErrorManager.formatName = formatName; |
| String fileName = "org/antlr/tool/templates/messages/formats/"+formatName+".stg"; |
| format = new STGroupFile(fileName); |
| format.setListener(initSTListener); |
| if ( !format.isDefined("message") ) { // pick random msg to load |
| if ( formatName.equals("antlr") ) { |
| rawError("no such message format file "+fileName+" retrying with default ANTLR format"); |
| setFormat("antlr"); // recurse on this rule, trying the default message format |
| return; |
| } |
| else { |
| setFormat("antlr"); // recurse on this rule, trying the default message format |
| } |
| } |
| |
| format.setListener(blankSTListener); |
| boolean formatOK = verifyFormat(); |
| if ( !formatOK && formatName.equals("antlr") ) { |
| rawError("ANTLR installation corrupted; ANTLR messages format file "+formatName+".stg incomplete"); |
| panic(); |
| } |
| else if ( !formatOK ) { |
| setFormat("antlr"); // recurse on this rule, trying the default message format |
| } |
| } |
| |
| /** Encodes the error handling found in setLocale, but does not trigger |
| * panics, which would make GUI tools die if ANTLR's installation was |
| * a bit screwy. Duplicated code...ick. |
| public static Locale getLocaleForValidMessages(Locale locale) { |
| ErrorManager.locale = locale; |
| String language = locale.getLanguage(); |
| String fileName = "org/antlr/tool/templates/messages/"+language+".stg"; |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); |
| InputStream is = cl.getResourceAsStream(fileName); |
| if ( is==null && language.equals(Locale.US.getLanguage()) ) { |
| return null; |
| } |
| else if ( is==null ) { |
| return getLocaleForValidMessages(Locale.US); // recurse on this rule, trying the US locale |
| } |
| |
| boolean messagesOK = verifyMessages(); |
| if ( !messagesOK && language.equals(Locale.US.getLanguage()) ) { |
| return null; |
| } |
| else if ( !messagesOK ) { |
| return getLocaleForValidMessages(Locale.US); // try US to see if that will work |
| } |
| return true; |
| } |
| */ |
| |
| /** In general, you'll want all errors to go to a single spot. |
| * However, in a GUI, you might have two frames up with two |
| * different grammars. Two threads might launch to process the |
| * grammars--you would want errors to go to different objects |
| * depending on the thread. I store a single listener per |
| * thread. |
| */ |
| public static void setErrorListener(ANTLRErrorListener listener) { |
| threadToListenerMap.put(Thread.currentThread(), listener); |
| } |
| |
| public static void removeErrorListener() { |
| threadToListenerMap.remove(Thread.currentThread()); |
| } |
| |
| public static void setTool(Tool tool) { |
| threadToToolMap.put(Thread.currentThread(), tool); |
| } |
| |
| /** Given a message ID, return a ST that somebody can fill |
| * with data. We need to convert the int ID to the name of a template |
| * in the messages ST group. |
| */ |
| public static ST getMessage(int msgID) { |
| String msgName = idToMessageTemplateName[msgID]; |
| return messages.getInstanceOf(msgName); |
| } |
| public static String getMessageType(int msgID) { |
| if (getErrorState().warningMsgIDs.member(msgID)) { |
| return messages.getInstanceOf("warning").render(); |
| } |
| else if (getErrorState().errorMsgIDs.member(msgID)) { |
| return messages.getInstanceOf("error").render(); |
| } |
| assertTrue(false, "Assertion failed! Message ID " + msgID + " created but is not present in errorMsgIDs or warningMsgIDs."); |
| return ""; |
| } |
| |
| /** Return a ST that refers to the current format used for |
| * emitting messages. |
| */ |
| public static ST getLocationFormat() { |
| return format.getInstanceOf("location"); |
| } |
| public static ST getReportFormat() { |
| return format.getInstanceOf("report"); |
| } |
| public static ST getMessageFormat() { |
| return format.getInstanceOf("message"); |
| } |
| public static boolean formatWantsSingleLineMessage() { |
| return format.getInstanceOf("wantsSingleLineMessage").render().equals("true"); |
| } |
| |
| public static ANTLRErrorListener getErrorListener() { |
| ANTLRErrorListener el = |
| (ANTLRErrorListener)threadToListenerMap.get(Thread.currentThread()); |
| if ( el==null ) { |
| return theDefaultErrorListener; |
| } |
| return el; |
| } |
| |
| public static ErrorState getErrorState() { |
| ErrorState ec = |
| (ErrorState)threadToErrorStateMap.get(Thread.currentThread()); |
| if ( ec==null ) { |
| ec = new ErrorState(); |
| threadToErrorStateMap.put(Thread.currentThread(), ec); |
| } |
| return ec; |
| } |
| |
| public static int getNumErrors() { |
| return getErrorState().errors; |
| } |
| |
| public static void resetErrorState() { |
| threadToListenerMap = new HashMap(); |
| ErrorState ec = new ErrorState(); |
| threadToErrorStateMap.put(Thread.currentThread(), ec); |
| } |
| |
| public static void info(String msg) { |
| getErrorState().infos++; |
| getErrorListener().info(msg); |
| } |
| |
| public static void error(int msgID) { |
| getErrorState().errors++; |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error(new ToolMessage(msgID)); |
| } |
| |
| public static void error(int msgID, Throwable e) { |
| getErrorState().errors++; |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error(new ToolMessage(msgID,e)); |
| } |
| |
| public static void error(int msgID, Object arg) { |
| getErrorState().errors++; |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error(new ToolMessage(msgID, arg)); |
| } |
| |
| public static void error(int msgID, Object arg, Object arg2) { |
| getErrorState().errors++; |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error(new ToolMessage(msgID, arg, arg2)); |
| } |
| |
| public static void error(int msgID, Object arg, Throwable e) { |
| getErrorState().errors++; |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error(new ToolMessage(msgID, arg, e)); |
| } |
| |
| public static void warning(int msgID, Object arg) { |
| getErrorState().warnings++; |
| getErrorState().warningMsgIDs.add(msgID); |
| getErrorListener().warning(new ToolMessage(msgID, arg)); |
| } |
| |
| public static void nondeterminism(DecisionProbe probe, |
| DFAState d) |
| { |
| getErrorState().warnings++; |
| Message msg = new GrammarNonDeterminismMessage(probe,d); |
| getErrorState().warningMsgIDs.add(msg.msgID); |
| getErrorListener().warning(msg); |
| } |
| |
| public static void danglingState(DecisionProbe probe, |
| DFAState d) |
| { |
| getErrorState().errors++; |
| Message msg = new GrammarDanglingStateMessage(probe,d); |
| getErrorState().errorMsgIDs.add(msg.msgID); |
| Set seen = (Set)emitSingleError.get("danglingState"); |
| if ( !seen.contains(d.dfa.decisionNumber+"|"+d.getAltSet()) ) { |
| getErrorListener().error(msg); |
| // we've seen this decision and this alt set; never again |
| seen.add(d.dfa.decisionNumber+"|"+d.getAltSet()); |
| } |
| } |
| |
| public static void analysisAborted(DecisionProbe probe) |
| { |
| getErrorState().warnings++; |
| Message msg = new GrammarAnalysisAbortedMessage(probe); |
| getErrorState().warningMsgIDs.add(msg.msgID); |
| getErrorListener().warning(msg); |
| } |
| |
| public static void unreachableAlts(DecisionProbe probe, |
| List alts) |
| { |
| getErrorState().errors++; |
| Message msg = new GrammarUnreachableAltsMessage(probe,alts); |
| getErrorState().errorMsgIDs.add(msg.msgID); |
| getErrorListener().error(msg); |
| } |
| |
| public static void insufficientPredicates(DecisionProbe probe, |
| DFAState d, |
| Map<Integer, Set<Token>> altToUncoveredLocations) |
| { |
| getErrorState().warnings++; |
| Message msg = new GrammarInsufficientPredicatesMessage(probe,d,altToUncoveredLocations); |
| getErrorState().warningMsgIDs.add(msg.msgID); |
| getErrorListener().warning(msg); |
| } |
| |
| public static void nonLLStarDecision(DecisionProbe probe) { |
| getErrorState().errors++; |
| Message msg = new NonRegularDecisionMessage(probe, probe.getNonDeterministicAlts()); |
| getErrorState().errorMsgIDs.add(msg.msgID); |
| getErrorListener().error(msg); |
| } |
| |
| public static void recursionOverflow(DecisionProbe probe, |
| DFAState sampleBadState, |
| int alt, |
| Collection targetRules, |
| Collection callSiteStates) |
| { |
| getErrorState().errors++; |
| Message msg = new RecursionOverflowMessage(probe,sampleBadState, alt, |
| targetRules, callSiteStates); |
| getErrorState().errorMsgIDs.add(msg.msgID); |
| getErrorListener().error(msg); |
| } |
| |
| /* |
| // TODO: we can remove I think. All detected now with cycles check. |
| public static void leftRecursion(DecisionProbe probe, |
| int alt, |
| Collection targetRules, |
| Collection callSiteStates) |
| { |
| getErrorState().warnings++; |
| Message msg = new LeftRecursionMessage(probe, alt, targetRules, callSiteStates); |
| getErrorState().warningMsgIDs.add(msg.msgID); |
| getErrorListener().warning(msg); |
| } |
| */ |
| |
| public static void leftRecursionCycles(Collection cycles) { |
| getErrorState().errors++; |
| Message msg = new LeftRecursionCyclesMessage(cycles); |
| getErrorState().errorMsgIDs.add(msg.msgID); |
| getErrorListener().error(msg); |
| } |
| |
| public static void grammarError(int msgID, |
| Grammar g, |
| Token token, |
| Object arg, |
| Object arg2) |
| { |
| getErrorState().errors++; |
| Message msg = new GrammarSemanticsMessage(msgID,g,token,arg,arg2); |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error(msg); |
| } |
| |
| public static void grammarError(int msgID, |
| Grammar g, |
| Token token, |
| Object arg) |
| { |
| grammarError(msgID,g,token,arg,null); |
| } |
| |
| public static void grammarError(int msgID, |
| Grammar g, |
| Token token) |
| { |
| grammarError(msgID,g,token,null,null); |
| } |
| |
| public static void grammarWarning(int msgID, |
| Grammar g, |
| Token token, |
| Object arg, |
| Object arg2) |
| { |
| getErrorState().warnings++; |
| Message msg = new GrammarSemanticsMessage(msgID,g,token,arg,arg2); |
| getErrorState().warningMsgIDs.add(msgID); |
| getErrorListener().warning(msg); |
| } |
| |
| public static void grammarWarning(int msgID, |
| Grammar g, |
| Token token, |
| Object arg) |
| { |
| grammarWarning(msgID,g,token,arg,null); |
| } |
| |
| public static void grammarWarning(int msgID, |
| Grammar g, |
| Token token) |
| { |
| grammarWarning(msgID,g,token,null,null); |
| } |
| |
| public static void syntaxError(int msgID, |
| Grammar grammar, |
| Token token, |
| Object arg, |
| RecognitionException re) |
| { |
| getErrorState().errors++; |
| getErrorState().errorMsgIDs.add(msgID); |
| getErrorListener().error( |
| new GrammarSyntaxMessage(msgID,grammar,token,arg,re) |
| ); |
| } |
| |
| public static void internalError(Object error, Throwable e) { |
| StackTraceElement location = getLastNonErrorManagerCodeLocation(e); |
| String msg = "Exception "+e+"@"+location+": "+error; |
| error(MSG_INTERNAL_ERROR, msg); |
| } |
| |
| public static void internalError(Object error) { |
| StackTraceElement location = |
| getLastNonErrorManagerCodeLocation(new Exception()); |
| String msg = location+": "+error; |
| error(MSG_INTERNAL_ERROR, msg); |
| } |
| |
| public static boolean doNotAttemptAnalysis() { |
| return !getErrorState().errorMsgIDs.and(ERRORS_FORCING_NO_ANALYSIS).isNil(); |
| } |
| |
| public static boolean doNotAttemptCodeGen() { |
| return doNotAttemptAnalysis() || |
| !getErrorState().errorMsgIDs.and(ERRORS_FORCING_NO_CODEGEN).isNil(); |
| } |
| |
| /** Return first non ErrorManager code location for generating messages */ |
| private static StackTraceElement getLastNonErrorManagerCodeLocation(Throwable e) { |
| StackTraceElement[] stack = e.getStackTrace(); |
| int i = 0; |
| for (; i < stack.length; i++) { |
| StackTraceElement t = stack[i]; |
| if ( t.toString().indexOf("ErrorManager")<0 ) { |
| break; |
| } |
| } |
| StackTraceElement location = stack[i]; |
| return location; |
| } |
| |
| // A S S E R T I O N C O D E |
| |
| public static void assertTrue(boolean condition, String message) { |
| if ( !condition ) { |
| internalError(message); |
| } |
| } |
| |
| // S U P P O R T C O D E |
| |
| protected static boolean initIdToMessageNameMapping() { |
| // make sure a message exists, even if it's just to indicate a problem |
| for (int i = 0; i < idToMessageTemplateName.length; i++) { |
| idToMessageTemplateName[i] = "INVALID MESSAGE ID: "+i; |
| } |
| // get list of fields and use it to fill in idToMessageTemplateName mapping |
| Field[] fields = ErrorManager.class.getFields(); |
| for (int i = 0; i < fields.length; i++) { |
| Field f = fields[i]; |
| String fieldName = f.getName(); |
| if ( !fieldName.startsWith("MSG_") ) { |
| continue; |
| } |
| String templateName = |
| fieldName.substring("MSG_".length(),fieldName.length()); |
| int msgID = 0; |
| try { |
| // get the constant value from this class object |
| msgID = f.getInt(ErrorManager.class); |
| } |
| catch (IllegalAccessException iae) { |
| System.err.println("cannot get const value for "+f.getName()); |
| continue; |
| } |
| if ( fieldName.startsWith("MSG_") ) { |
| idToMessageTemplateName[msgID] = templateName; |
| } |
| } |
| return true; |
| } |
| |
| /** Use reflection to find list of MSG_ fields and then verify a |
| * template exists for each one from the locale's group. |
| */ |
| protected static boolean verifyMessages() { |
| boolean ok = true; |
| Field[] fields = ErrorManager.class.getFields(); |
| for (int i = 0; i < fields.length; i++) { |
| Field f = fields[i]; |
| String fieldName = f.getName(); |
| String templateName = |
| fieldName.substring("MSG_".length(),fieldName.length()); |
| if ( fieldName.startsWith("MSG_") ) { |
| if ( !messages.isDefined(templateName) ) { |
| System.err.println("Message "+templateName+" in locale "+ |
| locale+" not found"); |
| ok = false; |
| } |
| } |
| } |
| // check for special templates |
| if (!messages.isDefined("warning")) { |
| System.err.println("Message template 'warning' not found in locale "+ locale); |
| ok = false; |
| } |
| if (!messages.isDefined("error")) { |
| System.err.println("Message template 'error' not found in locale "+ locale); |
| ok = false; |
| } |
| return ok; |
| } |
| |
| /** Verify the message format template group */ |
| protected static boolean verifyFormat() { |
| boolean ok = true; |
| if (!format.isDefined("location")) { |
| System.err.println("Format template 'location' not found in " + formatName); |
| ok = false; |
| } |
| if (!format.isDefined("message")) { |
| System.err.println("Format template 'message' not found in " + formatName); |
| ok = false; |
| } |
| if (!format.isDefined("report")) { |
| System.err.println("Format template 'report' not found in " + formatName); |
| ok = false; |
| } |
| return ok; |
| } |
| |
| /** If there are errors during ErrorManager init, we have no choice |
| * but to go to System.err. |
| */ |
| static void rawError(String msg) { |
| System.err.println(msg); |
| } |
| |
| static void rawError(String msg, Throwable e) { |
| rawError(msg); |
| e.printStackTrace(System.err); |
| } |
| |
| /** I *think* this will allow Tool subclasses to exit gracefully |
| * for GUIs etc... |
| */ |
| public static void panic() { |
| Tool tool = (Tool)threadToToolMap.get(Thread.currentThread()); |
| if ( tool==null ) { |
| // no tool registered, exit |
| throw new Error("ANTLR ErrorManager panic"); |
| } |
| else { |
| tool.panic(); |
| } |
| } |
| } |