From a13068c7c213252dfd1c620fcf8c3318d3d3c0e1 Mon Sep 17 00:00:00 2001 From: parrt Date: Wed, 15 Jun 2011 18:36:21 -0800 Subject: [PATCH] [git-p4: depot-paths = "//depot/code/antlr4/main/": change = 8640] --- tool/src/org/antlr/v4/Tool.java | 136 ++++++ tool/src/org/antlr/v4/tool/ANTLRMessage.java | 43 ++ .../org/antlr/v4/tool/ANTLRToolListener.java | 14 + .../antlr/v4/tool/DefaultToolListener.java | 36 ++ tool/src/org/antlr/v4/tool/ErrorManager.java | 413 ++++++++++++++++++ tool/src/org/antlr/v4/tool/ErrorSeverity.java | 67 +++ tool/src/org/antlr/v4/tool/ErrorType.java | 243 +++++++++++ tool/src/org/antlr/v4/tool/Grammar.java | 4 + .../v4/tool/GrammarSemanticsMessage.java | 45 ++ .../antlr/v4/tool/GrammarSyntaxMessage.java | 30 ++ .../v4/tool/LeftRecursionCyclesMessage.java | 13 + tool/src/org/antlr/v4/tool/ToolMessage.java | 21 + 12 files changed, 1065 insertions(+) create mode 100644 tool/src/org/antlr/v4/Tool.java create mode 100644 tool/src/org/antlr/v4/tool/ANTLRMessage.java create mode 100644 tool/src/org/antlr/v4/tool/ANTLRToolListener.java create mode 100644 tool/src/org/antlr/v4/tool/DefaultToolListener.java create mode 100644 tool/src/org/antlr/v4/tool/ErrorManager.java create mode 100644 tool/src/org/antlr/v4/tool/ErrorSeverity.java create mode 100644 tool/src/org/antlr/v4/tool/ErrorType.java create mode 100644 tool/src/org/antlr/v4/tool/Grammar.java create mode 100644 tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java create mode 100644 tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java create mode 100644 tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java create mode 100644 tool/src/org/antlr/v4/tool/ToolMessage.java diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java new file mode 100644 index 000000000..667c07328 --- /dev/null +++ b/tool/src/org/antlr/v4/Tool.java @@ -0,0 +1,136 @@ +package org.antlr.v4; + +import org.antlr.v4.tool.*; + +import java.util.*; + +public class Tool { + public String VERSION = "4.0-"+new Date(); + + public static enum OptionArgType { NONE, STRING, INT } + public static class Option { + String name; + OptionArgType argType; + Object defaultArgValue; + String description; + + public Option(String name, String description) { + this(name, OptionArgType.NONE, null, description); + } + + public Option(String name, OptionArgType argType, String description) { + this(name, argType, null, description); + } + + public Option(String name, OptionArgType argType, Object defaultArgValue, String description) { + this.name = name; + this.argType = argType; + this.defaultArgValue = defaultArgValue; + this.description = description; + } + } + + public static Option[] optionDefs = { + new Option("o", OptionArgType.STRING, ".", "specify output directory where all output is generated"), + new Option("fo", OptionArgType.STRING, "same as -o but force even files with relative paths to dir"), + new Option("lib", "specify location of .token files"), + new Option("report", "print out a report about the grammar(s) processed"), + new Option("print", "print out the grammar without actions"), + new Option("debug", "generate a parser that emits debugging events"), + new Option("profile", "generate a parser that computes profiling information"), + new Option("atn", "generate rule augmented transition networks"), + new Option("message-format", OptionArgType.STRING, "specify output style for messages"), + new Option("version", "print the version of ANTLR and exit"), + new Option("savelexer", "save temp lexer file created for combined grammars"), + new Option("dbgST", "launch StringTemplate visualizer on generated code"), + }; + + protected Map options = new HashMap(); + + protected String[] args; + + public ErrorManager errMgr = new ErrorManager(this); + + List listeners = + Collections.synchronizedList(new ArrayList()); + + /** Track separately so if someone adds a listener, it's the only one + * instead of it and the default stderr listener. + */ + DefaultToolListener defaultListener = new DefaultToolListener(this); + + public static void main(String[] args) { + Tool antlr = new Tool(args); + antlr.help(); + antlr.processGrammarsOnCommandLine(); + + if (antlr.errMgr.getNumErrors() > 0) { + antlr.exit(1); + } + antlr.exit(0); + +// if (!exitNow) { +// antlr.processGrammarsOnCommandLine(); +// if ( return_dont_exit ) return; +// } + } + + public Tool() { this(null); } + + public Tool(String[] args) { + this.args = args; + } + + public void processGrammarsOnCommandLine() { + + } + + + public void help() { + info("ANTLR Parser Generator Version " + new Tool().VERSION); + for (Option o : optionDefs) { + String name = o.name + (o.argType!=OptionArgType.NONE? " ___" : ""); + String s = String.format(" -%-19s %s", name, o.description); + info(s); + } + } + + public void addListener(ANTLRToolListener tl) { + if ( tl!=null ) listeners.add(tl); + } + public void removeListener(ANTLRToolListener tl) { listeners.remove(tl); } + public void removeListeners() { listeners.clear(); } + public List getListeners() { return listeners; } + + public void info(String msg) { + if ( listeners.size()==0 ) { + defaultListener.info(msg); + return; + } + for (ANTLRToolListener l : listeners) l.info(msg); + } + public void error(ANTLRMessage msg) { + if ( listeners.size()==0 ) { + defaultListener.error(msg); + return; + } + for (ANTLRToolListener l : listeners) l.error(msg); + } + public void warning(ANTLRMessage msg) { + if ( listeners.size()==0 ) { + defaultListener.warning(msg); + return; + } + for (ANTLRToolListener l : listeners) l.warning(msg); + } + + + public void version() { + info("ANTLR Parser Generator Version " + new Tool().VERSION); + } + + public void exit(int e) { System.exit(e); } + + public void panic() { throw new Error("ANTLR panic"); } + +} diff --git a/tool/src/org/antlr/v4/tool/ANTLRMessage.java b/tool/src/org/antlr/v4/tool/ANTLRMessage.java new file mode 100644 index 000000000..7ef15bd81 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ANTLRMessage.java @@ -0,0 +1,43 @@ +package org.antlr.v4.tool; + +import java.util.Arrays; + +public class ANTLRMessage { + public ErrorType errorType; + public Object[] args; + public Throwable e; + + // used for location template + public String fileName; + public int line = -1; + public int charPosition = -1; + + public ANTLRMessage() { + } + + public ANTLRMessage(ErrorType errorType) { + this.errorType = errorType; + } + + public ANTLRMessage(ErrorType errorType, Object... args) { + this(errorType); + this.args = args; + } + + public ANTLRMessage(ErrorType errorType, Throwable e, Object... args) { + this(errorType, args); + this.e = e; + } + + @Override + public String toString() { + return "Message{" + + "errorType=" + errorType + + ", args=" + (args == null ? null : Arrays.asList(args)) + + ", e=" + e + + ", fileName='" + fileName + '\'' + + ", line=" + line + + ", charPosition=" + charPosition + + '}'; + } +} diff --git a/tool/src/org/antlr/v4/tool/ANTLRToolListener.java b/tool/src/org/antlr/v4/tool/ANTLRToolListener.java new file mode 100644 index 000000000..d9b22b129 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ANTLRToolListener.java @@ -0,0 +1,14 @@ +package org.antlr.v4.tool; + +/** Defines behavior of object able to handle error messages from ANTLR including + * both tool errors like "can't write file" and grammar ambiguity warnings. + * To avoid having to change tools that use ANTLR (like GUIs), I am + * wrapping error data in Message objects and passing them to the listener. + * In this way, users of this interface are less sensitive to changes in + * the info I need for error messages. + */ +public interface ANTLRToolListener { + public void info(String msg); + public void error(ANTLRMessage msg); + public void warning(ANTLRMessage msg); +} diff --git a/tool/src/org/antlr/v4/tool/DefaultToolListener.java b/tool/src/org/antlr/v4/tool/DefaultToolListener.java new file mode 100644 index 000000000..f3809c02b --- /dev/null +++ b/tool/src/org/antlr/v4/tool/DefaultToolListener.java @@ -0,0 +1,36 @@ +package org.antlr.v4.tool; + +import org.antlr.v4.Tool; +import org.stringtemplate.v4.ST; + +/** */ +public class DefaultToolListener implements ANTLRToolListener { + public Tool tool; + + public DefaultToolListener(Tool tool) { this.tool = tool; } + + public void info(String msg) { + if (tool.errMgr.formatWantsSingleLineMessage()) { + msg = msg.replaceAll("\n", " "); + } + System.out.println(msg); + } + + public void error(ANTLRMessage msg) { + ST msgST = tool.errMgr.getMessageTemplate(msg); + String outputMsg = msgST.render(); + if (tool.errMgr.formatWantsSingleLineMessage()) { + outputMsg = outputMsg.replaceAll("\n", " "); + } + System.err.println(outputMsg); + } + + public void warning(ANTLRMessage msg) { + ST msgST = tool.errMgr.getMessageTemplate(msg); + String outputMsg = msgST.render(); + if (tool.errMgr.formatWantsSingleLineMessage()) { + outputMsg = outputMsg.replaceAll("\n", " "); + } + System.err.println(outputMsg); + } +} diff --git a/tool/src/org/antlr/v4/tool/ErrorManager.java b/tool/src/org/antlr/v4/tool/ErrorManager.java new file mode 100644 index 000000000..34ff1d8bf --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ErrorManager.java @@ -0,0 +1,413 @@ +package org.antlr.v4.tool; + +import org.antlr.runtime.Token; +import org.antlr.v4.Tool; +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.ErrorBuffer; +import org.stringtemplate.v4.misc.STMessage; + +import java.net.URL; +import java.util.Collection; +import java.util.Locale; + +/** 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? + */ +public class ErrorManager { + public static final String FORMATS_DIR = "org/antlr/v4/tool/templates/messages/formats/"; + public static final String MESSAGES_DIR = "org/antlr/v4/tool/templates/messages/languages/"; + + public Tool tool; + public int errors; + public int warnings; + + /** The group of templates that represent all possible ANTLR errors. */ + STGroup messages; + + /** The group of templates that represent the current message format. */ + STGroup format; + + /** Messages should be sensitive to the locale. */ + Locale locale; + String formatName; + + + + ErrorBuffer initSTListener = new ErrorBuffer(); + + STErrorListener theDefaultSTListener = + new STErrorListener() { + public void compileTimeError(STMessage msg) { + ErrorManager.internalError(msg.toString()); + } + + public void runTimeError(STMessage msg) { + ErrorManager.internalError(msg.toString()); + } + + public void IOError(STMessage msg) { + ErrorManager.internalError(msg.toString()); + } + + public void internalError(STMessage msg) { + ErrorManager.internalError(msg.toString()); + } + }; + + public ErrorManager(Tool tool) { + this.tool = tool; + //org.stringtemplate.v4.misc.ErrorManager.setErrorListener(initSTListener); + // 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"); + //org.stringtemplate.v4.misc.ErrorManager.setErrorListener(theDefaultSTListener); + } + + public void resetErrorState() { + errors = 0; + warnings = 0; + } + + public ST getMessageTemplate(ANTLRMessage msg) { + ST messageST = getMessageTemplate(msg.errorType); + ST locationST = getLocationFormat(); + ST reportST = getReportFormat(msg.errorType.getSeverity()); + ST messageFormatST = getMessageFormat(); + + if ( msg.args!=null ) { // fill in arg1, arg2, ... + for (int i=0; i0 ) attr += i + 1; + messageST.add(attr, msg.args[i]); + } + } + if ( msg.e!=null ) { + messageST.add("exception", msg.e); + messageST.add("stackTrace", msg.e.getStackTrace()); + } + + boolean locationValid = false; + if (msg.line != -1) { + locationST.add("line", msg.line); + locationValid = true; + } + if (msg.charPosition != -1) { + locationST.add("column", msg.charPosition); + locationValid = true; + } + if (msg.fileName != null) { + locationST.add("file", msg.fileName); + locationValid = true; + } + + messageFormatST.add("id", msg.errorType.ordinal()); + messageFormatST.add("text", messageST); + + if (locationValid) reportST.add("location", locationST); + reportST.add("message", messageFormatST); + //((DebugST)reportST).inspect(); + //reportST.impl.dump(); + return reportST; + } + + /** Return a StringTemplate that refers to the current format used for + * emitting messages. + */ + public ST getLocationFormat() { + return format.getInstanceOf("location"); + } + + public ST getReportFormat(ErrorSeverity severity) { + ST st = format.getInstanceOf("report"); + ST type = messages.getInstanceOf(severity.toString()); + st.add("type", type); + return st; + + } + public ST getMessageFormat() { + return format.getInstanceOf("message"); + } + public boolean formatWantsSingleLineMessage() { + return format.getInstanceOf("wantsSingleLineMessage").render().equals("true"); + } + public ST getMessageTemplate(ErrorType etype) { + String msgName = etype.toString(); + return messages.getInstanceOf(msgName); + } + + public void info(String msg) { tool.info(msg); } + + public void syntaxError(ErrorType etype, + String fileName, + org.antlr.runtime.Token token, + org.antlr.runtime.RecognitionException antlrException, + Object... args) + { + errors++; + ANTLRMessage msg = new GrammarSyntaxMessage(etype,fileName,token,antlrException,args); + tool.error(msg); + } + + public static void fatalInternalError(String error, Throwable e) { + internalError(error, e); + throw new RuntimeException(error, e); + } + + public static void internalError(String error, Throwable e) { + StackTraceElement location = getLastNonErrorManagerCodeLocation(e); + internalError("Exception "+e+"@"+location+": "+error); + } + + public static void internalError(String error) { + StackTraceElement location = + getLastNonErrorManagerCodeLocation(new Exception()); + String msg = location+": "+error; + System.err.println("internal error: "+msg); + } + + /** + * Raise a predefined message with some number of paramters for the StringTemplate but for which there + * is no location information possible. + * @param errorType The Message Descriptor + * @param args The arguments to pass to the StringTemplate + */ + public void toolError(ErrorType errorType, Object... args) { + errors++; + tool.error(new ToolMessage(errorType, args)); + } + + public void grammarError(ErrorType etype, + String fileName, + org.antlr.runtime.Token token, + Object... args) + { + errors++; + ANTLRMessage msg = new GrammarSemanticsMessage(etype,fileName,token,args); + tool.error(msg); + } + + public void grammarWarning(ErrorType etype, + String fileName, + Token token, + Object... args) + { + warnings++; + ANTLRMessage msg = new GrammarSemanticsMessage(etype,fileName,token,args); + tool.warning(msg); + } + + public void leftRecursionCycles(String fileName, Collection cycles) { + errors++; + ANTLRMessage msg = new LeftRecursionCyclesMessage(fileName, cycles); + tool.error(msg); + } + + public int getNumErrors() { + return errors; + } + + /** 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; + } + + // S U P P O R T C O D E + + /** 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 void setLocale(Locale locale) { + this.locale = locale; + String language = locale.getLanguage(); + String fileName = MESSAGES_DIR +language+".stg"; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL url = cl.getResource(fileName); + if ( url==null ) { + cl = ErrorManager.class.getClassLoader(); + url = cl.getResource(fileName); + } + if ( url==null && language.equals(Locale.US.getLanguage()) ) { + rawError("ANTLR installation corrupted; cannot find English messages file "+fileName); + panic(); + } + else if ( url==null ) { + //rawError("no such locale file "+fileName+" retrying with English locale"); + setLocale(Locale.US); // recurse on this rule, trying the US locale + return; + } + + messages = new STGroupFile(fileName, "UTF-8"); + messages.load(); + if ( initSTListener.errors.size()>0 ) { + rawError("ANTLR installation corrupted; can't load messages format file:\n"+ + initSTListener.toString()); + panic(); + } + + 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 void setFormat(String formatName) { + this.formatName = formatName; + String fileName = FORMATS_DIR +formatName+".stg"; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL url = cl.getResource(fileName); + if ( url==null ) { + cl = ErrorManager.class.getClassLoader(); + url = cl.getResource(fileName); + } + if ( url==null && formatName.equals("antlr") ) { + rawError("ANTLR installation corrupted; cannot find ANTLR messages format file "+fileName); + panic(); + } + else if ( url==null ) { + rawError("no such message format file "+fileName+" retrying with default ANTLR format"); + setFormat("antlr"); // recurse on this rule, trying the default message format + return; + } + + format = new STGroupFile(fileName, "UTF-8"); + format.load(); + + if ( initSTListener.errors.size()>0 ) { + rawError("ANTLR installation corrupted; can't load messages format file:\n"+ + initSTListener.toString()); + panic(); + } + + 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 + } + } + + /** Use reflection to find list of MSG_ fields and then verify a + * template exists for each one from the locale's group. + */ + protected boolean verifyMessages() { + boolean ok = true; + ErrorType[] errors = ErrorType.values(); + for (int i = 0; i < errors.length; i++) { + ErrorType e = errors[i]; + if ( !messages.isDefined(e.toString()) ) { + System.err.println("Message "+e.toString()+" 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 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); + } + + public static void panic() { + // can't call tool.panic since there may be multiple tools; just + // one error manager + throw new Error("ANTLR ErrorManager panic"); + } +} diff --git a/tool/src/org/antlr/v4/tool/ErrorSeverity.java b/tool/src/org/antlr/v4/tool/ErrorSeverity.java new file mode 100644 index 000000000..62a342cc0 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ErrorSeverity.java @@ -0,0 +1,67 @@ +/* + [The "BSD licence"] + Copyright (c) 2005-2008 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.v4.tool; + +/** + * Abstracts away the definition of Message severity and the text that should + * display to represent that severity if there is no StringTemplate available + * to do it. + * + * @author Jim Idle - Temporal Wave LLC (jimi@temporal-wave.com) + */ +public enum ErrorSeverity { + INFO ("Info"), + + WARNING ("Warning"), + + ERROR ("Error"), + + FATAL ("Fatal"), // TODO: add fatal for which phase? sync with ErrorManager + ; + + /** + * The text version of the ENUM value, used for display purposes + */ + private final String text; + + /** + * Standard getter method for the text that should be displayed in order to + * represent the severity to humans and product modelers. + * + * @return The human readable string representing the severity level + */ + public String getText() { return text; } + + /** + * Standard constructor to build an instance of the Enum entries + * + * @param text The human readable string representing the serverity level + */ + private ErrorSeverity(String text) { this.text = text; } +} + diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java new file mode 100644 index 000000000..f99a81aee --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -0,0 +1,243 @@ +/* + [The "BSD licence"] + Copyright (c) 2005-2008 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.v4.tool; + +/** + * A complex enumeration of all the error messages that the tool can issue. + * + * When adding error messages, also add a description of the message to the + * Wiki with a location under the Wiki page + * Errors Reported by the ANTLR Tool. + * + * TODO: add notion of single issuance of an error; some don't need to be repeated; AST_OP_IN_ALT_WITH_REWRITE and option issues + * + * @author Jim Idle + * @since 4.0 + */ +public enum ErrorType { + INVALID(ErrorSeverity.ERROR,true,true), + + // TODO: set all of the true, true appropriately + CANNOT_WRITE_FILE(ErrorSeverity.ERROR, true, true), + CANNOT_CLOSE_FILE(ErrorSeverity.ERROR, true, true), + CANNOT_FIND_TOKENS_FILE(ErrorSeverity.ERROR, true, true), + ERROR_READING_TOKENS_FILE(ErrorSeverity.ERROR, true, true), + DIR_NOT_FOUND(ErrorSeverity.ERROR, true, true), + OUTPUT_DIR_IS_FILE(ErrorSeverity.ERROR, true, true), + CANNOT_OPEN_FILE(ErrorSeverity.ERROR, true, true), + FILE_AND_GRAMMAR_NAME_DIFFER(ErrorSeverity.ERROR, true, true), + FILENAME_EXTENSION_ERROR(ErrorSeverity.ERROR, true, true), + + INTERNAL_ERROR(ErrorSeverity.ERROR, true, true), + INTERNAL_WARNING(ErrorSeverity.ERROR, true, true), + TOKENS_FILE_SYNTAX_ERROR(ErrorSeverity.ERROR, true, true), + CANNOT_GEN_DOT_FILE(ErrorSeverity.ERROR, true, true), + + // Code generation errors + MISSING_CODE_GEN_TEMPLATES(ErrorSeverity.ERROR, false, true), + CANNOT_CREATE_TARGET_GENERATOR(ErrorSeverity.ERROR, false, true), + CODE_TEMPLATE_ARG_ISSUE(ErrorSeverity.ERROR, false, true), + CODE_GEN_TEMPLATES_INCOMPLETE(ErrorSeverity.ERROR, false, true), + NO_MODEL_TO_TEMPLATE_MAPPING(ErrorSeverity.ERROR, false, true), + + // Grammar errors + SYNTAX_ERROR(ErrorSeverity.ERROR, true, true), + RULE_REDEFINITION(ErrorSeverity.ERROR, true, true), + LEXER_RULES_NOT_ALLOWED(ErrorSeverity.ERROR, true, true), + PARSER_RULES_NOT_ALLOWED(ErrorSeverity.ERROR, true, true), + REPEATED_PREQUEL(ErrorSeverity.ERROR, true, true), + NO_TOKEN_DEFINITION(ErrorSeverity.ERROR, true, true), + UNDEFINED_RULE_REF(ErrorSeverity.ERROR, true, true), + LITERAL_NOT_ASSOCIATED_WITH_LEXER_RULE(ErrorSeverity.ERROR, true, true), + CANNOT_ALIAS_TOKENS(ErrorSeverity.ERROR, true, true), + TOKEN_NAMES_MUST_START_UPPER(ErrorSeverity.ERROR, true, true), + ATTRIBUTE_REF_NOT_IN_RULE(ErrorSeverity.ERROR, true, true), + INVALID_RULE_SCOPE_ATTRIBUTE_REF(ErrorSeverity.ERROR, true, true), + UNKNOWN_SIMPLE_ATTRIBUTE(ErrorSeverity.ERROR, true, true), + INVALID_RULE_PARAMETER_REF(ErrorSeverity.ERROR, true, true), + UNKNOWN_RULE_ATTRIBUTE(ErrorSeverity.ERROR, true, true), + UNKNOWN_ATTRIBUTE_IN_SCOPE(ErrorSeverity.ERROR, true, true), + ISOLATED_RULE_REF(ErrorSeverity.ERROR, true, true), + SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE(ErrorSeverity.ERROR, true, true), + LABEL_CONFLICTS_WITH_RULE(ErrorSeverity.ERROR, true, true), + LABEL_CONFLICTS_WITH_TOKEN(ErrorSeverity.ERROR, true, true), + LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE(ErrorSeverity.ERROR, true, true), + LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL(ErrorSeverity.ERROR, true, true), + ATTRIBUTE_CONFLICTS_WITH_RULE(ErrorSeverity.ERROR, true, true), + ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL(ErrorSeverity.ERROR, true, true), + LABEL_TYPE_CONFLICT(ErrorSeverity.ERROR, true, true), + ARG_RETVAL_CONFLICT(ErrorSeverity.ERROR, true, true), + NONUNIQUE_REF(ErrorSeverity.ERROR, true, true), + FORWARD_ELEMENT_REF(ErrorSeverity.ERROR, true, true), + MISSING_RULE_ARGS(ErrorSeverity.ERROR, true, true), + RULE_HAS_NO_ARGS(ErrorSeverity.ERROR, true, true), + ARGS_ON_TOKEN_REF(ErrorSeverity.ERROR, true, true), + RULE_REF_AMBIG_WITH_RULE_IN_ALT(ErrorSeverity.ERROR, true, true), + ILLEGAL_OPTION(ErrorSeverity.ERROR, true, true), + LIST_LABEL_INVALID_UNLESS_RETVAL_STRUCT(ErrorSeverity.ERROR, true, true), + REWRITE_ELEMENT_NOT_PRESENT_ON_LHS(ErrorSeverity.ERROR, true, true), + //UNDEFINED_TOKEN_REF_IN_REWRITE(ErrorSeverity.ERROR, true, true), + ///UNDEFINED_LABEL_REF_IN_REWRITE(ErrorSeverity.ERROR, true, true), use previous + NO_GRAMMAR_START_RULE(ErrorSeverity.ERROR, true, true), + EMPTY_COMPLEMENT(ErrorSeverity.ERROR, true, true), + UNKNOWN_DYNAMIC_SCOPE(ErrorSeverity.ERROR, true, true), + UNKNOWN_DYNAMIC_SCOPE_ATTRIBUTE(ErrorSeverity.ERROR, true, true), + ISOLATED_RULE_ATTRIBUTE(ErrorSeverity.ERROR, true, true), + INVALID_ACTION_SCOPE(ErrorSeverity.ERROR, true, true), + ACTION_REDEFINITION(ErrorSeverity.ERROR, true, true), + SCOPE_REDEFINITION(ErrorSeverity.ERROR, true, true), + INVALID_TEMPLATE_ACTION(ErrorSeverity.ERROR, true, true), + ARG_INIT_VALUES_ILLEGAL(ErrorSeverity.ERROR, true, true), + REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION(ErrorSeverity.ERROR, true, true), + NO_RULES(ErrorSeverity.ERROR, true, true), + WRITE_TO_READONLY_ATTR(ErrorSeverity.ERROR, true, true), + MISSING_AST_TYPE_IN_TREE_GRAMMAR(ErrorSeverity.ERROR, true, true), + REWRITE_FOR_MULTI_ELEMENT_ALT(ErrorSeverity.ERROR, true, true), + RULE_INVALID_SET(ErrorSeverity.ERROR, true, true), + HETERO_ILLEGAL_IN_REWRITE_ALT(ErrorSeverity.ERROR, true, true), + NO_SUCH_GRAMMAR_SCOPE(ErrorSeverity.ERROR, true, true), + NO_SUCH_RULE_IN_SCOPE(ErrorSeverity.ERROR, true, true), + TOKEN_ALIAS_CONFLICT(ErrorSeverity.ERROR, true, true), + TOKEN_ALIAS_REASSIGNMENT(ErrorSeverity.ERROR, true, true), + TOKEN_VOCAB_IN_DELEGATE(ErrorSeverity.ERROR, true, true), + TOKEN_ALIAS_IN_DELEGATE(ErrorSeverity.ERROR, true, true), + INVALID_IMPORT(ErrorSeverity.ERROR, true, true), + IMPORTED_TOKENS_RULE_EMPTY(ErrorSeverity.ERROR, true, true), + IMPORT_NAME_CLASH(ErrorSeverity.ERROR, true, true), + AST_OP_WITH_NON_AST_OUTPUT_OPTION(ErrorSeverity.ERROR, true, true), + AST_OP_IN_ALT_WITH_REWRITE(ErrorSeverity.ERROR, true, true), + WILDCARD_AS_ROOT(ErrorSeverity.ERROR, true, true), + CONFLICTING_OPTION_IN_TREE_FILTER(ErrorSeverity.ERROR, true, true), + + AMBIGUITY(ErrorSeverity.ERROR, true, true), + UNREACHABLE_ALTS(ErrorSeverity.ERROR, true, true), + //MULTIPLE_RECURSIVE_ALTS(ErrorSeverity.ERROR, true, true), + INSUFFICIENT_PREDICATES(ErrorSeverity.ERROR, true, true), + + // these next 3 can happen in recursion-limited LL(*) + //RECURSION_OVERFLOW(ErrorSeverity.ERROR, true, true), + LEFT_RECURSION_CYCLES(ErrorSeverity.ERROR, true, true), + ANALYSIS_TIMEOUT(ErrorSeverity.ERROR, true, true), + + MODE_NOT_IN_LEXER(ErrorSeverity.ERROR, true, true), + + /** Documentation comment is unterminated */ + //UNTERMINATED_DOC_COMMENT(ErrorSeverity.ERROR, true, true), + + // Dependency sorting errors + // + /** t1.g -> t2.g -> t3.g ->t1.g */ + CIRCULAR_DEPENDENCY(ErrorSeverity.ERROR, true, true), + + // Simple informational messages + // + /** A standby generic message that jsut spits out the arguments it is given */ +// GENERIC_INFO(ErrorSeverity.INFO, false, false), +// /** How to print out the version of the ANTLR tool that we are */ +// ANTLR_VERSION(ErrorSeverity.INFO, false, false), +// +// // Command line tool errors/warnings +// /** -fo option was incorrectly formed */ +// MISSING_OUTPUT_FO(ErrorSeverity.WARNING, false, false), +// /** -lib option is missing a directory argument */ +// MISSING_LIBDIR(ErrorSeverity.WARNING, false, false), +// /** -format option was not given the name of a message format */ +// MISSING_FORMAT(ErrorSeverity.WARNING, false, false), +// /** Max state count missing from the option */ +// MISSING_MAXSTATES(ErrorSeverity.WARNING, false, false), +// /** Max labels in a switch argument is missing */ +// MISSING_MAXSWITCH(ErrorSeverity.WARNING, false, false), +// /** Min labels in a switch argument is missing */ +// MISSING_MINSWITCH(ErrorSeverity.WARNING, false, false), +// /** Missing recursion limit argument */ +// MISSING_MAXRECUR(ErrorSeverity.WARNING, false, false), +// /** Missing max edges argument */ +// MISSING_MAXEDGE(ErrorSeverity.WARNING, false, false), +// /** Misng ms timeout argument */ +// MISSING_MAXTIME(ErrorSeverity.WARNING, false, false), +// +// // Help messages +// HELP_USAGE(ErrorSeverity.INFO, false, false), +// HELP_EXTENDED(ErrorSeverity.INFO, false, false), + + ; + + /** + * Local storage for the severity level of the message + */ + private ErrorSeverity severity; + + /** + * Returns the severity level of this message + * @return + */ + public ErrorSeverity getSeverity() { + return severity; + } + + /** + * Internal storage for the flag that indicates whether this particular message + * should abort the analysis phase or not. + */ + private Boolean abortsAnalysis; + + /** + * Indicates whether the raising of this error messsage should abort the + * analysis phase (or prevent it from starting). + * + * @return true if this message should abort the analysis phase + */ + public Boolean abortsAnalysis() { + return abortsAnalysis; + } + + /** + * Indicates whether the raising of this error message aborts code + * generation or not. + */ + private Boolean abortsCodegen; + + /** + * Indicates whether the raising of this error message aborts code + * generation or not. + * + * @return true if this message should abort code generation + */ + public Boolean abortsCodegen() { + return abortsCodegen; + } + + /** + * Local constructor produces an instance of the entries in this Enum + */ + private ErrorType(ErrorSeverity severity, boolean abortsAnalysis, boolean abortsCodegen) { + this.severity = severity; + this.abortsAnalysis = abortsAnalysis; + + } +} diff --git a/tool/src/org/antlr/v4/tool/Grammar.java b/tool/src/org/antlr/v4/tool/Grammar.java new file mode 100644 index 000000000..a0f52a0a8 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/Grammar.java @@ -0,0 +1,4 @@ +package org.antlr.v4.tool; + +public class Grammar { +} diff --git a/tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java b/tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java new file mode 100644 index 000000000..c6c0272e5 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java @@ -0,0 +1,45 @@ +package org.antlr.v4.tool; + +import org.antlr.runtime.Token; + +/** A problem with the symbols and/or meaning of a grammar such as rule + * redefinition. + */ +public class GrammarSemanticsMessage extends ANTLRMessage { + public Grammar g; + /** Most of the time, we'll have a token such as an undefined rule ref + * and so this will be set. + */ + public Token offendingToken; + + /* + public GrammarSemanticsMessage(ErrorType etype, + Grammar g, + Token offendingToken, + Object... args) + { + super(etype,args); + this.g = g; + if ( g!=null ) fileName = g.fileName; + this.offendingToken = offendingToken; + if ( offendingToken!=null ) { + line = offendingToken.getLine(); + charPosition = offendingToken.getCharPositionInLine(); + } + } +*/ + public GrammarSemanticsMessage(ErrorType etype, + String fileName, + Token offendingToken, + Object... args) + { + super(etype,args); + this.fileName = fileName; + this.offendingToken = offendingToken; + if ( offendingToken!=null ) { + line = offendingToken.getLine(); + charPosition = offendingToken.getCharPositionInLine(); + } + } +} + diff --git a/tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java b/tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java new file mode 100644 index 000000000..28f7638ce --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java @@ -0,0 +1,30 @@ +package org.antlr.v4.tool; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; + +/** A problem with the syntax of your antlr grammar such as + * "The '{' came as a complete surprise to me at this point in your program" + */ +public class GrammarSyntaxMessage extends ANTLRMessage { + public Grammar g; + /** Most of the time, we'll have a token and so this will be set. */ + public Token offendingToken; + public RecognitionException antlrException; + + public GrammarSyntaxMessage(ErrorType etype, + String fileName, + Token offendingToken, + RecognitionException antlrException, + Object... args) + { + super(etype,args); + this.fileName = fileName; + this.offendingToken = offendingToken; + this.antlrException = antlrException; + if ( offendingToken!=null ) { + line = offendingToken.getLine(); + charPosition = offendingToken.getCharPositionInLine(); + } + } +} diff --git a/tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java b/tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java new file mode 100644 index 000000000..ec1918191 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java @@ -0,0 +1,13 @@ +package org.antlr.v4.tool; + +import java.util.Collection; + +public class LeftRecursionCyclesMessage extends ANTLRMessage { + public Collection cycles; + + public LeftRecursionCyclesMessage(String fileName, Collection cycles) { + super(ErrorType.LEFT_RECURSION_CYCLES, cycles); + this.cycles = cycles; + this.fileName = fileName; + } +} diff --git a/tool/src/org/antlr/v4/tool/ToolMessage.java b/tool/src/org/antlr/v4/tool/ToolMessage.java new file mode 100644 index 000000000..201c77938 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ToolMessage.java @@ -0,0 +1,21 @@ +package org.antlr.v4.tool; + +/** A generic message from the tool such as "file not found" type errors; there + * is no reason to create a special object for each error unlike the grammar + * errors, which may be rather complex. + * + * Sometimes you need to pass in a filename or something to say it is "bad". + * Allow a generic object to be passed in and the string template can deal + * with just printing it or pulling a property out of it. + */ +public class ToolMessage extends ANTLRMessage { + public ToolMessage(ErrorType errorType) { + super(errorType); + } + public ToolMessage(ErrorType errorType, Object... args) { + super(errorType, null, args); + } + public ToolMessage(ErrorType errorType, Throwable e, Object... args) { + super(errorType, e, args); + } +}