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) ::= <<
|
Parser_(parser, funcs, atn, sempredFuncs, ctor, superClass) ::= <<
|
||||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||||
public class <parser.name> extends <superClass> {
|
public class <parser.name> extends <superClass> {
|
||||||
|
static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>"); }
|
||||||
|
|
||||||
protected static final DFA[] _decisionToDFA;
|
protected static final DFA[] _decisionToDFA;
|
||||||
protected static final PredictionContextCache _sharedContextCache =
|
protected static final PredictionContextCache _sharedContextCache =
|
||||||
new PredictionContextCache();
|
new PredictionContextCache();
|
||||||
|
@ -848,6 +850,7 @@ import org.antlr.v4.runtime.misc.*;
|
||||||
Lexer(lexer, atn, actionFuncs, sempredFuncs, superClass) ::= <<
|
Lexer(lexer, atn, actionFuncs, sempredFuncs, superClass) ::= <<
|
||||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||||
public class <lexer.name> extends <superClass> {
|
public class <lexer.name> extends <superClass> {
|
||||||
|
static { RuntimeMetaData.checkVersion("<lexerFile.ANTLRVersion>"); }
|
||||||
protected static final DFA[] _decisionToDFA;
|
protected static final DFA[] _decisionToDFA;
|
||||||
protected static final PredictionContextCache _sharedContextCache =
|
protected static final PredictionContextCache _sharedContextCache =
|
||||||
new PredictionContextCache();
|
new PredictionContextCache();
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class Tool {
|
||||||
public static final String VERSION;
|
public static final String VERSION;
|
||||||
static {
|
static {
|
||||||
String version = Tool.class.getPackage().getImplementationVersion();
|
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";
|
public static final String GRAMMAR_EXTENSION = ".g4";
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class CodeGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public STGroup getTemplates() {
|
public STGroup getTemplates() {
|
||||||
return getTarget().getTemplates();
|
return getTarget().getTemplates();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadLanguageTarget(String language) {
|
protected void loadLanguageTarget(String language) {
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
package org.antlr.v4.codegen;
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.v4.Tool;
|
||||||
import org.antlr.v4.tool.ast.GrammarAST;
|
import org.antlr.v4.tool.ast.GrammarAST;
|
||||||
import org.stringtemplate.v4.STGroup;
|
import org.stringtemplate.v4.STGroup;
|
||||||
import org.stringtemplate.v4.StringRenderer;
|
import org.stringtemplate.v4.StringRenderer;
|
||||||
|
@ -39,10 +40,6 @@ import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Sam Harwell
|
|
||||||
*/
|
|
||||||
public class JavaTarget extends Target {
|
public class JavaTarget extends Target {
|
||||||
|
|
||||||
protected static final String[] javaKeywords = {
|
protected static final String[] javaKeywords = {
|
||||||
|
@ -63,7 +60,12 @@ public class JavaTarget extends Target {
|
||||||
super(gen, "Java");
|
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()) {
|
if (badWords.isEmpty()) {
|
||||||
addBadWords();
|
addBadWords();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
package org.antlr.v4.codegen;
|
package org.antlr.v4.codegen;
|
||||||
|
|
||||||
|
import org.antlr.v4.Tool;
|
||||||
import org.antlr.v4.codegen.model.RuleFunction;
|
import org.antlr.v4.codegen.model.RuleFunction;
|
||||||
import org.antlr.v4.codegen.model.SerializedATN;
|
import org.antlr.v4.codegen.model.SerializedATN;
|
||||||
import org.antlr.v4.misc.Utils;
|
import org.antlr.v4.misc.Utils;
|
||||||
|
@ -87,10 +88,22 @@ public abstract class Target {
|
||||||
return language;
|
return language;
|
||||||
}
|
}
|
||||||
|
|
||||||
public STGroup getTemplates() {
|
/** ANTLR tool should check output templates / target are compatible with tool code generation.
|
||||||
if (templates == null) {
|
* For now, a simple string match used on x.y of x.y.z scheme. We use a method to avoid mismatches
|
||||||
templates = loadTemplates();
|
* 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;
|
return templates;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,6 +169,12 @@ public enum ErrorType {
|
||||||
* no mapping to template name for output model class '<em>class</em>'</p>
|
* 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),
|
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
|
* Grammar errors
|
||||||
|
|
Loading…
Reference in New Issue