From 84c978062c47eb2c3969028029f87ee6b401e686 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Tue, 21 Jun 2022 14:41:55 +0300 Subject: [PATCH 01/20] Implement fast parallel lock-free in-process testing for Java Signed-off-by: Ivan Kochurkin --- .../runtime/helpers/RuntimeTestLexer.java | 10 +++ .../runtime/helpers/RuntimeTestParser.java | 14 ++++ .../v4/test/runtime/helpers/Test.java.stg | 40 ++++++++- .../v4/test/runtime/templates/Java.test.stg | 40 ++++----- .../org/antlr/v4/test/runtime/FileUtils.java | 13 +++ .../antlr/v4/test/runtime/GeneratedFile.java | 16 ++++ .../org/antlr/v4/test/runtime/RunOptions.java | 4 +- .../antlr/v4/test/runtime/RuntimeRunner.java | 28 ++++--- .../antlr/v4/test/runtime/RuntimeTests.java | 17 +++- .../antlr/v4/test/runtime/cpp/CppRunner.java | 3 +- .../antlr/v4/test/runtime/go/GoRunner.java | 16 ++-- .../v4/test/runtime/java/JavaRunner.java | 83 +++++++++++++++---- .../test/runtime/javascript/NodeRunner.java | 21 ++--- .../test/runtime/states/GeneratedState.java | 5 +- .../v4/test/runtime/swift/SwiftRunner.java | 4 +- .../Java/src/org/antlr/v4/runtime/Parser.java | 13 ++- .../antlr/v4/test/tool/TestDollarParser.java | 6 +- .../antlr/v4/test/tool/TestLexerActions.java | 30 +++---- .../antlr/v4/test/tool/TestParserExec.java | 2 +- .../org/antlr/v4/test/tool/ToolTestUtils.java | 3 +- 20 files changed, 264 insertions(+), 104 deletions(-) create mode 100644 runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestLexer.java create mode 100644 runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestParser.java create mode 100644 runtime-testsuite/test/org/antlr/v4/test/runtime/GeneratedFile.java diff --git a/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestLexer.java b/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestLexer.java new file mode 100644 index 0000000000..d1cb77866f --- /dev/null +++ b/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestLexer.java @@ -0,0 +1,10 @@ +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; + +public abstract class RuntimeTestLexer extends Lexer { + protected java.io.PrintStream outStream = System.out; + + public RuntimeTestLexer(CharStream input) { super(input); } + + public void setOutStream(java.io.PrintStream outStream) { this.outStream = outStream; } +} diff --git a/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestParser.java b/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestParser.java new file mode 100644 index 0000000000..1ed38f4959 --- /dev/null +++ b/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/RuntimeTestParser.java @@ -0,0 +1,14 @@ +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.TokenStream; + +public abstract class RuntimeTestParser extends Parser { + protected java.io.PrintStream outStream = System.out; + + public RuntimeTestParser(TokenStream input) { + super(input); + } + + public void setOutStream(java.io.PrintStream outStream) { + this.outStream = outStream; + } +} diff --git a/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/Test.java.stg b/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/Test.java.stg index 56475792d7..76eae4e316 100644 --- a/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/Test.java.stg +++ b/runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers/Test.java.stg @@ -1,20 +1,34 @@ import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; import org.antlr.v4.runtime.atn.*; + +import java.io.IOException; +import java.io.PrintStream; import java.nio.file.Paths; import java.util.Arrays; public class Test { public static void main(String[] args) throws Exception { + recognize(args[0], System.out, System.err); + } + + public static void recognize(String inputFile, PrintStream outStream, PrintStream errorStream) throws IOException { + CustomStreamErrorListener errorListener = new CustomStreamErrorListener(errorStream); - CharStream input = CharStreams.fromPath(Paths.get(args[0])); + CharStream input = CharStreams.fromPath(Paths.get(inputFile)); lexer = new (input); + lexer.setOutStream(outStream); + lexer.removeErrorListeners(); + lexer.addErrorListener(errorListener); CommonTokenStream tokens = new CommonTokenStream(lexer); CommonTokenStream tokens = null; // It's required for compilation parser = new (tokens); + parser.setOutStream(outStream); + parser.removeErrorListeners(); + parser.addErrorListener(errorListener); parser.addErrorListener(new DiagnosticErrorListener()); @@ -25,18 +39,36 @@ public class Test { ParserRuleContext tree = parser.(); - System.out.println(Arrays.toString(profiler.getDecisionInfo())); + outStream.println(Arrays.toString(profiler.getDecisionInfo())); ParseTreeWalker.DEFAULT.walk(new TreeShapeListener(), tree); tokens.fill(); - for (Object t : tokens.getTokens()) System.out.println(t); + for (Object t : tokens.getTokens()) outStream.println(t); - System.out.print(lexer.getInterpreter().getDFA(Lexer.DEFAULT_MODE).toLexerString()); + outStream.print(lexer.getInterpreter().getDFA(Lexer.DEFAULT_MODE).toLexerString()); } + static class CustomStreamErrorListener extends BaseErrorListener { + private final PrintStream printStream; + + public CustomStreamErrorListener(PrintStream printStream){ + this.printStream = printStream; + } + + @Override + public void syntaxError(Recognizer\ recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) { + printStream.println("line " + line + ":" + charPositionInLine + " " + msg); + } + } + static class TreeShapeListener implements ParseTreeListener { @Override public void visitTerminal(TerminalNode node) { } diff --git a/runtime-testsuite/resources/org/antlr/v4/test/runtime/templates/Java.test.stg b/runtime-testsuite/resources/org/antlr/v4/test/runtime/templates/Java.test.stg index c1a5bf606f..b1aa46db10 100644 --- a/runtime-testsuite/resources/org/antlr/v4/test/runtime/templates/Java.test.stg +++ b/runtime-testsuite/resources/org/antlr/v4/test/runtime/templates/Java.test.stg @@ -1,6 +1,6 @@ -writeln(s) ::= <);>> -write(s) ::= <);>> -writeList(s) ::= <);>> +writeln(s) ::= <);>> +write(s) ::= <);>> +writeList(s) ::= <);>> False() ::= "false" @@ -44,7 +44,7 @@ ModMemberEquals(n,m,v) ::= <%this. % == %> ModMemberNotEquals(n,m,v) ::= <%this. % != %> -DumpDFA() ::= "this.dumpDFA();" +DumpDFA() ::= "this.dumpDFA(outStream);" Pass() ::= "" @@ -186,9 +186,9 @@ protected static class PositionAdjustingLexerATNSimulator extends LexerATNSimula BasicListener(X) ::= << @parser::members { -public static class LeafListener extends TBaseListener { +public class LeafListener extends TBaseListener { public void visitTerminal(TerminalNode node) { - System.out.println(node.getSymbol().getText()); + outStream.println(node.getSymbol().getText()); } } } @@ -214,13 +214,13 @@ public static class MyRuleNode extends ParserRuleContext { TokenGetterListener(X) ::= << @parser::members { -public static class LeafListener extends TBaseListener { +public class LeafListener extends TBaseListener { public void exitA(TParser.AContext ctx) { if (ctx.getChildCount()==2) - System.out.printf("%s %s %s\n",ctx.INT(0).getSymbol().getText(), + outStream.printf("%s %s %s\n",ctx.INT(0).getSymbol().getText(), ctx.INT(1).getSymbol().getText(),ctx.INT()); else - System.out.println(ctx.ID().getSymbol()); + outStream.println(ctx.ID().getSymbol()); } } } @@ -228,13 +228,13 @@ public static class LeafListener extends TBaseListener { RuleGetterListener(X) ::= << @parser::members { -public static class LeafListener extends TBaseListener { +public class LeafListener extends TBaseListener { public void exitA(TParser.AContext ctx) { if (ctx.getChildCount()==2) { - System.out.printf("%s %s %s\n",ctx.b(0).start.getText(), + outStream.printf("%s %s %s\n",ctx.b(0).start.getText(), ctx.b(1).start.getText(),ctx.b().get(0).start.getText()); } else - System.out.println(ctx.b(0).start.getText()); + outStream.println(ctx.b(0).start.getText()); } } } @@ -243,13 +243,13 @@ public static class LeafListener extends TBaseListener { LRListener(X) ::= << @parser::members { -public static class LeafListener extends TBaseListener { +public class LeafListener extends TBaseListener { public void exitE(TParser.EContext ctx) { if (ctx.getChildCount()==3) { - System.out.printf("%s %s %s\n",ctx.e(0).start.getText(), + outStream.printf("%s %s %s\n",ctx.e(0).start.getText(), ctx.e(1).start.getText(), ctx.e().get(0).start.getText()); } else - System.out.println(ctx.INT().getSymbol().getText()); + outStream.println(ctx.INT().getSymbol().getText()); } } } @@ -257,12 +257,12 @@ public static class LeafListener extends TBaseListener { LRWithLabelsListener(X) ::= << @parser::members { -public static class LeafListener extends TBaseListener { +public class LeafListener extends TBaseListener { public void exitCall(TParser.CallContext ctx) { - System.out.printf("%s %s\n",ctx.e().start.getText(),ctx.eList()); + outStream.printf("%s %s\n",ctx.e().start.getText(),ctx.eList()); } public void exitInt(TParser.IntContext ctx) { - System.out.println(ctx.INT().getSymbol().getText()); + outStream.println(ctx.INT().getSymbol().getText()); } } } @@ -277,13 +277,13 @@ void foo() { >> Declare_foo() ::= << - public void foo() {System.out.println("foo");} + public void foo() {outStream.println("foo");} >> Invoke_foo() ::= "foo();" Declare_pred() ::= < options = new ArrayList<>(); + if (runOptions.useVisitor) { + options.add("-visitor"); + } + if (runOptions.superClass != null && runOptions.superClass.length() > 0) { + options.add("-DsuperClass=" + runOptions.superClass); + } ErrorQueue errorQueue = Generator.antlrOnString(getTempDirPath(), getLanguage(), - runOptions.grammarFileName, runOptions.grammarStr, false, options); + runOptions.grammarFileName, runOptions.grammarStr, false, options.toArray(new String[0])); - List generatedFiles = getGeneratedFiles(runOptions); + List generatedFiles = getGeneratedFiles(runOptions); GeneratedState generatedState = new GeneratedState(errorQueue, generatedFiles, null); if (generatedState.containsErrors() || runOptions.endStage == Stage.Generate) { @@ -152,28 +158,28 @@ public State run(RunOptions runOptions) { return executedState; } - protected List getGeneratedFiles(RunOptions runOptions) { - List files = new ArrayList<>(); + protected List getGeneratedFiles(RunOptions runOptions) { + List files = new ArrayList<>(); String extensionWithDot = "." + getExtension(); String fileGrammarName = grammarNameToFileName(runOptions.grammarName); boolean isCombinedGrammarOrGo = runOptions.lexerName != null && runOptions.parserName != null || getLanguage().equals("Go"); if (runOptions.lexerName != null) { - files.add(fileGrammarName + (isCombinedGrammarOrGo ? getLexerSuffix() : "") + extensionWithDot); + files.add(new GeneratedFile(fileGrammarName + (isCombinedGrammarOrGo ? getLexerSuffix() : "") + extensionWithDot, false)); } if (runOptions.parserName != null) { - files.add(fileGrammarName + (isCombinedGrammarOrGo ? getParserSuffix() : "") + extensionWithDot); + files.add(new GeneratedFile(fileGrammarName + (isCombinedGrammarOrGo ? getParserSuffix() : "") + extensionWithDot, true)); if (runOptions.useListener) { - files.add(fileGrammarName + getListenerSuffix() + extensionWithDot); + files.add(new GeneratedFile(fileGrammarName + getListenerSuffix() + extensionWithDot, true)); String baseListenerSuffix = getBaseListenerSuffix(); if (baseListenerSuffix != null) { - files.add(fileGrammarName + baseListenerSuffix + extensionWithDot); + files.add(new GeneratedFile(fileGrammarName + baseListenerSuffix + extensionWithDot, true)); } } if (runOptions.useVisitor) { - files.add(fileGrammarName + getVisitorSuffix() + extensionWithDot); + files.add(new GeneratedFile(fileGrammarName + getVisitorSuffix() + extensionWithDot, true)); String baseVisitorSuffix = getBaseVisitorSuffix(); if (baseVisitorSuffix != null) { - files.add(fileGrammarName + baseVisitorSuffix + extensionWithDot); + files.add(new GeneratedFile(fileGrammarName + baseVisitorSuffix + extensionWithDot, true)); } } } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java index 996316ebc3..701b60d9b2 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java @@ -7,6 +7,7 @@ package org.antlr.v4.test.runtime; import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.test.runtime.java.JavaRunner; import org.antlr.v4.test.runtime.java.JavaRuntimeTests; import org.antlr.v4.test.runtime.states.ExecutedState; import org.antlr.v4.test.runtime.states.State; @@ -146,15 +147,28 @@ private static void test(RuntimeTestDescriptor descriptor, RuntimeRunner runner) String lexerName, parserName; boolean useListenerOrVisitor; + String superClass; if (descriptor.testType == GrammarType.Parser || descriptor.testType == GrammarType.CompositeParser) { lexerName = grammarName + "Lexer"; parserName = grammarName + "Parser"; useListenerOrVisitor = true; + if (targetName.equals("Java")) { + superClass = JavaRunner.runtimeTestParserName; + } + else { + superClass = null; + } } else { lexerName = grammarName; parserName = null; useListenerOrVisitor = false; + if (targetName.equals("Java")) { + superClass = JavaRunner.runtimeTestLexerName; + } + else { + superClass = null; + } } RunOptions runOptions = new RunOptions(grammarName + ".g4", @@ -170,7 +184,8 @@ private static void test(RuntimeTestDescriptor descriptor, RuntimeRunner runner) descriptor.showDFA, Stage.Execute, false, - targetName + targetName, + superClass ); State result = runner.run(runOptions); diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/cpp/CppRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/cpp/CppRunner.java index 72d7c0d6e5..344064fa55 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/cpp/CppRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/cpp/CppRunner.java @@ -16,6 +16,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static org.antlr.v4.test.runtime.FileUtils.writeFile; import static org.antlr.v4.test.runtime.RuntimeTestUtils.getOS; @@ -167,7 +168,7 @@ protected CompiledState compile(RunOptions runOptions, GeneratedState generatedS buildCommand.add("-o"); buildCommand.add(getTestFileName() + ".out"); buildCommand.add(getTestFileWithExt()); - buildCommand.addAll(generatedState.generatedFiles); + buildCommand.addAll(generatedState.generatedFiles.stream().map(file -> file.name).collect(Collectors.toList())); } runCommand(buildCommand.toArray(new String[0]), getTempDirPath(), "build test c++ binary"); diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/GoRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/GoRunner.java index a936ccd98f..c5408680b2 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/GoRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/GoRunner.java @@ -16,6 +16,8 @@ import java.nio.file.Paths; import java.util.*; +import static org.antlr.v4.test.runtime.FileUtils.replaceInFile; + public class GoRunner extends RuntimeRunner { @Override public String getLanguage() { @@ -116,20 +118,18 @@ public boolean accept(File dir, String name) { @Override protected CompiledState compile(RunOptions runOptions, GeneratedState generatedState) { - List files = generatedState.generatedFiles; + List generatedFiles = generatedState.generatedFiles; String tempDirPath = getTempDirPath(); File generatedFir = new File(tempDirPath, "parser"); if (!generatedFir.mkdir()) { return new CompiledState(generatedState, new Exception("can't make dir " + generatedFir)); } - for (String file : files) { + for (GeneratedFile generatedFile : generatedFiles) { try { - Path originalFile = Paths.get(tempDirPath, file); - String content = new String(Files.readAllBytes(originalFile), StandardCharsets.UTF_8); - String newContent = content.replaceAll(GoRuntimeImportPath, antlrTestPackageName); - try (PrintWriter out = new PrintWriter(Paths.get(tempDirPath, "parser", file).toString())) { - out.println(newContent); - } + Path originalFile = Paths.get(tempDirPath, generatedFile.name); + replaceInFile(originalFile, + Paths.get(tempDirPath, "parser", generatedFile.name), + GoRuntimeImportPath, antlrTestPackageName); originalFile.toFile().delete(); } catch (IOException e) { return new CompiledState(generatedState, e); diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/java/JavaRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/java/JavaRunner.java index 50230dc835..01b143934d 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/java/JavaRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/java/JavaRunner.java @@ -9,8 +9,7 @@ import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.misc.Pair; import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.test.runtime.RuntimeRunner; -import org.antlr.v4.test.runtime.RunOptions; +import org.antlr.v4.test.runtime.*; import org.antlr.v4.test.runtime.states.*; import javax.tools.JavaCompiler; @@ -22,11 +21,14 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static org.antlr.v4.test.runtime.FileUtils.replaceInFile; import static org.antlr.v4.test.runtime.RuntimeTestUtils.PathSeparator; +import static org.antlr.v4.test.runtime.RuntimeTestUtils.getTextFromResource; public class JavaRunner extends RuntimeRunner { @Override @@ -36,8 +38,18 @@ public String getLanguage() { public static final String classPath = System.getProperty("java.class.path"); + public static final String runtimeTestLexerName = "RuntimeTestLexer"; + public static final String runtimeTestParserName = "RuntimeTestParser"; + + private final static String testLexerContent; + private final static String testParserContent; private static JavaCompiler compiler; + static { + testLexerContent = getTextFromResource("org/antlr/v4/test/runtime/helpers/" + runtimeTestLexerName + ".java"); + testParserContent = getTextFromResource("org/antlr/v4/test/runtime/helpers/" + runtimeTestParserName + ".java"); + } + public JavaRunner(Path tempDir, boolean saveTestDir) { super(tempDir, saveTestDir); } @@ -53,6 +65,27 @@ protected void initRuntime() { @Override protected JavaCompiledState compile(RunOptions runOptions, GeneratedState generatedState) { + String tempTestDir = getTempDirPath(); + + List generatedFiles = generatedState.generatedFiles; + GeneratedFile firstFile = generatedFiles.get(0); + + if (!firstFile.isParser) { + FileUtils.writeFile(tempTestDir, runtimeTestLexerName + ".java", testLexerContent); + try { + // superClass for combined grammar generates the same extends base class for Lexer and Parser + // So, for lexer it should be replaced on correct base lexer class + replaceInFile(Paths.get(getTempDirPath(), firstFile.name), + "extends " + runtimeTestParserName + " {", + "extends " + runtimeTestLexerName + " {"); + } catch (IOException e) { + return new JavaCompiledState(generatedState, null, null, null, e); + } + } + if (generatedFiles.stream().anyMatch(file -> file.isParser)) { + FileUtils.writeFile(tempTestDir, runtimeTestParserName + ".java", testParserContent); + } + ClassLoader loader = null; Class lexer = null; Class parser = null; @@ -63,7 +96,6 @@ protected JavaCompiledState compile(RunOptions runOptions, GeneratedState genera ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - String tempTestDir = getTempDirPath(); List files = new ArrayList<>(); File f = new File(tempTestDir, getTestFileWithExt()); files.add(f); @@ -72,7 +104,7 @@ protected JavaCompiledState compile(RunOptions runOptions, GeneratedState genera Iterable compileOptions = Arrays.asList("-g", "-source", "1.8", "-target", "1.8", "-implicit:class", "-Xlint:-options", "-d", - getTempDirPath(), "-cp", getTempDirPath() + PathSeparator + classPath); + tempTestDir, "-cp", tempTestDir + PathSeparator + classPath); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, compileOptions, null, @@ -101,7 +133,7 @@ protected ExecutedState execute(RunOptions runOptions, CompiledState compiledSta if (runOptions.returnObject) { result = execWithObject(runOptions, javaCompiledState); } else { - result = super.execute(runOptions, javaCompiledState); + result = execCommon(javaCompiledState); } return result; } @@ -130,15 +162,36 @@ private JavaExecutedState execWithObject(RunOptions runOptions, JavaCompiledStat return new JavaExecutedState(javaCompiledState, null, null, parseTree, exception); } - @Override - protected String[] getExtraRunArgs() { - return new String[] { - "-classpath", - getTempDirPath() + PathSeparator + classPath, - "-Dfile.encoding=UTF-8", - }; + private ExecutedState execCommon(JavaCompiledState compiledState) { + Exception exception = null; + String output = null; + String errors = null; + try { + final Class mainClass = compiledState.loader.loadClass(getTestFileName()); + final Method recognizeMethod = mainClass.getDeclaredMethod("recognize", String.class, + PrintStream.class, PrintStream.class); + + PipedInputStream stdoutIn = new PipedInputStream(); + PipedInputStream stderrIn = new PipedInputStream(); + PipedOutputStream stdoutOut = new PipedOutputStream(stdoutIn); + PipedOutputStream stderrOut = new PipedOutputStream(stderrIn); + StreamReader stdoutReader = new StreamReader(stdoutIn); + StreamReader stderrReader = new StreamReader(stderrIn); + stdoutReader.start(); + stderrReader.start(); + + recognizeMethod.invoke(null, new File(getTempDirPath(), "input").getAbsolutePath(), + new PrintStream(stdoutOut), new PrintStream(stderrOut)); + + stdoutOut.close(); + stderrOut.close(); + stdoutReader.join(); + stderrReader.join(); + output = stdoutReader.toString(); + errors = stderrReader.toString(); + } catch (Exception ex) { + exception = ex; + } + return new JavaExecutedState(compiledState, output, errors, null, exception); } - - @Override - protected String getExecFileName() { return getTestFileName(); } } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/NodeRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/NodeRunner.java index 53e4f89146..69b26d17a6 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/NodeRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/NodeRunner.java @@ -5,18 +5,12 @@ */ package org.antlr.v4.test.runtime.javascript; -import org.antlr.v4.test.runtime.RuntimeRunner; -import org.antlr.v4.test.runtime.RunOptions; -import org.antlr.v4.test.runtime.RuntimeTestUtils; +import org.antlr.v4.test.runtime.*; import org.antlr.v4.test.runtime.states.CompiledState; import org.antlr.v4.test.runtime.states.GeneratedState; import org.stringtemplate.v4.ST; import java.io.IOException; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; @@ -46,15 +40,12 @@ public String getLanguage() { @Override protected CompiledState compile(RunOptions runOptions, GeneratedState generatedState) { - List generatedFiles = generatedState.generatedFiles; - for (String file : generatedFiles) { + List generatedFiles = generatedState.generatedFiles; + for (GeneratedFile generatedFile : generatedFiles) { try { - Path path = Paths.get(getTempDirPath(), file); - String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); - String newContent = content.replaceAll("import antlr4 from 'antlr4';", newImportAntlrString); - try (PrintWriter out = new PrintWriter(path.toString())) { - out.println(newContent); - } + FileUtils.replaceInFile(Paths.get(getTempDirPath(), generatedFile.name), + "import antlr4 from 'antlr4';", + newImportAntlrString); } catch (IOException e) { return new CompiledState(generatedState, e); } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java index c533632bd3..3a70f3dc95 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java @@ -7,6 +7,7 @@ package org.antlr.v4.test.runtime.states; import org.antlr.v4.test.runtime.ErrorQueue; +import org.antlr.v4.test.runtime.GeneratedFile; import org.antlr.v4.test.runtime.Stage; import org.antlr.v4.tool.ANTLRMessage; @@ -21,7 +22,7 @@ public Stage getStage() { } public final ErrorQueue errorQueue; - public final List generatedFiles; + public final List generatedFiles; @Override public boolean containsErrors() { @@ -41,7 +42,7 @@ public String getErrorMessage() { return null; } - public GeneratedState(ErrorQueue errorQueue, List generatedFiles, Exception exception) { + public GeneratedState(ErrorQueue errorQueue, List generatedFiles, Exception exception) { super(null, exception); this.errorQueue = errorQueue; this.generatedFiles = generatedFiles; diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java index d4badff5c7..9017054932 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java @@ -57,8 +57,8 @@ protected CompiledState compile(RunOptions runOptions, GeneratedState generatedS runCommand(initPackageArgs, projectDir); - for (String generatedFile : generatedState.generatedFiles) { - moveFile(testDir, destDir, generatedFile); + for (GeneratedFile generatedFile : generatedState.generatedFiles) { + moveFile(testDir, destDir, generatedFile.name); } moveFile(testDir, destDir, getTestFileWithExt()); diff --git a/runtime/Java/src/org/antlr/v4/runtime/Parser.java b/runtime/Java/src/org/antlr/v4/runtime/Parser.java index 5b52fa786c..34ab288108 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/Parser.java +++ b/runtime/Java/src/org/antlr/v4/runtime/Parser.java @@ -27,6 +27,7 @@ import org.antlr.v4.runtime.tree.pattern.ParseTreePattern; import org.antlr.v4.runtime.tree.pattern.ParseTreePatternMatcher; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -873,16 +874,20 @@ public List getDFAStrings() { } } - /** For debugging and other purposes. */ public void dumpDFA() { + dumpDFA(System.out); + } + + /** For debugging and other purposes. */ + public void dumpDFA(PrintStream dumpStream) { synchronized (_interp.decisionToDFA) { boolean seenOne = false; for (int d = 0; d < _interp.decisionToDFA.length; d++) { DFA dfa = _interp.decisionToDFA[d]; if ( !dfa.states.isEmpty() ) { - if ( seenOne ) System.out.println(); - System.out.println("Decision " + dfa.decision + ":"); - System.out.print(dfa.toString(getVocabulary())); + if ( seenOne ) dumpStream.println(); + dumpStream.println("Decision " + dfa.decision + ":"); + dumpStream.print(dfa.toString(getVocabulary())); seenOne = true; } } diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestDollarParser.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestDollarParser.java index ca9307ea87..816a9fabd4 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestDollarParser.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestDollarParser.java @@ -17,9 +17,9 @@ public class TestDollarParser { @Test public void testSimpleCall() { String grammar = "grammar T;\n" + - "a : ID { System.out.println( new java.io.File($parser.getSourceName()).getAbsolutePath() ); }\n" + - " ;\n" + - "ID : 'a'..'z'+ ;\n"; + "a : ID { outStream.println(new java.io.File($parser.getSourceName()).getAbsolutePath()); }\n" + + " ;\n" + + "ID : 'a'..'z'+ ;\n"; ExecutedState executedState = execParser("T.g4", grammar, "TParser", "TLexer", "a", "x", true); assertTrue(executedState.output.contains("input")); assertEquals("", executedState.errors); diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestLexerActions.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestLexerActions.java index 5772117887..8e9944231e 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestLexerActions.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestLexerActions.java @@ -18,7 +18,7 @@ public class TestLexerActions { @Test public void testActionExecutedInDFA() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+ + "I : '0'..'9'+ {outStream.println(\"I\");} ;\n"+ "WS : (' '|'\\n') -> skip ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "34 34"); String expecting = @@ -33,7 +33,7 @@ public class TestLexerActions { @Test public void testActionEvalsAtCorrectIndex() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : [0-9] {System.out.println(\"2nd char: \"+(char)_input.LA(1));} [0-9]+ ;\n"+ + "I : [0-9] {outStream.println(\"2nd char: \"+(char)_input.LA(1));} [0-9]+ ;\n"+ "WS : (' '|'\\n') -> skip ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "123 45"); String expecting = @@ -65,8 +65,8 @@ public class TestLexerActions { " return lexer._input.getText (new Interval (start_index, stop_index));\n" + " }\n" + "\n" + - " public void start () { start_index = lexer._input.index (); System.out.println (\"Start:\" + start_index);}\n" + - " public void stop () { stop_index = lexer._input.index (); System.out.println (\"Stop:\" + stop_index);}\n" + + " public void start () { start_index = lexer._input.index (); outStream.println (\"Start:\" + start_index);}\n" + + " public void stop () { stop_index = lexer._input.index (); outStream.println (\"Stop:\" + stop_index);}\n" + "\n" + " private int start_index = 0;\n" + " private int stop_index = 0;\n" + @@ -76,7 +76,7 @@ public class TestLexerActions { "Marker m_name = new Marker (this);\n" + "}\n" + "\n" + - "HELLO: 'hello' WS { m_name.start (); } NAME { m_name.stop (); } '\\n' { System.out.println (\"Hello: \" + m_name.getText ()); };\n" + + "HELLO: 'hello' WS { m_name.start (); } NAME { m_name.stop (); } '\\n' { outStream.println (\"Hello: \" + m_name.getText ()); };\n" + "NAME: ('a'..'z' | 'A'..'Z')+ ('\\n')?;\n" + "\n" + "fragment WS: [ \\r\\t\\n]+ ;\n"; @@ -94,7 +94,7 @@ public class TestLexerActions { @Test public void test2ActionsIn1Rule() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : [0-9] {System.out.println(\"x\");} [0-9]+ {System.out.println(\"y\");} ;\n"+ + "I : [0-9] {outStream.println(\"x\");} [0-9]+ {outStream.println(\"y\");} ;\n"+ "WS : (' '|'\\n') -> skip ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "123 45"); String expecting = @@ -111,10 +111,10 @@ public class TestLexerActions { @Test public void testAltActionsIn1Rule() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : ( [0-9]+ {System.out.print(\"int\");}\n" + - " | [a-z]+ {System.out.print(\"id\");}\n" + + "I : ( [0-9]+ {outStream.print(\"int\");}\n" + + " | [a-z]+ {outStream.print(\"id\");}\n" + " )\n" + - " {System.out.println(\" last\");}\n" + + " {outStream.println(\" last\");}\n" + " ;\n"+ "WS : (' '|'\\n') -> skip ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "123 ab"); @@ -130,7 +130,7 @@ public class TestLexerActions { @Test public void testActionPlusCommand() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : '0'..'9'+ {System.out.println(\"I\");} -> skip ;\n"+ + "I : '0'..'9'+ {outStream.println(\"I\");} -> skip ;\n"+ "WS : (' '|'\\n') -> skip ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "34 34"); String expecting = @@ -145,7 +145,7 @@ public class TestLexerActions { @Test public void testSkipCommand() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+ + "I : '0'..'9'+ {outStream.println(\"I\");} ;\n"+ "WS : (' '|'\\n') -> skip ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "34 34"); String expecting = @@ -160,7 +160,7 @@ public class TestLexerActions { @Test public void testMoreCommand() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+ + "I : '0'..'9'+ {outStream.println(\"I\");} ;\n"+ "WS : '#' -> more ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "34#10"); String expecting = @@ -175,7 +175,7 @@ public class TestLexerActions { @Test public void testTypeCommand() throws Exception { String grammar = "lexer grammar L;\n"+ - "I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+ + "I : '0'..'9'+ {outStream.println(\"I\");} ;\n"+ "HASH : '#' -> type(HASH) ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "34#"); String expecting = @@ -188,8 +188,8 @@ public class TestLexerActions { @Test public void testCombinedCommand() throws Exception { String grammar = - "lexer grammar L;\n"+ - "I : '0'..'9'+ {System.out.println(\"I\");} ;\n"+ + "lexer grammar L;\n" + + "I : '0'..'9'+ {outStream.println(\"I\");} ;\n"+ "HASH : '#' -> type(100), skip, more ;"; ExecutedState executedState = execLexer("L.g4", grammar, "L", "34#11"); String expecting = diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestParserExec.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestParserExec.java index ca8d75d62b..9522a302d7 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestParserExec.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestParserExec.java @@ -138,7 +138,7 @@ public class TestParserExec { "\n" + "file : group+ EOF; \n" + "\n" + - "group: INT sequence {System.out.println($sequence.values.size());} ; \n" + + "group: INT sequence {outStream.println($sequence.values.size());} ; \n" + "\n" + "sequence returns [List values = new ArrayList()] \n" + " locals[List localValues = new ArrayList()]\n" + diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/ToolTestUtils.java b/tool-testsuite/test/org/antlr/v4/test/tool/ToolTestUtils.java index 8c819f7fe1..b68286ae35 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/ToolTestUtils.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/ToolTestUtils.java @@ -89,7 +89,8 @@ public static RunOptions createOptionsForJavaToolTests( Stage endStage, boolean returnObject ) { return new RunOptions(grammarFileName, grammarStr, parserName, lexerName, useListener, useVisitor, startRuleName, - input, profile, showDiagnosticErrors, false, endStage, returnObject, "Java"); + input, profile, showDiagnosticErrors, false, endStage, returnObject, "Java", + JavaRunner.runtimeTestParserName); } public static void testErrors(String[] pairs, boolean printTree) { From e942e1ab98c957afa5ccb0a00f120890f4d839e4 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 17 Jun 2022 22:05:51 +0300 Subject: [PATCH 02/20] Add final modifier to all static fields where it's possible Signed-off-by: Ivan Kochurkin --- .../org/antlr/v4/test/runtime/CustomDescriptors.java | 2 +- .../src/org/antlr/v4/runtime/misc/IntegerList.java | 2 +- .../Java/src/org/antlr/v4/runtime/misc/Interval.java | 2 +- .../org/antlr/v4/runtime/tree/xpath/XPathLexer.java | 2 +- .../org/antlr/v4/test/tool/TestActionSplitter.java | 2 +- .../antlr/v4/test/tool/TestBasicSemanticErrors.java | 2 +- .../test/org/antlr/v4/test/tool/TestPerformance.java | 2 +- .../test/org/antlr/v4/test/tool/TestScopeParsing.java | 2 +- .../test/org/antlr/v4/test/tool/TestSymbolIssues.java | 10 +++++----- .../org/antlr/v4/test/tool/TestToolSyntaxErrors.java | 2 +- tool/src/org/antlr/v4/Tool.java | 2 +- tool/src/org/antlr/v4/misc/CharSupport.java | 4 ++-- .../org/antlr/v4/semantics/BasicSemanticChecks.java | 2 +- tool/src/org/antlr/v4/tool/DOTGenerator.java | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java index 9be4d4be04..92dcb92585 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java @@ -9,7 +9,7 @@ import java.util.*; public class CustomDescriptors { - public static HashMap descriptors; + public final static HashMap descriptors; static { descriptors = new HashMap<>(); diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/IntegerList.java b/runtime/Java/src/org/antlr/v4/runtime/misc/IntegerList.java index d6c700d8f6..9381b7c997 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/IntegerList.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/IntegerList.java @@ -17,7 +17,7 @@ */ public class IntegerList { - private static int[] EMPTY_DATA = new int[0]; + private final static int[] EMPTY_DATA = new int[0]; private static final int INITIAL_SIZE = 4; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java b/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java index ab3f46ba46..1dd949442f 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java @@ -11,7 +11,7 @@ public class Interval { public static final Interval INVALID = new Interval(-1,-2); - static Interval[] cache = new Interval[INTERVAL_POOL_MAX_VALUE+1]; + static final Interval[] cache = new Interval[INTERVAL_POOL_MAX_VALUE+1]; public int a; public int b; diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/xpath/XPathLexer.java b/runtime/Java/src/org/antlr/v4/runtime/tree/xpath/XPathLexer.java index 4d120a4cca..5178b3e5b4 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/xpath/XPathLexer.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/xpath/XPathLexer.java @@ -20,7 +20,7 @@ public class XPathLexer extends Lexer { public static final int TOKEN_REF=1, RULE_REF=2, ANYWHERE=3, ROOT=4, WILDCARD=5, BANG=6, ID=7, STRING=8; - public static String[] modeNames = { + public final static String[] modeNames = { "DEFAULT_MODE" }; diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestActionSplitter.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestActionSplitter.java index b828a4e1ba..82908eb347 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestActionSplitter.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestActionSplitter.java @@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TestActionSplitter { - static String[] exprs = { + final static String[] exprs = { "foo", "['foo'<" + ActionSplitter.TEXT + ">]", "$x", "['$x'<" + ActionSplitter.ATTR + ">]", "\\$x", "['\\$x'<" + ActionSplitter.TEXT + ">]", diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestBasicSemanticErrors.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestBasicSemanticErrors.java index 5536065152..4a41630baf 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestBasicSemanticErrors.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestBasicSemanticErrors.java @@ -13,7 +13,7 @@ import static org.antlr.v4.test.tool.ToolTestUtils.testErrors; public class TestBasicSemanticErrors { - static String[] U = { + final static String[] U = { // INPUT "parser grammar U;\n" + "options { foo=bar; k=3;}\n" + diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestPerformance.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestPerformance.java index ac3a463a86..b56216389f 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestPerformance.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestPerformance.java @@ -1543,7 +1543,7 @@ protected ATNConfigSet computeReachSet(ATNConfigSet closure, int t, boolean full } private static class DescriptiveErrorListener extends BaseErrorListener { - public static DescriptiveErrorListener INSTANCE = new DescriptiveErrorListener(); + public final static DescriptiveErrorListener INSTANCE = new DescriptiveErrorListener(); @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java index 775edb0a93..75584b545a 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestScopeParsing.java @@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TestScopeParsing { - static String[] argPairs = { + final static String[] argPairs = { "", "", " ", "", "int i", "i:int", diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java index 551c584ad3..c1efd0ce85 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestSymbolIssues.java @@ -16,7 +16,7 @@ /** */ public class TestSymbolIssues { - static String[] A = { + final static String[] A = { // INPUT "grammar A;\n" + "options { opt='sss'; k=3; }\n" + @@ -44,7 +44,7 @@ public class TestSymbolIssues { "error(" + ErrorType.MISSING_RULE_ARGS.code + "): A.g4:10:31: missing argument(s) on rule reference: a\n" }; - static String[] B = { + final static String[] B = { // INPUT "parser grammar B;\n" + "tokens { ID, FOO, X, Y }\n" + @@ -62,7 +62,7 @@ public class TestSymbolIssues { "error(" + ErrorType.IMPLICIT_STRING_DEFINITION.code + "): B.g4:4:20: cannot create implicit token for string literal in non-combined grammar: '.'\n" }; - static String[] D = { + final static String[] D = { // INPUT "parser grammar D;\n" + "tokens{ID}\n" + @@ -79,7 +79,7 @@ public class TestSymbolIssues { "error(" + ErrorType.RETVAL_CONFLICTS_WITH_ARG.code + "): D.g4:6:22: return value i conflicts with parameter with same name\n" }; - static String[] E = { + final static String[] E = { // INPUT "grammar E;\n" + "tokens {\n" + @@ -93,7 +93,7 @@ public class TestSymbolIssues { "warning(" + ErrorType.TOKEN_NAME_REASSIGNMENT.code + "): E.g4:3:4: token name A is already defined\n" }; - static String[] F = { + final static String[] F = { // INPUT "lexer grammar F;\n" + "A: 'a';\n" + diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestToolSyntaxErrors.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestToolSyntaxErrors.java index 8568d5b041..a501a023b5 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestToolSyntaxErrors.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestToolSyntaxErrors.java @@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; public class TestToolSyntaxErrors { - static String[] A = { + final static String[] A = { // INPUT "grammar A;\n" + "", diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java index 0b13ea23c4..951f0ef6bb 100644 --- a/tool/src/org/antlr/v4/Tool.java +++ b/tool/src/org/antlr/v4/Tool.java @@ -113,7 +113,7 @@ public Option(String fieldName, String name, OptionArgType argType, String descr public boolean longMessages = false; public boolean exact_output_dir = false; - public static Option[] optionDefs = { + public final static Option[] optionDefs = { new Option("outputDirectory", "-o", OptionArgType.STRING, "specify output directory where all output is generated"), new Option("libDirectory", "-lib", OptionArgType.STRING, "specify location of grammars, tokens files"), new Option("generate_ATN_dot", "-atn", "generate rule augmented transition network diagrams"), diff --git a/tool/src/org/antlr/v4/misc/CharSupport.java b/tool/src/org/antlr/v4/misc/CharSupport.java index f4113fa7b4..0892af9ca0 100644 --- a/tool/src/org/antlr/v4/misc/CharSupport.java +++ b/tool/src/org/antlr/v4/misc/CharSupport.java @@ -17,11 +17,11 @@ public class CharSupport { /** When converting ANTLR char and string literals, here is the * value set of escape chars. */ - public static int[] ANTLRLiteralEscapedCharValue = new int[255]; + public final static int[] ANTLRLiteralEscapedCharValue = new int[255]; /** Given a char, we need to be able to show as an ANTLR literal. */ - public static String[] ANTLRLiteralCharValueEscape = new String[255]; + public final static String[] ANTLRLiteralCharValueEscape = new String[255]; static { ANTLRLiteralEscapedCharValue['n'] = '\n'; diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java index 604233f07a..b0987088c2 100644 --- a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java +++ b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java @@ -62,7 +62,7 @@ public class BasicSemanticChecks extends GrammarTreeVisitor { * validDelegations.get(LEXER) gives list of the kinds of delegators * that can import lexers. */ - public static MultiMap validImportTypes = + public final static MultiMap validImportTypes = new MultiMap() { { map(ANTLRParser.LEXER, ANTLRParser.LEXER); diff --git a/tool/src/org/antlr/v4/tool/DOTGenerator.java b/tool/src/org/antlr/v4/tool/DOTGenerator.java index 062f2da1dc..cb077d1a32 100644 --- a/tool/src/org/antlr/v4/tool/DOTGenerator.java +++ b/tool/src/org/antlr/v4/tool/DOTGenerator.java @@ -48,7 +48,7 @@ public class DOTGenerator { protected String rankdir="LR"; /** Library of output templates; use {@code } format. */ - public static STGroup stlib = new STGroupFile("org/antlr/v4/tool/templates/dot/graphs.stg"); + public final static STGroup stlib = new STGroupFile("org/antlr/v4/tool/templates/dot/graphs.stg"); protected Grammar grammar; From 36b885ca966bb3a44a627baaf18a2f2654be5718 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 17 Jun 2022 22:07:53 +0300 Subject: [PATCH 03/20] Remove doubtful and not used static fields Signed-off-by: Ivan Kochurkin --- runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java | 5 ----- .../test/org/antlr/v4/test/tool/TestATNParserPrediction.java | 1 - tool/src/org/antlr/v4/Tool.java | 5 ----- tool/src/org/antlr/v4/misc/Utils.java | 2 -- 4 files changed, 13 deletions(-) diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java b/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java index 1dd949442f..a07d035407 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/Interval.java @@ -16,11 +16,6 @@ public class Interval { public int a; public int b; - public static int creates = 0; - public static int misses = 0; - public static int hits = 0; - public static int outOfRange = 0; - public Interval(int a, int b) { this.a=a; this.b=b; } /** Interval objects are used readonly so share all with the diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestATNParserPrediction.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestATNParserPrediction.java index 51a1f91859..6831287813 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestATNParserPrediction.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestATNParserPrediction.java @@ -491,7 +491,6 @@ public class TestATNParserPrediction { public void checkPredictedAlt(LexerGrammar lg, Grammar g, int decision, String inputString, int expectedAlt) { - Tool.internalOption_ShowATNConfigsInDFA = true; ATN lexatn = createATN(lg, true); LexerATNSimulator lexInterp = new LexerATNSimulator(lexatn,new DFA[] { new DFA(lexatn.modeToStartState.get(Lexer.DEFAULT_MODE)) },new PredictionContextCache()); diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java index 951f0ef6bb..1b7eea7535 100644 --- a/tool/src/org/antlr/v4/Tool.java +++ b/tool/src/org/antlr/v4/Tool.java @@ -139,10 +139,6 @@ public Option(String fieldName, String name, OptionArgType argType, String descr protected boolean haveOutputDir = false; protected boolean return_dont_exit = false; - // The internal options are for my use on the command line during dev - public static boolean internalOption_PrintGrammarTree = false; - public static boolean internalOption_ShowATNConfigsInDFA = false; - public final String[] args; @@ -365,7 +361,6 @@ public void process(Grammar g, boolean gencode) { public void processNonCombinedGrammar(Grammar g, boolean gencode) { if ( g.ast==null || g.ast.hasErrors ) return; - if ( internalOption_PrintGrammarTree ) System.out.println(g.ast.toStringTree()); boolean ruleFail = checkForRuleIssues(g); if ( ruleFail ) return; diff --git a/tool/src/org/antlr/v4/misc/Utils.java b/tool/src/org/antlr/v4/misc/Utils.java index 35d435bce8..06d8a6f48b 100644 --- a/tool/src/org/antlr/v4/misc/Utils.java +++ b/tool/src/org/antlr/v4/misc/Utils.java @@ -31,8 +31,6 @@ public interface Func1 { TResult exec(T1 arg1); } - static Integer[] ints = new Integer[INTEGER_POOL_MAX_VALUE+1]; - public static String stripFileExtension(String name) { if ( name==null ) return null; int lastDot = name.lastIndexOf('.'); From 4ccbb41923c76f724ed1a5e87a271bdb5058d8d1 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 17 Jun 2022 22:12:07 +0300 Subject: [PATCH 04/20] Fix potential deadlocks in Java runtime Referencing subclass ParserRuleContext from superclass RuleContext initializer might lead to class loading deadlock Signed-off-by: Ivan Kochurkin --- .../runtime/java/api/TestExpectedTokens.java | 2 +- .../antlr/v4/runtime/ParserRuleContext.java | 2 + .../src/org/antlr/v4/runtime/RuleContext.java | 2 - .../org/antlr/v4/runtime/atn/ATNConfig.java | 9 ++-- .../antlr/v4/runtime/atn/ATNConfigSet.java | 4 +- .../runtime/atn/EmptyPredictionContext.java | 8 +++- .../org/antlr/v4/runtime/atn/LL1Analyzer.java | 4 +- .../antlr/v4/runtime/atn/LexerATNConfig.java | 4 +- .../v4/runtime/atn/LexerATNSimulator.java | 4 +- .../v4/runtime/atn/ParserATNSimulator.java | 12 ++--- .../v4/runtime/atn/PredictionContext.java | 47 +++++++++---------- .../runtime/atn/PredictionContextCache.java | 2 +- .../antlr/v4/runtime/atn/PredictionMode.java | 2 +- .../antlr/v4/runtime/atn/SemanticContext.java | 37 +++++++++------ .../atn/SingletonPredictionContext.java | 2 +- .../antlr/v4/test/tool/TestGraphNodes.java | 43 +++++++++-------- 16 files changed, 97 insertions(+), 87 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/java/api/TestExpectedTokens.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/java/api/TestExpectedTokens.java index 4ac625ab6b..9123d1d3b5 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/java/api/TestExpectedTokens.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/java/api/TestExpectedTokens.java @@ -92,7 +92,7 @@ public void testEpsilonAltSubrule() throws Exception { // From the start of 'b' with empty stack, can only see B and EOF int blkStartStateNumber = 9; - IntervalSet tokens = atn.getExpectedTokens(blkStartStateNumber, RuleContext.EMPTY); + IntervalSet tokens = atn.getExpectedTokens(blkStartStateNumber, ParserRuleContext.EMPTY); assertEquals("{, B}", tokens.toString(g.getTokenNames())); // Now call from 'a' diff --git a/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java b/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java index 4513b09862..635b1a473b 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/ParserRuleContext.java @@ -40,6 +40,8 @@ * satisfy the superclass interface. */ public class ParserRuleContext extends RuleContext { + public static final ParserRuleContext EMPTY = new ParserRuleContext(); + /** If we are debugging or building a parse tree for a visitor, * we need to track all of the tokens and rule invocations associated * with this rule's context. This is empty for parsing w/o tree constr. diff --git a/runtime/Java/src/org/antlr/v4/runtime/RuleContext.java b/runtime/Java/src/org/antlr/v4/runtime/RuleContext.java index 8b7f168b2b..e87e41e58f 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/RuleContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/RuleContext.java @@ -66,8 +66,6 @@ * @see ParserRuleContext */ public class RuleContext implements RuleNode { - public static final ParserRuleContext EMPTY = new ParserRuleContext(); - /** What context invoked this rule? */ public RuleContext parent; diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java index a3e4d1d8b6..c24739ab1f 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java @@ -7,8 +7,11 @@ package org.antlr.v4.runtime.atn; import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.misc.DoubleKeyMap; import org.antlr.v4.runtime.misc.MurmurHash; +import java.util.Objects; + /** A tuple: (ATN state, predicted alt, syntactic, semantic context). * The syntactic context is a graph-structured stack node whose * path(s) to the root is the rule invocation(s) @@ -76,7 +79,7 @@ public ATNConfig(ATNState state, int alt, PredictionContext context) { - this(state, alt, context, SemanticContext.NONE); + this(state, alt, context, SemanticContext.Empty.Instance); } public ATNConfig(ATNState state, @@ -168,7 +171,7 @@ else if (other == null) { return this.state.stateNumber==other.state.stateNumber && this.alt==other.alt - && (this.context==other.context || (this.context != null && this.context.equals(other.context))) + && Objects.equals(this.context, other.context) && this.semanticContext.equals(other.semanticContext) && this.isPrecedenceFilterSuppressed() == other.isPrecedenceFilterSuppressed(); } @@ -206,7 +209,7 @@ public String toString(Recognizer recog, boolean showAlt) { buf.append(context.toString()); buf.append("]"); } - if ( semanticContext!=null && semanticContext != SemanticContext.NONE ) { + if ( semanticContext!=null && semanticContext != SemanticContext.Empty.Instance ) { buf.append(","); buf.append(semanticContext); } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java index d40dfb4e37..5c40e327e9 100755 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java @@ -137,7 +137,7 @@ public boolean add( DoubleKeyMap mergeCache) { if ( readonly ) throw new IllegalStateException("This set is readonly"); - if ( config.semanticContext!=SemanticContext.NONE ) { + if ( config.semanticContext != SemanticContext.Empty.Instance ) { hasSemanticContext = true; } if (config.getOuterContextDepth() > 0) { @@ -199,7 +199,7 @@ public BitSet getAlts() { public List getPredicates() { List preds = new ArrayList(); for (ATNConfig c : configs) { - if ( c.semanticContext!=SemanticContext.NONE ) { + if ( c.semanticContext!=SemanticContext.Empty.Instance ) { preds.add(c.semanticContext); } } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/EmptyPredictionContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/EmptyPredictionContext.java index a11ff159ec..9feaad3dc6 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/EmptyPredictionContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/EmptyPredictionContext.java @@ -7,7 +7,13 @@ package org.antlr.v4.runtime.atn; public class EmptyPredictionContext extends SingletonPredictionContext { - public EmptyPredictionContext() { + /** + * Represents {@code $} in local context prediction, which means wildcard. + * {@code *+x = *}. + */ + public static final EmptyPredictionContext Instance = new EmptyPredictionContext(); + + private EmptyPredictionContext() { super(null, EMPTY_RETURN_STATE); } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/LL1Analyzer.java b/runtime/Java/src/org/antlr/v4/runtime/atn/LL1Analyzer.java index 8d5117b68d..97f0dca4e7 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/LL1Analyzer.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/LL1Analyzer.java @@ -45,7 +45,7 @@ public IntervalSet[] getDecisionLookahead(ATNState s) { look[alt] = new IntervalSet(); Set lookBusy = new HashSet(); boolean seeThruPreds = false; // fail to get lookahead upon pred - _LOOK(s.transition(alt).target, null, PredictionContext.EMPTY, + _LOOK(s.transition(alt).target, null, EmptyPredictionContext.Instance, look[alt], lookBusy, new BitSet(), seeThruPreds, false); // Wipe out lookahead for this alternative if we found nothing // or we had a predicate when we !seeThruPreds @@ -167,7 +167,7 @@ else if (ctx.isEmpty() && addEOF) { return; } - if ( ctx != PredictionContext.EMPTY ) { + if ( ctx != EmptyPredictionContext.Instance ) { // run thru all possible stack tops in ctx boolean removed = calledRuleStack.get(s.ruleIndex); try { diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java index ad8766c49b..a48abd28e9 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java @@ -21,7 +21,7 @@ public LexerATNConfig(ATNState state, int alt, PredictionContext context) { - super(state, alt, context, SemanticContext.NONE); + super(state, alt, context, SemanticContext.Empty.Instance); this.passedThroughNonGreedyDecision = false; this.lexerActionExecutor = null; } @@ -31,7 +31,7 @@ public LexerATNConfig(ATNState state, PredictionContext context, LexerActionExecutor lexerActionExecutor) { - super(state, alt, context, SemanticContext.NONE); + super(state, alt, context, SemanticContext.Empty.Instance); this.lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = false; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java index 79d9030530..254e17839b 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNSimulator.java @@ -380,7 +380,7 @@ protected ATNState getReachableTarget(Transition trans, int t) { protected ATNConfigSet computeStartState(CharStream input, ATNState p) { - PredictionContext initialContext = PredictionContext.EMPTY; + PredictionContext initialContext = EmptyPredictionContext.Instance; ATNConfigSet configs = new OrderedATNConfigSet(); for (int i=0; i splitAccordingToSemanticValidity( ATNConfigSet succeeded = new ATNConfigSet(configs.fullCtx); ATNConfigSet failed = new ATNConfigSet(configs.fullCtx); for (ATNConfig c : configs) { - if ( c.semanticContext!=SemanticContext.NONE ) { + if ( c.semanticContext!=SemanticContext.Empty.Instance ) { boolean predicateEvaluationResult = evalSemanticContext(c.semanticContext, outerContext, c.alt, configs.fullCtx); if ( predicateEvaluationResult ) { succeeded.add(c); @@ -1370,7 +1370,7 @@ protected BitSet evalSemanticContext(DFAState.PredPrediction[] predPredictions, { BitSet predictions = new BitSet(); for (DFAState.PredPrediction pair : predPredictions) { - if ( pair.pred==SemanticContext.NONE ) { + if ( pair.pred==SemanticContext.Empty.Instance ) { predictions.set(pair.alt); if (!complete) { break; @@ -1468,7 +1468,7 @@ protected void closureCheckingStopState(ATNConfig config, for (int i = 0; i < config.context.size(); i++) { if ( config.context.getReturnState(i)==PredictionContext.EMPTY_RETURN_STATE ) { if (fullCtx) { - configs.add(new ATNConfig(config, config.state, PredictionContext.EMPTY), mergeCache); + configs.add(new ATNConfig(config, config.state, EmptyPredictionContext.Instance), mergeCache); continue; } else { diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java index e2a90bf373..8fe349d6a6 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java @@ -6,6 +6,7 @@ package org.antlr.v4.runtime.atn; +import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.misc.DoubleKeyMap; @@ -21,12 +22,6 @@ import java.util.Map; public abstract class PredictionContext { - /** - * Represents {@code $} in local context prediction, which means wildcard. - * {@code *+x = *}. - */ - public static final EmptyPredictionContext EMPTY = new EmptyPredictionContext(); - /** * Represents {@code $} in an array in full context mode, when {@code $} * doesn't mean wildcard: {@code $ + x = [$,x]}. Here, @@ -67,19 +62,19 @@ protected PredictionContext(int cachedHashCode) { } /** Convert a {@link RuleContext} tree to a {@link PredictionContext} graph. - * Return {@link #EMPTY} if {@code outerContext} is empty or null. + * Return {@link EmptyPredictionContext#Instance} if {@code outerContext} is empty or null. */ public static PredictionContext fromRuleContext(ATN atn, RuleContext outerContext) { - if ( outerContext==null ) outerContext = RuleContext.EMPTY; + if ( outerContext==null ) outerContext = ParserRuleContext.EMPTY; // if we are in RuleContext of start rule, s, then PredictionContext // is EMPTY. Nobody called us. (if we are empty, return empty) - if ( outerContext.parent==null || outerContext==RuleContext.EMPTY ) { - return PredictionContext.EMPTY; + if ( outerContext.parent==null || outerContext==ParserRuleContext.EMPTY ) { + return EmptyPredictionContext.Instance; } // If we have a parent, convert it to a PredictionContext graph - PredictionContext parent = EMPTY; + PredictionContext parent = EmptyPredictionContext.Instance; parent = PredictionContext.fromRuleContext(atn, outerContext.parent); ATNState state = atn.states.get(outerContext.invokingState); @@ -93,9 +88,9 @@ public static PredictionContext fromRuleContext(ATN atn, RuleContext outerContex public abstract int getReturnState(int index); - /** This means only the {@link #EMPTY} (wildcard? not sure) context is in set. */ + /** This means only the {@link EmptyPredictionContext#Instance} (wildcard? not sure) context is in set. */ public boolean isEmpty() { - return this == EMPTY; + return this == EmptyPredictionContext.Instance; } public boolean hasEmptyPath() { @@ -270,18 +265,18 @@ public static PredictionContext mergeSingletons( /** * Handle case where at least one of {@code a} or {@code b} is - * {@link #EMPTY}. In the following diagrams, the symbol {@code $} is used - * to represent {@link #EMPTY}. + * {@link EmptyPredictionContext#Instance}. In the following diagrams, the symbol {@code $} is used + * to represent {@link EmptyPredictionContext#Instance}. * *

Local-Context Merges

* *

These local-context merge operations are used when {@code rootIsWildcard} * is true.

* - *

{@link #EMPTY} is superset of any graph; return {@link #EMPTY}.
+ *

{@link EmptyPredictionContext#Instance} is superset of any graph; return {@link EmptyPredictionContext#Instance}.
*

* - *

{@link #EMPTY} and anything is {@code #EMPTY}, so merged parent is + *

{@link EmptyPredictionContext#Instance} and anything is {@code #EMPTY}, so merged parent is * {@code #EMPTY}; return left graph.
*

* @@ -295,7 +290,7 @@ public static PredictionContext mergeSingletons( * *

* - *

Must keep all contexts; {@link #EMPTY} in array is a special value (and + *

Must keep all contexts; {@link EmptyPredictionContext#Instance} in array is a special value (and * null parent).
*

* @@ -311,19 +306,19 @@ public static PredictionContext mergeRoot(SingletonPredictionContext a, boolean rootIsWildcard) { if ( rootIsWildcard ) { - if ( a == EMPTY ) return EMPTY; // * + b = * - if ( b == EMPTY ) return EMPTY; // a + * = * + if ( a == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // * + b = * + if ( b == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // a + * = * } else { - if ( a == EMPTY && b == EMPTY ) return EMPTY; // $ + $ = $ - if ( a == EMPTY ) { // $ + x = [x,$] + if ( a == EmptyPredictionContext.Instance && b == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // $ + $ = $ + if ( a == EmptyPredictionContext.Instance) { // $ + x = [x,$] int[] payloads = {b.returnState, EMPTY_RETURN_STATE}; PredictionContext[] parents = {b.parent, null}; PredictionContext joined = new ArrayPredictionContext(parents, payloads); return joined; } - if ( b == EMPTY ) { // x + $ = [x,$] ($ is always last if present) + if ( b == EmptyPredictionContext.Instance) { // x + $ = [x,$] ($ is always last if present) int[] payloads = {a.returnState, EMPTY_RETURN_STATE}; PredictionContext[] parents = {a.parent, null}; PredictionContext joined = @@ -521,7 +516,7 @@ public int compare(PredictionContext o1, PredictionContext o2) { } for (PredictionContext current : nodes) { - if ( current==EMPTY ) continue; + if ( current== EmptyPredictionContext.Instance) continue; for (int i = 0; i < current.size(); i++) { if ( current.getParent(i)==null ) continue; String s = String.valueOf(current.id); @@ -585,7 +580,7 @@ public static PredictionContext getCachedContext( PredictionContext updated; if (parents.length == 0) { - updated = EMPTY; + updated = EmptyPredictionContext.Instance; } else if (parents.length == 1) { updated = SingletonPredictionContext.create(parents[0], context.getReturnState(0)); @@ -651,7 +646,7 @@ public String toString(Recognizer recog) { } public String[] toStrings(Recognizer recognizer, int currentState) { - return toStrings(recognizer, EMPTY, currentState); + return toStrings(recognizer, EmptyPredictionContext.Instance, currentState); } // FROM SAM diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContextCache.java b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContextCache.java index 11711ee226..0db6f4d1fd 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContextCache.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContextCache.java @@ -22,7 +22,7 @@ public class PredictionContextCache { * Protect shared cache from unsafe thread access. */ public PredictionContext add(PredictionContext ctx) { - if ( ctx==PredictionContext.EMPTY ) return PredictionContext.EMPTY; + if ( ctx==EmptyPredictionContext.Instance ) return EmptyPredictionContext.Instance; PredictionContext existing = cache.get(ctx); if ( existing!=null ) { // System.out.println(name+" reuses "+existing); diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java index 51a51d2c50..b023606f02 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java @@ -228,7 +228,7 @@ public static boolean hasSLLConflictTerminatingPrediction(PredictionMode mode, A // dup configs, tossing out semantic predicates ATNConfigSet dup = new ATNConfigSet(); for (ATNConfig c : configs) { - c = new ATNConfig(c,SemanticContext.NONE); + c = new ATNConfig(c,SemanticContext.Empty.Instance); dup.add(c); } configs = dup; diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/SemanticContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/SemanticContext.java index 299ec99965..783b8ea454 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/SemanticContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/SemanticContext.java @@ -28,12 +28,6 @@ * {@link SemanticContext} within the scope of this outer class.

*/ public abstract class SemanticContext { - /** - * The default {@link SemanticContext}, which is semantically equivalent to - * a predicate of the form {@code {true}?}. - */ - public static final SemanticContext NONE = new Predicate(); - /** * For context independent predicates, we evaluate them without a local * context (i.e., null context). That way, we can evaluate them without @@ -57,7 +51,7 @@ public abstract class SemanticContext { * @return The simplified semantic context after precedence predicates are * evaluated, which will be one of the following values. *
    - *
  • {@link #NONE}: if the predicate simplifies to {@code true} after + *
  • {@link Empty#Instance}: if the predicate simplifies to {@code true} after * precedence predicates are evaluated.
  • *
  • {@code null}: if the predicate simplifies to {@code false} after * precedence predicates are evaluated.
  • @@ -71,6 +65,19 @@ public SemanticContext evalPrecedence(Recognizer parser, RuleContext parser return this; } + public static class Empty extends SemanticContext { + /** + * The default {@link SemanticContext}, which is semantically equivalent to + * a predicate of the form {@code {true}?}. + */ + public static final Empty Instance = new Empty(); + + @Override + public boolean eval(Recognizer parser, RuleContext parserCallStack) { + return false; + } + } + public static class Predicate extends SemanticContext { public final int ruleIndex; public final int predIndex; @@ -139,7 +146,7 @@ public boolean eval(Recognizer parser, RuleContext parserCallStack) { @Override public SemanticContext evalPrecedence(Recognizer parser, RuleContext parserCallStack) { if (parser.precpred(parserCallStack, precedence)) { - return SemanticContext.NONE; + return Empty.Instance; } else { return null; @@ -266,7 +273,7 @@ public SemanticContext evalPrecedence(Recognizer parser, RuleContext parse // The AND context is false if any element is false return null; } - else if (evaluated != NONE) { + else if (evaluated != Empty.Instance) { // Reduce the result by skipping true elements operands.add(evaluated); } @@ -278,7 +285,7 @@ else if (evaluated != NONE) { if (operands.isEmpty()) { // all elements were true, so the AND context is true - return NONE; + return Empty.Instance; } SemanticContext result = operands.get(0); @@ -359,9 +366,9 @@ public SemanticContext evalPrecedence(Recognizer parser, RuleContext parse for (SemanticContext context : opnds) { SemanticContext evaluated = context.evalPrecedence(parser, parserCallStack); differs |= (evaluated != context); - if (evaluated == NONE) { + if (evaluated == Empty.Instance) { // The OR context is true if any element is true - return NONE; + return Empty.Instance; } else if (evaluated != null) { // Reduce the result by skipping false elements @@ -393,8 +400,8 @@ public String toString() { } public static SemanticContext and(SemanticContext a, SemanticContext b) { - if ( a == null || a == NONE ) return b; - if ( b == null || b == NONE ) return a; + if ( a == null || a == Empty.Instance ) return b; + if ( b == null || b == Empty.Instance ) return a; AND result = new AND(a, b); if (result.opnds.length == 1) { return result.opnds[0]; @@ -410,7 +417,7 @@ public static SemanticContext and(SemanticContext a, SemanticContext b) { public static SemanticContext or(SemanticContext a, SemanticContext b) { if ( a == null ) return b; if ( b == null ) return a; - if ( a == NONE || b == NONE ) return NONE; + if ( a == Empty.Instance || b == Empty.Instance ) return Empty.Instance; OR result = new OR(a, b); if (result.opnds.length == 1) { return result.opnds[0]; diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/SingletonPredictionContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/SingletonPredictionContext.java index ca5ea66097..98631f23b3 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/SingletonPredictionContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/SingletonPredictionContext.java @@ -20,7 +20,7 @@ public class SingletonPredictionContext extends PredictionContext { public static SingletonPredictionContext create(PredictionContext parent, int returnState) { if ( returnState == EMPTY_RETURN_STATE && parent == null ) { // someone can pass in the bits of an array ctx that mean $ - return EMPTY; + return EmptyPredictionContext.Instance; } return new SingletonPredictionContext(parent, returnState); } diff --git a/tool-testsuite/test/org/antlr/v4/test/tool/TestGraphNodes.java b/tool-testsuite/test/org/antlr/v4/test/tool/TestGraphNodes.java index 62ad263a8f..bf5808de54 100644 --- a/tool-testsuite/test/org/antlr/v4/test/tool/TestGraphNodes.java +++ b/tool-testsuite/test/org/antlr/v4/test/tool/TestGraphNodes.java @@ -7,6 +7,7 @@ package org.antlr.v4.test.tool; import org.antlr.v4.runtime.atn.ArrayPredictionContext; +import org.antlr.v4.runtime.atn.EmptyPredictionContext; import org.antlr.v4.runtime.atn.PredictionContext; import org.antlr.v4.runtime.atn.SingletonPredictionContext; import org.junit.jupiter.api.Disabled; @@ -24,9 +25,8 @@ public class TestGraphNodes { public boolean fullCtx() { return false; } @Test public void test_$_$() { - PredictionContext r = PredictionContext.merge(PredictionContext.EMPTY, - PredictionContext.EMPTY, - rootIsWildcard(), null); + PredictionContext r = PredictionContext.merge( + EmptyPredictionContext.Instance, EmptyPredictionContext.Instance, rootIsWildcard(), null); // System.out.println(toDOTString(r, rootIsWildcard())); String expecting = "digraph G {\n" + @@ -37,9 +37,8 @@ public class TestGraphNodes { } @Test public void test_$_$_fullctx() { - PredictionContext r = PredictionContext.merge(PredictionContext.EMPTY, - PredictionContext.EMPTY, - fullCtx(), null); + PredictionContext r = PredictionContext.merge( + EmptyPredictionContext.Instance, EmptyPredictionContext.Instance, fullCtx(), null); // System.out.println(toDOTString(r, fullCtx())); String expecting = "digraph G {\n" + @@ -50,7 +49,7 @@ public class TestGraphNodes { } @Test public void test_x_$() { - PredictionContext r = PredictionContext.merge(x(), PredictionContext.EMPTY, rootIsWildcard(), null); + PredictionContext r = PredictionContext.merge(x(), EmptyPredictionContext.Instance, rootIsWildcard(), null); // System.out.println(toDOTString(r, rootIsWildcard())); String expecting = "digraph G {\n" + @@ -61,7 +60,7 @@ public class TestGraphNodes { } @Test public void test_x_$_fullctx() { - PredictionContext r = PredictionContext.merge(x(), PredictionContext.EMPTY, fullCtx(), null); + PredictionContext r = PredictionContext.merge(x(), EmptyPredictionContext.Instance, fullCtx(), null); // System.out.println(toDOTString(r, fullCtx())); String expecting = "digraph G {\n" + @@ -74,7 +73,7 @@ public class TestGraphNodes { } @Test public void test_$_x() { - PredictionContext r = PredictionContext.merge(PredictionContext.EMPTY, x(), rootIsWildcard(), null); + PredictionContext r = PredictionContext.merge(EmptyPredictionContext.Instance, x(), rootIsWildcard(), null); // System.out.println(toDOTString(r, rootIsWildcard())); String expecting = "digraph G {\n" + @@ -85,7 +84,7 @@ public class TestGraphNodes { } @Test public void test_$_x_fullctx() { - PredictionContext r = PredictionContext.merge(PredictionContext.EMPTY, x(), fullCtx(), null); + PredictionContext r = PredictionContext.merge(EmptyPredictionContext.Instance, x(), fullCtx(), null); // System.out.println(toDOTString(r, fullCtx())); String expecting = "digraph G {\n" + @@ -161,7 +160,7 @@ public class TestGraphNodes { } @Test public void test_aa$_a$_$_fullCtx() { - PredictionContext empty = PredictionContext.EMPTY; + PredictionContext empty = EmptyPredictionContext.Instance; PredictionContext child1 = createSingleton(empty, 8); PredictionContext right = PredictionContext.merge(empty, child1, false, null); PredictionContext left = createSingleton(right, 8); @@ -424,8 +423,8 @@ public class TestGraphNodes { // Array merges @Test public void test_A$_A$_fullctx() { - ArrayPredictionContext A1 = array(PredictionContext.EMPTY); - ArrayPredictionContext A2 = array(PredictionContext.EMPTY); + ArrayPredictionContext A1 = array(EmptyPredictionContext.Instance); + ArrayPredictionContext A2 = array(EmptyPredictionContext.Instance); PredictionContext r = PredictionContext.merge(A1, A2, fullCtx(), null); // System.out.println(toDOTString(r, fullCtx())); String expecting = @@ -747,39 +746,39 @@ public class TestGraphNodes { // ------------ SUPPORT ------------------------- protected SingletonPredictionContext a() { - return createSingleton(PredictionContext.EMPTY, 1); + return createSingleton(EmptyPredictionContext.Instance, 1); } private SingletonPredictionContext b() { - return createSingleton(PredictionContext.EMPTY, 2); + return createSingleton(EmptyPredictionContext.Instance, 2); } private SingletonPredictionContext c() { - return createSingleton(PredictionContext.EMPTY, 3); + return createSingleton(EmptyPredictionContext.Instance, 3); } private SingletonPredictionContext d() { - return createSingleton(PredictionContext.EMPTY, 4); + return createSingleton(EmptyPredictionContext.Instance, 4); } private SingletonPredictionContext u() { - return createSingleton(PredictionContext.EMPTY, 6); + return createSingleton(EmptyPredictionContext.Instance, 6); } private SingletonPredictionContext v() { - return createSingleton(PredictionContext.EMPTY, 7); + return createSingleton(EmptyPredictionContext.Instance, 7); } private SingletonPredictionContext w() { - return createSingleton(PredictionContext.EMPTY, 8); + return createSingleton(EmptyPredictionContext.Instance, 8); } private SingletonPredictionContext x() { - return createSingleton(PredictionContext.EMPTY, 9); + return createSingleton(EmptyPredictionContext.Instance, 9); } private SingletonPredictionContext y() { - return createSingleton(PredictionContext.EMPTY, 10); + return createSingleton(EmptyPredictionContext.Instance, 10); } public SingletonPredictionContext createSingleton(PredictionContext parent, int payload) { From dbce6b2b3e1e90f23cc26ed34ae7825e9d1638a0 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 19 Jun 2022 20:50:35 +0300 Subject: [PATCH 05/20] Unify C#, C++, Dart, Go, Python runtimes Signed-off-by: Ivan Kochurkin --- runtime/CSharp/src/Atn/ATN.cs | 2 +- runtime/CSharp/src/Atn/ATNConfig.cs | 4 +-- runtime/CSharp/src/Atn/ATNConfigSet.cs | 4 +-- .../CSharp/src/Atn/EmptyPredictionContext.cs | 3 +- runtime/CSharp/src/Atn/LL1Analyzer.cs | 4 +-- runtime/CSharp/src/Atn/LexerATNConfig.cs | 4 +-- runtime/CSharp/src/Atn/LexerATNSimulator.cs | 4 +-- runtime/CSharp/src/Atn/ParserATNSimulator.cs | 20 ++++++------ runtime/CSharp/src/Atn/PredicateEvalInfo.cs | 2 +- runtime/CSharp/src/Atn/PredictionContext.cs | 24 +++++++------- .../CSharp/src/Atn/PredictionContextCache.cs | 4 +-- runtime/CSharp/src/Atn/PredictionMode.cs | 2 +- runtime/CSharp/src/Atn/SemanticContext.cs | 30 ++++++++++------- .../src/Atn/SingletonPredictionContext.cs | 2 +- runtime/CSharp/src/Dfa/DFAState.cs | 2 +- runtime/Cpp/runtime/src/atn/ATNConfig.cpp | 6 ++-- runtime/Cpp/runtime/src/atn/ATNConfigSet.cpp | 6 ++-- .../runtime/src/atn/ParserATNSimulator.cpp | 10 +++--- .../Cpp/runtime/src/atn/PredictionMode.cpp | 4 +-- .../Cpp/runtime/src/atn/SemanticContext.cpp | 22 ++++++------- runtime/Cpp/runtime/src/atn/SemanticContext.h | 18 +++++++---- runtime/Dart/lib/src/atn/src/atn_config.dart | 6 ++-- .../Dart/lib/src/atn/src/atn_config_set.dart | 4 +-- .../Dart/lib/src/atn/src/atn_simulator.dart | 2 +- .../lib/src/atn/src/lexer_atn_simulator.dart | 4 +-- .../lib/src/atn/src/parser_atn_simulator.dart | 16 +++++----- .../lib/src/atn/src/semantic_context.dart | 32 ++++++++++++------- runtime/Dart/lib/src/ll1_analyzer.dart | 4 +-- runtime/Dart/lib/src/prediction_context.dart | 32 +++++++++---------- runtime/Go/antlr/parser_atn_simulator.go | 4 +-- runtime/Go/antlr/parser_rule_context.go | 2 +- runtime/Go/antlr/prediction_context.go | 4 +-- runtime/Go/antlr/semantic_context.go | 2 +- .../Python2/src/antlr4/atn/SemanticContext.py | 5 ++- .../Python3/src/antlr4/atn/SemanticContext.py | 5 ++- 35 files changed, 161 insertions(+), 138 deletions(-) diff --git a/runtime/CSharp/src/Atn/ATN.cs b/runtime/CSharp/src/Atn/ATN.cs index 8f2a20f3be..a8044ca2cf 100644 --- a/runtime/CSharp/src/Atn/ATN.cs +++ b/runtime/CSharp/src/Atn/ATN.cs @@ -104,7 +104,7 @@ public virtual PredictionContext GetCachedContext(PredictionContext context) /// If /// /// is - /// + /// /// , the set of tokens will not include what can follow /// the rule surrounding /// diff --git a/runtime/CSharp/src/Atn/ATNConfig.cs b/runtime/CSharp/src/Atn/ATNConfig.cs index 11e3e6bfb3..7ca6e6235f 100644 --- a/runtime/CSharp/src/Atn/ATNConfig.cs +++ b/runtime/CSharp/src/Atn/ATNConfig.cs @@ -81,7 +81,7 @@ public ATNConfig(ATNConfig old) public ATNConfig(ATNState state, int alt, PredictionContext context) - : this(state, alt, context, SemanticContext.NONE) + : this(state, alt, context, SemanticContext.Empty.Instance) { } @@ -229,7 +229,7 @@ public String ToString(IRecognizer recog, bool showAlt) buf.Append(context.ToString()); buf.Append("]"); } - if (semanticContext != null && semanticContext != SemanticContext.NONE) + if (semanticContext != null && semanticContext != SemanticContext.Empty.Instance) { buf.Append(","); buf.Append(semanticContext); diff --git a/runtime/CSharp/src/Atn/ATNConfigSet.cs b/runtime/CSharp/src/Atn/ATNConfigSet.cs index 2ea9fe4b30..7c3d68d593 100644 --- a/runtime/CSharp/src/Atn/ATNConfigSet.cs +++ b/runtime/CSharp/src/Atn/ATNConfigSet.cs @@ -95,7 +95,7 @@ public bool Add(ATNConfig config, MergeCache mergeCache) { if (readOnly) throw new Exception("This set is readonly"); - if (config.semanticContext != SemanticContext.NONE) + if (config.semanticContext != SemanticContext.Empty.Instance) { hasSemanticContext = true; } @@ -171,7 +171,7 @@ public List GetPredicates() List preds = new List(); foreach (ATNConfig c in configs) { - if (c.semanticContext != SemanticContext.NONE) + if (c.semanticContext != SemanticContext.Empty.Instance) { preds.Add(c.semanticContext); } diff --git a/runtime/CSharp/src/Atn/EmptyPredictionContext.cs b/runtime/CSharp/src/Atn/EmptyPredictionContext.cs index 937092d88b..dfc971b751 100644 --- a/runtime/CSharp/src/Atn/EmptyPredictionContext.cs +++ b/runtime/CSharp/src/Atn/EmptyPredictionContext.cs @@ -12,14 +12,13 @@ namespace Antlr4.Runtime.Atn #pragma warning disable 0659 // 'class' overrides Object.Equals(object o) but does not override Object.GetHashCode() public sealed class EmptyPredictionContext : SingletonPredictionContext { + public static readonly EmptyPredictionContext Instance = new EmptyPredictionContext(); internal EmptyPredictionContext() : base(null, EMPTY_RETURN_STATE) { } - - public override PredictionContext GetParent(int index) { return null; diff --git a/runtime/CSharp/src/Atn/LL1Analyzer.cs b/runtime/CSharp/src/Atn/LL1Analyzer.cs index e679334128..87239c8d3c 100644 --- a/runtime/CSharp/src/Atn/LL1Analyzer.cs +++ b/runtime/CSharp/src/Atn/LL1Analyzer.cs @@ -48,7 +48,7 @@ public virtual IntervalSet[] GetDecisionLookahead(ATNState s) HashSet lookBusy = new HashSet(); bool seeThruPreds = false; // fail to get lookahead upon pred - Look_(s.Transition(alt).target, null, PredictionContext.EMPTY, look[alt], lookBusy, new BitSet(), seeThruPreds, false); + Look_(s.Transition(alt).target, null, EmptyPredictionContext.Instance, look[alt], lookBusy, new BitSet(), seeThruPreds, false); // Wipe out lookahead for this alternative if we found nothing // or we had a predicate when we !seeThruPreds if (look[alt].Count == 0 || look[alt].Contains(HitPred)) @@ -171,7 +171,7 @@ protected internal virtual void Look_(ATNState s, ATNState stopState, Prediction look.Add(TokenConstants.EOF); return; } - if (ctx != PredictionContext.EMPTY) + if (ctx != EmptyPredictionContext.Instance) { bool removed = calledRuleStack.Get(s.ruleIndex); try diff --git a/runtime/CSharp/src/Atn/LexerATNConfig.cs b/runtime/CSharp/src/Atn/LexerATNConfig.cs index 1004a49641..aa78cffe51 100644 --- a/runtime/CSharp/src/Atn/LexerATNConfig.cs +++ b/runtime/CSharp/src/Atn/LexerATNConfig.cs @@ -20,7 +20,7 @@ public class LexerATNConfig : ATNConfig public LexerATNConfig(ATNState state, int alt, PredictionContext context) - : base(state, alt, context/*, SemanticContext.NONE*/) // TODO + : base(state, alt, context/*, SemanticContext.Empty.Instance*/) // TODO { this.passedThroughNonGreedyDecision = false; this.lexerActionExecutor = null; @@ -30,7 +30,7 @@ public LexerATNConfig(ATNState state, int alt, PredictionContext context, LexerActionExecutor lexerActionExecutor) - : base(state, alt, context, SemanticContext.NONE) + : base(state, alt, context, SemanticContext.Empty.Instance) { this.lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = false; diff --git a/runtime/CSharp/src/Atn/LexerATNSimulator.cs b/runtime/CSharp/src/Atn/LexerATNSimulator.cs index 11effa8483..3c9479ca80 100644 --- a/runtime/CSharp/src/Atn/LexerATNSimulator.cs +++ b/runtime/CSharp/src/Atn/LexerATNSimulator.cs @@ -383,7 +383,7 @@ protected ATNState GetReachableTarget(Transition trans, int t) protected ATNConfigSet ComputeStartState(ICharStream input, ATNState p) { - PredictionContext initialContext = PredictionContext.EMPTY; + PredictionContext initialContext = EmptyPredictionContext.Instance; ATNConfigSet configs = new OrderedATNConfigSet(); for (int i = 0; i < p.NumberOfTransitions; i++) { @@ -432,7 +432,7 @@ protected bool Closure(ICharStream input, LexerATNConfig config, ATNConfigSet co return true; } else { - configs.Add(new LexerATNConfig(config, config.state, PredictionContext.EMPTY)); + configs.Add(new LexerATNConfig(config, config.state, EmptyPredictionContext.Instance)); currentAltReachedAcceptState = true; } } diff --git a/runtime/CSharp/src/Atn/ParserATNSimulator.cs b/runtime/CSharp/src/Atn/ParserATNSimulator.cs index 38ae76b9c1..eb6c2bfcb1 100644 --- a/runtime/CSharp/src/Atn/ParserATNSimulator.cs +++ b/runtime/CSharp/src/Atn/ParserATNSimulator.cs @@ -1238,11 +1238,11 @@ protected SemanticContext[] GetPredsForAmbigAlts(BitSet ambigAlts, /* altToPred starts as an array of all null contexts. The entry at index i * corresponds to alternative i. altToPred[i] may have one of three values: * 1. null: no ATNConfig c is found such that c.alt==i - * 2. SemanticContext.NONE: At least one ATNConfig c exists such that - * c.alt==i and c.semanticContext==SemanticContext.NONE. In other words, + * 2. SemanticContext.Empty.Instance: At least one ATNConfig c exists such that + * c.alt==i and c.semanticContext==SemanticContext.Empty.Instance. In other words, * alt i has at least one unpredicated config. * 3. Non-NONE Semantic Context: There exists at least one, and for all - * ATNConfig c such that c.alt==i, c.semanticContext!=SemanticContext.NONE. + * ATNConfig c such that c.alt==i, c.semanticContext!=SemanticContext.Empty.Instance. * * From this, it is clear that NONE||anything==NONE. */ @@ -1260,9 +1260,9 @@ protected SemanticContext[] GetPredsForAmbigAlts(BitSet ambigAlts, { if (altToPred[i] == null) { - altToPred[i] = SemanticContext.NONE; + altToPred[i] = SemanticContext.Empty.Instance; } - else if (altToPred[i] != SemanticContext.NONE) + else if (altToPred[i] != SemanticContext.Empty.Instance) { nPredAlts++; } @@ -1288,13 +1288,13 @@ protected PredPrediction[] GetPredicatePredictions(BitSet ambigAlts, { SemanticContext pred = altToPred[i]; - // unpredicated is indicated by SemanticContext.NONE + // unpredicated is indicated by SemanticContext.Empty.Instance if (ambigAlts != null && ambigAlts[i]) { pairs.Add(new PredPrediction(pred, i)); } - if (pred != SemanticContext.NONE) containsPredicate = true; + if (pred != SemanticContext.Empty.Instance) containsPredicate = true; } if (!containsPredicate) @@ -1407,7 +1407,7 @@ protected Pair SplitAccordingToSemanticValidity( ATNConfigSet failed = new ATNConfigSet(configSet.fullCtx); foreach (ATNConfig c in configSet.configs) { - if (c.semanticContext != SemanticContext.NONE) + if (c.semanticContext != SemanticContext.Empty.Instance) { bool predicateEvaluationResult = EvalSemanticContext(c.semanticContext, outerContext, c.alt, configSet.fullCtx); if (predicateEvaluationResult) @@ -1438,7 +1438,7 @@ protected virtual BitSet EvalSemanticContext(PredPrediction[] predPredictions, BitSet predictions = new BitSet(); foreach (PredPrediction pair in predPredictions) { - if (pair.pred == SemanticContext.NONE) + if (pair.pred == SemanticContext.Empty.Instance) { predictions[pair.alt] = true; if (!complete) @@ -1547,7 +1547,7 @@ protected void ClosureCheckingStopState(ATNConfig config, { if (fullCtx) { - configSet.Add(new ATNConfig(config, config.state, PredictionContext.EMPTY), mergeCache); + configSet.Add(new ATNConfig(config, config.state, EmptyPredictionContext.Instance), mergeCache); continue; } else { diff --git a/runtime/CSharp/src/Atn/PredicateEvalInfo.cs b/runtime/CSharp/src/Atn/PredicateEvalInfo.cs index cbc3d084d3..93d3e3e980 100644 --- a/runtime/CSharp/src/Atn/PredicateEvalInfo.cs +++ b/runtime/CSharp/src/Atn/PredicateEvalInfo.cs @@ -31,7 +31,7 @@ public class PredicateEvalInfo : DecisionEventInfo /// . Note that other ATN /// configurations may predict the same alternative which are guarded by /// other semantic contexts and/or - /// + /// /// . /// public readonly int predictedAlt; diff --git a/runtime/CSharp/src/Atn/PredictionContext.cs b/runtime/CSharp/src/Atn/PredictionContext.cs index ac5dd9eb88..b8334c382d 100644 --- a/runtime/CSharp/src/Atn/PredictionContext.cs +++ b/runtime/CSharp/src/Atn/PredictionContext.cs @@ -13,8 +13,6 @@ public abstract class PredictionContext { public static readonly int EMPTY_RETURN_STATE = int.MaxValue; - public static readonly EmptyPredictionContext EMPTY = new EmptyPredictionContext(); - private static readonly int INITIAL_HASH = 1; protected internal static int CalculateEmptyHashCode() @@ -60,7 +58,7 @@ public static PredictionContext FromRuleContext(ATN atn, RuleContext outerContex if (outerContext == null) outerContext = ParserRuleContext.EMPTY; if (outerContext.Parent == null || outerContext == ParserRuleContext.EMPTY) - return PredictionContext.EMPTY; + return EmptyPredictionContext.Instance; PredictionContext parent = PredictionContext.FromRuleContext(atn, outerContext.Parent); ATNState state = atn.states[outerContext.invokingState]; RuleTransition transition = (RuleTransition)state.Transition(0); @@ -80,7 +78,7 @@ public virtual bool IsEmpty { get { - return this == EMPTY; + return this == EmptyPredictionContext.Instance; } } @@ -372,14 +370,14 @@ public static PredictionContext MergeRoot(SingletonPredictionContext a, { if (rootIsWildcard) { - if (a == PredictionContext.EMPTY) - return PredictionContext.EMPTY; // * + b = * - if (b == PredictionContext.EMPTY) - return PredictionContext.EMPTY; // a + * = * + if (a == EmptyPredictionContext.Instance) + return EmptyPredictionContext.Instance; // * + b = * + if (b == EmptyPredictionContext.Instance) + return EmptyPredictionContext.Instance; // a + * = * } else { - if (a == EMPTY && b == EMPTY) return EMPTY; // $ + $ = $ - if (a == EMPTY) + if (a == EmptyPredictionContext.Instance && b == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // $ + $ = $ + if (a == EmptyPredictionContext.Instance) { // $ + x = [$,x] int[] payloads = { b.returnState, EMPTY_RETURN_STATE }; PredictionContext[] parents = { b.parent, null }; @@ -387,7 +385,7 @@ public static PredictionContext MergeRoot(SingletonPredictionContext a, new ArrayPredictionContext(parents, payloads); return joined; } - if (b == EMPTY) + if (b == EmptyPredictionContext.Instance) { // x + $ = [$,x] ($ is always first if present) int[] payloads = { a.returnState, EMPTY_RETURN_STATE }; PredictionContext[] parents = { a.parent, null }; @@ -452,7 +450,7 @@ public static PredictionContext GetCachedContext(PredictionContext context, Pred PredictionContext updated; if (parents.Length == 0) { - updated = EMPTY; + updated = EmptyPredictionContext.Instance; } else if (parents.Length == 1) { @@ -478,7 +476,7 @@ public virtual PredictionContext GetChild(int returnState) public virtual string[] ToStrings(IRecognizer recognizer, int currentState) { - return ToStrings(recognizer, PredictionContext.EMPTY, currentState); + return ToStrings(recognizer, EmptyPredictionContext.Instance, currentState); } public virtual string[] ToStrings(IRecognizer recognizer, PredictionContext stop, int currentState) diff --git a/runtime/CSharp/src/Atn/PredictionContextCache.cs b/runtime/CSharp/src/Atn/PredictionContextCache.cs index c07ee7871b..8a36db59a0 100644 --- a/runtime/CSharp/src/Atn/PredictionContextCache.cs +++ b/runtime/CSharp/src/Atn/PredictionContextCache.cs @@ -19,8 +19,8 @@ public class PredictionContextCache */ public PredictionContext Add(PredictionContext ctx) { - if (ctx == PredictionContext.EMPTY) - return PredictionContext.EMPTY; + if (ctx == EmptyPredictionContext.Instance) + return EmptyPredictionContext.Instance; PredictionContext existing = cache.Get(ctx); if (existing != null) { diff --git a/runtime/CSharp/src/Atn/PredictionMode.cs b/runtime/CSharp/src/Atn/PredictionMode.cs index 8cd7d6e76e..1d801c9560 100644 --- a/runtime/CSharp/src/Atn/PredictionMode.cs +++ b/runtime/CSharp/src/Atn/PredictionMode.cs @@ -258,7 +258,7 @@ public static bool HasSLLConflictTerminatingPrediction(PredictionMode mode, ATNC ATNConfigSet dup = new ATNConfigSet(); foreach (ATNConfig c in configSet.configs) { - dup.Add(new ATNConfig(c, SemanticContext.NONE)); + dup.Add(new ATNConfig(c, SemanticContext.Empty.Instance)); } configSet = dup; } diff --git a/runtime/CSharp/src/Atn/SemanticContext.cs b/runtime/CSharp/src/Atn/SemanticContext.cs index 4d8f357b0e..33db8649d9 100644 --- a/runtime/CSharp/src/Atn/SemanticContext.cs +++ b/runtime/CSharp/src/Atn/SemanticContext.cs @@ -12,8 +12,6 @@ namespace Antlr4.Runtime.Atn { public abstract class SemanticContext { - public static readonly SemanticContext NONE = new SemanticContext.Predicate(); - public abstract bool Eval(Recognizer parser, RuleContext parserCallStack) where ATNInterpreter : ATNSimulator; @@ -23,6 +21,16 @@ public virtual SemanticContext EvalPrecedence(Recognizer return this; } + public class Empty : SemanticContext + { + public static readonly SemanticContext Instance = new Empty(); + + public override bool Eval(Recognizer parser, RuleContext parserCallStack) + { + return false; + } + } + public class Predicate : SemanticContext { public readonly int ruleIndex; @@ -105,7 +113,7 @@ public override SemanticContext EvalPrecedence(Recognize { if (parser.Precpred(parserCallStack, precedence)) { - return SemanticContext.NONE; + return SemanticContext.Empty.Instance; } else { @@ -243,7 +251,7 @@ public override SemanticContext EvalPrecedence(Recognize } else { - if (evaluated != NONE) + if (evaluated != Empty.Instance) { // Reduce the result by skipping true elements operands.Add(evaluated); @@ -257,7 +265,7 @@ public override SemanticContext EvalPrecedence(Recognize if (operands.Count == 0) { // all elements were true, so the AND context is true - return NONE; + return Empty.Instance; } SemanticContext result = operands[0]; for (int i = 1; i < operands.Count; i++) @@ -354,10 +362,10 @@ public override SemanticContext EvalPrecedence(Recognize { SemanticContext evaluated = context.EvalPrecedence(parser, parserCallStack); differs |= (evaluated != context); - if (evaluated == NONE) + if (evaluated == Empty.Instance) { // The OR context is true if any element is true - return NONE; + return Empty.Instance; } else { @@ -393,11 +401,11 @@ public override string ToString() public static SemanticContext AndOp(SemanticContext a, SemanticContext b) { - if (a == null || a == NONE) + if (a == null || a == Empty.Instance) { return b; } - if (b == null || b == NONE) + if (b == null || b == Empty.Instance) { return a; } @@ -419,9 +427,9 @@ public static SemanticContext OrOp(SemanticContext a, SemanticContext b) { return a; } - if (a == NONE || b == NONE) + if (a == Empty.Instance || b == Empty.Instance) { - return NONE; + return Empty.Instance; } SemanticContext.OR result = new SemanticContext.OR(a, b); if (result.opnds.Length == 1) diff --git a/runtime/CSharp/src/Atn/SingletonPredictionContext.cs b/runtime/CSharp/src/Atn/SingletonPredictionContext.cs index e162dcaca7..c378554a2d 100644 --- a/runtime/CSharp/src/Atn/SingletonPredictionContext.cs +++ b/runtime/CSharp/src/Atn/SingletonPredictionContext.cs @@ -15,7 +15,7 @@ public static PredictionContext Create(PredictionContext parent, int returnState if (returnState == EMPTY_RETURN_STATE && parent == null) { // someone can pass in the bits of an array ctx that mean $ - return PredictionContext.EMPTY; + return EmptyPredictionContext.Instance; } return new SingletonPredictionContext(parent, returnState); } diff --git a/runtime/CSharp/src/Dfa/DFAState.cs b/runtime/CSharp/src/Dfa/DFAState.cs index 0986ac6c03..4e2df69cfd 100644 --- a/runtime/CSharp/src/Dfa/DFAState.cs +++ b/runtime/CSharp/src/Dfa/DFAState.cs @@ -168,7 +168,7 @@ public override String ToString() public class PredPrediction { - public SemanticContext pred; // never null; at least SemanticContext.NONE + public SemanticContext pred; // never null; at least SemanticContext.Empty.Instance public int alt; public PredPrediction(SemanticContext pred, int alt) { diff --git a/runtime/Cpp/runtime/src/atn/ATNConfig.cpp b/runtime/Cpp/runtime/src/atn/ATNConfig.cpp index a9f83a61cd..be4d5bfa8c 100755 --- a/runtime/Cpp/runtime/src/atn/ATNConfig.cpp +++ b/runtime/Cpp/runtime/src/atn/ATNConfig.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. +/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. * Use of this file is governed by the BSD 3-clause license that * can be found in the LICENSE.txt file in the project root. */ @@ -23,7 +23,7 @@ inline constexpr size_t SUPPRESS_PRECEDENCE_FILTER = 0x40000000; } ATNConfig::ATNConfig(ATNState *state, size_t alt, Ref context) - : ATNConfig(state, alt, std::move(context), 0, SemanticContext::NONE) {} + : ATNConfig(state, alt, std::move(context), 0, SemanticContext::Empty::Instance) {} ATNConfig::ATNConfig(ATNState *state, size_t alt, Ref context, Ref semanticContext) : ATNConfig(state, alt, std::move(context), 0, std::move(semanticContext)) {} @@ -94,7 +94,7 @@ std::string ATNConfig::toString(bool showAlt) const { if (context) { ss << ",[" << context->toString() << "]"; } - if (semanticContext != nullptr && semanticContext != SemanticContext::NONE) { + if (semanticContext != nullptr && semanticContext != SemanticContext::Empty::Instance) { ss << ",[" << semanticContext->toString() << "]"; } if (getOuterContextDepth() > 0) { diff --git a/runtime/Cpp/runtime/src/atn/ATNConfigSet.cpp b/runtime/Cpp/runtime/src/atn/ATNConfigSet.cpp index d44e46d5ee..4ebdf8882b 100755 --- a/runtime/Cpp/runtime/src/atn/ATNConfigSet.cpp +++ b/runtime/Cpp/runtime/src/atn/ATNConfigSet.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. +/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. * Use of this file is governed by the BSD 3-clause license that * can be found in the LICENSE.txt file in the project root. */ @@ -43,7 +43,7 @@ bool ATNConfigSet::add(const Ref &config, PredictionContextMergeCache if (_readonly) { throw IllegalStateException("This set is readonly"); } - if (config->semanticContext != SemanticContext::NONE) { + if (config->semanticContext != SemanticContext::Empty::Instance) { hasSemanticContext = true; } if (config->getOuterContextDepth() > 0) { @@ -114,7 +114,7 @@ std::vector> ATNConfigSet::getPredicates() const { std::vector> preds; preds.reserve(configs.size()); for (const auto &c : configs) { - if (c->semanticContext != SemanticContext::NONE) { + if (c->semanticContext != SemanticContext::Empty::Instance) { preds.push_back(c->semanticContext); } } diff --git a/runtime/Cpp/runtime/src/atn/ParserATNSimulator.cpp b/runtime/Cpp/runtime/src/atn/ParserATNSimulator.cpp index ed9ca78c51..0111e9e792 100755 --- a/runtime/Cpp/runtime/src/atn/ParserATNSimulator.cpp +++ b/runtime/Cpp/runtime/src/atn/ParserATNSimulator.cpp @@ -692,8 +692,8 @@ std::vector> ParserATNSimulator::getPredsForAmbigAlts size_t nPredAlts = 0; for (size_t i = 1; i <= nalts; i++) { if (altToPred[i] == nullptr) { - altToPred[i] = SemanticContext::NONE; - } else if (altToPred[i] != SemanticContext::NONE) { + altToPred[i] = SemanticContext::Empty::Instance; + } else if (altToPred[i] != SemanticContext::Empty::Instance) { nPredAlts++; } } @@ -712,7 +712,7 @@ std::vector> ParserATNSimulator::getPredsForAmbigAlts std::vector ParserATNSimulator::getPredicatePredictions(const antlrcpp::BitSet &ambigAlts, const std::vector> &altToPred) { bool containsPredicate = std::find_if(altToPred.begin(), altToPred.end(), [](const Ref &context) { - return context != SemanticContext::NONE; + return context != SemanticContext::Empty::Instance; }) != altToPred.end(); std::vector pairs; if (containsPredicate) { @@ -767,7 +767,7 @@ std::pair ParserATNSimulator::splitAccordingToSe ATNConfigSet *succeeded(new ATNConfigSet(configs->fullCtx)); ATNConfigSet *failed(new ATNConfigSet(configs->fullCtx)); for (const auto &c : configs->configs) { - if (c->semanticContext != SemanticContext::NONE) { + if (c->semanticContext != SemanticContext::Empty::Instance) { bool predicateEvaluationResult = evalSemanticContext(c->semanticContext, outerContext, c->alt, configs->fullCtx); if (predicateEvaluationResult) { succeeded->add(c); @@ -785,7 +785,7 @@ BitSet ParserATNSimulator::evalSemanticContext(const std::vectorconfigs) { - Ref c = std::make_shared(*config, SemanticContext::NONE); + Ref c = std::make_shared(*config, SemanticContext::Empty::Instance); dup.add(c); } std::vector altsets = getConflictingAltSubsets(&dup); diff --git a/runtime/Cpp/runtime/src/atn/SemanticContext.cpp b/runtime/Cpp/runtime/src/atn/SemanticContext.cpp index 7ac88cceeb..7d7fe068df 100755 --- a/runtime/Cpp/runtime/src/atn/SemanticContext.cpp +++ b/runtime/Cpp/runtime/src/atn/SemanticContext.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. +/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. * Use of this file is governed by the BSD 3-clause license that * can be found in the LICENSE.txt file in the project root. */ @@ -137,7 +137,7 @@ bool SemanticContext::PrecedencePredicate::eval(Recognizer *parser, RuleContext Ref SemanticContext::PrecedencePredicate::evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const { if (parser->precpred(parserCallStack, precedence)) { - return SemanticContext::NONE; + return SemanticContext::Empty::Instance; } return nullptr; } @@ -237,7 +237,7 @@ Ref SemanticContext::AND::evalPrecedence(Recognizer *pars // The AND context is false if any element is false. return nullptr; } - if (evaluated != NONE) { + if (evaluated != Empty::Instance) { // Reduce the result by skipping true elements. operands.push_back(std::move(evaluated)); } @@ -249,7 +249,7 @@ Ref SemanticContext::AND::evalPrecedence(Recognizer *pars if (operands.empty()) { // All elements were true, so the AND context is true. - return NONE; + return Empty::Instance; } Ref result = std::move(operands[0]); @@ -337,9 +337,9 @@ Ref SemanticContext::OR::evalPrecedence(Recognizer *parse for (const auto &context : getOperands()) { auto evaluated = context->evalPrecedence(parser, parserCallStack); differs |= (evaluated != context); - if (evaluated == NONE) { + if (evaluated == Empty::Instance) { // The OR context is true if any element is true. - return NONE; + return Empty::Instance; } if (evaluated != nullptr) { // Reduce the result by skipping false elements. @@ -374,18 +374,18 @@ std::string SemanticContext::OR::toString() const { //------------------ SemanticContext ----------------------------------------------------------------------------------- -const Ref SemanticContext::NONE = std::make_shared(INVALID_INDEX, INVALID_INDEX, false); +const Ref SemanticContext::Empty::Instance = std::make_shared(INVALID_INDEX, INVALID_INDEX, false); Ref SemanticContext::evalPrecedence(Recognizer * /*parser*/, RuleContext * /*parserCallStack*/) const { return shared_from_this(); } Ref SemanticContext::And(Ref a, Ref b) { - if (!a || a == NONE) { + if (!a || a == Empty::Instance) { return b; } - if (!b || b == NONE) { + if (!b || b == Empty::Instance) { return a; } @@ -405,8 +405,8 @@ Ref SemanticContext::Or(Ref a, Ref return a; } - if (a == NONE || b == NONE) { - return NONE; + if (a == Empty::Instance || b == Empty::Instance) { + return Empty::Instance; } Ref result = std::make_shared(std::move(a), std::move(b)); diff --git a/runtime/Cpp/runtime/src/atn/SemanticContext.h b/runtime/Cpp/runtime/src/atn/SemanticContext.h index 367b726a4a..e9a884846b 100755 --- a/runtime/Cpp/runtime/src/atn/SemanticContext.h +++ b/runtime/Cpp/runtime/src/atn/SemanticContext.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. +/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. * Use of this file is governed by the BSD 3-clause license that * can be found in the LICENSE.txt file in the project root. */ @@ -20,12 +20,6 @@ namespace atn { /// SemanticContext within the scope of this outer class. class ANTLR4CPP_PUBLIC SemanticContext : public std::enable_shared_from_this { public: - /** - * The default {@link SemanticContext}, which is semantically equivalent to - * a predicate of the form {@code {true}?}. - */ - static const Ref NONE; - virtual ~SemanticContext() = default; SemanticContextType getContextType() const { return _contextType; } @@ -76,6 +70,7 @@ namespace atn { /// See also: ParserATNSimulator::getPredsForAmbigAlts. static Ref Or(Ref a, Ref b); + class Empty; class Predicate; class PrecedencePredicate; class Operator; @@ -97,6 +92,15 @@ namespace atn { return !operator==(lhs, rhs); } + class ANTLR4CPP_PUBLIC SemanticContext::Empty final : public SemanticContext{ + public: + /** + * The default {@link SemanticContext}, which is semantically equivalent to + * a predicate of the form {@code {true}?}. + */ + static const Ref Instance; + }; + class ANTLR4CPP_PUBLIC SemanticContext::Predicate final : public SemanticContext { public: static bool is(const SemanticContext &semanticContext) { return semanticContext.getContextType() == SemanticContextType::PREDICATE; } diff --git a/runtime/Dart/lib/src/atn/src/atn_config.dart b/runtime/Dart/lib/src/atn/src/atn_config.dart index 2b414fa9a1..82acda6aa1 100644 --- a/runtime/Dart/lib/src/atn/src/atn_config.dart +++ b/runtime/Dart/lib/src/atn/src/atn_config.dart @@ -89,7 +89,7 @@ class ATNConfig { this.state, this.alt, this.context, [ - this.semanticContext = SemanticContext.NONE, + this.semanticContext = EmptySemanticContext.Instance, ]) : reachesIntoOuterContext = 0; ATNConfig.dup( @@ -169,7 +169,7 @@ class ATNConfig { buf.write(context.toString()); buf.write(']'); } - if (semanticContext != SemanticContext.NONE) { + if (semanticContext != EmptySemanticContext.Instance) { buf.write(','); buf.write(semanticContext); } @@ -194,7 +194,7 @@ class LexerATNConfig extends ATNConfig { int alt, PredictionContext context, [ this.lexerActionExecutor, - ]) : super(state, alt, context, SemanticContext.NONE) { + ]) : super(state, alt, context, EmptySemanticContext.Instance) { passedThroughNonGreedyDecision = false; } diff --git a/runtime/Dart/lib/src/atn/src/atn_config_set.dart b/runtime/Dart/lib/src/atn/src/atn_config_set.dart index 78f3d1593d..874eef6534 100644 --- a/runtime/Dart/lib/src/atn/src/atn_config_set.dart +++ b/runtime/Dart/lib/src/atn/src/atn_config_set.dart @@ -109,7 +109,7 @@ class ATNConfigSet extends Iterable { mergeCache, ]) { if (readOnly) throw StateError('This set is readonly'); - if (config.semanticContext != SemanticContext.NONE) { + if (config.semanticContext != EmptySemanticContext.Instance) { hasSemanticContext = true; } if (config.outerContextDepth > 0) { @@ -176,7 +176,7 @@ class ATNConfigSet extends Iterable { List get predicates { final preds = []; for (var c in configs) { - if (c.semanticContext != SemanticContext.NONE) { + if (c.semanticContext != EmptySemanticContext.Instance) { preds.add(c.semanticContext); } } diff --git a/runtime/Dart/lib/src/atn/src/atn_simulator.dart b/runtime/Dart/lib/src/atn/src/atn_simulator.dart index 4c3eb3e607..be598c4973 100644 --- a/runtime/Dart/lib/src/atn/src/atn_simulator.dart +++ b/runtime/Dart/lib/src/atn/src/atn_simulator.dart @@ -77,7 +77,7 @@ class PredictionContextCache { /// return that one instead and do not add a new context to the cache. /// Protect shared cache from unsafe thread access. PredictionContext add(PredictionContext ctx) { - if (ctx == PredictionContext.EMPTY) return PredictionContext.EMPTY; + if (ctx == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; final existing = cache[ctx]; if (existing != null) { // System.out.println(name+" reuses "+existing); diff --git a/runtime/Dart/lib/src/atn/src/lexer_atn_simulator.dart b/runtime/Dart/lib/src/atn/src/lexer_atn_simulator.dart index 0f983c02ef..3801f727f3 100644 --- a/runtime/Dart/lib/src/atn/src/lexer_atn_simulator.dart +++ b/runtime/Dart/lib/src/atn/src/lexer_atn_simulator.dart @@ -401,7 +401,7 @@ class LexerATNSimulator extends ATNSimulator { } ATNConfigSet computeStartState(CharStream input, ATNState p) { - PredictionContext initialContext = PredictionContext.EMPTY; + PredictionContext initialContext = EmptyPredictionContext.Instance; ATNConfigSet configs = OrderedATNConfigSet(); for (var i = 0; i < p.numberOfTransitions; i++) { final target = p.transition(i).target; @@ -445,7 +445,7 @@ class LexerATNSimulator extends ATNSimulator { configs.add(LexerATNConfig.dup( config, config.state, - context: PredictionContext.EMPTY, + context: EmptyPredictionContext.Instance, )); currentAltReachedAcceptState = true; } diff --git a/runtime/Dart/lib/src/atn/src/parser_atn_simulator.dart b/runtime/Dart/lib/src/atn/src/parser_atn_simulator.dart index 6f14f1cb36..cd814692c1 100644 --- a/runtime/Dart/lib/src/atn/src/parser_atn_simulator.dart +++ b/runtime/Dart/lib/src/atn/src/parser_atn_simulator.dart @@ -1234,8 +1234,8 @@ class ParserATNSimulator extends ATNSimulator { var nPredAlts = 0; for (var i = 1; i <= nalts; i++) { if (altToPred[i] == null) { - altToPred[i] = SemanticContext.NONE; - } else if (altToPred[i] != SemanticContext.NONE) { + altToPred[i] = EmptySemanticContext.Instance; + } else if (altToPred[i] != EmptySemanticContext.Instance) { nPredAlts++; } } @@ -1266,7 +1266,7 @@ class ParserATNSimulator extends ATNSimulator { if (ambigAlts != null && ambigAlts[i]) { pairs.add(PredPrediction(pred!, i)); } - if (pred != SemanticContext.NONE) containsPredicate = true; + if (pred != EmptySemanticContext.Instance) containsPredicate = true; } if (!containsPredicate) { @@ -1370,7 +1370,7 @@ class ParserATNSimulator extends ATNSimulator { final succeeded = ATNConfigSet(configs.fullCtx); final failed = ATNConfigSet(configs.fullCtx); for (var c in configs) { - if (c.semanticContext != SemanticContext.NONE) { + if (c.semanticContext != EmptySemanticContext.Instance) { final predicateEvaluationResult = evalSemanticContextOne( c.semanticContext, outerContext, @@ -1390,7 +1390,7 @@ class ParserATNSimulator extends ATNSimulator { } /// Look through a list of predicate/alt pairs, returning alts for the - /// pairs that win. A [NONE] predicate indicates an alt containing an + /// pairs that win. A [Instance] predicate indicates an alt containing an /// unpredicated config which behaves as "always true." If !complete /// then we stop at the first predicate that evaluates to true. This /// includes pairs with null predicates. @@ -1401,7 +1401,7 @@ class ParserATNSimulator extends ATNSimulator { ) { final predictions = BitSet(); for (var pair in predPredictions) { - if (pair.pred == SemanticContext.NONE) { + if (pair.pred == EmptySemanticContext.Instance) { predictions.set(pair.alt); if (!complete) { break; @@ -1511,7 +1511,7 @@ class ParserATNSimulator extends ATNSimulator { ATNConfig.dup( config, state: config.state, - context: PredictionContext.EMPTY, + context: EmptyPredictionContext.Instance, ), mergeCache); continue; @@ -2431,7 +2431,7 @@ extension PredictionModeExtension on PredictionMode { // dup configs, tossing out semantic predicates final dup = ATNConfigSet(); for (var c in configs) { - c = ATNConfig.dup(c, semanticContext: SemanticContext.NONE); + c = ATNConfig.dup(c, semanticContext: EmptySemanticContext.Instance); dup.add(c); } configs = dup; diff --git a/runtime/Dart/lib/src/atn/src/semantic_context.dart b/runtime/Dart/lib/src/atn/src/semantic_context.dart index a78e1f6573..6661e8fef6 100644 --- a/runtime/Dart/lib/src/atn/src/semantic_context.dart +++ b/runtime/Dart/lib/src/atn/src/semantic_context.dart @@ -4,6 +4,7 @@ * can be found in the LICENSE.txt file in the project root. */ +import 'package:antlr4/src/atn/src/atn_simulator.dart'; import 'package:collection/collection.dart'; import '../../recognizer.dart'; @@ -17,10 +18,6 @@ import '../../util/murmur_hash.dart'; ///

    I have scoped the [AND], [OR], and [Predicate] subclasses of /// [SemanticContext] within the scope of this outer class.

    abstract class SemanticContext { - /// The default [SemanticContext], which is semantically equivalent to - /// a predicate of the form {@code {true}?}. - static const SemanticContext NONE = Predicate(); - const SemanticContext(); /// For context independent predicates, we evaluate them without a local @@ -60,8 +57,8 @@ abstract class SemanticContext { } static SemanticContext? and(SemanticContext? a, SemanticContext? b) { - if (a == null || a == NONE) return b; - if (b == null || b == NONE) return a; + if (a == null || a == EmptySemanticContext.Instance) return b; + if (b == null || b == EmptySemanticContext.Instance) return a; final result = AND(a, b); if (result.opnds.length == 1) { return result.opnds[0]; @@ -75,7 +72,7 @@ abstract class SemanticContext { static SemanticContext? or(SemanticContext? a, SemanticContext? b) { if (a == null) return b; if (b == null) return a; - if (a == NONE || b == NONE) return NONE; + if (a == EmptySemanticContext.Instance || b == EmptySemanticContext.Instance) return EmptySemanticContext.Instance; final result = OR(a, b); if (result.opnds.length == 1) { return result.opnds[0]; @@ -95,6 +92,17 @@ abstract class SemanticContext { } } +class EmptySemanticContext extends SemanticContext { + /// The default [SemanticContext], which is semantically equivalent to + /// a predicate of the form {@code {true}?}. + static const SemanticContext Instance = Predicate(); + + @override + bool eval(Recognizer parser, RuleContext? parserCallStack) { + return false; + } +} + class Predicate extends SemanticContext { final int ruleIndex; final int predIndex; @@ -150,7 +158,7 @@ class PrecedencePredicate extends SemanticContext RuleContext? parserCallStack, ) { if (parser.precpred(parserCallStack, precedence)) { - return SemanticContext.NONE; + return EmptySemanticContext.Instance; } else { return null; } @@ -273,7 +281,7 @@ class AND extends Operator { if (evaluated == null) { // The AND context is false if any element is false return null; - } else if (evaluated != SemanticContext.NONE) { + } else if (evaluated != EmptySemanticContext.Instance) { // Reduce the result by skipping true elements operands.add(evaluated); } @@ -285,7 +293,7 @@ class AND extends Operator { if (operands.isEmpty) { // all elements were true, so the AND context is true - return SemanticContext.NONE; + return EmptySemanticContext.Instance; } SemanticContext? result = operands[0]; @@ -374,9 +382,9 @@ class OR extends Operator { for (var context in opnds) { final evaluated = context.evalPrecedence(parser, parserCallStack); differs |= (evaluated != context); - if (evaluated == SemanticContext.NONE) { + if (evaluated == EmptySemanticContext.Instance) { // The OR context is true if any element is true - return SemanticContext.NONE; + return EmptySemanticContext.Instance; } else if (evaluated != null) { // Reduce the result by skipping false elements operands.add(evaluated); diff --git a/runtime/Dart/lib/src/ll1_analyzer.dart b/runtime/Dart/lib/src/ll1_analyzer.dart index 5ae5d2673b..b2821a7be6 100644 --- a/runtime/Dart/lib/src/ll1_analyzer.dart +++ b/runtime/Dart/lib/src/ll1_analyzer.dart @@ -38,7 +38,7 @@ class LL1Analyzer { _LOOK( s.transition(n).target, null, - PredictionContext.EMPTY, + EmptyPredictionContext.Instance, lookAlt, lookBusy, BitSet(), @@ -154,7 +154,7 @@ class LL1Analyzer { return; } - if (ctx != PredictionContext.EMPTY) { + if (ctx != EmptyPredictionContext.Instance) { // run thru all possible stack tops in ctx final removed = calledRuleStack[s.ruleIndex]; try { diff --git a/runtime/Dart/lib/src/prediction_context.dart b/runtime/Dart/lib/src/prediction_context.dart index 2e0843a91c..c069200ac6 100644 --- a/runtime/Dart/lib/src/prediction_context.dart +++ b/runtime/Dart/lib/src/prediction_context.dart @@ -13,10 +13,6 @@ import 'rule_context.dart'; import 'util/murmur_hash.dart'; abstract class PredictionContext { - /// Represents {@code $} in local context prediction, which means wildcard. - /// {@code *+x = *}. - static final EmptyPredictionContext EMPTY = EmptyPredictionContext(); - /// Represents {@code $} in an array in full context mode, when {@code $} /// doesn't mean wildcard: {@code $ + x = [$,x]}. Here, /// {@code $} = {@link #EMPTY_RETURN_STATE}. @@ -58,11 +54,11 @@ abstract class PredictionContext { // if we are in RuleContext of start rule, s, then PredictionContext // is EMPTY. Nobody called us. (if we are empty, return empty) if (outerContext.parent == null || outerContext == RuleContext.EMPTY) { - return PredictionContext.EMPTY; + return EmptyPredictionContext.Instance; } // If we have a parent, convert it to a PredictionContext graph - PredictionContext parent = EMPTY; + PredictionContext parent = EmptyPredictionContext.Instance; parent = PredictionContext.fromRuleContext(atn, outerContext.parent); final state = atn.states[outerContext.invokingState]!; @@ -81,7 +77,7 @@ abstract class PredictionContext { /// This means only the {@link #EMPTY} (wildcard? not sure) context is in set. */ bool get isEmpty { - return this == EMPTY; + return this == EmptyPredictionContext.Instance; } bool hasEmptyPath() { @@ -298,18 +294,18 @@ abstract class PredictionContext { bool rootIsWildcard, ) { if (rootIsWildcard) { - if (a == EMPTY) return EMPTY; // * + b = * - if (b == EMPTY) return EMPTY; // a + * = * + if (a == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // * + b = * + if (b == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // a + * = * } else { - if (a == EMPTY && b == EMPTY) return EMPTY; // $ + $ = $ - if (a == EMPTY) { + if (a == EmptyPredictionContext.Instance && b == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // $ + $ = $ + if (a == EmptyPredictionContext.Instance) { // $ + x = [x,$] final payloads = [b.returnState, EMPTY_RETURN_STATE]; final parents = [b.parent, null]; PredictionContext joined = ArrayPredictionContext(parents, payloads); return joined; } - if (b == EMPTY) { + if (b == EmptyPredictionContext.Instance) { // x + $ = [x,$] ($ is always last if present) final payloads = [a.returnState, EMPTY_RETURN_STATE]; final parents = [a.parent, null]; @@ -518,7 +514,7 @@ abstract class PredictionContext { } for (var current in nodes) { - if (current == EMPTY) continue; + if (current == EmptyPredictionContext.Instance) continue; for (var i = 0; i < current.length; i++) { if (current.getParent(i) == null) continue; final s = current.id.toString(); @@ -590,7 +586,7 @@ abstract class PredictionContext { PredictionContext updated; if (parents.isEmpty) { - updated = EMPTY; + updated = EmptyPredictionContext.Instance; } else if (parents.length == 1) { updated = SingletonPredictionContext.create( parents[0], context.getReturnState(0)); @@ -706,7 +702,7 @@ abstract class PredictionContext { } } stateNumber = p.getReturnState(index); - p = p.getParent(index) ?? EMPTY; + p = p.getParent(index) ?? EmptyPredictionContext.Instance; } localBuffer.write(']'); result.add(localBuffer.toString()); @@ -737,7 +733,7 @@ class SingletonPredictionContext extends PredictionContext { ) { if (returnState == PredictionContext.EMPTY_RETURN_STATE && parent == null) { // someone can pass in the bits of an array ctx that mean $ - return PredictionContext.EMPTY; + return EmptyPredictionContext.Instance; } return SingletonPredictionContext(parent, returnState); } @@ -789,6 +785,10 @@ class SingletonPredictionContext extends PredictionContext { } class EmptyPredictionContext extends SingletonPredictionContext { + /// Represents {@code $} in local context prediction, which means wildcard. + /// {@code *+x = *}. + static final EmptyPredictionContext Instance = EmptyPredictionContext(); + EmptyPredictionContext() : super(null, PredictionContext.EMPTY_RETURN_STATE); @override diff --git a/runtime/Go/antlr/parser_atn_simulator.go b/runtime/Go/antlr/parser_atn_simulator.go index 888d512975..01d50e1416 100644 --- a/runtime/Go/antlr/parser_atn_simulator.go +++ b/runtime/Go/antlr/parser_atn_simulator.go @@ -111,7 +111,7 @@ func (p *ParserATNSimulator) AdaptivePredict(input TokenStream, decision int, ou if s0 == nil { if outerContext == nil { - outerContext = RuleContextEmpty + outerContext = ParserRuleContextEmpty } if ParserATNSimulatorDebug || ParserATNSimulatorListATNDecisions { fmt.Println("predictATN decision " + strconv.Itoa(dfa.decision) + @@ -119,7 +119,7 @@ func (p *ParserATNSimulator) AdaptivePredict(input TokenStream, decision int, ou ", outerContext=" + outerContext.String(p.parser.GetRuleNames(), nil)) } fullCtx := false - s0Closure := p.computeStartState(dfa.atnStartState, RuleContextEmpty, fullCtx) + s0Closure := p.computeStartState(dfa.atnStartState, ParserRuleContextEmpty, fullCtx) p.atn.stateMu.Lock() if dfa.getPrecedenceDfa() { diff --git a/runtime/Go/antlr/parser_rule_context.go b/runtime/Go/antlr/parser_rule_context.go index 49cd10c5ff..780cb4dbb3 100644 --- a/runtime/Go/antlr/parser_rule_context.go +++ b/runtime/Go/antlr/parser_rule_context.go @@ -340,7 +340,7 @@ func (prc *BaseParserRuleContext) String(ruleNames []string, stop RuleContext) s return s } -var RuleContextEmpty = NewBaseParserRuleContext(nil, -1) +var ParserRuleContextEmpty = NewBaseParserRuleContext(nil, -1) type InterpreterRuleContext interface { ParserRuleContext diff --git a/runtime/Go/antlr/prediction_context.go b/runtime/Go/antlr/prediction_context.go index 9fdfd52b26..f85d202a4a 100644 --- a/runtime/Go/antlr/prediction_context.go +++ b/runtime/Go/antlr/prediction_context.go @@ -343,11 +343,11 @@ func (a *ArrayPredictionContext) String() string { // / func predictionContextFromRuleContext(a *ATN, outerContext RuleContext) PredictionContext { if outerContext == nil { - outerContext = RuleContextEmpty + outerContext = ParserRuleContextEmpty } // if we are in RuleContext of start rule, s, then BasePredictionContext // is EMPTY. Nobody called us. (if we are empty, return empty) - if outerContext.GetParent() == nil || outerContext == RuleContextEmpty { + if outerContext.GetParent() == nil || outerContext == ParserRuleContextEmpty { return BasePredictionContextEMPTY } // If we have a parent, convert it to a BasePredictionContext graph diff --git a/runtime/Go/antlr/semantic_context.go b/runtime/Go/antlr/semantic_context.go index 9ada430779..4c54429fd6 100644 --- a/runtime/Go/antlr/semantic_context.go +++ b/runtime/Go/antlr/semantic_context.go @@ -78,7 +78,7 @@ func NewPredicate(ruleIndex, predIndex int, isCtxDependent bool) *Predicate { //The default {@link SemanticContext}, which is semantically equivalent to //a predicate of the form {@code {true}?}. -var SemanticContextNone SemanticContext = NewPredicate(-1, -1, false) +var SemanticContextNone = NewPredicate(-1, -1, false) func (p *Predicate) evalPrecedence(parser Recognizer, outerContext RuleContext) SemanticContext { return p diff --git a/runtime/Python2/src/antlr4/atn/SemanticContext.py b/runtime/Python2/src/antlr4/atn/SemanticContext.py index 03b6ebd3d8..22ec04221e 100644 --- a/runtime/Python2/src/antlr4/atn/SemanticContext.py +++ b/runtime/Python2/src/antlr4/atn/SemanticContext.py @@ -92,6 +92,9 @@ def orContext(a, b): def filterPrecedencePredicates(collection): return [context for context in collection if isinstance(context, PrecedencePredicate)] +class EmptySemanticContext(SemanticContext): + pass + class Predicate(SemanticContext): def __init__(self, ruleIndex=-1, predIndex=-1, isCtxDependent=False): @@ -315,4 +318,4 @@ def __unicode__(self): return buf.getvalue() -SemanticContext.NONE = Predicate() \ No newline at end of file +SemanticContext.NONE = EmptySemanticContext() diff --git a/runtime/Python3/src/antlr4/atn/SemanticContext.py b/runtime/Python3/src/antlr4/atn/SemanticContext.py index 8f4dc31088..cd0d5ee417 100644 --- a/runtime/Python3/src/antlr4/atn/SemanticContext.py +++ b/runtime/Python3/src/antlr4/atn/SemanticContext.py @@ -94,6 +94,9 @@ def filterPrecedencePredicates(collection:set): return [context for context in collection if isinstance(context, PrecedencePredicate)] +class EmptySemanticContext(SemanticContext): + pass + class Predicate(SemanticContext): __slots__ = ('ruleIndex', 'predIndex', 'isCtxDependent') @@ -320,4 +323,4 @@ def __str__(self): return buf.getvalue() -SemanticContext.NONE = Predicate() +SemanticContext.NONE = EmptySemanticContext() From 8861ac4e7721a5a55fa6194d779f02e6af62f96b Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 17 Jun 2022 22:18:15 +0300 Subject: [PATCH 06/20] Use AtomicInteger instead of int for static PredictionContext.globalNodeCount Signed-off-by: Ivan Kochurkin --- .../Java/src/org/antlr/v4/runtime/atn/PredictionContext.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java index 8fe349d6a6..b5f204c237 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java @@ -20,6 +20,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; public abstract class PredictionContext { /** @@ -31,8 +32,8 @@ public abstract class PredictionContext { private static final int INITIAL_HASH = 1; - public static int globalNodeCount = 0; - public final int id = globalNodeCount++; + private static final AtomicInteger globalNodeCount = new AtomicInteger(); + public final int id = globalNodeCount.getAndIncrement(); /** * Stores the computed hash code of this {@link PredictionContext}. The hash From 3c30268bcae9fa50aacb5cc908826b018ad99be8 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 17 Jun 2022 19:14:52 +0300 Subject: [PATCH 07/20] Remove lock in Generator (now ANTLR tool is thread-safe) Signed-off-by: Ivan Kochurkin --- .../test/org/antlr/v4/test/runtime/Generator.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/Generator.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/Generator.java index 697b9d7e9d..18bd776494 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/Generator.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/Generator.java @@ -19,9 +19,6 @@ import static org.antlr.v4.test.runtime.FileUtils.writeFile; public class Generator { - /** ANTLR isn't thread-safe to process grammars so we use a global lock for testing */ - public static final Object antlrLock = new Object(); - /** Write a grammar to tmpdir and run antlr */ public static ErrorQueue antlrOnString(String workdir, String targetName, @@ -69,9 +66,7 @@ public static ErrorQueue antlrOnString(String workdir, if (defaultListener) { antlr.addListener(new DefaultToolListener(antlr)); } - synchronized (antlrLock) { - antlr.processGrammarsOnCommandLine(); - } + antlr.processGrammarsOnCommandLine(); List errors = new ArrayList<>(); From 7c5b3c2860b243ff59d92b80039246c473768cc8 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 26 Nov 2021 23:05:42 +0300 Subject: [PATCH 08/20] Cache templates for codegen to static field, load only one time Remove useless loadTemplates overloads Signed-off-by: Ivan Kochurkin --- tool/src/org/antlr/v4/codegen/Target.java | 30 ++++++++------ .../antlr/v4/codegen/target/CSharpTarget.java | 35 ---------------- .../antlr/v4/codegen/target/CppTarget.java | 41 ------------------- .../antlr/v4/codegen/target/DartTarget.java | 15 +------ .../org/antlr/v4/codegen/target/GoTarget.java | 20 --------- .../v4/codegen/target/JavaScriptTarget.java | 25 ----------- .../antlr/v4/codegen/target/JavaTarget.java | 25 ----------- .../antlr/v4/codegen/target/PHPTarget.java | 10 ----- .../v4/codegen/target/Python2Target.java | 17 -------- .../v4/codegen/target/Python3Target.java | 17 -------- .../antlr/v4/codegen/target/SwiftTarget.java | 32 --------------- 11 files changed, 18 insertions(+), 249 deletions(-) diff --git a/tool/src/org/antlr/v4/codegen/Target.java b/tool/src/org/antlr/v4/codegen/Target.java index 618d891a21..2f6b6f522d 100644 --- a/tool/src/org/antlr/v4/codegen/Target.java +++ b/tool/src/org/antlr/v4/codegen/Target.java @@ -18,12 +18,7 @@ import org.antlr.v4.tool.Grammar; import org.antlr.v4.tool.Rule; import org.antlr.v4.tool.ast.GrammarAST; -import org.stringtemplate.v4.NumberRenderer; -import org.stringtemplate.v4.ST; -import org.stringtemplate.v4.STErrorListener; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.STGroupFile; -import org.stringtemplate.v4.StringRenderer; +import org.stringtemplate.v4.*; import org.stringtemplate.v4.misc.STMessage; import java.util.HashMap; @@ -32,8 +27,9 @@ /** */ public abstract class Target { + private final static Map languageTemplates = new HashMap<>(); + protected final CodeGenerator gen; - private STGroup templates; protected static final Map defaultCharValueEscape; static { @@ -92,14 +88,22 @@ public String getVersion() { } public STGroup getTemplates() { + String language = getLanguage(); + STGroup templates = languageTemplates.get(language); + if (templates == null) { - String version = getVersion(); - if ( version==null || - !RuntimeMetaData.getMajorMinorVersion(version).equals(RuntimeMetaData.getMajorMinorVersion(Tool.VERSION))) - { - gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, getLanguage()); + synchronized (languageTemplates) { + templates = languageTemplates.get(language); + if (templates == null) { + String version = getVersion(); + if (version == null || + !RuntimeMetaData.getMajorMinorVersion(version).equals(RuntimeMetaData.getMajorMinorVersion(Tool.VERSION))) { + gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, language); + } + templates = loadTemplates(); + languageTemplates.put(language, templates); + } } - templates = loadTemplates(); } return templates; diff --git a/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java b/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java index 3d15c1cb14..c6212df68f 100644 --- a/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java @@ -136,41 +136,6 @@ protected String escapeWord(String word) { return "@" + word; } - @Override - protected STGroup loadTemplates() { - // override the superclass behavior to put all C# templates in the same folder - STGroup result = new STGroupFile(CodeGenerator.TEMPLATE_ROOT+"/CSharp/"+ getLanguage()+STGroup.GROUP_FILE_EXTENSION); - result.registerRenderer(Integer.class, new NumberRenderer()); - result.registerRenderer(String.class, new StringRenderer()); - result.setListener(new STErrorListener() { - @Override - public void compileTimeError(STMessage msg) { - reportError(msg); - } - - @Override - public void runTimeError(STMessage msg) { - reportError(msg); - } - - @Override - public void IOError(STMessage msg) { - reportError(msg); - } - - @Override - public void internalError(STMessage msg) { - reportError(msg); - } - - private void reportError(STMessage msg) { - getCodeGenerator().tool.errMgr.toolError(ErrorType.STRING_TEMPLATE_WARNING, msg.cause, msg.toString()); - } - }); - - return result; - } - @Override public boolean isATNSerializedAsInts() { return true; diff --git a/tool/src/org/antlr/v4/codegen/target/CppTarget.java b/tool/src/org/antlr/v4/codegen/target/CppTarget.java index d5cc5fff57..f8fc4d2702 100644 --- a/tool/src/org/antlr/v4/codegen/target/CppTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/CppTarget.java @@ -8,14 +8,7 @@ import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.codegen.Target; -import org.antlr.v4.tool.ErrorType; -import org.stringtemplate.v4.NumberRenderer; import org.stringtemplate.v4.ST; -import org.stringtemplate.v4.STErrorListener; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; -import org.stringtemplate.v4.misc.STMessage; - import java.util.*; public class CppTarget extends Target { @@ -124,38 +117,4 @@ public String getBaseVisitorFileName(boolean header) { String listenerName = gen.g.name + "BaseVisitor"; return listenerName+extST.render(); } - - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(Integer.class, new NumberRenderer()); - result.registerRenderer(String.class, new StringRenderer()); - result.setListener(new STErrorListener() { - @Override - public void compileTimeError(STMessage msg) { - reportError(msg); - } - - @Override - public void runTimeError(STMessage msg) { - reportError(msg); - } - - @Override - public void IOError(STMessage msg) { - reportError(msg); - } - - @Override - public void internalError(STMessage msg) { - reportError(msg); - } - - private void reportError(STMessage msg) { - getCodeGenerator().tool.errMgr.toolError(ErrorType.STRING_TEMPLATE_WARNING, msg.cause, msg.toString()); - } - }); - - return result; - } } diff --git a/tool/src/org/antlr/v4/codegen/target/DartTarget.java b/tool/src/org/antlr/v4/codegen/target/DartTarget.java index 64dc2ee3b5..30e1209f63 100644 --- a/tool/src/org/antlr/v4/codegen/target/DartTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/DartTarget.java @@ -8,18 +8,13 @@ import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.codegen.Target; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; import java.util.*; public class DartTarget extends Target { protected static final Map targetCharValueEscape; static { - HashMap map = new HashMap<>(); - for (Map.Entry entry : defaultCharValueEscape.entrySet()) { - map.put(entry.getKey(), entry.getValue()); - } + HashMap map = new HashMap<>(defaultCharValueEscape); addEscapedChar(map, '$'); targetCharValueEscape = map; } @@ -63,14 +58,6 @@ public Set getReservedWords() { return reservedWords; } - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(String.class, new StringRenderer(), true); - - return result; - } - @Override public boolean isATNSerializedAsInts() { return true; diff --git a/tool/src/org/antlr/v4/codegen/target/GoTarget.java b/tool/src/org/antlr/v4/codegen/target/GoTarget.java index 21eb79fc4d..672507883b 100644 --- a/tool/src/org/antlr/v4/codegen/target/GoTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/GoTarget.java @@ -99,13 +99,6 @@ public int getInlineTestSetWordSize() { return 32; } - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(String.class, new JavaStringRenderer(), true); - return result; - } - public String getRecognizerFileName(boolean header) { CodeGenerator gen = getCodeGenerator(); Grammar g = gen.g; @@ -164,17 +157,4 @@ public String getBaseVisitorFileName(boolean header) { assert g.name != null; return g.name.toLowerCase()+"_base_visitor.go"; } - - protected static class JavaStringRenderer extends StringRenderer { - @Override - public String toString(Object o, String formatString, Locale locale) { - - if ("java-escape".equals(formatString)) { - // 5C is the hex code for the \ itself - return ((String)o).replace("\\u", "\\u005Cu"); - } - - return super.toString(o, formatString, locale); - } - } } diff --git a/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java b/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java index d199f3ed5f..ae30501414 100644 --- a/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java @@ -8,13 +8,9 @@ import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.codegen.Target; -import org.antlr.v4.tool.ast.GrammarAST; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; import java.util.Arrays; import java.util.HashSet; -import java.util.Locale; import java.util.Set; public class JavaScriptTarget extends Target { @@ -56,27 +52,6 @@ public int getInlineTestSetWordSize() { return 32; } - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(String.class, new JavaStringRenderer(), true); - return result; - } - - protected static class JavaStringRenderer extends StringRenderer { - - @Override - public String toString(Object o, String formatString, Locale locale) { - if ("java-escape".equals(formatString)) { - // 5C is the hex code for the \ itself - return ((String)o).replace("\\u", "\\u005Cu"); - } - - return super.toString(o, formatString, locale); - } - - } - @Override public boolean wantsBaseListener() { return false; diff --git a/tool/src/org/antlr/v4/codegen/target/JavaTarget.java b/tool/src/org/antlr/v4/codegen/target/JavaTarget.java index 869c98e445..4b1a12b7c7 100644 --- a/tool/src/org/antlr/v4/codegen/target/JavaTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/JavaTarget.java @@ -49,31 +49,6 @@ public int getSerializedATNSegmentLimit() { return 65535 / 3; } - @Override - protected STGroup loadTemplates() { - STGroup result = targetTemplates.get(); - if (result == null) { - result = super.loadTemplates(); - result.registerRenderer(String.class, new JavaStringRenderer(), true); - targetTemplates.set(result); - } - - return result; - } - - protected static class JavaStringRenderer extends StringRenderer { - - @Override - public String toString(Object o, String formatString, Locale locale) { - if ("java-escape".equals(formatString)) { - // 5C is the hex code for the \ itself - return ((String)o).replace("\\u", "\\u005Cu"); - } - - return super.toString(o, formatString, locale); - } - } - @Override public boolean isATNSerializedAsInts() { return false; diff --git a/tool/src/org/antlr/v4/codegen/target/PHPTarget.java b/tool/src/org/antlr/v4/codegen/target/PHPTarget.java index b8c362f2f6..4f9188df3b 100644 --- a/tool/src/org/antlr/v4/codegen/target/PHPTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/PHPTarget.java @@ -8,8 +8,6 @@ import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.codegen.Target; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; import java.util.*; @@ -73,14 +71,6 @@ protected Set getReservedWords() { return reservedWords; } - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(String.class, new StringRenderer(), true); - - return result; - } - @Override public boolean supportsOverloadedMethods() { return false; diff --git a/tool/src/org/antlr/v4/codegen/target/Python2Target.java b/tool/src/org/antlr/v4/codegen/target/Python2Target.java index 6f4ac59041..9efa20f448 100644 --- a/tool/src/org/antlr/v4/codegen/target/Python2Target.java +++ b/tool/src/org/antlr/v4/codegen/target/Python2Target.java @@ -8,8 +8,6 @@ import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.codegen.Target; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; import java.util.*; @@ -76,21 +74,6 @@ protected Set getReservedWords() { return reservedWords; } - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(String.class, new PythonStringRenderer(), true); - return result; - } - - protected static class PythonStringRenderer extends StringRenderer { - - @Override - public String toString(Object o, String formatString, Locale locale) { - return super.toString(o, formatString, locale); - } - } - @Override public boolean wantsBaseListener() { return false; diff --git a/tool/src/org/antlr/v4/codegen/target/Python3Target.java b/tool/src/org/antlr/v4/codegen/target/Python3Target.java index c4684cc68b..768dbc1ef7 100644 --- a/tool/src/org/antlr/v4/codegen/target/Python3Target.java +++ b/tool/src/org/antlr/v4/codegen/target/Python3Target.java @@ -8,8 +8,6 @@ import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.codegen.Target; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; import java.util.*; @@ -75,21 +73,6 @@ protected Set getReservedWords() { return reservedWords; } - @Override - protected STGroup loadTemplates() { - STGroup result = super.loadTemplates(); - result.registerRenderer(String.class, new PythonStringRenderer(), true); - return result; - } - - protected static class PythonStringRenderer extends StringRenderer { - - @Override - public String toString(Object o, String formatString, Locale locale) { - return super.toString(o, formatString, locale); - } - } - @Override public boolean wantsBaseListener() { return false; diff --git a/tool/src/org/antlr/v4/codegen/target/SwiftTarget.java b/tool/src/org/antlr/v4/codegen/target/SwiftTarget.java index aee548b335..320bc786df 100644 --- a/tool/src/org/antlr/v4/codegen/target/SwiftTarget.java +++ b/tool/src/org/antlr/v4/codegen/target/SwiftTarget.java @@ -10,22 +10,14 @@ import org.antlr.v4.codegen.Target; import org.antlr.v4.tool.Grammar; import org.stringtemplate.v4.ST; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.StringRenderer; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.Locale; import java.util.Map; import java.util.Set; public class SwiftTarget extends Target { - /** - * The Swift target can cache the code generation templates. - */ - private static final ThreadLocal targetTemplates = new ThreadLocal<>(); - protected static final Map targetCharValueEscape; static { // https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html @@ -78,30 +70,6 @@ protected void genFile(Grammar g, ST outputFileST, String fileName) { super.genFile(g,outputFileST,fileName); } - @Override - protected STGroup loadTemplates() { - STGroup result = targetTemplates.get(); - if (result == null) { - result = super.loadTemplates(); - result.registerRenderer(String.class, new SwiftStringRenderer(), true); - targetTemplates.set(result); - } - - return result; - } - - protected static class SwiftStringRenderer extends StringRenderer { - @Override - public String toString(Object o, String formatString, Locale locale) { - if ("java-escape".equals(formatString)) { - // 5C is the hex code for the \ itself - return ((String)o).replace("\\u", "\\u005Cu"); - } - - return super.toString(o, formatString, locale); - } - } - @Override public boolean isATNSerializedAsInts() { return true; From f19cd678bcfa28d9774d734135b996885655445d Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sat, 18 Jun 2022 16:14:14 +0300 Subject: [PATCH 09/20] Cache LeftRecursiveRules.stg to static field Signed-off-by: Ivan Kochurkin --- .../analysis/LeftRecursiveRuleAnalyzer.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java index 51abe78659..fb5969bc36 100644 --- a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java +++ b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java @@ -27,6 +27,7 @@ import org.stringtemplate.v4.STGroup; import org.stringtemplate.v4.STGroupFile; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -55,12 +56,24 @@ public static enum ASSOC { left, right } public GrammarAST retvals; - public STGroup recRuleTemplates; - public STGroup codegenTemplates; - public String language; + public final static STGroup recRuleTemplates; + public final STGroup codegenTemplates; + public final String language; public Map altAssociativity = new HashMap(); + static { + String templateGroupFile = "org/antlr/v4/tool/templates/LeftRecursiveRules.stg"; + recRuleTemplates = new STGroupFile(templateGroupFile); + if (!recRuleTemplates.isDefined("recRule")) { + try { + throw new FileNotFoundException("can't find code generation templates: LeftRecursiveRules"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + } + public LeftRecursiveRuleAnalyzer(GrammarAST ruleAST, Tool tool, String ruleName, String language) { @@ -73,16 +86,6 @@ public LeftRecursiveRuleAnalyzer(GrammarAST ruleAST, throw new NullPointerException("grammar must have a token stream"); } - loadPrecRuleTemplates(); - } - - public void loadPrecRuleTemplates() { - String templateGroupFile = "org/antlr/v4/tool/templates/LeftRecursiveRules.stg"; - recRuleTemplates = new STGroupFile(templateGroupFile); - if ( !recRuleTemplates.isDefined("recRule") ) { - tool.errMgr.toolError(ErrorType.MISSING_CODE_GEN_TEMPLATES, "LeftRecursiveRules"); - } - // use codegen to get correct language templates; that's it though codegenTemplates = CodeGenerator.create(tool, null, language).getTemplates(); } From 6681a7a6b0e0b276578029f6ce2ff377e9cbf85f Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sat, 18 Jun 2022 16:36:52 +0300 Subject: [PATCH 10/20] Cache ANTLR error message templates to static field Signed-off-by: Ivan Kochurkin --- tool/src/org/antlr/v4/tool/ErrorManager.java | 53 +++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/tool/src/org/antlr/v4/tool/ErrorManager.java b/tool/src/org/antlr/v4/tool/ErrorManager.java index 9f1c65afab..242b3871c9 100644 --- a/tool/src/org/antlr/v4/tool/ErrorManager.java +++ b/tool/src/org/antlr/v4/tool/ErrorManager.java @@ -16,9 +16,12 @@ import java.net.URL; import java.util.Collection; import java.util.EnumSet; +import java.util.HashMap; import java.util.Set; public class ErrorManager { + private final static HashMap loadedFormats = new HashMap<>(); + public static final String FORMATS_DIR = "org/antlr/v4/tool/templates/messages/formats/"; public Tool tool; @@ -216,25 +219,37 @@ public void emit(ErrorType etype, ANTLRMessage msg) { * Otherwise we just use the default "antlr". */ public void setFormat(String formatName) { - this.formatName = formatName; - String fileName = FORMATS_DIR +formatName+STGroup.GROUP_FILE_EXTENSION; - 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(url, "UTF-8", '<', '>'); - format.load(); + STGroupFile loadedFormat = loadedFormats.get(formatName); + if (loadedFormat == null) { + synchronized (loadedFormats) { + loadedFormat = loadedFormats.get(formatName); + if (loadedFormat == null) { + String fileName = FORMATS_DIR +formatName+STGroup.GROUP_FILE_EXTENSION; + 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; + } + loadedFormat = new STGroupFile(url, "UTF-8", '<', '>'); + loadedFormat.load(); + + loadedFormats.put(formatName, loadedFormat); + } + } + } + + this.formatName = formatName; + this.format = loadedFormat; if ( !initSTListener.errors.isEmpty() ) { rawError("ANTLR installation corrupted; can't load messages format file:\n"+ From 93970f4bd0b205e461aa54847d00d61c0325d4b9 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sat, 18 Jun 2022 17:53:27 +0300 Subject: [PATCH 11/20] Cache runtime test templates to static field Signed-off-by: Ivan Kochurkin --- .../antlr/v4/test/runtime/RuntimeTests.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java index 701b60d9b2..dd3c56c44b 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java @@ -15,13 +15,11 @@ import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; -import org.stringtemplate.v4.ST; -import org.stringtemplate.v4.STGroup; -import org.stringtemplate.v4.STGroupString; -import org.stringtemplate.v4.StringRenderer; +import org.stringtemplate.v4.*; import java.io.File; import java.io.IOException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; @@ -44,9 +42,9 @@ public abstract class RuntimeTests { protected abstract RuntimeRunner createRuntimeRunner(); - private final static StringRenderer rendered = new StringRenderer(); - private final static HashMap testDescriptors = new HashMap<>(); + private final static HashMap cachedTargetTemplates = new HashMap<>(); + private final static StringRenderer rendered = new StringRenderer(); static { File descriptorsDir = new File(Paths.get(RuntimeTestUtils.resourcePath.toString(), "org/antlr/v4/test/runtime/descriptors").toString()); @@ -120,10 +118,20 @@ private static void test(RuntimeTestDescriptor descriptor, RuntimeRunner runner) FileUtils.mkdir(runner.getTempDirPath()); - String sourceName = "org/antlr/v4/test/runtime/templates/" + targetName + ".test.stg"; - String template = RuntimeTestUtils.getTextFromResource(sourceName); - STGroup targetTemplates = new STGroupString(sourceName, template, '<', '>'); - targetTemplates.registerRenderer(String.class, rendered); + STGroup targetTemplates = cachedTargetTemplates.get(targetName); + if (targetTemplates == null) { + synchronized (cachedTargetTemplates) { + targetTemplates = cachedTargetTemplates.get(targetName); + if (targetTemplates == null) { + ClassLoader classLoader = RuntimeTests.class.getClassLoader(); + URL templates = classLoader.getResource("org/antlr/v4/test/runtime/templates/" + targetName + ".test.stg"); + assert templates != null; + targetTemplates = new STGroupFile(templates, "UTF-8", '<', '>'); + targetTemplates.registerRenderer(String.class, rendered); + cachedTargetTemplates.put(targetName, targetTemplates); + } + } + } // write out any slave grammars List> slaveGrammars = descriptor.slaveGrammars; From 1b9d77c0325305ffc9ca56cd449c1c2d11727143 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 19 Jun 2022 21:41:31 +0300 Subject: [PATCH 12/20] Clarify message of failed runtime tests Signed-off-by: Ivan Kochurkin --- .../org/antlr/v4/test/runtime/Processor.java | 10 ++++--- .../antlr/v4/test/runtime/RuntimeRunner.java | 10 ++++--- .../v4/test/runtime/RuntimeTestUtils.java | 11 ++++++++ .../antlr/v4/test/runtime/RuntimeTests.java | 27 +++++++++++-------- .../test/runtime/states/GeneratedState.java | 14 ++++------ 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/Processor.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/Processor.java index 01086c4385..b53d5dbc98 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/Processor.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/Processor.java @@ -11,6 +11,8 @@ import java.util.HashMap; import java.util.Map; +import static org.antlr.v4.test.runtime.RuntimeTestUtils.joinLines; + public class Processor { public final String[] arguments; public final String workingDirectory; @@ -19,11 +21,11 @@ public class Processor { public static ProcessorResult run(String[] arguments, String workingDirectory, Map environmentVariables ) throws InterruptedException, IOException { - return new Processor(arguments, workingDirectory, environmentVariables, true).Start(); + return new Processor(arguments, workingDirectory, environmentVariables, true).start(); } public static ProcessorResult run(String[] arguments, String workingDirectory) throws InterruptedException, IOException { - return new Processor(arguments, workingDirectory, new HashMap<>(), true).Start(); + return new Processor(arguments, workingDirectory, new HashMap<>(), true).start(); } public Processor(String[] arguments, String workingDirectory, Map environmentVariables, @@ -34,7 +36,7 @@ public Processor(String[] arguments, String workingDirectory, Map getGeneratedFiles(RunOptions runOptions) { diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java index 4b9682bab5..fd6a7da38b 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java @@ -107,4 +107,15 @@ public static void checkRuleATN(Grammar g, String ruleName, String expecting) { assertEquals(expecting, result); } + + public static String joinLines(Object... args) { + StringBuilder result = new StringBuilder(); + for (Object arg : args) { + String str = arg.toString(); + result.append(str); + if (!str.endsWith("\n")) + result.append("\n"); + } + return result.toString(); + } } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java index dd3c56c44b..d000df6010 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java @@ -29,6 +29,7 @@ import java.util.stream.Stream; import static org.antlr.v4.test.runtime.FileUtils.writeFile; +import static org.antlr.v4.test.runtime.RuntimeTestUtils.joinLines; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; import static org.junit.jupiter.api.DynamicTest.dynamicTest; @@ -99,7 +100,11 @@ public List runtimeTests() { for (RuntimeTestDescriptor descriptor : descriptors) { descriptorTests.add(dynamicTest(descriptor.name, () -> { try (RuntimeRunner runner = createRuntimeRunner()) { - test(descriptor, runner); + String errorMessage = test(descriptor, runner); + if (errorMessage != null) { + runner.setSaveTestDir(true); + fail(joinLines("Test: " + descriptor.name + "; " + errorMessage, "Test directory: " + runner.getTempDirPath())); + } } })); } @@ -109,11 +114,11 @@ public List runtimeTests() { return result; } - private static void test(RuntimeTestDescriptor descriptor, RuntimeRunner runner) { + private static String test(RuntimeTestDescriptor descriptor, RuntimeRunner runner) { String targetName = runner.getLanguage(); if (descriptor.ignore(targetName)) { System.out.println("Ignore " + descriptor); - return; + return null; } FileUtils.mkdir(runner.getTempDirPath()); @@ -198,21 +203,19 @@ private static void test(RuntimeTestDescriptor descriptor, RuntimeRunner runner) State result = runner.run(runOptions); - assertCorrectOutput(descriptor, targetName, result); + return assertCorrectOutput(descriptor, targetName, result); } - private static void assertCorrectOutput(RuntimeTestDescriptor descriptor, String targetName, State state) { + private static String assertCorrectOutput(RuntimeTestDescriptor descriptor, String targetName, State state) { ExecutedState executedState; if (state instanceof ExecutedState) { executedState = (ExecutedState)state; if (executedState.exception != null) { - fail(state.getErrorMessage()); - return; + return state.getErrorMessage(); } } else { - fail(state.getErrorMessage()); - return; + return state.getErrorMessage(); } String expectedOutput = descriptor.output; @@ -229,10 +232,12 @@ private static void assertCorrectOutput(RuntimeTestDescriptor descriptor, String "expectedOutput:<" + expectedOutput + ">; actualOutput:<" + executedState.output + ">; "; } - fail("[" + targetName + ":" + descriptor.name + "] " + + return "[" + targetName + ":" + descriptor.name + "] " + message + "expectedParseErrors:<" + expectedParseErrors + ">;" + - "actualParseErrors:<" + executedState.errors + ">."); + "actualParseErrors:<" + executedState.errors + ">."; } + + return null; } } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java index 3a70f3dc95..faecec5c62 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/states/GeneratedState.java @@ -9,11 +9,10 @@ import org.antlr.v4.test.runtime.ErrorQueue; import org.antlr.v4.test.runtime.GeneratedFile; import org.antlr.v4.test.runtime.Stage; -import org.antlr.v4.tool.ANTLRMessage; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; + +import static org.antlr.v4.test.runtime.RuntimeTestUtils.joinLines; public class GeneratedState extends State { @Override @@ -30,16 +29,13 @@ public boolean containsErrors() { } public String getErrorMessage() { - if (exception != null) { - return exception.toString(); - } + String result = super.getErrorMessage(); if (errorQueue.errors.size() > 0) { - List errors = errorQueue.errors.stream().map(ANTLRMessage::toString).collect(Collectors.toList()); - return String.join("\n", errors); + result = joinLines(result, errorQueue.toString(true)); } - return null; + return result; } public GeneratedState(ErrorQueue errorQueue, List generatedFiles, Exception exception) { From 99fab5c8036d3f73858d5c9abdd0f43da235734c Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 20 Jun 2022 17:15:13 +0300 Subject: [PATCH 13/20] Pass testSourceUri to dynamicTest and to dynamicContainer for convenient navigation to tests data in IntelliJ Signed-off-by: Ivan Kochurkin --- .../antlr/v4/test/runtime/CustomDescriptors.java | 14 ++++++++++---- .../v4/test/runtime/RuntimeTestDescriptor.java | 7 ++++++- .../test/runtime/RuntimeTestDescriptorParser.java | 5 +++-- .../org/antlr/v4/test/runtime/RuntimeTests.java | 9 ++++++--- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java index 92dcb92585..de55462546 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/CustomDescriptors.java @@ -6,12 +6,18 @@ package org.antlr.v4.test.runtime; +import java.net.URI; +import java.nio.file.Paths; import java.util.*; public class CustomDescriptors { public final static HashMap descriptors; + private final static URI uri; static { + uri = Paths.get(RuntimeTestUtils.runtimeTestsuitePath.toString(), + "test", "org", "antlr", "v4", "test", "runtime", "CustomDescriptors.java").toUri(); + descriptors = new HashMap<>(); descriptors.put("LexerExec", new RuntimeTestDescriptor[]{ @@ -40,7 +46,7 @@ private static RuntimeTestDescriptor getLineSeparatorLfDescriptor() { "lexer grammar L;\n" + "T: ~'\\n'+;\n" + "SEPARATOR: '\\n';", - null, false, false, null); + null, false, false, null, uri); } private static RuntimeTestDescriptor getLineSeparatorCrLfDescriptor() { @@ -61,7 +67,7 @@ private static RuntimeTestDescriptor getLineSeparatorCrLfDescriptor() { "lexer grammar L;\n" + "T: ~'\\r'+;\n" + "SEPARATOR: '\\r\\n';", - null, false, false, null); + null, false, false, null, uri); } private static RuntimeTestDescriptor getLargeLexerDescriptor() { @@ -88,7 +94,7 @@ private static RuntimeTestDescriptor getLargeLexerDescriptor() { "", grammarName, grammar.toString(), - null, false, false, null); + null, false, false, null, uri); } private static RuntimeTestDescriptor getAtnStatesSizeMoreThan65535Descriptor() { @@ -138,6 +144,6 @@ private static RuntimeTestDescriptor getAtnStatesSizeMoreThan65535Descriptor() { grammarName, grammar.toString(), null, false, false, - new String[] {"CSharp", "Python2", "Python3", "Go", "PHP", "Swift", "JavaScript", "Dart"}); + new String[] {"CSharp", "Python2", "Python3", "Go", "PHP", "Swift", "JavaScript", "Dart"}, uri); } } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptor.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptor.java index 3ca9749a72..a4f377b8c2 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptor.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptor.java @@ -8,6 +8,7 @@ import org.antlr.v4.runtime.misc.Pair; +import java.net.URI; import java.util.Arrays; import java.util.List; @@ -52,11 +53,14 @@ public class RuntimeTestDescriptor { public final String[] skipTargets; + public final URI uri; + public RuntimeTestDescriptor(GrammarType testType, String name, String notes, String input, String output, String errors, String startRule, String grammarName, String grammar, List> slaveGrammars, - boolean showDFA, boolean showDiagnosticErrors, String[] skipTargets) { + boolean showDFA, boolean showDiagnosticErrors, String[] skipTargets, + URI uri) { this.testType = testType; this.name = name; this.notes = notes; @@ -70,6 +74,7 @@ public RuntimeTestDescriptor(GrammarType testType, String name, String notes, this.showDFA = showDFA; this.showDiagnosticErrors = showDiagnosticErrors; this.skipTargets = skipTargets != null ? skipTargets : new String[0]; + this.uri = uri; } /** Return true if this test should be ignored for the indicated target */ diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptorParser.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptorParser.java index 31602f23da..16867cbd41 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptorParser.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestDescriptorParser.java @@ -8,6 +8,7 @@ import org.antlr.v4.runtime.misc.Pair; +import java.net.URI; import java.util.*; public class RuntimeTestDescriptorParser { @@ -62,7 +63,7 @@ public class RuntimeTestDescriptorParser { a : b {}; b : B; */ - public static RuntimeTestDescriptor parse(String name, String text) throws RuntimeException { + public static RuntimeTestDescriptor parse(String name, String text, URI uri) throws RuntimeException { String currentField = null; StringBuilder currentValue = new StringBuilder(); @@ -162,7 +163,7 @@ else if ( value.indexOf('\n')>=0 ) { } } return new RuntimeTestDescriptor(testType, name, notes, input, output, errors, startRule, grammarName, grammar, - slaveGrammars, showDFA, showDiagnosticErrors, skipTargets); + slaveGrammars, showDFA, showDiagnosticErrors, skipTargets, uri); } /** Get A, B, or C from: diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java index d000df6010..54ad5b48bb 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -73,7 +74,7 @@ public abstract class RuntimeTests { } catch (IOException e) { throw new RuntimeException(e); } - descriptors.add(RuntimeTestDescriptorParser.parse(name, text)); + descriptors.add(RuntimeTestDescriptorParser.parse(name, text, descriptorFile.toURI())); } testDescriptors.put(groupName, descriptors.toArray(new RuntimeTestDescriptor[0])); @@ -98,7 +99,7 @@ public List runtimeTests() { ArrayList descriptorTests = new ArrayList<>(); RuntimeTestDescriptor[] descriptors = testDescriptors.get(group); for (RuntimeTestDescriptor descriptor : descriptors) { - descriptorTests.add(dynamicTest(descriptor.name, () -> { + descriptorTests.add(dynamicTest(descriptor.name, descriptor.uri, () -> { try (RuntimeRunner runner = createRuntimeRunner()) { String errorMessage = test(descriptor, runner); if (errorMessage != null) { @@ -108,7 +109,9 @@ public List runtimeTests() { } })); } - result.add(dynamicContainer(group, descriptorTests)); + + Path descriptorGroupPath = Paths.get(RuntimeTestUtils.resourcePath.toString(), "descriptors", group); + result.add(dynamicContainer(group, descriptorGroupPath.toUri(), Arrays.stream(descriptorTests.toArray(new DynamicNode[0])))); } return result; From 8e6e1ed41625927384cde02abfdb9168465745b6 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 20 Jun 2022 18:13:19 +0300 Subject: [PATCH 14/20] Extract prepareGrammars method Signed-off-by: Ivan Kochurkin --- .../antlr/v4/test/runtime/RuntimeTests.java | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java index 54ad5b48bb..9d2bd41fd8 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java @@ -126,40 +126,8 @@ private static String test(RuntimeTestDescriptor descriptor, RuntimeRunner runne FileUtils.mkdir(runner.getTempDirPath()); - STGroup targetTemplates = cachedTargetTemplates.get(targetName); - if (targetTemplates == null) { - synchronized (cachedTargetTemplates) { - targetTemplates = cachedTargetTemplates.get(targetName); - if (targetTemplates == null) { - ClassLoader classLoader = RuntimeTests.class.getClassLoader(); - URL templates = classLoader.getResource("org/antlr/v4/test/runtime/templates/" + targetName + ".test.stg"); - assert templates != null; - targetTemplates = new STGroupFile(templates, "UTF-8", '<', '>'); - targetTemplates.registerRenderer(String.class, rendered); - cachedTargetTemplates.put(targetName, targetTemplates); - } - } - } - - // write out any slave grammars - List> slaveGrammars = descriptor.slaveGrammars; - if ( slaveGrammars!=null ) { - for (Pair spair : slaveGrammars) { - STGroup g = new STGroup('<', '>'); - g.registerRenderer(String.class, rendered); - g.importTemplates(targetTemplates); - ST grammarST = new ST(g, spair.b); - writeFile(runner.getTempDirPath(), spair.a + ".g4", grammarST.render()); - } - } - String grammarName = descriptor.grammarName; - String grammar = descriptor.grammar; - STGroup g = new STGroup('<', '>'); - g.importTemplates(targetTemplates); - g.registerRenderer(String.class, rendered); - ST grammarST = new ST(g, grammar); - grammar = grammarST.render(); + String grammar = prepareGrammars(descriptor, runner); String lexerName, parserName; boolean useListenerOrVisitor; @@ -209,6 +177,42 @@ private static String test(RuntimeTestDescriptor descriptor, RuntimeRunner runne return assertCorrectOutput(descriptor, targetName, result); } + private static String prepareGrammars(RuntimeTestDescriptor descriptor, RuntimeRunner runner) { + String targetName = runner.getLanguage(); + STGroup targetTemplates = cachedTargetTemplates.get(targetName); + if (targetTemplates == null) { + synchronized (cachedTargetTemplates) { + targetTemplates = cachedTargetTemplates.get(targetName); + if (targetTemplates == null) { + ClassLoader classLoader = RuntimeTests.class.getClassLoader(); + URL templates = classLoader.getResource("org/antlr/v4/test/runtime/templates/" + targetName + ".test.stg"); + assert templates != null; + targetTemplates = new STGroupFile(templates, "UTF-8", '<', '>'); + targetTemplates.registerRenderer(String.class, rendered); + cachedTargetTemplates.put(targetName, targetTemplates); + } + } + } + + // write out any slave grammars + List> slaveGrammars = descriptor.slaveGrammars; + if ( slaveGrammars!=null ) { + for (Pair spair : slaveGrammars) { + STGroup g = new STGroup('<', '>'); + g.registerRenderer(String.class, rendered); + g.importTemplates(targetTemplates); + ST grammarST = new ST(g, spair.b); + writeFile(runner.getTempDirPath(), spair.a + ".g4", grammarST.render()); + } + } + + STGroup g = new STGroup('<', '>'); + g.importTemplates(targetTemplates); + g.registerRenderer(String.class, rendered); + ST grammarST = new ST(g, descriptor.grammar); + return grammarST.render(); + } + private static String assertCorrectOutput(RuntimeTestDescriptor descriptor, String targetName, State state) { ExecutedState executedState; if (state instanceof ExecutedState) { From 2e8e3a84247381421b7f76504bcb298989e0c123 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 20 Jun 2022 21:43:50 +0300 Subject: [PATCH 15/20] Remove useless and outdated files Signed-off-by: Ivan Kochurkin --- .gitmodules | 0 tool/MIGRATION.txt | 23 -------------------- tool/playground/Main.java | 44 --------------------------------------- tool/playground/T.g4 | 12 ----------- 4 files changed, 79 deletions(-) delete mode 100644 .gitmodules delete mode 100644 tool/MIGRATION.txt delete mode 100644 tool/playground/Main.java delete mode 100644 tool/playground/T.g4 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tool/MIGRATION.txt b/tool/MIGRATION.txt deleted file mode 100644 index c4aacd8fe0..0000000000 --- a/tool/MIGRATION.txt +++ /dev/null @@ -1,23 +0,0 @@ -Parsers - -* Full context LL(*) not SLL(*) -* Adaptive, takes all but indirect left-recursion - -Actions/scopes - -* no global scopes. no scope[n]. - -Trees - -* no ASTs -* no tree grammars -* parse trees created by default -* moved methods to Trees - -Lexers - -* Added [Abc] notation - -* unicode rule/token names - -* -> skip notation \ No newline at end of file diff --git a/tool/playground/Main.java b/tool/playground/Main.java deleted file mode 100644 index 8001d315a1..0000000000 --- a/tool/playground/Main.java +++ /dev/null @@ -1,44 +0,0 @@ -public class Main -{ -// public static void main(String[] args) -// { -// TParser parser = new TParser(new CommonTokenStream(new TLexer(new ANTLRInputStream("b")))); -// parser.addParseListener(new MyTBaseListener()); -// -// parser.a(); -// -// System.out.println("######################"); -// parser = new TParser(new CommonTokenStream(new TLexer(new ANTLRInputStream("x")))); -// parser.addParseListener(new MyTBaseListener()); -// parser.b(); -// } -// -// private static class MyTBaseListener extends TBaseListener { -// @Override -// public void enterAlt1(TParser.Alt1Context ctx) -// { -// System.out.println("entering alt1"); -// } -// -// @Override -// public void exitAlt1(TParser.Alt1Context ctx) -// { -// System.out.println("exiting alt1"); -// } -// -// @Override -// public void enterB(TParser.BContext ctx) { -// System.out.println("enter b"); -// } -// -// @Override -// public void exitB(TParser.BContext ctx) { -// System.out.println("exiting b"); -// } -// -// @Override -// public void enterEveryRule(ParserRuleContext ctx) { -// System.out.println("enterEveryRule"); -// } -// } -} diff --git a/tool/playground/T.g4 b/tool/playground/T.g4 deleted file mode 100644 index 47265ebbfc..0000000000 --- a/tool/playground/T.g4 +++ /dev/null @@ -1,12 +0,0 @@ -grammar T; - -a - : 'b' #alt1 - | 'c' #alt2 - ; - -b : 'x' | 'y' {} ; - -e : e '*' e - | 'foo' - ; From 22cbf3c37901caa08fdd8d7326d300c37342d6da Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 26 Jun 2022 01:16:47 +0300 Subject: [PATCH 16/20] Update pom.xml in runtime-testsuite Signed-off-by: Ivan Kochurkin --- runtime-testsuite/pom.xml | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/runtime-testsuite/pom.xml b/runtime-testsuite/pom.xml index 9e0a3d837c..5e379d9ab4 100644 --- a/runtime-testsuite/pom.xml +++ b/runtime-testsuite/pom.xml @@ -30,7 +30,7 @@ org.antlr ST4 - 4.3.1 + 4.3.3 test @@ -72,19 +72,6 @@ test - - - resources - - - ../runtime - - **/.build/** - **/target/** - Swift/*.xcodeproj/** - - - org.apache.maven.plugins @@ -93,18 +80,6 @@ -Dfile.encoding=UTF-8 - - **/csharp/Test*.java - **/java/Test*.java - **/java/api/Test*.java - **/go/Test*.java - **/javascript/Test*.java - **/python2/Test*.java - **/python3/Test*.java - **/php/Test*.java - **/dart/Test*.java - ${antlr.tests.swift} - From d287f1cf20bb53e1834072fff3f464bffcfd917b Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 26 Jun 2022 01:17:38 +0300 Subject: [PATCH 17/20] Use runtimePath instead of targetClassesPath for runtimes' sources Signed-off-by: Ivan Kochurkin --- .../test/org/antlr/v4/test/runtime/RuntimeRunner.java | 7 ++----- .../test/org/antlr/v4/test/runtime/RuntimeTestUtils.java | 2 ++ .../test/org/antlr/v4/test/runtime/swift/SwiftRunner.java | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeRunner.java index 9735c84baf..2a7e659392 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeRunner.java @@ -19,8 +19,7 @@ import java.util.*; import static org.antlr.v4.test.runtime.FileUtils.*; -import static org.antlr.v4.test.runtime.RuntimeTestUtils.FileSeparator; -import static org.antlr.v4.test.runtime.RuntimeTestUtils.TempDirectory; +import static org.antlr.v4.test.runtime.RuntimeTestUtils.*; public abstract class RuntimeRunner implements AutoCloseable { public abstract String getLanguage(); @@ -93,7 +92,6 @@ public void close() { private final static Object runtimeInitLockObject = new Object(); public final static String cacheDirectory; - public final static Path targetClassesPath; private static class InitializationStatus { public final Object lockObject = new Object(); @@ -104,7 +102,6 @@ private static class InitializationStatus { private final static HashMap runtimeInitializationStatuses = new HashMap<>(); static { - targetClassesPath = Paths.get(RuntimeTestUtils.runtimeTestsuitePath.toString(), "target", "classes"); cacheDirectory = new File(System.getProperty("java.io.tmpdir"), "ANTLR-runtime-testsuite-cache").getAbsolutePath(); } @@ -121,7 +118,7 @@ protected final String getRuntimePath() { } public static String getRuntimePath(String language) { - return targetClassesPath.toString() + FileSeparator + language; + return runtimePath.toString() + FileSeparator + language; } public State run(RunOptions runOptions) { diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java index fd6a7da38b..1b0f262c0e 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java @@ -26,6 +26,7 @@ public abstract class RuntimeTestUtils { public static final String FileSeparator = System.getProperty("file.separator"); public static final String TempDirectory = System.getProperty("java.io.tmpdir"); + public final static Path runtimePath; public final static Path runtimeTestsuitePath; public final static Path resourcePath; @@ -49,6 +50,7 @@ public abstract class RuntimeTestUtils { runtimeTestsuitePath = Paths.get("..", "runtime-testsuite").normalize(); } + runtimePath = Paths.get(runtimeTestsuitePath.toString(), "..", "runtime").normalize(); resourcePath = Paths.get(runtimeTestsuitePath.toString(), "resources"); } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java index 9017054932..624d4605da 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/swift/SwiftRunner.java @@ -33,7 +33,7 @@ public String getTestFileName() { static { String swiftRuntimePath = getRuntimePath("Swift"); - antlrRuntimePath = Paths.get(swiftRuntimePath, "..", "..", "..", "..").normalize().toString(); + antlrRuntimePath = Paths.get(swiftRuntimePath, "..", "..").normalize().toString(); String dylibPath = antlrRuntimePath + "/.build/release/"; initPackageArgs = new String[]{"swift", "package", "init", "--type", "executable"}; buildProjectArgs = new String[]{"swift", "build", "-c", "release", "-Xswiftc", "-I" + dylibPath, "-Xlinker", "-L" + dylibPath, From f235d332fab5442fad2ddaa2084d80c6ac0b9a73 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 26 Jun 2022 01:18:18 +0300 Subject: [PATCH 18/20] Update doc Signed-off-by: Ivan Kochurkin --- doc/antlr-project-testing.md | 2 ++ doc/creating-a-language-target.md | 39 ++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/doc/antlr-project-testing.md b/doc/antlr-project-testing.md index ca5cffe443..a1b32949cc 100644 --- a/doc/antlr-project-testing.md +++ b/doc/antlr-project-testing.md @@ -87,6 +87,8 @@ And the result of testing the entire subdirectory: All test are run in parallel both via maven and via IDE. +In IntelliJ, it's very easy to go to source by right-click on any test and pressing `Jump to source` (F4). + ## Running test subsets From the `runtime-testsuite` dir diff --git a/doc/creating-a-language-target.md b/doc/creating-a-language-target.md index dd06208ea6..f22d83d415 100644 --- a/doc/creating-a-language-target.md +++ b/doc/creating-a-language-target.md @@ -6,19 +6,42 @@ This document describes how to make ANTLR generate parsers in a new language, *X Creating a new target involves the following key elements: -1. For the tool, create class *X*Target as a subclass of class `Target` in package `org.antlr.v4.codegen.target`. This class describes language specific details about escape characters and strings and so on. There is very little to do here typically. -1. Create *X*.stg in directory tool/resources/org/antlr/v4/tool/templates/codegen/*X*/*X*.stg. This is a [StringTemplate](http://www.stringtemplate.org/) group file (`.stg`) that tells ANTLR how to express all of the parsing elements needed to generate code. You will see templates called `ParserFile`, `Parser`, `Lexer`, `CodeBlockForAlt`, `AltBlock`, etc... Each of these must be described how to build the indicated chunk of code. Your best bet is to find the closest existing target, copy that template file, and tweak to suit. -1. Create a runtime library to support the parsers generated by ANTLR. Under directory runtime/*X*, you are in complete control of the directory structure as dictated by common usage of that target language. For example, Java has: `runtime/Java/lib` and `runtime/Java/src` directories. Under `src`, you will find a directory structure for package `org.antlr.v4.runtime` and below. -1. Create a template file for runtime tests. All you have to do is provide a few templates that indicate how to print values and declare variables. Our runtime test mechanism in dir `runtime-testsuite` will automatically generate code using these templates for each target and check the test results. It needs to know how to define various class fields, compare members and so on. You must create a *X*.test.stg file underneath [runtime-testsuite/resources/org/antlr/v4/test/runtime](https://github.com/antlr/antlr4/tree/master/runtime-testsuite/resources/org/antlr/v4/test/runtime). Again, your best bet is to copy the templates from the closest language to your target and tweak it to suit. -1. Create test files under [/runtime-testsuite/test/org/antlr/v4/test/runtime](https://github.com/antlr/antlr4/tree/master/runtime-testsuite/test/org/antlr/v4/test/runtime). They will load defined test cases in each test descriptor. Also add the `/runtime-testsuite/test/org/antlr/v4/test/runtime/X/BaseXTest.java` which defines how test cases will execute and output. -1. Create/edit shell scripts in [/.travis](https://github.com/antlr/antlr4/blob/master/.travis) and [/appveyor.yml](https://github.com/antlr/antlr4/blob/master/appveyor.yml) to run tests in CI pipelines. +1. For the tool, create class *X*Target as a subclass of class `Target` in package `org.antlr.v4.codegen.target`. + This class describes language specific details about escape characters and strings and so on. + There is very little to do here typically. +2. Create `*X*.stg` in directory `tool/resources/org/antlr/v4/tool/templates/codegen/*X*/*X*.stg`. + This is a [StringTemplate](http://www.stringtemplate.org/) group file (`.stg`) that tells ANTLR how to express + all the parsing elements needed to generate code. + You will see templates called `ParserFile`, `Parser`, `Lexer`, `CodeBlockForAlt`, `AltBlock`, etc... + Each of these must be described how to build the indicated chunk of code. + Your best bet is to find the closest existing target, copy that template file, and tweak to suit. +3. Create a runtime library to support the parsers generated by ANTLR. + Under directory `runtime/*X*`, you are in complete control of the directory structure as dictated by common usage of that target language. + For example, Java has: `runtime/Java/lib` and `runtime/Java/src` directories. + Under `src`, you will find a directory structure for package `org.antlr.v4.runtime` and below. +4. Create a template file for runtime tests. + All you have to do is provide a few templates that indicate how to print values and declare variables. + Our runtime test mechanism in dir `runtime-testsuite` will automatically generate code using these templates for each target and check the test results. + It needs to know how to define various class fields, compare members and so on. + You must create a `*X*.test.stg` file underneath [runtime-testsuite/resources/org/antlr/v4/test/runtime](../runtime-testsuite/resources/org/antlr/v4/test/runtime) + and `Test.*x*.stg` underneath [runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers](../runtime-testsuite/resources/org/antlr/v4/test/runtime/helpers). + Again, your best bet is to copy the templates from the closest language to your target and tweak it to suit. +6. Create test files under [/runtime-testsuite/test/org/antlr/v4/test/runtime](../runtime-testsuite/test/org/antlr/v4/test/runtime). + They will load defined test cases in each test descriptor. + Also add the `/runtime-testsuite/test/org/antlr/v4/test/runtime/X/BaseXTest.java` which defines how test cases will execute and output. +7. Create/edit shell scripts in [/.github](../.github) and [/.circleci](../.circleci) to run tests in CI pipelines. ## Getting started -1. Fork the `antlr/antlr4` repository at github to your own user so that you have repository `username/antlr4`. -2. Clone `username/antlr4`, the forked repository, to your local disk. Your remote `origin` will be the forked repository on GitHub. Add a remote `upstream` to the original `antlr/antlr4` repository (URL `https://github.com/antlr/antlr4.git`). Changes that you would like to contribute back to the project are done with [pull requests](https://help.github.com/articles/using-pull-requests/). +1. Fork the `antlr/antlr4` repository at GitHub to your own user so that you have repository `username/antlr4`. +2. Clone `username/antlr4`, the forked repository, to your local disk. + Your remote `origin` will be the forked repository on GitHub. + Add a remote `upstream` to the original `antlr/antlr4` repository (URL `https://github.com/antlr/antlr4.git`). + Changes that you would like to contribute back to the project are done with [pull requests](https://help.github.com/articles/using-pull-requests/). 3. Try to build it before doing anything + ```bash $ mvn compile ``` + That should proceed with success. See [Building ANTLR](building-antlr.md) for more details. From d40067d19ed03e72fadd1b0fb65b972f56251d9b Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 26 Jun 2022 22:51:17 +0300 Subject: [PATCH 19/20] Fix potential problems with concurrent access to hash maps Signed-off-by: Ivan Kochurkin --- .../v4/test/runtime/RuntimeTestUtils.java | 14 +--- .../antlr/v4/test/runtime/RuntimeTests.java | 32 ++++--- tool/src/org/antlr/v4/codegen/Target.java | 19 ++--- tool/src/org/antlr/v4/tool/ErrorManager.java | 84 +++++++++---------- 4 files changed, 65 insertions(+), 84 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java index 1b0f262c0e..8411d8ce6c 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTestUtils.java @@ -30,7 +30,6 @@ public abstract class RuntimeTestUtils { public final static Path runtimeTestsuitePath; public final static Path resourcePath; - private final static Object resourceLockObject = new Object(); private final static Map resourceCache = new HashMap<>(); private static OSType detectedOS; private static Boolean isWindows; @@ -81,18 +80,13 @@ else if (os.contains("nux")) { return detectedOS; } - public static String getTextFromResource(String name) { + public static synchronized String getTextFromResource(String name) { try { String text = resourceCache.get(name); if (text == null) { - synchronized (resourceLockObject) { - text = resourceCache.get(name); - if (text == null) { - Path path = Paths.get(resourcePath.toString(), name); - text = new String(Files.readAllBytes(path)); - resourceCache.put(name, text); - } - } + Path path = Paths.get(resourcePath.toString(), name); + text = new String(Files.readAllBytes(path)); + resourceCache.put(name, text); } return text; } diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java index 9d2bd41fd8..f3f367e8ff 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/RuntimeTests.java @@ -23,10 +23,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.stream.Stream; import static org.antlr.v4.test.runtime.FileUtils.writeFile; @@ -45,7 +42,7 @@ public abstract class RuntimeTests { protected abstract RuntimeRunner createRuntimeRunner(); private final static HashMap testDescriptors = new HashMap<>(); - private final static HashMap cachedTargetTemplates = new HashMap<>(); + private final static Map cachedTargetTemplates = new HashMap<>(); private final static StringRenderer rendered = new StringRenderer(); static { @@ -179,24 +176,23 @@ private static String test(RuntimeTestDescriptor descriptor, RuntimeRunner runne private static String prepareGrammars(RuntimeTestDescriptor descriptor, RuntimeRunner runner) { String targetName = runner.getLanguage(); - STGroup targetTemplates = cachedTargetTemplates.get(targetName); - if (targetTemplates == null) { - synchronized (cachedTargetTemplates) { - targetTemplates = cachedTargetTemplates.get(targetName); - if (targetTemplates == null) { - ClassLoader classLoader = RuntimeTests.class.getClassLoader(); - URL templates = classLoader.getResource("org/antlr/v4/test/runtime/templates/" + targetName + ".test.stg"); - assert templates != null; - targetTemplates = new STGroupFile(templates, "UTF-8", '<', '>'); - targetTemplates.registerRenderer(String.class, rendered); - cachedTargetTemplates.put(targetName, targetTemplates); - } + + STGroup targetTemplates; + synchronized (cachedTargetTemplates) { + targetTemplates = cachedTargetTemplates.get(targetName); + if (targetTemplates == null) { + ClassLoader classLoader = RuntimeTests.class.getClassLoader(); + URL templates = classLoader.getResource("org/antlr/v4/test/runtime/templates/" + targetName + ".test.stg"); + assert templates != null; + targetTemplates = new STGroupFile(templates, "UTF-8", '<', '>'); + targetTemplates.registerRenderer(String.class, rendered); + cachedTargetTemplates.put(targetName, targetTemplates); } } // write out any slave grammars List> slaveGrammars = descriptor.slaveGrammars; - if ( slaveGrammars!=null ) { + if (slaveGrammars != null) { for (Pair spair : slaveGrammars) { STGroup g = new STGroup('<', '>'); g.registerRenderer(String.class, rendered); diff --git a/tool/src/org/antlr/v4/codegen/Target.java b/tool/src/org/antlr/v4/codegen/Target.java index 2f6b6f522d..2d89281030 100644 --- a/tool/src/org/antlr/v4/codegen/Target.java +++ b/tool/src/org/antlr/v4/codegen/Target.java @@ -87,23 +87,18 @@ public String getVersion() { return Tool.VERSION; } - public STGroup getTemplates() { + public synchronized STGroup getTemplates() { String language = getLanguage(); STGroup templates = languageTemplates.get(language); if (templates == null) { - synchronized (languageTemplates) { - templates = languageTemplates.get(language); - if (templates == null) { - String version = getVersion(); - if (version == null || - !RuntimeMetaData.getMajorMinorVersion(version).equals(RuntimeMetaData.getMajorMinorVersion(Tool.VERSION))) { - gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, language); - } - templates = loadTemplates(); - languageTemplates.put(language, templates); - } + String version = getVersion(); + if (version == null || + !RuntimeMetaData.getMajorMinorVersion(version).equals(RuntimeMetaData.getMajorMinorVersion(Tool.VERSION))) { + gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, language); } + templates = loadTemplates(); + languageTemplates.put(language, templates); } return templates; diff --git a/tool/src/org/antlr/v4/tool/ErrorManager.java b/tool/src/org/antlr/v4/tool/ErrorManager.java index 242b3871c9..59b1dced3b 100644 --- a/tool/src/org/antlr/v4/tool/ErrorManager.java +++ b/tool/src/org/antlr/v4/tool/ErrorManager.java @@ -14,13 +14,10 @@ import java.io.File; import java.net.URL; -import java.util.Collection; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Set; +import java.util.*; public class ErrorManager { - private final static HashMap loadedFormats = new HashMap<>(); + private final static Map loadedFormats = new HashMap<>(); public static final String FORMATS_DIR = "org/antlr/v4/tool/templates/messages/formats/"; @@ -219,53 +216,52 @@ public void emit(ErrorType etype, ANTLRMessage msg) { * Otherwise we just use the default "antlr". */ public void setFormat(String formatName) { - STGroupFile loadedFormat = loadedFormats.get(formatName); - if (loadedFormat == null) { - synchronized (loadedFormats) { - loadedFormat = loadedFormats.get(formatName); - if (loadedFormat == null) { - String fileName = FORMATS_DIR +formatName+STGroup.GROUP_FILE_EXTENSION; - 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; - } - loadedFormat = new STGroupFile(url, "UTF-8", '<', '>'); - loadedFormat.load(); - - loadedFormats.put(formatName, loadedFormat); + STGroupFile loadedFormat; + + synchronized (loadedFormats) { + loadedFormat = loadedFormats.get(formatName); + if (loadedFormat == null) { + String fileName = FORMATS_DIR + formatName + STGroup.GROUP_FILE_EXTENSION; + 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; + } + loadedFormat = new STGroupFile(url, "UTF-8", '<', '>'); + loadedFormat.load(); + + loadedFormats.put(formatName, loadedFormat); } } this.formatName = formatName; this.format = loadedFormat; - if ( !initSTListener.errors.isEmpty() ) { - rawError("ANTLR installation corrupted; can't load messages format file:\n"+ - initSTListener.toString()); - panic(); - } + if (!initSTListener.errors.isEmpty()) { + 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 - } - } + 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 + } + } /** Verify the message format template group */ protected boolean verifyFormat() { From de255cc19aefb2764f1f1eec91c7a522833c32ae Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 26 Jun 2022 22:59:26 +0300 Subject: [PATCH 20/20] Restore info about `mvn -Dtest=java.** test` in antlr-project-testing.md Fix misprint Signed-off-by: Ivan Kochurkin --- doc/antlr-project-testing.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/antlr-project-testing.md b/doc/antlr-project-testing.md index a1b32949cc..840921ebe4 100644 --- a/doc/antlr-project-testing.md +++ b/doc/antlr-project-testing.md @@ -87,7 +87,7 @@ And the result of testing the entire subdirectory: All test are run in parallel both via maven and via IDE. -In IntelliJ, it's very easy to go to source by right-click on any test and pressing `Jump to source` (F4). +In IntelliJ, it's very easy to go to source by right-clicking on any test and pressing `Jump to source` (F4). ## Running test subsets @@ -98,12 +98,14 @@ From the `runtime-testsuite` dir ```bash $ cd runtime-testsuite $ export MAVEN_OPTS="-Xmx1G" # don't forget this on linux -$ mvn -Dtest=JavaRuntimeTests test +$ mvn -Dtest=java.** test ------------------------------------------------------- T E S T S ------------------------------------------------------- +[INFO] Running org.antlr.v4.test.runtime.java.TestIntegerList [INFO] Running org.antlr.v4.test.runtime.java.JavaRuntimeTests ... +[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 s - in org.antlr.v4.test.runtime.java.TestIntegerList [INFO] Tests run: 348, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 19.269 s - in org.antlr.v4.test.runtime.java.JavaRuntimeTests ... ```