forked from jasder/antlr
Updated TestPerformance (features, grammar fixes)
This commit is contained in:
parent
19afb4a8bc
commit
707311996d
|
@ -169,7 +169,6 @@
|
|||
* Character.isJavaIdentifierPart(int) returns true."
|
||||
*/
|
||||
grammar Java;
|
||||
options {backtrack=true; memoize=true;}
|
||||
|
||||
@lexer::members {
|
||||
protected boolean enumIsKeyword = true;
|
||||
|
@ -184,7 +183,9 @@ compilationUnit
|
|||
( packageDeclaration importDeclaration* typeDeclaration*
|
||||
| classOrInterfaceDeclaration typeDeclaration*
|
||||
)
|
||||
EOF
|
||||
| packageDeclaration? importDeclaration* typeDeclaration*
|
||||
EOF
|
||||
;
|
||||
|
||||
packageDeclaration
|
||||
|
@ -499,7 +500,7 @@ constructorBody
|
|||
|
||||
explicitConstructorInvocation
|
||||
: nonWildcardTypeArguments? ('this' | 'super') arguments ';'
|
||||
| expression '.' nonWildcardTypeArguments? 'super' arguments ';'
|
||||
| primary '.' nonWildcardTypeArguments? 'super' arguments ';'
|
||||
;
|
||||
|
||||
|
||||
|
@ -711,17 +712,12 @@ constantExpression
|
|||
;
|
||||
|
||||
expression
|
||||
: parExpression
|
||||
| 'this'
|
||||
| 'super'
|
||||
| literal
|
||||
| Identifier
|
||||
: primary
|
||||
| expression '.' Identifier
|
||||
| expression '.' 'class' // should be type.class but causes backtracking
|
||||
| expression '.' 'this'
|
||||
| expression '.' 'super' '(' expressionList? ')'
|
||||
| expression '.' 'super' '.' Identifier arguments?
|
||||
| expression '.' 'new' Identifier '(' expressionList? ')'
|
||||
| expression '.' 'super' '.' Identifier arguments?
|
||||
| expression '.' explicitGenericInvocation
|
||||
| 'new' creator
|
||||
| expression '[' expression ']'
|
||||
|
@ -754,8 +750,20 @@ expression
|
|||
| '>' '>' '='<assoc=right>
|
||||
| '>' '>' '>' '='<assoc=right>
|
||||
| '<' '<' '='<assoc=right>
|
||||
| '%='<assoc=right>) expression
|
||||
;
|
||||
| '%='<assoc=right>
|
||||
)
|
||||
expression
|
||||
;
|
||||
|
||||
primary
|
||||
: '(' expression ')'
|
||||
| 'this'
|
||||
| 'super'
|
||||
| literal
|
||||
| Identifier
|
||||
| type '.' 'class'
|
||||
| 'void' '.' 'class'
|
||||
;
|
||||
|
||||
creator
|
||||
: nonWildcardTypeArguments createdName classCreatorRest
|
||||
|
@ -827,15 +835,16 @@ FloatingPointLiteral
|
|||
| ('0'..'9')+ Exponent FloatTypeSuffix?
|
||||
| ('0'..'9')+ FloatTypeSuffix
|
||||
| '0' ('x'|'X')
|
||||
( HexDigit+ '.' HexDigit* Exponent? FloatTypeSuffix?
|
||||
| '.' HexDigit+ Exponent? FloatTypeSuffix?
|
||||
| HexDigit+ Exponent FloatTypeSuffix?
|
||||
| HexDigit+ FloatTypeSuffix
|
||||
( HexDigit+ ('.' HexDigit*)? HexExponent FloatTypeSuffix?
|
||||
| '.' HexDigit+ HexExponent FloatTypeSuffix?
|
||||
)
|
||||
;
|
||||
|
||||
fragment
|
||||
Exponent : ('e'|'E'|'p'|'P') ('+'|'-')? ('0'..'9')+ ;
|
||||
Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
|
||||
|
||||
fragment
|
||||
HexExponent : ('p'|'P') ('+'|'-')? ('0'..'9')+ ;
|
||||
|
||||
fragment
|
||||
FloatTypeSuffix : ('f'|'F'|'d'|'D') ;
|
||||
|
@ -917,13 +926,13 @@ JavaIDDigit
|
|||
'\u1040'..'\u1049'
|
||||
;
|
||||
|
||||
WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ {$channel=HIDDEN;}
|
||||
WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ -> channel(HIDDEN)
|
||||
;
|
||||
|
||||
COMMENT
|
||||
: '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
|
||||
: '/*' .* '*/' -> channel(HIDDEN)
|
||||
;
|
||||
|
||||
LINE_COMMENT
|
||||
: '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
|
||||
: '//' ~('\n'|'\r')* '\r'? '\n' -> channel(HIDDEN)
|
||||
;
|
||||
|
|
|
@ -1021,7 +1021,7 @@ WS : (' '|'\r'|'\t'|'\u000C'|'\n')+ {$channel=HIDDEN;}
|
|||
;
|
||||
|
||||
COMMENT
|
||||
: '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
|
||||
: '/*' .* '*/' {$channel=HIDDEN;}
|
||||
;
|
||||
|
||||
LINE_COMMENT
|
||||
|
|
|
@ -1,3 +1,31 @@
|
|||
/*
|
||||
* [The "BSD license"]
|
||||
* Copyright (c) 2012 Sam Harwell
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.antlr.v4.test;
|
||||
|
||||
import org.antlr.v4.runtime.*;
|
||||
|
@ -14,11 +42,19 @@ import java.lang.reflect.Method;
|
|||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.antlr.v4.runtime.atn.ATNConfig;
|
||||
import org.antlr.v4.runtime.atn.ParserATNSimulator;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.dfa.DFAState;
|
||||
|
||||
public class TestPerformance extends BaseTest {
|
||||
/** Parse all java files under this package within the JDK_SOURCE_ROOT. */
|
||||
private static final String TOP_PACKAGE = "java";
|
||||
private static final String TOP_PACKAGE = "java.lang";
|
||||
/** True to load java files from sub-packages of {@link #TOP_PACKAGE}. */
|
||||
private static final boolean RECURSIVE = true;
|
||||
|
||||
|
@ -27,26 +63,41 @@ public class TestPerformance extends BaseTest {
|
|||
* the standard grammar (Java.g). In either case, the grammar is renamed in the temporary directory to Java.g
|
||||
* before compiling.
|
||||
*/
|
||||
private static final boolean USE_LR_GRAMMAR = false;
|
||||
private static final boolean USE_LR_GRAMMAR = true;
|
||||
/**
|
||||
* True to specify the -Xforceatn option when generating the grammar, forcing all decisions in JavaParser to
|
||||
* be handled by {@link v2ParserATNSimulator#adaptivePredict}.
|
||||
* True to specify the -Xforce-atn option when generating the grammar, forcing all decisions in JavaParser to
|
||||
* be handled by {@link ParserATNSimulator#adaptivePredict}.
|
||||
*/
|
||||
private static final boolean FORCE_ATN = false;
|
||||
/**
|
||||
* True to specify the -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;
|
||||
/**
|
||||
* True to delete temporary (generated and compiled) files when the test completes.
|
||||
*/
|
||||
private static final boolean DELETE_TEMP_FILES = true;
|
||||
|
||||
private static final boolean PAUSE_FOR_HEAP_DUMP = false;
|
||||
|
||||
/** Parse each file with JavaParser.compilationUnit */
|
||||
private static final boolean RUN_PARSER = true;
|
||||
/** True to use {@link BailErrorStrategy}, False to use {@link DefaultErrorStrategy} */
|
||||
private static final boolean BAIL_ON_ERROR = false;
|
||||
/** This value is passed to {@link org.antlr.v4.runtime.Parser#setBuildParseTree}. */
|
||||
private static final boolean BAIL_ON_ERROR = true;
|
||||
/** This value is passed to {@link Parser#setBuildParseTree}. */
|
||||
private static final boolean BUILD_PARSE_TREES = false;
|
||||
/**
|
||||
* Use ParseTreeWalker.DEFAULT.walk with the BlankJavaParserListener to show parse tree walking overhead.
|
||||
* If {@link #BUILD_PARSE_TREES} is false, the listener will instead be called during the parsing process via
|
||||
* {@link org.antlr.v4.runtime.Parser#addParseListener}.
|
||||
* {@link Parser#addParseListener}.
|
||||
*/
|
||||
private static final boolean BLANK_LISTENER = false;
|
||||
|
||||
private static final boolean SHOW_DFA_STATE_STATS = true;
|
||||
|
||||
private static final boolean SHOW_CONFIG_STATS = false;
|
||||
|
||||
/**
|
||||
* If true, a single JavaLexer will be used, and {@link Lexer#setInputStream} will be called to initialize it
|
||||
* for each source file. In this mode, the cached DFA will be persisted throughout the lexing process.
|
||||
|
@ -67,25 +118,30 @@ public class TestPerformance extends BaseTest {
|
|||
/** Total number of passes to make over the source */
|
||||
private static final int PASSES = 4;
|
||||
|
||||
private Lexer sharedLexer;
|
||||
private Parser sharedParser;
|
||||
private static Lexer sharedLexer;
|
||||
private static Parser sharedParser;
|
||||
@SuppressWarnings({"FieldCanBeLocal"})
|
||||
private ParseTreeListener<Token> sharedListener;
|
||||
private static ParseTreeListener<Token> sharedListener;
|
||||
|
||||
private int tokenCount;
|
||||
private int currentPass;
|
||||
|
||||
@Test
|
||||
// @Ignore
|
||||
//@Ignore
|
||||
public void compileJdk() throws IOException {
|
||||
compileParser(USE_LR_GRAMMAR);
|
||||
JavaParserFactory factory = getParserFactory();
|
||||
String jdkSourceRoot = System.getenv("JDK_SOURCE_ROOT");
|
||||
if (jdkSourceRoot == null) {
|
||||
jdkSourceRoot = System.getProperty("JDK_SOURCE_ROOT");
|
||||
}
|
||||
if (jdkSourceRoot == null) {
|
||||
System.err.println("The JDK_SOURCE_ROOT environment variable must be set for performance testing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TOP_PACKAGE.isEmpty()) {
|
||||
compileParser(USE_LR_GRAMMAR);
|
||||
JavaParserFactory factory = getParserFactory();
|
||||
|
||||
if (!TOP_PACKAGE.isEmpty()) {
|
||||
jdkSourceRoot = jdkSourceRoot + '/' + TOP_PACKAGE.replace('.', '/');
|
||||
}
|
||||
|
||||
|
@ -93,10 +149,13 @@ public class TestPerformance extends BaseTest {
|
|||
assertTrue(directory.isDirectory());
|
||||
|
||||
Collection<CharStream> sources = loadSources(directory, RECURSIVE);
|
||||
System.out.format("Lex=true, Parse=%s, ForceAtn=%s, Bail=%s, BuildParseTree=%s, BlankListener=%s\n",
|
||||
RUN_PARSER, FORCE_ATN, BAIL_ON_ERROR, BUILD_PARSE_TREES, BLANK_LISTENER);
|
||||
|
||||
System.out.print(getOptionsDescription());
|
||||
|
||||
currentPass = 0;
|
||||
parse1(factory, sources);
|
||||
for (int i = 0; i < PASSES - 1; i++) {
|
||||
currentPass = i + 1;
|
||||
if (CLEAR_DFA) {
|
||||
sharedLexer = null;
|
||||
sharedParser = null;
|
||||
|
@ -104,6 +163,55 @@ public class TestPerformance extends BaseTest {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void eraseTempDir() {
|
||||
if (DELETE_TEMP_FILES) {
|
||||
super.eraseTempDir();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getOptionsDescription() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Input=");
|
||||
if (TestPerformance.TOP_PACKAGE.isEmpty()) {
|
||||
builder.append("*");
|
||||
}
|
||||
else {
|
||||
builder.append(TOP_PACKAGE).append(".*");
|
||||
}
|
||||
|
||||
builder.append(", Grammar=").append(USE_LR_GRAMMAR ? "LR" : "Standard");
|
||||
builder.append(", ForceAtn=").append(FORCE_ATN);
|
||||
|
||||
builder.append('\n');
|
||||
|
||||
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('\n');
|
||||
|
||||
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');
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,6 +266,8 @@ public class TestPerformance extends BaseTest {
|
|||
}
|
||||
}
|
||||
|
||||
int configOutputSize = 0;
|
||||
|
||||
protected void parseSources(JavaParserFactory factory, Collection<CharStream> sources) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
tokenCount = 0;
|
||||
|
@ -167,7 +277,7 @@ public class TestPerformance extends BaseTest {
|
|||
input.seek(0);
|
||||
inputSize += input.size();
|
||||
// this incurred a great deal of overhead and was causing significant variations in performance results.
|
||||
//System.out.format("Parsing file %s\n", file.getAbsolutePath());
|
||||
//System.out.format("Parsing file %s\n", input.getSourceName());
|
||||
try {
|
||||
factory.parseFile(input);
|
||||
} catch (IllegalStateException ex) {
|
||||
|
@ -180,6 +290,80 @@ public class TestPerformance extends BaseTest {
|
|||
inputSize / 1024,
|
||||
tokenCount,
|
||||
System.currentTimeMillis() - startTime);
|
||||
|
||||
if (RUN_PARSER) {
|
||||
// make sure the individual DFAState objects actually have unique ATNConfig arrays
|
||||
final ParserATNSimulator<?> interpreter = sharedParser.getInterpreter();
|
||||
final DFA[] decisionToDFA = interpreter.decisionToDFA;
|
||||
|
||||
if (SHOW_DFA_STATE_STATS) {
|
||||
int states = 0;
|
||||
|
||||
for (int i = 0; i < decisionToDFA.length; i++) {
|
||||
DFA dfa = decisionToDFA[i];
|
||||
if (dfa == null || dfa.states == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
states += dfa.states.size();
|
||||
}
|
||||
|
||||
System.out.format("There are %d DFAState instances.\n", states);
|
||||
}
|
||||
|
||||
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.configset.size() >= contextsInDFAState.length) {
|
||||
contextsInDFAState = Arrays.copyOf(contextsInDFAState, state.configset.size() + 1);
|
||||
}
|
||||
|
||||
if (state.isAcceptState) {
|
||||
boolean hasGlobal = false;
|
||||
for (ATNConfig config : state.configset) {
|
||||
if (config.reachesIntoOuterContext > 0) {
|
||||
globalConfigCount++;
|
||||
hasGlobal = true;
|
||||
} else {
|
||||
localConfigCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasGlobal) {
|
||||
globalDfaCount++;
|
||||
} else {
|
||||
localDfaCount++;
|
||||
}
|
||||
}
|
||||
|
||||
contextsInDFAState[state.configset.size()]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 compileParser(boolean leftRecursive) throws IOException {
|
||||
|
@ -187,8 +371,15 @@ public class TestPerformance extends BaseTest {
|
|||
String sourceName = leftRecursive ? "Java-LR.g" : "Java.g";
|
||||
String body = load(sourceName, null);
|
||||
@SuppressWarnings({"ConstantConditions"})
|
||||
String[] extraOptions = FORCE_ATN ? new String[] {"-Xforceatn"} : new String[0];
|
||||
boolean success = rawGenerateAndBuildRecognizer(grammarFileName, body, "JavaParser", "JavaLexer", extraOptions);
|
||||
List<String> extraOptions = new ArrayList<String>();
|
||||
if (FORCE_ATN) {
|
||||
extraOptions.add("-Xforce-atn");
|
||||
}
|
||||
if (EXPORT_ATN_GRAPHS) {
|
||||
extraOptions.add("-atn");
|
||||
}
|
||||
String[] extraOptionsArray = extraOptions.toArray(new String[extraOptions.size()]);
|
||||
boolean success = rawGenerateAndBuildRecognizer(grammarFileName, body, "JavaParser", "JavaLexer", extraOptionsArray);
|
||||
assertTrue(success);
|
||||
}
|
||||
|
||||
|
@ -228,7 +419,7 @@ public class TestPerformance extends BaseTest {
|
|||
final Class<? extends Parser> parserClass = (Class<? extends Parser>)loader.loadClass("JavaParser");
|
||||
@SuppressWarnings({"unchecked"})
|
||||
final Class<? extends ParseTreeListener<Token>> listenerClass = (Class<? extends ParseTreeListener<Token>>)loader.loadClass("BlankJavaListener");
|
||||
this.sharedListener = listenerClass.newInstance();
|
||||
TestPerformance.sharedListener = listenerClass.newInstance();
|
||||
|
||||
final Constructor<? extends Lexer> lexerCtor = lexerClass.getConstructor(CharStream.class);
|
||||
final Constructor<? extends Parser> parserCtor = parserClass.getConstructor(TokenStream.class);
|
||||
|
@ -271,7 +462,7 @@ public class TestPerformance extends BaseTest {
|
|||
|
||||
Method parseMethod = parserClass.getMethod("compilationUnit");
|
||||
Object parseResult = parseMethod.invoke(sharedParser);
|
||||
assert parseResult instanceof ParseTree;
|
||||
Assert.assertTrue(parseResult instanceof ParseTree);
|
||||
|
||||
if (BUILD_PARSE_TREES && BLANK_LISTENER) {
|
||||
ParseTreeWalker.DEFAULT.walk(sharedListener, (ParseTree)parseResult);
|
||||
|
|
Loading…
Reference in New Issue