forked from jasder/antlr
Merge pull request #569 from parrt/runtime-tool-version-sync
add tool-template and generatedcode-runtime version compatibility check
This commit is contained in:
commit
f3fcf5a712
|
@ -0,0 +1,55 @@
|
|||
package org.antlr.v4.runtime;
|
||||
|
||||
/** Because targets can be updated at different times than the core tool, which includes this Java target,
|
||||
* we created this runtime information object. The goal is to test compatibility of the generated parser and
|
||||
* the runtime library for that target it executes with.
|
||||
*
|
||||
* Targets generate a tiny bit of code that executes upon loading of
|
||||
* a parser or lexer, which should check its version against what is contained in this object. For example,
|
||||
* here is something from the Java templates:
|
||||
*
|
||||
* public class <parser.name> extends <superClass> {
|
||||
* static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>"); }
|
||||
* ...
|
||||
*
|
||||
* This way, previous versions of target X that are incompatible (e.g., 4.1 and 4.2) will result in
|
||||
* a warning emitted through the appropriate channel for that target. This can be a msg to stderr or
|
||||
* an exception; it's up to the target developer. I have decided to throw an exception for Java target
|
||||
* as a message might not be seen if the parser were embedded in a server or something.
|
||||
*/
|
||||
public class RuntimeMetaData {
|
||||
public static class ANTLRVersionMismatchException extends RuntimeException {
|
||||
public String generatedCodeVersion;
|
||||
public String runtimeLibVersion;
|
||||
|
||||
public ANTLRVersionMismatchException(String message, String generatedCodeVersion, String runtimeLibVersion) {
|
||||
super(message);
|
||||
this.generatedCodeVersion = generatedCodeVersion;
|
||||
this.runtimeLibVersion = runtimeLibVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage()+": code version "+generatedCodeVersion+" != runtime version "+runtimeLibVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/** Must match version of tool that generated recognizers */
|
||||
public static final String VERSION = "4.3"; // use just "x.y" and don't include bug fix release number
|
||||
|
||||
/** As parser or lexer class is loaded, it checks that the version used to generate the code
|
||||
* is compatible with the runtime version. ANTLR tool generates recognizers with a hardcoded string created by
|
||||
* the tool during code gen. That version is passed to checkVersion().
|
||||
*/
|
||||
public static void checkVersion(String toolVersion) {
|
||||
// I believe that 4.2-generated parsers are compatible with the runtime for 4.3 so no exception in this case.
|
||||
// Technically, we don't need this check because 4.2 it doesn't actually check the version. ;) I am just
|
||||
// being explicit. Later we can always build a more sophisticated versioning check.
|
||||
if ( (toolVersion.startsWith("4.2.") || toolVersion.equals("4.2")) ) {
|
||||
return;
|
||||
}
|
||||
if ( !VERSION.equals(toolVersion) ) {
|
||||
throw new ANTLRVersionMismatchException("ANTLR runtime and generated code versions disagree",toolVersion,VERSION);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -214,6 +214,8 @@ Parser(parser, funcs, atn, sempredFuncs, superClass) ::= <<
|
|||
Parser_(parser, funcs, atn, sempredFuncs, ctor, superClass) ::= <<
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||
public class <parser.name> extends <superClass> {
|
||||
static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>"); }
|
||||
|
||||
protected static final DFA[] _decisionToDFA;
|
||||
protected static final PredictionContextCache _sharedContextCache =
|
||||
new PredictionContextCache();
|
||||
|
@ -848,6 +850,7 @@ import org.antlr.v4.runtime.misc.*;
|
|||
Lexer(lexer, atn, actionFuncs, sempredFuncs, superClass) ::= <<
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||
public class <lexer.name> extends <superClass> {
|
||||
static { RuntimeMetaData.checkVersion("<lexerFile.ANTLRVersion>"); }
|
||||
protected static final DFA[] _decisionToDFA;
|
||||
protected static final PredictionContextCache _sharedContextCache =
|
||||
new PredictionContextCache();
|
||||
|
|
|
@ -90,7 +90,7 @@ public class Tool {
|
|||
public static final String VERSION;
|
||||
static {
|
||||
String version = Tool.class.getPackage().getImplementationVersion();
|
||||
VERSION = version != null ? version : "4.x";
|
||||
VERSION = version != null ? version : "4.3";
|
||||
}
|
||||
|
||||
public static final String GRAMMAR_EXTENSION = ".g4";
|
||||
|
|
|
@ -88,7 +88,7 @@ public class CodeGenerator {
|
|||
}
|
||||
|
||||
public STGroup getTemplates() {
|
||||
return getTarget().getTemplates();
|
||||
return getTarget().getTemplates();
|
||||
}
|
||||
|
||||
protected void loadLanguageTarget(String language) {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
package org.antlr.v4.codegen;
|
||||
|
||||
import org.antlr.v4.Tool;
|
||||
import org.antlr.v4.tool.ast.GrammarAST;
|
||||
import org.stringtemplate.v4.STGroup;
|
||||
import org.stringtemplate.v4.StringRenderer;
|
||||
|
@ -39,10 +40,6 @@ import java.util.HashSet;
|
|||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sam Harwell
|
||||
*/
|
||||
public class JavaTarget extends Target {
|
||||
|
||||
protected static final String[] javaKeywords = {
|
||||
|
@ -63,7 +60,12 @@ public class JavaTarget extends Target {
|
|||
super(gen, "Java");
|
||||
}
|
||||
|
||||
public Set<String> getBadWords() {
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return Tool.VERSION; // Java and tool versions move in lock step
|
||||
}
|
||||
|
||||
public Set<String> getBadWords() {
|
||||
if (badWords.isEmpty()) {
|
||||
addBadWords();
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
package org.antlr.v4.codegen;
|
||||
|
||||
import org.antlr.v4.Tool;
|
||||
import org.antlr.v4.codegen.model.RuleFunction;
|
||||
import org.antlr.v4.codegen.model.SerializedATN;
|
||||
import org.antlr.v4.misc.Utils;
|
||||
|
@ -87,10 +88,22 @@ public abstract class Target {
|
|||
return language;
|
||||
}
|
||||
|
||||
public STGroup getTemplates() {
|
||||
if (templates == null) {
|
||||
templates = loadTemplates();
|
||||
}
|
||||
/** ANTLR tool should check output templates / target are compatible with tool code generation.
|
||||
* For now, a simple string match used on x.y of x.y.z scheme. We use a method to avoid mismatches
|
||||
* between a template called VERSION. This value is checked against Tool.VERSION during load of templates.
|
||||
*
|
||||
* This additional method forces all targets 4.3 and beyond to add this method.
|
||||
*/
|
||||
public abstract String getVersion();
|
||||
|
||||
public STGroup getTemplates() {
|
||||
if (templates == null) {
|
||||
String version = getVersion();
|
||||
if (version == null || !version.equals(Tool.VERSION)) {
|
||||
gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, language);
|
||||
}
|
||||
templates = loadTemplates();
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
|
|
@ -169,6 +169,12 @@ public enum ErrorType {
|
|||
* no mapping to template name for output model class '<em>class</em>'</p>
|
||||
*/
|
||||
NO_MODEL_TO_TEMPLATE_MAPPING(34, "no mapping to template name for output model class '<arg>'", ErrorSeverity.ERROR),
|
||||
/**
|
||||
* Compiler Error 35.
|
||||
*
|
||||
* <p>templates/target and tool aren't compatible</p>
|
||||
*/
|
||||
INCOMPATIBLE_TOOL_AND_TEMPLATES(35, "<arg3> code generation target requires ANTLR <arg2>; it can't be loaded by the current ANTLR <arg>", ErrorSeverity.ERROR),
|
||||
|
||||
/*
|
||||
* Grammar errors
|
||||
|
@ -694,7 +700,7 @@ public enum ErrorType {
|
|||
* <p>Some lexer commands require an argument.</p>
|
||||
*
|
||||
* <p>The following rule produces this error.</p>
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* X : 'foo' -> type(Foo); // ok
|
||||
* Y : 'foo' -> type; // error 150 (the type command requires an argument)
|
||||
|
|
Loading…
Reference in New Issue