Added TestPerformance (body is unaltered Java code)

This commit is contained in:
Sam Harwell 2013-02-24 14:44:40 -06:00
parent 48485e0cda
commit 2d68418dbc
2 changed files with 972 additions and 0 deletions

View File

@ -54,6 +54,7 @@
<Compile Include="TestBitSet.cs" />
<Compile Include="TestGraphNodes.cs" />
<Compile Include="TestIntervalSet.cs" />
<Compile Include="TestPerformance.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Antlr4.Runtime\Antlr4.Runtime.csproj">

View File

@ -0,0 +1,971 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Antlr4.Runtime.Test
{
public class TestPerformance
{
/**
* Parse all java files under this package within the JDK_SOURCE_ROOT
* (environment variable or property defined on the Java command line).
*/
private static final String TOP_PACKAGE = "java.lang";
/**
* {@code true} to load java files from sub-packages of
* {@link #TOP_PACKAGE}.
*/
private static final boolean RECURSIVE = true;
/**
* {@code true} to use the Java grammar with expressions in the v4
* left-recursive syntax (Java-LR.g4). {@code false} to use the standard
* grammar (Java.g4). In either case, the grammar is renamed in the
* temporary directory to Java.g4 before compiling.
*/
private static final boolean USE_LR_GRAMMAR = true;
/**
* {@code true} to specify the {@code -Xforce-atn} option when generating
* the grammar, forcing all decisions in {@code JavaParser} to be handled by
* {@link ParserATNSimulator#adaptivePredict}.
*/
private static final boolean FORCE_ATN = false;
/**
* {@code true} to specify the {@code -atn} option when generating the
* grammar. This will cause ANTLR to export the ATN for each decision as a
* DOT (GraphViz) file.
*/
private static final boolean EXPORT_ATN_GRAPHS = true;
/**
* {@code true} to specify the {@code -XdbgST} option when generating the
* grammar.
*/
private static final boolean DEBUG_TEMPLATES = false;
/**
* {@code true} to specify the {@code -XdbgSTWait} option when generating the
* grammar.
*/
private static final boolean DEBUG_TEMPLATES_WAIT = DEBUG_TEMPLATES;
/**
* {@code true} to delete temporary (generated and compiled) files when the
* test completes.
*/
private static final boolean DELETE_TEMP_FILES = true;
/**
* {@code true} to call {@link System#gc} and then wait for 5 seconds at the
* end of the test to make it easier for a profiler to grab a heap dump at
* the end of the test run.
*/
private static final boolean PAUSE_FOR_HEAP_DUMP = false;
/**
* Parse each file with {@code JavaParser.compilationUnit}.
*/
private static final boolean RUN_PARSER = true;
/**
* {@code true} to use {@link BailErrorStrategy}, {@code false} to use
* {@link DefaultErrorStrategy}.
*/
private static final boolean BAIL_ON_ERROR = true;
/**
* {@code true} to compute a checksum for verifying consistency across
* optimizations and multiple passes.
*/
private static final boolean COMPUTE_CHECKSUM = true;
/**
* This value is passed to {@link Parser#setBuildParseTree}.
*/
private static final boolean BUILD_PARSE_TREES = false;
/**
* Use
* {@link ParseTreeWalker#DEFAULT}{@code .}{@link ParseTreeWalker#walk walk}
* with the {@code JavaParserBaseListener} to show parse tree walking
* overhead. If {@link #BUILD_PARSE_TREES} is {@code false}, the listener
* will instead be called during the parsing process via
* {@link Parser#addParseListener}.
*/
private static final boolean BLANK_LISTENER = false;
private static final boolean EXPORT_LARGEST_CONFIG_CONTEXTS = false;
/**
* Shows the number of {@link DFAState} and {@link ATNConfig} instances in
* the DFA cache at the end of each pass. If {@link #REUSE_LEXER_DFA} and/or
* {@link #REUSE_PARSER_DFA} are false, the corresponding instance numbers
* will only apply to one file (the last file if {@link #NUMBER_OF_THREADS}
* is 0, otherwise the last file which was parsed on the first thread).
*/
private static final boolean SHOW_DFA_STATE_STATS = true;
private static final boolean ENABLE_LEXER_DFA = true;
private static final boolean ENABLE_PARSER_DFA = true;
private static final PredictionMode PREDICTION_MODE = PredictionMode.LL;
private static final boolean FORCE_GLOBAL_CONTEXT = false;
private static final boolean TRY_LOCAL_CONTEXT_FIRST = true;
private static final boolean OPTIMIZE_LL1 = true;
private static final boolean OPTIMIZE_UNIQUE_CLOSURE = true;
private static final boolean OPTIMIZE_HIDDEN_CONFLICTED_CONFIGS = false;
private static final boolean OPTIMIZE_TAIL_CALLS = true;
private static final boolean TAIL_CALL_PRESERVES_SLL = true;
private static final boolean TREAT_SLLK1_CONFLICT_AS_AMBIGUITY = false;
private static final boolean TWO_STAGE_PARSING = true;
private static final boolean SHOW_CONFIG_STATS = false;
private static final boolean REPORT_SYNTAX_ERRORS = true;
private static final boolean REPORT_AMBIGUITIES = false;
private static final boolean REPORT_FULL_CONTEXT = false;
private static final boolean REPORT_CONTEXT_SENSITIVITY = REPORT_FULL_CONTEXT;
/**
* If {@code true}, a single {@code JavaLexer} will be used, and
* {@link Lexer#setInputStream} will be called to initialize it for each
* source file. Otherwise, a new instance will be created for each file.
*/
private static final boolean REUSE_LEXER = false;
/**
* If {@code true}, a single DFA will be used for lexing which is shared
* across all threads and files. Otherwise, each file will be lexed with its
* own DFA which is accomplished by creating one ATN instance per thread and
* clearing its DFA cache before lexing each file.
*/
private static final boolean REUSE_LEXER_DFA = true;
/**
* If {@code true}, a single {@code JavaParser} will be used, and
* {@link Parser#setInputStream} will be called to initialize it for each
* source file. Otherwise, a new instance will be created for each file.
*/
private static final boolean REUSE_PARSER = false;
/**
* If {@code true}, a single DFA will be used for parsing which is shared
* across all threads and files. Otherwise, each file will be parsed with
* its own DFA which is accomplished by creating one ATN instance per thread
* and clearing its DFA cache before parsing each file.
*/
private static final boolean REUSE_PARSER_DFA = true;
/**
* If {@code true}, the shared lexer and parser are reset after each pass.
* If {@code false}, all passes after the first will be fully "warmed up",
* which makes them faster and can compare them to the first warm-up pass,
* but it will not distinguish bytecode load/JIT time from warm-up time
* during the first pass.
*/
private static final boolean CLEAR_DFA = false;
/**
* Total number of passes to make over the source.
*/
private static final int PASSES = 4;
/**
* Number of parser threads to use.
*/
private static final int NUMBER_OF_THREADS = 1;
private static final Lexer[] sharedLexers = new Lexer[NUMBER_OF_THREADS];
private static final ATN[] sharedLexerATNs = new ATN[NUMBER_OF_THREADS];
private static final Parser[] sharedParsers = new Parser[NUMBER_OF_THREADS];
private static final ATN[] sharedParserATNs = new ATN[NUMBER_OF_THREADS];
private static final ParseTreeListener[] sharedListeners = new ParseTreeListener[NUMBER_OF_THREADS];
private final AtomicInteger tokenCount = new AtomicInteger();
private int currentPass;
@Test
//@org.junit.Ignore
public void compileJdk() throws IOException, InterruptedException {
String jdkSourceRoot = getSourceRoot("JDK");
assertTrue("The JDK_SOURCE_ROOT environment variable must be set for performance testing.", jdkSourceRoot != null && !jdkSourceRoot.isEmpty());
compileJavaParser(USE_LR_GRAMMAR);
final String lexerName = "JavaLexer";
final String parserName = "JavaParser";
final String listenerName = "JavaBaseListener";
final String entryPoint = "compilationUnit";
ParserFactory factory = getParserFactory(lexerName, parserName, listenerName, entryPoint);
if (!TOP_PACKAGE.isEmpty()) {
jdkSourceRoot = jdkSourceRoot + '/' + TOP_PACKAGE.replace('.', '/');
}
File directory = new File(jdkSourceRoot);
assertTrue(directory.isDirectory());
Collection<CharStream> sources = loadSources(directory, new FileExtensionFilenameFilter(".java"), RECURSIVE);
System.out.print(getOptionsDescription(TOP_PACKAGE));
currentPass = 0;
parse1(factory, sources);
for (int i = 0; i < PASSES - 1; i++) {
currentPass = i + 1;
if (CLEAR_DFA) {
if (sharedLexers.length > 0) {
sharedLexers[0].getATN().clearDFA();
}
if (sharedParsers.length > 0) {
sharedParsers[0].getATN().clearDFA();
}
Arrays.fill(sharedLexers, null);
Arrays.fill(sharedParsers, null);
}
parse2(factory, sources);
}
sources.clear();
if (PAUSE_FOR_HEAP_DUMP) {
System.gc();
System.out.println("Pausing before application exit.");
try {
Thread.sleep(4000);
} catch (InterruptedException ex) {
Logger.getLogger(TestPerformance.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private String getSourceRoot(String prefix) {
String sourceRoot = System.getenv(prefix+"_SOURCE_ROOT");
if (sourceRoot == null) {
sourceRoot = System.getProperty(prefix+"_SOURCE_ROOT");
}
return sourceRoot;
}
@Override
protected void eraseTempDir() {
if (DELETE_TEMP_FILES) {
super.eraseTempDir();
}
}
public static String getOptionsDescription(String topPackage) {
StringBuilder builder = new StringBuilder();
builder.append("Input=");
if (topPackage.isEmpty()) {
builder.append("*");
}
else {
builder.append(topPackage).append(".*");
}
builder.append(", Grammar=").append(USE_LR_GRAMMAR ? "LR" : "Standard");
builder.append(", ForceAtn=").append(FORCE_ATN);
builder.append(", Lexer:").append(ENABLE_LEXER_DFA ? "DFA" : "ATN");
builder.append(", Parser:").append(ENABLE_PARSER_DFA ? "DFA" : "ATN");
builder.append(newline);
builder.append("Op=Lex").append(RUN_PARSER ? "+Parse" : " only");
builder.append(", Strategy=").append(BAIL_ON_ERROR ? BailErrorStrategy.class.getSimpleName() : DefaultErrorStrategy.class.getSimpleName());
builder.append(", BuildParseTree=").append(BUILD_PARSE_TREES);
builder.append(", WalkBlankListener=").append(BLANK_LISTENER);
builder.append(newline);
builder.append("Lexer=").append(REUSE_LEXER ? "setInputStream" : "newInstance");
builder.append(", Parser=").append(REUSE_PARSER ? "setInputStream" : "newInstance");
builder.append(", AfterPass=").append(CLEAR_DFA ? "newInstance" : "setInputStream");
builder.append('\n');
builder.append("UniqueClosure=").append(OPTIMIZE_UNIQUE_CLOSURE ? "optimize" : "complete");
builder.append(newline);
return builder.toString();
}
/**
* This method is separate from {@link #parse2} so the first pass can be distinguished when analyzing
* profiler results.
*/
protected void parse1(ParserFactory factory, Collection<CharStream> sources) throws InterruptedException {
System.gc();
parseSources(factory, sources);
}
/**
* This method is separate from {@link #parse1} so the first pass can be distinguished when analyzing
* profiler results.
*/
protected void parse2(ParserFactory factory, Collection<CharStream> sources) throws InterruptedException {
System.gc();
parseSources(factory, sources);
}
protected Collection<CharStream> loadSources(File directory, FilenameFilter filter, boolean recursive) {
return loadSources(directory, filter, null, recursive);
}
protected Collection<CharStream> loadSources(File directory, FilenameFilter filter, String encoding, boolean recursive) {
Collection<CharStream> result = new ArrayList<CharStream>();
loadSources(directory, filter, encoding, recursive, result);
return result;
}
protected void loadSources(File directory, FilenameFilter filter, String encoding, boolean recursive, Collection<CharStream> result) {
assert directory.isDirectory();
File[] sources = directory.listFiles(filter);
for (File file : sources) {
try {
CharStream input = new ANTLRFileStream(file.getAbsolutePath(), encoding);
result.add(input);
} catch (IOException ex) {
}
}
if (recursive) {
File[] children = directory.listFiles();
for (File child : children) {
if (child.isDirectory()) {
loadSources(child, filter, encoding, true, result);
}
}
}
}
int configOutputSize = 0;
@SuppressWarnings("unused")
protected void parseSources(final ParserFactory factory, Collection<CharStream> sources) throws InterruptedException {
long startTime = System.currentTimeMillis();
tokenCount.set(0);
int inputSize = 0;
Collection<Future<Integer>> results = new ArrayList<Future<Integer>>();
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS, new NumberedThreadFactory());
for (final CharStream input : sources) {
input.seek(0);
inputSize += input.size();
Future<Integer> futureChecksum = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() {
// this incurred a great deal of overhead and was causing significant variations in performance results.
//System.out.format("Parsing file %s\n", input.getSourceName());
try {
return factory.parseFile(input, ((NumberedThread)Thread.currentThread()).getThreadNumber());
} catch (IllegalStateException ex) {
ex.printStackTrace(System.err);
} catch (Throwable t) {
t.printStackTrace(System.err);
}
return -1;
}
});
results.add(futureChecksum);
}
Checksum checksum = new CRC32();
for (Future<Integer> future : results) {
int value = 0;
try {
value = future.get();
} catch (ExecutionException ex) {
Logger.getLogger(TestPerformance.class.getName()).log(Level.SEVERE, null, ex);
}
if (COMPUTE_CHECKSUM) {
updateChecksum(checksum, value);
}
}
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
System.out.format("Total parse time for %d files (%d KB, %d tokens, checksum 0x%8X): %dms%n",
sources.size(),
inputSize / 1024,
tokenCount.get(),
COMPUTE_CHECKSUM ? checksum.getValue() : 0,
System.currentTimeMillis() - startTime);
if (sharedLexers.length > 0) {
Lexer lexer = sharedLexers[0];
final LexerATNSimulator lexerInterpreter = lexer.getInterpreter();
final DFA[] modeToDFA = lexerInterpreter.atn.modeToDFA;
if (SHOW_DFA_STATE_STATS) {
int states = 0;
int configs = 0;
Set<ATNConfig> uniqueConfigs = new HashSet<ATNConfig>();
for (int i = 0; i < modeToDFA.length; i++) {
DFA dfa = modeToDFA[i];
if (dfa == null || dfa.states == null) {
continue;
}
states += dfa.states.size();
for (DFAState state : dfa.states.values()) {
configs += state.configs.size();
uniqueConfigs.addAll(state.configs);
}
}
System.out.format("There are %d lexer DFAState instances, %d configs (%d unique), %d prediction contexts.%n", states, configs, uniqueConfigs.size(), lexerInterpreter.atn.getContextCacheSize());
}
}
if (RUN_PARSER && sharedParsers.length > 0) {
Parser parser = sharedParsers[0];
// make sure the individual DFAState objects actually have unique ATNConfig arrays
final ParserATNSimulator interpreter = parser.getInterpreter();
final DFA[] decisionToDFA = interpreter.atn.decisionToDFA;
if (SHOW_DFA_STATE_STATS) {
int states = 0;
int configs = 0;
Set<ATNConfig> uniqueConfigs = new HashSet<ATNConfig>();
for (int i = 0; i < decisionToDFA.length; i++) {
DFA dfa = decisionToDFA[i];
if (dfa == null || dfa.states == null) {
continue;
}
states += dfa.states.size();
for (DFAState state : dfa.states.values()) {
configs += state.configs.size();
uniqueConfigs.addAll(state.configs);
}
}
System.out.format("There are %d parser DFAState instances, %d configs (%d unique), %d prediction contexts.%n", states, configs, uniqueConfigs.size(), interpreter.atn.getContextCacheSize());
}
int localDfaCount = 0;
int globalDfaCount = 0;
int localConfigCount = 0;
int globalConfigCount = 0;
int[] contextsInDFAState = new int[0];
for (int i = 0; i < decisionToDFA.length; i++) {
DFA dfa = decisionToDFA[i];
if (dfa == null || dfa.states == null) {
continue;
}
if (SHOW_CONFIG_STATS) {
for (DFAState state : dfa.states.keySet()) {
if (state.configs.size() >= contextsInDFAState.length) {
contextsInDFAState = Arrays.copyOf(contextsInDFAState, state.configs.size() + 1);
}
if (state.isAcceptState) {
boolean hasGlobal = false;
for (ATNConfig config : state.configs) {
if (config.getReachesIntoOuterContext()) {
globalConfigCount++;
hasGlobal = true;
} else {
localConfigCount++;
}
}
if (hasGlobal) {
globalDfaCount++;
} else {
localDfaCount++;
}
}
contextsInDFAState[state.configs.size()]++;
}
}
if (EXPORT_LARGEST_CONFIG_CONTEXTS) {
for (DFAState state : dfa.states.keySet()) {
for (ATNConfig config : state.configs) {
String configOutput = config.toDotString();
if (configOutput.length() <= configOutputSize) {
continue;
}
configOutputSize = configOutput.length();
writeFile(tmpdir, "d" + dfa.decision + ".s" + state.stateNumber + ".a" + config.getAlt() + ".config.dot", configOutput);
}
}
}
}
if (SHOW_CONFIG_STATS && currentPass == 0) {
System.out.format(" DFA accept states: %d total, %d with only local context, %d with a global context%n", localDfaCount + globalDfaCount, localDfaCount, globalDfaCount);
System.out.format(" Config stats: %d total, %d local, %d global%n", localConfigCount + globalConfigCount, localConfigCount, globalConfigCount);
if (SHOW_DFA_STATE_STATS) {
for (int i = 0; i < contextsInDFAState.length; i++) {
if (contextsInDFAState[i] != 0) {
System.out.format(" %d configs = %d%n", i, contextsInDFAState[i]);
}
}
}
}
}
}
protected void compileJavaParser(boolean leftRecursive) throws IOException {
String grammarFileName = "Java.g4";
String sourceName = leftRecursive ? "Java-LR.g4" : "Java.g4";
String body = load(sourceName, null);
List<String> extraOptions = new ArrayList<String>();
extraOptions.add("-Werror");
if (FORCE_ATN) {
extraOptions.add("-Xforce-atn");
}
if (EXPORT_ATN_GRAPHS) {
extraOptions.add("-atn");
}
if (DEBUG_TEMPLATES) {
extraOptions.add("-XdbgST");
if (DEBUG_TEMPLATES_WAIT) {
extraOptions.add("-XdbgSTWait");
}
}
extraOptions.add("-visitor");
String[] extraOptionsArray = extraOptions.toArray(new String[extraOptions.size()]);
boolean success = rawGenerateAndBuildRecognizer(grammarFileName, body, "JavaParser", "JavaLexer", true, extraOptionsArray);
assertTrue(success);
}
protected String load(String fileName, @Nullable String encoding)
throws IOException
{
if ( fileName==null ) {
return null;
}
String fullFileName = getClass().getPackage().getName().replace('.', '/') + '/' + fileName;
int size = 65000;
InputStreamReader isr;
InputStream fis = getClass().getClassLoader().getResourceAsStream(fullFileName);
if ( encoding!=null ) {
isr = new InputStreamReader(fis, encoding);
}
else {
isr = new InputStreamReader(fis);
}
try {
char[] data = new char[size];
int n = isr.read(data);
return new String(data, 0, n);
}
finally {
isr.close();
}
}
private static void updateChecksum(Checksum checksum, int value) {
checksum.update((value) & 0xFF);
checksum.update((value >>> 8) & 0xFF);
checksum.update((value >>> 16) & 0xFF);
checksum.update((value >>> 24) & 0xFF);
}
private static void updateChecksum(Checksum checksum, Token token) {
if (token == null) {
checksum.update(0);
return;
}
updateChecksum(checksum, token.getStartIndex());
updateChecksum(checksum, token.getStopIndex());
updateChecksum(checksum, token.getLine());
updateChecksum(checksum, token.getCharPositionInLine());
updateChecksum(checksum, token.getType());
updateChecksum(checksum, token.getChannel());
}
protected ParserFactory getParserFactory(String lexerName, String parserName, String listenerName, final String entryPoint) {
try {
ClassLoader loader = new URLClassLoader(new URL[] { new File(tmpdir).toURI().toURL() }, ClassLoader.getSystemClassLoader());
final Class<? extends Lexer> lexerClass = loader.loadClass(lexerName).asSubclass(Lexer.class);
final Class<? extends Parser> parserClass = loader.loadClass(parserName).asSubclass(Parser.class);
final Class<? extends ParseTreeListener> listenerClass = (Class<? extends ParseTreeListener>)loader.loadClass(listenerName).asSubclass(ParseTreeListener.class);
final Constructor<? extends Lexer> lexerCtor = lexerClass.getConstructor(CharStream.class);
final Constructor<? extends Parser> parserCtor = parserClass.getConstructor(TokenStream.class);
// construct initial instances of the lexer and parser to deserialize their ATNs
TokenSource tokenSource = lexerCtor.newInstance(new ANTLRInputStream(""));
parserCtor.newInstance(new CommonTokenStream(tokenSource));
if (!REUSE_LEXER_DFA) {
Field lexerSerializedATNField = lexerClass.getField("_serializedATN");
String lexerSerializedATN = (String)lexerSerializedATNField.get(null);
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
sharedLexerATNs[i] = ATNSimulator.deserialize(lexerSerializedATN.toCharArray());
}
}
if (RUN_PARSER && !REUSE_PARSER_DFA) {
Field parserSerializedATNField = parserClass.getField("_serializedATN");
String parserSerializedATN = (String)parserSerializedATNField.get(null);
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
sharedParserATNs[i] = ATNSimulator.deserialize(parserSerializedATN.toCharArray());
}
}
return new ParserFactory() {
@SuppressWarnings("unused")
@Override
public int parseFile(CharStream input, int thread) {
final Checksum checksum = new CRC32();
assert thread >= 0 && thread < NUMBER_OF_THREADS;
try {
ParseTreeListener listener = sharedListeners[thread];
if (listener == null) {
listener = listenerClass.newInstance();
sharedListeners[thread] = listener;
}
Lexer lexer = sharedLexers[thread];
if (REUSE_LEXER && lexer != null) {
lexer.setInputStream(input);
} else {
lexer = lexerCtor.newInstance(input);
sharedLexers[thread] = lexer;
if (!ENABLE_LEXER_DFA) {
lexer.setInterpreter(new NonCachingLexerATNSimulator(lexer, lexer.getATN()));
} else if (!REUSE_LEXER_DFA) {
lexer.setInterpreter(new LexerATNSimulator(lexer, sharedLexerATNs[thread]));
}
}
lexer.getInterpreter().optimize_tail_calls = OPTIMIZE_TAIL_CALLS;
if (ENABLE_LEXER_DFA && !REUSE_LEXER_DFA) {
lexer.getInterpreter().atn.clearDFA();
}
CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.fill();
tokenCount.addAndGet(tokens.size());
if (COMPUTE_CHECKSUM) {
for (Token token : tokens.getTokens()) {
updateChecksum(checksum, token);
}
}
if (!RUN_PARSER) {
return (int)checksum.getValue();
}
Parser parser = sharedParsers[thread];
if (REUSE_PARSER && parser != null) {
parser.setInputStream(tokens);
} else {
Parser newParser = parserCtor.newInstance(tokens);
parser = newParser;
sharedParsers[thread] = parser;
}
parser.removeErrorListeners();
if (!TWO_STAGE_PARSING) {
parser.addErrorListener(DescriptiveErrorListener.INSTANCE);
parser.addErrorListener(new SummarizingDiagnosticErrorListener());
}
if (!ENABLE_PARSER_DFA) {
parser.setInterpreter(new NonCachingParserATNSimulator(parser, parser.getATN()));
} else if (!REUSE_PARSER_DFA) {
parser.setInterpreter(new ParserATNSimulator(parser, sharedParserATNs[thread]));
}
if (ENABLE_PARSER_DFA && !REUSE_PARSER_DFA) {
parser.getInterpreter().atn.clearDFA();
}
parser.getInterpreter().setPredictionMode(TWO_STAGE_PARSING ? PredictionMode.SLL : PREDICTION_MODE);
parser.getInterpreter().force_global_context = FORCE_GLOBAL_CONTEXT && !TWO_STAGE_PARSING;
parser.getInterpreter().always_try_local_context = TRY_LOCAL_CONTEXT_FIRST || TWO_STAGE_PARSING;
parser.getInterpreter().optimize_ll1 = OPTIMIZE_LL1;
parser.getInterpreter().optimize_unique_closure = OPTIMIZE_UNIQUE_CLOSURE;
parser.getInterpreter().optimize_hidden_conflicted_configs = OPTIMIZE_HIDDEN_CONFLICTED_CONFIGS;
parser.getInterpreter().optimize_tail_calls = OPTIMIZE_TAIL_CALLS;
parser.getInterpreter().tail_call_preserves_sll = TAIL_CALL_PRESERVES_SLL;
parser.getInterpreter().treat_sllk1_conflict_as_ambiguity = TREAT_SLLK1_CONFLICT_AS_AMBIGUITY;
parser.setBuildParseTree(BUILD_PARSE_TREES);
if (!BUILD_PARSE_TREES && BLANK_LISTENER) {
parser.addParseListener(listener);
}
if (BAIL_ON_ERROR || TWO_STAGE_PARSING) {
parser.setErrorHandler(new BailErrorStrategy());
}
Method parseMethod = parserClass.getMethod(entryPoint);
Object parseResult;
ParseTreeListener checksumParserListener = null;
try {
if (COMPUTE_CHECKSUM) {
checksumParserListener = new ChecksumParseTreeListener(checksum);
parser.addParseListener(checksumParserListener);
}
parseResult = parseMethod.invoke(parser);
} catch (InvocationTargetException ex) {
if (!TWO_STAGE_PARSING) {
throw ex;
}
String sourceName = tokens.getSourceName();
sourceName = sourceName != null && !sourceName.isEmpty() ? sourceName+": " : "";
System.err.println(sourceName+"Forced to retry with full context.");
if (!(ex.getCause() instanceof ParseCancellationException)) {
throw ex;
}
tokens.reset();
if (REUSE_PARSER && sharedParsers[thread] != null) {
parser.setInputStream(tokens);
} else {
Parser newParser = parserCtor.newInstance(tokens);
parser = newParser;
sharedParsers[thread] = parser;
}
parser.removeErrorListeners();
parser.addErrorListener(DescriptiveErrorListener.INSTANCE);
parser.addErrorListener(new SummarizingDiagnosticErrorListener());
if (!ENABLE_PARSER_DFA) {
parser.setInterpreter(new NonCachingParserATNSimulator(parser, parser.getATN()));
}
parser.getInterpreter().setPredictionMode(PREDICTION_MODE);
parser.getInterpreter().force_global_context = FORCE_GLOBAL_CONTEXT;
parser.getInterpreter().always_try_local_context = TRY_LOCAL_CONTEXT_FIRST;
parser.getInterpreter().optimize_ll1 = OPTIMIZE_LL1;
parser.getInterpreter().optimize_unique_closure = OPTIMIZE_UNIQUE_CLOSURE;
parser.getInterpreter().optimize_hidden_conflicted_configs = OPTIMIZE_HIDDEN_CONFLICTED_CONFIGS;
parser.getInterpreter().optimize_tail_calls = OPTIMIZE_TAIL_CALLS;
parser.getInterpreter().tail_call_preserves_sll = TAIL_CALL_PRESERVES_SLL;
parser.getInterpreter().treat_sllk1_conflict_as_ambiguity = TREAT_SLLK1_CONFLICT_AS_AMBIGUITY;
parser.setBuildParseTree(BUILD_PARSE_TREES);
if (!BUILD_PARSE_TREES && BLANK_LISTENER) {
parser.addParseListener(listener);
}
if (BAIL_ON_ERROR) {
parser.setErrorHandler(new BailErrorStrategy());
}
parseResult = parseMethod.invoke(parser);
}
finally {
if (checksumParserListener != null) {
parser.removeParseListener(checksumParserListener);
}
}
assertThat(parseResult, instanceOf(ParseTree.class));
if (BUILD_PARSE_TREES && BLANK_LISTENER) {
ParseTreeWalker.DEFAULT.walk(listener, (ParserRuleContext)parseResult);
}
} catch (Exception e) {
if (!REPORT_SYNTAX_ERRORS && e instanceof ParseCancellationException) {
return (int)checksum.getValue();
}
e.printStackTrace(System.out);
throw new IllegalStateException(e);
}
return (int)checksum.getValue();
}
};
} catch (Exception e) {
e.printStackTrace(System.out);
Assert.fail(e.getMessage());
throw new IllegalStateException(e);
}
}
protected interface ParserFactory {
int parseFile(CharStream input, int thread);
}
private static class DescriptiveErrorListener extends BaseErrorListener {
public static DescriptiveErrorListener INSTANCE = new DescriptiveErrorListener();
@Override
public <T extends Token> void syntaxError(Recognizer<T, ?> recognizer, T offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
if (!REPORT_SYNTAX_ERRORS) {
return;
}
String sourceName = recognizer.getInputStream().getSourceName();
if (!sourceName.isEmpty()) {
sourceName = String.format("%s:%d:%d: ", sourceName, line, charPositionInLine);
}
System.err.println(sourceName+"line "+line+":"+charPositionInLine+" "+msg);
}
}
private static class SummarizingDiagnosticErrorListener extends DiagnosticErrorListener {
@Override
public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, BitSet ambigAlts, ATNConfigSet configs) {
if (!REPORT_AMBIGUITIES) {
return;
}
super.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, ambigAlts, configs);
}
@Override
public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, SimulatorState initialState) {
if (!REPORT_FULL_CONTEXT) {
return;
}
super.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, initialState);
}
@Override
public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, SimulatorState acceptState) {
if (!REPORT_CONTEXT_SENSITIVITY) {
return;
}
super.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, acceptState);
}
@Override
protected String getDecisionDescription(Parser recognizer, int decision) {
String format = "%d(%s)";
String ruleName = recognizer.getRuleNames()[recognizer.getATN().decisionToState.get(decision).ruleIndex];
return String.format(format, decision, ruleName);
}
}
protected static class FileExtensionFilenameFilter implements FilenameFilter {
private final String extension;
public FileExtensionFilenameFilter(String extension) {
if (!extension.startsWith(".")) {
extension = '.' + extension;
}
this.extension = extension;
}
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(extension);
}
}
protected static class NonCachingLexerATNSimulator extends LexerATNSimulator {
public NonCachingLexerATNSimulator(Lexer recog, ATN atn) {
super(recog, atn);
}
@Override
protected DFAState addDFAState(ATNConfigSet configs) {
return null;
}
}
protected static class NonCachingParserATNSimulator extends ParserATNSimulator {
public NonCachingParserATNSimulator(Parser parser, ATN atn) {
super(parser, atn);
}
@NotNull
@Override
protected DFAState createDFAState(@NotNull ATNConfigSet configs) {
return new DFAState(configs, -1, -1);
}
}
protected static class NumberedThread extends Thread {
private final int threadNumber;
public NumberedThread(Runnable target, int threadNumber) {
super(target);
this.threadNumber = threadNumber;
}
public final int getThreadNumber() {
return threadNumber;
}
}
protected static class NumberedThreadFactory implements ThreadFactory {
private final AtomicInteger nextThread = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
int threadNumber = nextThread.getAndIncrement();
assert threadNumber < NUMBER_OF_THREADS;
return new NumberedThread(r, threadNumber);
}
}
protected static class ChecksumParseTreeListener implements ParseTreeListener {
private static final int VISIT_TERMINAL = 1;
private static final int VISIT_ERROR_NODE = 2;
private static final int ENTER_RULE = 3;
private static final int EXIT_RULE = 4;
private final Checksum checksum;
public ChecksumParseTreeListener(Checksum checksum) {
this.checksum = checksum;
}
@Override
public void visitTerminal(TerminalNode node) {
checksum.update(VISIT_TERMINAL);
updateChecksum(checksum, node.getSymbol());
}
@Override
public void visitErrorNode(ErrorNode node) {
checksum.update(VISIT_ERROR_NODE);
updateChecksum(checksum, node.getSymbol());
}
@Override
public void enterEveryRule(ParserRuleContext ctx) {
checksum.update(ENTER_RULE);
updateChecksum(checksum, ctx.getRuleIndex());
updateChecksum(checksum, ctx.getStart());
}
@Override
public void exitEveryRule(ParserRuleContext ctx) {
checksum.update(EXIT_RULE);
updateChecksum(checksum, ctx.getRuleIndex());
updateChecksum(checksum, ctx.getStop());
}
}
}
}