Merge branch 'master' into precedence-predicates

This commit is contained in:
Sam Harwell 2013-10-10 20:36:38 -05:00
commit b14ca56441
295 changed files with 11690 additions and 14756 deletions

26
.gitignore vendored
View File

@ -1,18 +1,16 @@
# Maven build folders
/target/
/tool/target/
/runtime/Java/target/
/antlr4-maven-plugin/target/
target/
# Ant build folders
/build/
/dist/
build/
dist/
lib/
user.build.properties
# NetBeans user configuration files
/nbactions.xml
/tool/nbactions.xml
/runtime/Java/nbactions.xml
/antlr4-maven-plugin/nbactions.xml
nbactions*.xml
/nbproject/private/
*/nbproject/private/
# IntelliJ projects
*.iml
@ -20,5 +18,13 @@
*.iws
.idea/
# Eclipse projects
.classpath
.project
.settings/
# Profiler results
*.hprof
# Playground
/tool/playground/

View File

@ -1,5 +1,297 @@
ANTLR v4 Honey Badger
August 31, 2013
* Lots of little fixes thanks to Coverity Scan
August 7, 2013
* [BREAKING CHANGE] Altered left-recursion elimination to be simpler. Now,
we use the following patterns:
* Binary expressions are expressions which contain a recursive invocation of
the rule as the first and last element of the alternative.
* Suffix expressions contain a recursive invocation of the rule as the first
element of the alternative, but not as the last element.
* Prefix expressions contain a recursive invocation of the rule as the last
element of the alternative, but not as the first element.
There is no such thing as a "ternary" expression--they are just binary
expressions in disguise.
The right associativity specifiers no longer on the individual tokens because
it's done on alternative basis anyway. The option is now on the individual
alternative; e.g.,
e : e '*' e
| e '+' e
|<assoc=right> e '?' e ':' e
|<assoc=right> e '=' e
| INT
;
If your language uses a right-associative ternary operator, you will need
to update your grammar to include <assoc=right> on the alternative operator.
This also fixes #245 and fixes #268:
https://github.com/antlr/antlr4/issues/245
https://github.com/antlr/antlr4/issues/268
To smooth the transition, <assoc=right> is still allowed on token references
but it is ignored.
June 30, 2013 -- 4.1 release
June 24, 2013
* Resize ANTLRInputStream.data after reading a file with fewer characters than
bytes
* Fix ATN created for non-greedy optional block with multiple alternatives
* Support Unicode escape sequences with indirection in JavaUnicodeInputStream
(fixes #287)
* Remove the ParserRuleContext.altNum field (fixes #288)
* PredictionContext no longer implements Iterable<SingletonPredictionContext>
* PredictionContext no longer implements Comparable<PredictionContext>
* Add the EPSILON_CLOSURE error and EPSILON_OPTIONAL warning
* Optimized usage of closureBusy set (fixes #282)
June 9, 2013
* Add regression test for #239 (already passes)
June 8, 2013
* Support list labels on a set of tokens (fixes #270)
* Fix associativity of XOR in Java LR grammar (fixes #280)
June 1, 2013
* DiagnosticErrorListener includes rule names for each decision in its reports
* Document ANTLRErrorListener and DiagnosticErrorListener (fixes #265)
* Support '\uFFFF' (fixes #267)
* Optimize serialized ATN
May 26, 2013
* Report errors that occur while lexing a grammar (fixes #262)
* Improved error message for unterminated string literals (fixes #243)
May 24, 2013
* Significantly improve performance of JavaUnicodeInputStream.LA(1)
May 20, 2013
* Generate Javadoc for generated visitor and listener interfaces and classes
* Fix unit tests
May 18, 2013
* Group terminals in Java grammars so ATN can collapse sets
* Improved Java 7 support in Java grammars (numeric literals)
* Updated error listener interfaces
* Support detailed statistics in TestPerformance
May 17, 2013
* Add JavaUnicodeInputStream to handle Unicode escapes in Java code
* Proper Unicode identifier handling in Java grammars
* Report file names with lexer errors in TestPerformance
May 14, 2013
* Use a called rule stack to prevent stack overflow in LL1Analyzer
* Use 0-based indexing for several arrays in the tool
* Code simplification, assertions, documentation
May 13, 2013
* Unit test updates to ensure exceptions are not hidden
May 12, 2013
* Updates to TestPerformance
May 5, 2013
* Updated several classes to use MurmurHash 3 hashing
May 1, 2013
* Added parse tree JTree to TreeViewer (Bart Kiers)
April 30, 2013
* Updated TestPerformance to support parallelization across passes
April 24, 2013
* Remove unused stub class ParserATNPathFinder
* Remove ParserInterpreter.predictATN
* Remove DFA.getATNStatesAlongPath
* Encapsulate implementation methods in LexerATNSimulator and ParserATNSimulator
* Updated documentation
* Simplify creation of new DFA edges
* Fix handling of previously cached error edges
* Fix DFA created during forced-SLL parsing (PredictionMode.SLL)
* Extract methods ParserATNSimulator.getExistingTargetState and
ParserATNSimulator.computeTargetState.
April 22, 2013
* Lazy initialization of ParserATNSimulator.mergeCache
* Improved hash code for DFAState
* Improved hash code with caching for ATNConfigSet
* Add new configuration parameters to TestPerformance
* Update Java LR and Java Std to support Java 7 syntax
April 21, 2013
* Add new configuration parameters to TestPerformance
April 18, 2013
* Must check rule transition follow states before eliminating states in
the ATN (fixes #224)
* Simplify ParserATNSimulator and improve performance by combining execDFA and
execATN and using DFA edges even after edge computation is required
April 15, 2013
* Fix code in TestPerformance that clears the DFA
April 12, 2013
* Improved initialization and concurrency control in DFA updates
* Fix EOF handling in edge case (fixes #218)
April 4, 2013
* Improved testing of error reporting
* Fix NPE revealed by updated testing method
* Strict handling of redefined rules - prevents code generation (fixes #210)
* Updated documentation in Tool
March 27, 2013
* Avoid creating empty action methods in lexer (fixes #202)
* Split serialized ATN when it exceeds Java's 65535 byte limit (fixes #76)
* Fix incorrect reports of label type conflicts across separated labeled outer
alternatives (fixes #195)
* Update Maven plugin site documentation
March 26, 2013
* Fix bugs with the closureBusy set in ParserATNSimulator.closure
* Fix handling of empty options{} block (fixes #194)
* Add error 149 INVALID_LEXER_COMMAND (fixes #190)
* Add error 150 MISSING_LEXER_COMMAND_ARGUMENT
* Add error 151 UNWANTED_LEXER_COMMAND_ARGUMENT
* Updated documentation in the Parser and RecognitionException classes
* Refactored and extensively documented the ANTLRErrorStrategy interface and
DefaultErrorStrategy default implementation
* Track the number of syntax errors in Parser.notifyErrorListeners instead of in
the error strategy
* Move primary implementation of getExpectedTokens to ATN, fixes #191
* Updated ATN documentation
* Use UUID instead of incremented integer for serialized ATN versioning
March 7, 2013
* Added export to PNG feature to the parse tree viewer
March 6, 2013
* Allow direct calls to left-recursive rules (fixes #161)
* Change error type 146 (EPSILON_TOKEN) to a warning (fixes #180)
* Specify locale for all format operations (fixes #158)
* Fix generation of invalid Unicode escape sequences in Java code (fixes #164)
* Do not require escape for $ in action when not followed by an ID start char
(fixes #176)
February 23, 2013
* Refactoring Target-related classes to improve support for additional language
targets
February 22, 2013
* Do not allow raw newline characters in literals
* Pair and Triple are immutable; Triple is not a Pair
February 5, 2013
* Fix IntervalSet.add when multiple merges are required (fixes #153)
January 29, 2013
* don't call process() if args aren't specified (Dave Parfitt)
January 21, 2013 -- Release 4.0
* Updated PredictionContext Javadocs
* Updated Maven site documentation
* Minor tweaks in Java.stg
January 15, 2013
* Tweak error messages
* (Tool) Make TokenVocabParser fields `protected final`
* Fix generated escape sequences for literals containing backslashes
January 14, 2013
* Relax parser in favor of errors during semantic analysis
* Add error 145: lexer mode must contain at least one non-fragment rule
* Add error 146: non-fragment lexer rule can match the empty string
January 11, 2013
* Updated error 72, 76; added 73-74 and 136-143: detailed errors about name
conflicts
* Report exact location for parameter/retval/local name conflicts
* Add error 144: multi-character literals are not allowed in lexer sets
* Error 134 now only applies to rule references in lexer sets
* Updated error messages (cleanup)
* Reduce size of _serializedATN by adding 2 to each element: new representation
avoids embedded values 0 and 0xFFFF which are common and have multi-byte
representations in Java's modified UTF-8
January 10, 2013
* Add error 135: cannot assign a value to list label: $label
(fixes antlr/antlr4#128)
January 2, 2013
* Fix EOF handling (antlr/antlr4#110)
* Remove TREE_PARSER reference
* Additional validation checks in ATN deserialization
* Fix potential NPE in parser predicate evaluation
* Fix termination condition detection in full-context parsing
January 1, 2013
* Updated documentation
* Minor code cleanup
* Added the `-XdbgSTWait` command line option for the Tool
* Removed method override since bug was fixed in V3 runtime
December 31, 2012
* I altered Target.getTargetStringLiteralFromANTLRStringLiteral() so that
it converts \uXXXX in an ANTLR string to \\uXXXX, thus, avoiding Java's
conversion to a single character before compilation.
December 16, 2012
* Encapsulate some fields in ANTLRMessage
* Remove ErrorType.INVALID
* Update error/warning messages, show all v3 compatibility messages
December 12, 2012
* Use arrays instead of HashSet to save memory in SemanticContext.AND/OR

View File

@ -1,5 +1,5 @@
[The "BSD license"]
Copyright (c) 2012 Terence Parr, Sam Harwell
Copyright (c) 2013 Terence Parr, Sam Harwell
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,64 +1,92 @@
ANTLR v4
Terence Parr, parrt at cs usfca edu
Terence Parr, parrt@cs.usfca.edu
ANTLR project lead and supreme dictator for life
University of San Francisco
INTRODUCTION
Hi and welcome to the Honey Badger 4.0 release of ANTLR!
Hi and welcome to the Honey Badger 4.1 release of ANTLR!
INSTALLATION
$ cd /usr/local/lib
$ curl -O --silent http://www.antlr.org/download/antlr-4.0-complete.jar
UNIX
Or just download from http://www.antlr.org/download/antlr-4.0-complete.jar
and put it somewhere rational for your operating system.
0. Install Java (version 1.6 or higher)
You can either add to your CLASSPATH:
1. Download
$ export CLASSPATH=".:/usr/local/lib/antlr-4.0-complete.jar:$CLASSPATH"
$ cd /usr/local/lib
$ curl -O http://www.antlr4.org/download/antlr-4.1-complete.jar
and launch org.antlr.v4.Tool directly:
Or just download in browser using URL:
http://www.antlr4.org/download/antlr-4.1-complete.jar
and put it somewhere rational like /usr/local/lib.
2. Add antlr-4.1-complete.jar to your CLASSPATH:
$ export CLASSPATH=".:/usr/local/lib/antlr-4.1-complete.jar:$CLASSPATH"
Is also a good idea to put this in your .bash_profile or whatever your
startup script is.
3. Create aliases for the ANTLR Tool, and TestRig.
$ alias antlr4='java -jar /usr/local/lib/antlr-4.1-complete.jar'
$ alias grun='java org.antlr.v4.runtime.misc.TestRig'
WINDOWS (Thanks to Graham Wideman)
0. Install Java (version 1.6 or higher)
1. Download http://antlr.org/download/antlr-4.1-complete.jar
Save to your directory for 3rd party Java libraries, say C:\Javalib
2. Add antlr-4.1-complete.jar to CLASSPATH, either:
* Permanently: Using System Properties dialog > Environment variables >
Create or append to CLASSPATH variable
* Temporarily, at command line:
SET CLASSPATH=C:\Javalib\antlr-4.1-complete.jar;%CLASSPATH%
3. Create short convenient commands for the ANTLR Tool, and TestRig,
using batch files or doskey commands:
* Batch files (in directory in system PATH)
antlr4.bat: java org.antlr.v4.Tool %*
run.bat: java org.antlr.v4.runtime.misc.TestRig %*
* Or, use doskey commands:
doskey antlr4=java org.antlr.v4.Tool $*
doskey grun =java org.antlr.v4.runtime.misc.TestRig $*
TESTING INSTALLATION
Either launch org.antlr.v4.Tool directly:
$ java org.antlr.v4.Tool
ANTLR Parser Generator Version 4.0
ANTLR Parser Generator Version 4.1
-o ___ specify output directory where all output is generated
-lib ___ specify location of .tokens files
...
or use -jar option on java:
$ java -jar /usr/local/lib/antlr-4.0-complete.jar
ANTLR Parser Generator Version 4.0
$ java -jar /usr/local/lib/antlr-4.1-complete.jar
ANTLR Parser Generator Version 4.1
-o ___ specify output directory where all output is generated
-lib ___ specify location of .tokens files
...
You can make a script, /usr/local/bin/antlr4:
#!/bin/sh
java -cp "/usr/local/lib/antlr4-complete.jar:$CLASSPATH" org.antlr.v4.Tool $*
On Windows, you can do something like this (assuming you put the
jar in C:\libraries) for antlr4.bat:
java -cp C:\libraries\antlr-4.0-complete.jar;%CLASSPATH% org.antlr.v4.Tool %*
You can also use an alias
$ alias antlr4='java -jar /usr/local/lib/antlr-4.0-complete.jar'
Either way, say just antlr4 to run ANTLR now.
The TestRig class is very useful for testing your grammars:
$ alias grun='java org.antlr.v4.runtime.misc.TestRig'
EXAMPLE
In /tmp/Hello.g4, paste this:
In a temporary directory, put the following grammar inside file Hello.g4:
// Define a grammar called Hello
// match keyword hello followed by an identifier
@ -87,3 +115,7 @@ the parse tree in LISP notation.
BOOK SOURCE CODE
http://pragprog.com/titles/tpantlr2/source_code
GRAMMARS
https://github.com/antlr/grammars-v4

View File

@ -41,7 +41,7 @@
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr4-master</artifactId>
<version>4.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
</parent>
<artifactId>antlr4-maven-plugin</artifactId>
@ -49,6 +49,7 @@
<name>ANTLR 4 Maven plugin</name>
<description>Maven plugin for ANTLR 4 grammars</description>
<url>http://www.antlr.org</url>
<prerequisites>
<maven>3.0</maven>
@ -75,7 +76,7 @@
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.0.4</version>
<version>3.0.5</version>
<scope>compile</scope>
</dependency>
@ -88,7 +89,7 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-api</artifactId>
<version>2.0</version>
<version>2.2</version>
</dependency>
<!--
@ -99,7 +100,7 @@
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4</artifactId>
<version>4.0-SNAPSHOT</version>
<version>${project.version}</version>
</dependency>
<!--
@ -163,13 +164,13 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.2</version>
<version>3.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.6</version>
<version>2.7</version>
<configuration>
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
</configuration>
@ -179,4 +180,28 @@
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9</version>
<configuration>
<quiet>true</quiet>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.3</version>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -66,15 +66,14 @@ import java.util.Map;
import java.util.Set;
/**
* Goal that picks up all the ANTLR grammars in a project and moves those that
* are required for generation of the compilable sources into the location
* that we use to compile them, such as {@code target/generated-sources/antlr4}.
*
* Parses ANTLR 4 grammar files {@code *.g4} and transforms them into Java
* source files.
*
* @author Sam Harwell
*/
@Mojo(
name = "antlr",
defaultPhase = LifecyclePhase.PROCESS_SOURCES,
name = "antlr4",
defaultPhase = LifecyclePhase.GENERATE_SOURCES,
requiresDependencyResolution = ResolutionScope.COMPILE,
requiresProject = true)
public class Antlr4Mojo extends AbstractMojo {
@ -84,8 +83,8 @@ public class Antlr4Mojo extends AbstractMojo {
//
/**
* If set to true then the ANTLR tool will generate a description of the atn
* for each rule in <a href="http://www.graphviz.org">Dot format</a>
* If set to true then the ANTLR tool will generate a description of the ATN
* for each rule in <a href="http://www.graphviz.org">Dot format</a>.
*/
@Parameter(property = "antlr4.atn", defaultValue = "false")
protected boolean atn;
@ -93,29 +92,29 @@ public class Antlr4Mojo extends AbstractMojo {
/**
* specify grammar file encoding; e.g., euc-jp
*/
@Parameter
@Parameter(property = "project.build.sourceEncoding")
protected String encoding;
/**
* generate parse tree listener (default)
* Generate parse tree listener interface and base class.
*/
@Parameter(property = "antlr4.listener", defaultValue = "true")
protected boolean listener;
/**
* generate parse tree visitor
* Generate parse tree visitor interface and base class.
*/
@Parameter(property = "antlr4.visitor", defaultValue = "false")
protected boolean visitor;
/**
* treat warnings as errors
* Treat warnings as errors.
*/
@Parameter(property = "antlr4.treatWarningsAsErrors", defaultValue = "false")
protected boolean treatWarningsAsErrors;
/**
* use the ATN simulator for all predictions
* Use the ATN simulator for all predictions.
*/
@Parameter(property = "antlr4.forceATN", defaultValue = "false")
protected boolean forceATN;
@ -135,50 +134,49 @@ public class Antlr4Mojo extends AbstractMojo {
protected List<String> arguments;
/* --------------------------------------------------------------------
* The following are Maven specific parameters, rather than specificlly
* The following are Maven specific parameters, rather than specific
* options that the ANTLR tool can use.
*/
/**
* Provides an explicit list of all the grammars that should
* be included in the generate phase of the plugin. Note that the plugin
* is smart enough to realize that imported grammars should be included but
* not acted upon directly by the ANTLR Tool.
*
* Unless otherwise specified, the include list scans for and includes all
* files that end in ".g" in any directory beneath src/main/antlr4. Note that
* this version of the plugin looks for the directory antlr4 and not the directory
* antlr, so as to avoid clashes and confusion for projects that use both v3 and v4 grammars
* such as ANTLR itself.
*/
/**
* Provides an explicit list of all the grammars that should be included in
* the generate phase of the plugin. Note that the plugin is smart enough to
* realize that imported grammars should be included but not acted upon
* directly by the ANTLR Tool.
* <p/>
* A set of Ant-like inclusion patterns used to select files from the source
* directory for processing. By default, the pattern
* <code>**&#47;*.g4</code> is used to select grammar files.
*/
@Parameter
protected Set<String> includes = new HashSet<String>();
/**
* Provides an explicit list of any grammars that should be excluded from
* the generate phase of the plugin. Files listed here will not be sent for
* processing by the ANTLR tool.
* A set of Ant-like exclusion patterns used to prevent certain files from
* being processed. By default, this set is empty such that no files are
* excluded.
*/
@Parameter
protected Set<String> excludes = new HashSet<String>();
/**
* The current Maven project.
*/
@Parameter(property = "project", required = true, readonly = true)
protected MavenProject project;
/**
* Specifies the ANTLR directory containing grammar files.
* The directory where the ANTLR grammar files ({@code *.g4}) are located.
*/
@Parameter(defaultValue = "${basedir}/src/main/antlr4", required = true)
@Parameter(defaultValue = "${basedir}/src/main/antlr4")
private File sourceDirectory;
/**
* Specify output directory where the Java files are generated.
*/
@Parameter(defaultValue = "${project.build.directory}/generated-sources/antlr4", required = true)
@Parameter(defaultValue = "${project.build.directory}/generated-sources/antlr4")
private File outputDirectory;
/**
* Specify location of grammars and tokens files.
* Specify location of imported grammars and tokens files.
*/
@Parameter(defaultValue = "${basedir}/src/main/antlr4/imports")
private File libDirectory;
@ -207,42 +205,28 @@ public class Antlr4Mojo extends AbstractMojo {
/**
* The main entry point for this Mojo, it is responsible for converting
* ANTLR 4.x grammars into the target language specified by the grammar.
*
* @throws MojoExecutionException When something is discovered such as a missing source
* @throws MojoFailureException When something really bad happens such as not being able to create the ANTLR Tool
*
* @throws MojoExecutionException if a configuration or grammar error causes
* the code generation process to fail
* @throws MojoFailureException if an instance of the ANTLR 4 {@link Tool}
* cannot be created
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Log log = getLog();
// Check to see if the user asked for debug information, then dump all the
// parameters we have picked up if they did.
//
if (log.isDebugEnabled()) {
// Excludes
//
for (String e : excludes) {
log.debug("ANTLR: Exclude: " + e);
}
// Includes
//
for (String e : includes) {
log.debug("ANTLR: Include: " + e);
}
// Output location
//
log.debug("ANTLR: Output: " + outputDirectory);
// Library directory
//
log.debug("ANTLR: Library: " + libDirectory);
// Flags
//
}
if (!sourceDirectory.isDirectory()) {
@ -363,7 +347,7 @@ public class Antlr4Mojo extends AbstractMojo {
/**
*
* @param sourceDirectory
* @throws org.codehaus.plexus.compiler.util.scan.InclusionScanException
* @throws InclusionScanException
*/
@NotNull
private List<List<String>> processGrammarFiles(List<String> args, File sourceDirectory) throws InclusionScanException {
@ -445,7 +429,7 @@ public class Antlr4Mojo extends AbstractMojo {
* relative to the base of the output directory and reflect the input
* organization of the grammar files.
*
* @param sourceDirectory The source directory File object
* @param sourceDirectory The source directory {@link File} object
* @param grammarFileName The full path to the input grammar file
* @return The path to the grammar file relative to the source directory
*/

View File

@ -1,8 +1,8 @@
Imported Grammar Files
In order to have the ANTLR plugin automatically locate and use grammars used
as imports in your main .g4 files, you need to place the imported grammar
files in the imports directory beneath the root directory of your grammar
as imports in your main <<<.g4>>> files, you need to place the imported grammar
files in the <<<imports>>> directory beneath the root directory of your grammar
files (which is <<<src/main/antlr4>>> by default of course).
For a default layout, place your import grammars in the directory: <<<src/main/antlr4/imports>>>

View File

@ -2,23 +2,23 @@ Libraries
The introduction of the import directive in a grammar allows reuse of common grammar files
as well as the ability to divide up functional components of large grammars. However it has
caused some confusion in regard to the fact that generated vocab files (<<<xxx.tokens>>>) can also
caused some confusion in regard to the fact that generated vocabulary files (<<<*.tokens>>>) can also
be searched for with the <<<<libDirectory>>>> directive.
This has confused two separate functions and imposes a structure upon the layout of
your grammar files in certain cases. If you have grammars that both use the import
directive and also require the use of a vocab file then you will need to locate
the grammar that generates the .tokens file alongside the grammar that uses it. This
directive and also require the use of a vocabulary file then you will need to locate
the grammar that generates the <<<.tokens>>> file alongside the grammar that uses it. This
is because you will need to use the <<<<libDirectory>>>> directive to specify the
location of your imported grammars and ANTLR will not find any vocab files in
location of your imported grammars and ANTLR will not find any vocabulary files in
this directory.
The .tokens files for any grammars are generated within the same output directory structure
as the .java files. So, whereever the .java files are generated, you will also find the .tokens
files. ANTLR looks for .tokens files in both the <<<<libDirectory>>>> and the output directory
where it is placing the geenrated .java files. Hence when you locate the grammars that generate
.tokens files in the same source directory as the ones that use the .tokens files, then
the Maven plugin will find the expected .tokens files.
The <<<.tokens>>> files for any grammars are generated within the same output directory structure
as the <<<.java>>> files. So, wherever the <<<.java>>> files are generated, you will also find the <<<.tokens>>>
files. ANTLR looks for <<<.tokens>>> files in both the <<<<libDirectory>>>> and the output directory
where it is placing the generated <<<.java>>> files. Hence when you locate the grammars that generate
<<<.tokens>>> files in the same source directory as the ones that use the <<<.tokens>>> files, then
the Maven plugin will find the expected <<<.tokens>>> files.
The <<<<libDirectory>>>> is specified like any other directory parameter in Maven. Here is an
example:
@ -27,14 +27,13 @@ Libraries
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.0-SNAPSHOT</version>
<version>${project.version}</version>
<executions>
<execution>
<configuration>
<goals>
<goal>antlr</goal>
<goal>antlr4</goal>
</goals>
<libDirectory>src/main/antlr4_imports</libDirectory>
</configuration>

View File

@ -1,18 +1,18 @@
Simple configuration
If your grammar files are organized into the default locations as described in the {{{../index.html}introduction}},
then configuring the pom.xml file for your project is as simple as adding this to it
then configuring the <<<pom.xml>>> file for your project is as simple as adding this to it
+--
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.0-SNAPSHOT</version>
<version>${project.version}</version>
<executions>
<execution>
<goals>
<goal>antlr</goal>
<goal>antlr4</goal>
</goals>
</execution>
</executions>
@ -21,12 +21,12 @@ Simple configuration
</plugins>
+--
When the mvn command is executed all grammar files under <<<src/main/antlr4>>>, except any
When the <<<mvn>>> command is executed all grammar files under <<<src/main/antlr4>>>, except any
import grammars under <<<src/main/antlr4/imports>>> will be analyzed and converted to
java source code in the output directory <<<target/generated-sources/antlr4>>>.
Java source code in the output directory <<<target/generated-sources/antlr4>>>.
Your input files under <<<antlr4>>> should be stored in sub directories that
reflect the package structure of your java parsers. If your grammar file parser.g4 contains:
reflect the package structure of your java parsers. If your grammar file <<<parser.g4>>> contains:
+---
@header {
@ -34,7 +34,7 @@ package org.jimi.themuss;
}
+---
Then the .g4 file should be stored in: <<<src/main/antlr4/org/jimi/themuss/parser.g4>>>. THis way
the generated .java files will correctly reflect the package structure in which they will
Then the <<<.g4>>> file should be stored in: <<<src/main/antlr4/org/jimi/themuss/parser.g4>>>. This way
the generated <<<.java>>> files will correctly reflect the package structure in which they will
finally rest as classes.

View File

@ -0,0 +1 @@
FAQ

View File

@ -28,7 +28,7 @@ ANTLR v4 Maven plugin
use version 4.0 of the plugin, you will build your grammars using version 4.0 of the
ANTLR tool, version 4.2 of the plugin will use version 4.2 of the ANTLR tool and so on.
You may also find that there are patch versions of the plugin suchas 4.0-1 4.0-2 and
You may also find that there are patch versions of the plugin such as 4.0-1 4.0-2 and
so on. Use the latest patch release of the plugin.
The current version of the plugin is shown at the top of this page after the <<Last Deployed>> date.
@ -49,10 +49,10 @@ ANTLR v4 Maven plugin
+--- imports/ .g4 files that are imported by other grammars.
+--
If your grammar is intended to be part of a package called org.foo.bar then you would
If your grammar is intended to be part of a package called <<<org.foo.bar>>> then you would
place it in the directory <<<src/main/antlr4/org/foo/bar>>>. The plugin will then produce
.java and .tokens files in the output directory <<<target/generated-sources/antlr4/org/foo/bar>>>
When the Java files are compiled they will be in the correct location for the javac
<<<.java>>> and <<<.tokens>>> files in the output directory <<<target/generated-sources/antlr4/org/foo/bar>>>
When the Java files are compiled they will be in the correct location for the Javac
compiler without any special configuration. The generated java files are automatically
submitted for compilation by the plugin.
@ -60,4 +60,3 @@ ANTLR v4 Maven plugin
any grammar files that are imported by other grammar files (do not make subdirectories here.)
Such files are never built on their own, but the plugin will automatically tell the ANTLR
tool to look in this directory for library files.

View File

@ -1,193 +1,59 @@
Usage
The Maven plugin for antlr is simple to use but is at its simplest when you use the default
layouts for your grammars, as so:
The ANTLR 4 plugin for Maven can generate parsers for any number of grammars in
your project.
* Compiling Grammars into Parsers
By default, the <<<{{{./antlr4-mojo.html}antlr4}}>>> goal will search for grammar
files in the directory <<<$\{basedir\}/src/main/antlr4>>> and any additional
<<<.tokens>>> files in the directory <<<$\{basedir\}/src/main/antlr4/imports>>>.
This can be configured to search other directories using the plugin configuration
parameters as described in the <<<{{{./antlr4-mojo.html}antlr4}}>>> goal
documentation.
The following figure shows the expected layout of files for the default
configuration of this plugin.
+--
src/main/
|
+--- antlr4/... .g4 files organized in the required package structure
+--- antlr4/... .g4 files organized in the required package structure
|
+--- imports/ .g4 files that are imported by other grammars.
+--- imports/ user-created .tokens files and .g4 files that are imported by other grammars
+--
However, if you are not able to use this structure for whatever reason, you
can configure the locations of the grammar files, where library/import files
are located and where the output files should be generated.
* Plugin Descriptor
The current version of the plugin is shown at the top of this page after the <<Last Deployed>> date.
The full layout of the descriptor (at least, those parts that are not standard Maven things),
showing the default values of the configuration options, is as follows:
The next step is to configure your POM to call the plugin. The goals will
normally run during the generate-sources phase of the build. Examples of how to
configure your POM can be found on the various examples pages, reachable via
the page menu. If you stick with the default values, the snippet below will
suffice:
+--
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.0-SNAPSHOT</version>
<executions>
<execution>
<configuration>
<goals>
<goal>antlr</goal>
</goals>
<conversionTimeout>10000</conversionTimeout>
<debug>false</debug>
<dfa>false</dfa>
<nfa>false</nfa>
<excludes><exclude/></excludes>
<includes><include/></includes>
<libDirectory>src/main/antlr4/imports</libDirectory>
<messageFormat>antlr</messageFormat>
<outputDirectory>target/generated-sources/antlr4</outputDirectory>
<printGrammar>false</printGrammar>
<profile>false</profile>
<report>false</report>
<sourceDirectory>src/main/antlr4</sourceDirectory>
<trace>false</trace>
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>antlr</id>
<goals>
<goal>antlr4</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
...
</project>
+--
Note that you can create multiple executions, and thus build some grammars with different
options to others (such as setting the debug option for instance).
** Configuration parameters
*** report
If set to true, then after the tool has processed an input grammar file
it will report variaous statistics about the parser, such as information
on cyclic DFAs, which rules may use backtracking, and so on.
default-value="false"
*** printGrammar
If set to true, then the ANTLR tool will print a version of the input
grammar which is devoid of any actions that may be present in the input file.
default-value = "false"
*** debug
If set to true, then the code generated by the ANTLR code generator will
be set to debug mode. This means that when run, the code will 'hang' and
wait for a debug connection on a TCP port (49100 by default).
default-value="false"
*** profile
If set to true, then then the generated parser will compute and report on
profile information at runtime.
default-value="false"
*** nfa
If set to true then the ANTLR tool will generate a description of the nfa
for each rule in <a href="http://www.graphviz.org">Dot format</a>
default-value="false"
protected boolean nfa;
*** dfa
If set to true then the ANTLR tool will generate a description of the DFA
for each decision in the grammar in <a href="http://www.graphviz.org">Dot format</a>
default-value="false"
*** trace
If set to true, the generated parser code will log rule entry and exit points
to stdout as an aid to debugging.
default-value="false"
*** messageFormat
If this parameter is set, it indicates that any warning or error messages returned
by ANLTR, shoould be formatted in the specified way. Currently, ANTLR supports the
built-in formats of antlr, gnu and vs2005.
default-value="antlr"
*** verbose
If this parameter is set to true, then ANTLR will report all sorts of things
about what it is doing such as the names of files and the version of ANTLR and so on.
default-value="true"
*** conversionTimeout
The number of milliseconds ANTLR will wait for analysis of each
alternative in the grammar to complete before giving up. You may raise
this value if ANTLR gives up on a complicated alt and tells you that
there are lots of ambiguties, but you know that it just needed to spend
more time on it. Note that this is an absolute time and not CPU time.
default-value="10000"
*** includes
Provides an explicit list of all the grammars that should
be included in the generate phase of the plugin. Note that the plugin
is smart enough to realize that imported grammars should be included but
not acted upon directly by the ANTLR Tool.
Unless otherwise specified, the include list scans for and includes all
files that end in ".g4" in any directory beneath src/main/antlr4. Note that
this version of the plugin looks for the directory antlr4 and not the directory
antlr, so as to avoid clashes and confusion for projects that use both v3 and v4 grammars
such as ANTLR itself.
*** excludes
Provides an explicit list of any grammars that should be excluded from
the generate phase of the plugin. Files listed here will not be sent for
processing by the ANTLR tool.
*** sourceDirectory
Specifies the Antlr directory containing grammar files. For
antlr version 4.x we default this to a directory in the tree
called antlr4 because the antlr directory is occupied by version
2.x grammars.
<<NB>> Take careful note that the default location for antlr grammars
is now <<antlr4>> and NOT <<antlr>>
default-value="<<<${basedir}/src/main/antlr4>>>"
*** outputDirectory
Location for generated Java files. For antlr version 4.x we default
this to a directory in the tree called antlr4 because the antlr
directory is occupied by version 2.x grammars.
default-value="<<<${project.build.directory}/generated-sources/antlr4>>>"
*** libDirectory
Location for imported token files, e.g. <code>.tokens</code> and imported grammars.
Note that ANTLR will not try to process grammars that it finds in this directory, but
will include this directory in the search for .tokens files and import grammars.
<<NB>> If you change the lib directory from the default but the directory is
still under<<<${basedir}/src/main/antlr4>>>, then you will need to exclude
the grammars from processing specifically, using the <<<<excludes>>>> option.
default-value="<<<${basedir}/src/main/antlr4/imports>>>"
Note that you can create multiple executions, and thus build some grammars with
different options to others (such as setting the <<<debug>>> option for
instance).

View File

@ -17,7 +17,9 @@
<menu name="Overview">
<item name="Introduction" href="index.html"/>
<item name="Goals" href="plugin-info.html"/>
<item name="Usage" href="usage.html"/>
<item name="FAQ" href="faq.html"/>
</menu>
<menu name="Examples">

View File

@ -1,4 +0,0 @@
version=4.0b4
antlr3.jar=/usr/local/lib/antlr-3.5-complete.jar
build.sysclasspath=ignore

236
build.xml
View File

@ -1,130 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Make build.properties like this:
version=4.0b2
antlr3.jar=/usr/local/lib/antlr-3.5-complete.jar
build.sysclasspath=ignore
-->
<project name="ANTLR4" default="distribute" basedir=".">
<property file="build.properties" />
<property name="src.dir" value="${basedir}/src" />
<property name="dist.dir" value="${basedir}/dist" />
<property name="build.dir" value="${basedir}/build" />
<property name="lib.dir" value="${basedir}/lib" />
<property name="install.root.dir" value="${dist.dir}/antlr-${version}" />
<property name="jar.file" value="${dist.dir}/antlr-${version}-complete.jar" />
<path id="classpath">
<pathelement location="${antlr3.jar}"/> <!-- general setup -->
<pathelement location="${ant-antlr3.jar}"/>
<pathelement location="${basedir}/runtime/Java/lib/org.abego.treelayout.core.jar"/>
</path>
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
<target name="basic-init">
<property file="user.build.properties"/>
<property name="dist.dir" value="${basedir}/dist" />
<property name="build.dir" value="${basedir}/build" />
<property name="lib.dir" value="${basedir}/lib" />
</target>
<target name="init">
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build.dir}/gen/org/antlr/v4" />
<target name="antlr3-init" depends="basic-init">
<property name="antlr3.version" value="3.5"/>
<property name="antlr3.jar.name" value="antlr-${antlr3.version}-complete-no-st3.jar"/>
<property name="antlr3.jar" value="${lib.dir}/${antlr3.jar.name}"/>
<mkdir dir="${lib.dir}"/>
<get src="http://antlr3.org/download/${antlr3.jar.name}" dest="${antlr3.jar}" skipexisting="true"/>
<path id="cp.antlr3" path="${antlr3.jar}"/>
<property name="build.antlr3.dir" value="${build.dir}/generated-sources/antlr3" />
<property name="antlr3.touch" value="${build.dir}/antlr3-${antlr3.version}.touch"/>
</target>
<target name="build-init" depends="basic-init">
<property name="version" value="4.1.1-dev"/>
<property name="build.sysclasspath" value="ignore"/>
<property name="install.root.dir" value="${dist.dir}/antlr-${version}" />
<property name="jar.file" value="${dist.dir}/antlr-${version}-complete.jar" />
</target>
<target name="clean" depends="basic-init">
<delete dir="${build.dir}" includeemptydirs="true"/>
<delete dir="${dist.dir}" includeemptydirs="true"/>
</target>
<target name="antlr" depends="init">
<echo>parse grammars</echo>
<java classname="org.antlr.Tool" fork="true" failonerror="false" maxmemory="300m"
dir="${basedir}/tool/src/org/antlr/v4/parse">
<arg value="-make"/>
<arg value="ANTLRParser.g"/>
<arg value="ANTLRLexer.g"/>
<arg value="ActionSplitter.g"/>
<arg value="ATNBuilder.g"/>
<arg value="BlockSetTransformer.g"/>
<arg value="GrammarTreeVisitor.g"/>
<arg value="LeftRecursiveRuleWalker.g"/>
<classpath>
<pathelement location="${antlr3.jar}"/>
<pathelement path="${java.class.path}"/>
</classpath>
</java>
<target name="clean-all" depends="clean">
<delete dir="${lib.dir}" includeemptydirs="true"/>
</target>
<!--
<echo>semantics grammars</echo>
<java classname="org.antlr.Tool" fork="true" failonerror="false" maxmemory="300m"
dir="${basedir}/tool/src/org/antlr/v4/semantics">
<arg value="-make"/>
<arg value="-lib"/>
<arg value="../parse"/>
<arg value="BasicSemanticTriggers.g"/>
<arg value="CollectSymbols.g"/>
<arg value="Refs.g"/>
<classpath>
<pathelement location="${antlr3.jar}"/>
<pathelement path="${java.class.path}"/>
</classpath>
</java>
-->
<target name="antlr3-up-to-date" depends="basic-init,antlr3-init">
<uptodate targetfile="${antlr3.touch}" property="is.antlr3.uptodate">
<srcfiles dir="${basedir}/tool/src">
<include name="**/*.g"/>
<include name="**/*.tokens"/>
</srcfiles>
<srcfiles file="${antlr3.jar}"/>
</uptodate>
</target>
<echo>codegen grammars</echo>
<java classname="org.antlr.Tool" fork="true" failonerror="false" maxmemory="300m"
dir="${basedir}/tool/src/org/antlr/v4/codegen">
<arg value="-make"/>
<arg value="-lib"/>
<arg value="../parse"/>
<arg value="SourceGenTriggers.g"/>
<classpath>
<pathelement location="${antlr3.jar}"/>
<pathelement path="${java.class.path}"/>
</classpath>
</java>
<target name="up-to-date" depends="antlr3-up-to-date,build-init">
<uptodate targetfile="${jar.file}" property="is.source.uptodate">
<srcfiles dir="${basedir}/tool/src">
<include name="**/*.java"/>
<include name="**/*.g"/>
<include name="**/*.tokens"/>
</srcfiles>
<srcfiles dir="${basedir}/tool/resources">
<include name="**/*.st"/>
<include name="**/*.stg"/>
</srcfiles>
<srcfiles dir="${basedir}/runtime/Java/src/">
<include name="**/*.java"/>
<include name="**/*.g"/>
<include name="**/*.st"/>
<include name="**/*.stg"/>
</srcfiles>
<srcfiles dir="${build.antlr3.dir}"/>
<srcfiles file="${basedir}/runtime/Java/lib/org.abego.treelayout.core.jar"/>
<srcfiles file="${antlr3.jar}"/>
</uptodate>
<!--
<echo>gunit grammars</echo>
<java classname="org.antlr.Tool" fork="true" failonerror="false" maxmemory="300m"
dir="${basedir}/gunit/src/org/antlr/v4/gunit">
<arg value="-make"/>
<arg value="ASTVerifier.g"/>
<arg value="gUnit.g"/>
<arg value="jUnitGen.g"/>
<arg value="Semantics.g"/>
<classpath>
<pathelement location="${antlr3.jar}"/>
<pathelement path="${java.class.path}"/>
</classpath>
</java>
-->
<condition property="is.jar.uptodate">
<and>
<istrue value="${is.source.uptodate}"/>
<istrue value="${is.antlr3.uptodate}"/>
</and>
</condition>
</target>
<macrodef name="antlr3">
<attribute name="srcpath"/>
<element name="args" optional="true"/>
<sequential>
<local name="path.antlr3.local"/>
<local name="sources.antlr3.local"/>
<path id="path.antlr3.local">
<fileset dir="${basedir}/tool/src/@{srcpath}" includes="*.g"/>
</path>
<pathconvert pathsep=" " property="sources.antlr3.local" refid="path.antlr3.local">
<map from="${basedir}/tool/src/@{srcpath}/" to=""/>
</pathconvert>
<mkdir dir="${build.antlr3.dir}/@{srcpath}"/>
<java classname="org.antlr.Tool" fork="true" failonerror="true" maxmemory="300m"
dir="${basedir}/tool/src/@{srcpath}">
<arg value="-o"/>
<arg value="${build.antlr3.dir}/@{srcpath}"/>
<args/>
<arg line="${sources.antlr3.local}"/>
<classpath>
<path refid="cp.antlr3"/>
<pathelement location="${java.class.path}"/>
</classpath>
</java>
</sequential>
</macrodef>
<target name="antlr3" depends="build-init,antlr3-init,antlr3-up-to-date" unless="is.antlr3.uptodate">
<mkdir dir="${build.antlr3.dir}" />
<path id="sources.antlr3">
<fileset dir="${basedir}/tool/src" includes="**/*.g"/>
</path>
<pathconvert pathsep="${line.separator} " property="echo.sources.antlr3" refid="sources.antlr3">
<map from="${basedir}/tool/src/" to=""/>
</pathconvert>
<echo message="Generating ANTLR 3 grammars:${line.separator} ${echo.sources.antlr3}"/>
<antlr3 srcpath="org/antlr/v4/parse"/>
<antlr3 srcpath="org/antlr/v4/codegen">
<args>
<arg value="-lib"/>
<arg value="${build.antlr3.dir}/org/antlr/v4/parse"/>
</args>
</antlr3>
<touch file="${antlr3.touch}" mkdirs="true"/>
</target>
<target name="compile" depends="antlr" description="Compile for generic OS">
<target name="compile" depends="build-init,antlr3,up-to-date" description="Compile for generic OS" unless="is.jar.uptodate">
<mkdir dir="${build.dir}/classes"/>
<mkdir dir="${build.dir}/src"/>
<copy todir="${build.dir}/src" >
<fileset dir="${basedir}/tool/src/"/>
<fileset dir="${basedir}/runtime/Java/src/"/>
<!-- <fileset dir="${basedir}/gunit/src/"/> -->
</copy>
<replace dir="${build.dir}/src" token="@version@" value="${version}"/>
<javac
destdir="${build.dir}/classes"
source="1.5"
target="1.5"
debug="true"
excludes="org/antlr/v4/test/**">
<classpath refid="classpath"/>
<src path="${build.dir}/src:${build.dir}/gen"/>
<compilerarg value="-Xlint"/>
<compilerarg value="-Xlint:-serial"/>
<classpath>
<path refid="cp.antlr3"/>
<pathelement location="${basedir}/runtime/Java/lib/org.abego.treelayout.core.jar"/>
</classpath>
<src path="${basedir}/tool/src:${basedir}/runtime/Java/src:${build.antlr3.dir}"/>
</javac>
</target>
<target name="build-jar" depends="compile" description="Build ANTLR 4.jar">
<target name="build-jar" depends="up-to-date,compile" description="Build ANTLR 4.jar" unless="is.jar.uptodate">
<mkdir dir="${dist.dir}"/>
<jar jarfile="${jar.file}">
@ -163,14 +182,7 @@ build.sysclasspath=ignore
<include name="**/*.st"/>
<include name="**/*.stg"/>
</fileset>
<!--
<fileset dir="${basedir}/gunit/src/">
<include name="**/*.java"/>
<include name="**/*.g"/>
<include name="**/*.st"/>
<include name="**/*.stg"/>
</fileset>
-->
<fileset dir="${build.antlr3.dir}"/>
</copy>
<copy todir="${install.root.dir}">

View File

@ -52,3 +52,6 @@ YYYY/MM/DD, github id, Full name, email
2012/09/18, sharwell, Sam Harwell, sam@tunnelvisionlabs.com
2012/10/10, stephengaito, Stephen Gaito, stephen@percepitsys.co.uk
2012/11/23, maguro, Alan Cabrera, adc@toolazydogs.com
2013/01/29, metadave, Dave Parfitt, diparfitt@gmail.com
2013/03/06, bkiers, Bart Kiers, bkiers@gmail.com
2013/08/20, cayhorstmann, Cay Horstmann, cay@horstmann.com

126
pom.xml
View File

@ -10,12 +10,17 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.antlr</groupId>
<artifactId>antlr4-master</artifactId>
<version>4.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>ANTLR 4</name>
<description>ANTLR 4 Master Build POM</description>
<url>http://www.antlr.org</url>
<inceptionYear>1992</inceptionYear>
<organization>
<name>ANTLR</name>
<url>http://www.antlr.org</url>
</organization>
<licenses>
<license>
@ -43,6 +48,15 @@
</roles>
</developer>
<developer>
<name>Jim Idle</name>
<email>jimi@idle.ws</email>
<url>http://www.linkedin.com/in/jimidle</url>
<roles>
<role>Developer - Maven Plugin</role>
</roles>
</developer>
</developers>
<modules>
@ -60,25 +74,65 @@
<bootclasspath.java6>${java6.home}/lib/rt.jar</bootclasspath.java6>
<bootclasspath.compile>${bootclasspath.java6}</bootclasspath.compile>
<bootclasspath.testCompile>${bootclasspath.java6}</bootclasspath.testCompile>
<antlr.testinprocess>true</antlr.testinprocess>
</properties>
<mailingLists>
<mailingList>
<name>antlr-discussion</name>
<archive>https://groups.google.com/forum/?fromgroups#!forum/antlr-discussion</archive>
</mailingList>
</mailingLists>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/antlr/antlr4/issues</url>
</issueManagement>
<scm>
<url>git://github.com/antlr/antlr4.git</url>
<url>https://github.com/antlr/antlr4/tree/master</url>
<connection>scm:git:git://github.com/antlr/antlr4.git</connection>
<developerConnection>scm:git:git@github.com:antlr/antlr4.git</developerConnection>
</scm>
<profiles>
<profile>
<id>sonatype-oss-release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<compilerArguments>
<bootclasspath>${bootclasspath.compile}</bootclasspath>
</compilerArguments>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<configuration>
<compilerArguments>
<bootclasspath>${bootclasspath.testCompile}</bootclasspath>
</compilerArguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<version>3.1</version>
<configuration>
<sourceDirectory>src</sourceDirectory>
<showWarnings>true</showWarnings>
@ -93,7 +147,6 @@
<target>1.6</target>
<compilerArgument>-Xlint:-serial</compilerArgument>
<compilerArguments>
<bootclasspath>${bootclasspath.compile}</bootclasspath>
<Xlint/>
</compilerArguments>
</configuration>
@ -105,7 +158,6 @@
<target>1.6</target>
<compilerArgument>-Xlint:-serial</compilerArgument>
<compilerArguments>
<bootclasspath>${bootclasspath.testCompile}</bootclasspath>
<Xlint/>
</compilerArguments>
</configuration>
@ -113,37 +165,6 @@
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<quiet>true</quiet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@ -157,19 +178,34 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.15</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<!-- override the version inherited from the parent -->
<version>2.2.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<!-- override the version inherited from the parent -->
<version>2.9</version>
<configuration>
<quiet>true</quiet>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<!-- override the version inherited from the parent -->
<version>1.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

View File

@ -6,7 +6,7 @@
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "ANTLR v4 API"
PROJECT_NUMBER = 4.0
OUTPUT_DIRECTORY = api
OUTPUT_DIRECTORY = api/Java
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
@ -33,7 +33,7 @@ MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
TAB_SIZE = 4
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = YES
@ -113,7 +113,7 @@ VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
@ -126,6 +126,7 @@ HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
HTML_DYNAMIC_SECTIONS = YES
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
@ -134,8 +135,8 @@ BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
GENERATE_TREEVIEW = YES
TREEVIEW_WIDTH = 210
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
@ -214,9 +215,9 @@ MSCGEN_PATH = /Applications/Doxygen.app/Contents/Resources/
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = YES
COLLABORATION_GRAPH = NO
GROUP_GRAPHS = NO
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
@ -235,4 +236,4 @@ DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO
SEARCHENGINE = YES

View File

@ -6,6 +6,10 @@ The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<spellchecker-wordlist xmlns="http://www.netbeans.org/ns/spellchecker-wordlist/1">
<word>rule's</word>
<word>validator</word>
</spellchecker-wordlist>
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
@ -28,9 +32,8 @@ Any value defined here will override the pom.xml file value but is only applicab
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width>80</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-line-wrap>none</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-line-wrap>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.continuationIndentSize>4</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.continuationIndentSize>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.allowConvertToStarImport>false</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.allowConvertToStarImport>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.allowConvertToStaticStarImport>false</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.allowConvertToStaticStarImport>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder>*;java</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder>*;javax;java</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder>
<netbeans.compile.on.save>test</netbeans.compile.on.save>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceAfterTypeCast>false</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceAfterTypeCast>
</properties>
</project-shared-configuration>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.antlr</groupId>
<artifactId>antlr4-master</artifactId>
<version>4.0-SNAPSHOT</version>
<version>4.1.1-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
@ -24,6 +24,32 @@
</dependency>
</dependencies>
<profiles>
<profile>
<id>sonatype-oss-release</id>
<build>
<plugins>
<plugin>
<groupId>us.bryon</groupId>
<artifactId>graphviz-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>dot</goal>
</goals>
<configuration>
<destdir>${project.build.directory}/apidocs</destdir>
<output>svg</output>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<sourceDirectory>src</sourceDirectory>

View File

@ -0,0 +1,77 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph clusterA {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u1[label="u"];
A_v1[label="v"];
}
A_left[shape="record";label="{<a> | a} | {<b> | b}"];
{ edge[dir="back"];
A_u1:s -> A_left:a:n;
A_v1:s -> A_left:b:n;
}
}
subgraph AB {
temp0[color="invis";shape="point";label=""];
temp1[shape="none";label="+"];
temp0 -> temp1[style="invisible";dir="none"];
}
subgraph clusterB {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_w1[label="w"];
A_x1[label="x"];
}
A_right[shape="record";label="{<c> | c} | {<d> | d}"];
{ edge[dir="back"];
A_w1:s -> A_right:c:n;
A_x1:s -> A_right:d:n;
}
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterC {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u2[label="u"];
A_v2[label="v"];
A_w2[label="w"];
A_x2[label="x"];
}
A_result[shape="record";label="{<a> | a} | {<b> | b} | {<c> | c} | {<d> | d}"];
{ edge[dir="back"];
A_u2:s -> A_result:a:n;
A_v2:s -> A_result:b:n;
A_w2:s -> A_result:c:n;
A_x2:s -> A_result:d:n;
}
}
}

View File

@ -0,0 +1,72 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph clusterA {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u1[label="x"];
}
A_left[shape="record";label="{<a> | a}"];
{ edge[dir="back"];
A_u1:s -> A_left:a:n;
}
}
subgraph AB {
temp0[color="invis";shape="point";label=""];
temp1[shape="none";label="+"];
temp0 -> temp1[style="invisible";dir="none"];
}
subgraph clusterB {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_w1[label="y"];
}
A_right[shape="record";label="{<a> | a}"];
{ edge[dir="back"];
A_w1:s -> A_right:a:n;
}
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterC {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u2[label=""];
A_v2[label=""];
}
A_result[shape="record";label="{<x> | x} | {<y> | y}"];
A_ap[label="a'";shape="none"];
{ edge[dir="back"];
A_u2:s -> A_result:x:n;
A_v2:s -> A_result:y:n;
A_result:s -> A_ap:n;
}
}
}

View File

@ -0,0 +1,83 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph clusterA {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u1[label="u"];
A_v1[label="v"];
}
A_left[shape="record";label="{<a> | a} | {<b> | b}"];
{ edge[dir="back"];
A_u1:s -> A_left:a:n;
A_v1:s -> A_left:b:n;
}
}
subgraph AB {
temp0[color="invis";shape="point";label=""];
temp1[shape="none";label="+"];
temp0 -> temp1[style="invisible";dir="none"];
}
subgraph clusterB {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_w1[label="w"];
A_x1[label="x"];
}
A_right[shape="record";label="{<b> | b} | {<d> | d}"];
{ edge[dir="back"];
A_w1:s -> A_right:b:n;
A_x1:s -> A_right:d:n;
}
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterC {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u2[label="u"];
A_vw[shape="record";label="{<v> | v} | {<w> | w}"];
A_x2[label="x"];
}
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_vp[label=""];
A_wp[label=""];
}
A_result[shape="record";label="{<a> | a} | {<b> | b'} | {<d> | d}"];
{ edge[dir="back"];
A_u2:s -> A_result:a:n;
A_vw:s -> A_result:b:n;
A_x2:s -> A_result:d:n;
A_vp:s -> A_vw:v:n;
A_wp:s -> A_vw:w:n;
}
}
}

View File

@ -0,0 +1,75 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph clusterA {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u1[label="u"];
A_v1[label="v"];
}
A_left[shape="record";label="{<a> | a} | {<b> | b}"];
{ edge[dir="back"];
A_u1:s -> A_left:a:n;
A_v1:s -> A_left:b:n;
}
}
subgraph AB {
temp0[color="invis";shape="point";label=""];
temp1[shape="none";label="+"];
temp0 -> temp1[style="invisible";dir="none"];
}
subgraph clusterB {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_w1[label="v"];
A_x1[label="x"];
}
A_right[shape="record";label="{<b> | b} | {<d> | d}"];
{ edge[dir="back"];
A_w1:s -> A_right:b:n;
A_x1:s -> A_right:d:n;
}
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterC {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u2[label="u"];
A_v2[label="v"];
A_x2[label="x"];
}
A_result[shape="record";label="{<a> | a} | {<b> | b} | {<d> | d}"];
{ edge[dir="back"];
A_u2:s -> A_result:a:n;
A_v2:s -> A_result:b:n;
A_x2:s -> A_result:d:n;
}
}
}

View File

@ -0,0 +1,87 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph clusterA {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_u1[label="u"];
A_v1[label="v"];
}
A_left[shape="record";label="{<a> | a} | {<b> | b}"];
{ edge[dir="back"];
A_u1:s -> A_left:a:n;
A_v1:s -> A_left:b:n;
}
}
subgraph AB {
temp0[color="invis";shape="point";label=""];
temp1[shape="none";label="+"];
temp0 -> temp1[style="invisible";dir="none"];
}
subgraph clusterB {
label="";
color="invis";
{ rank="same";
node[shape="invtriangle";margin="0.01,0.01"];
A_w1[label="v"];
A_x1[label="u"];
}
A_right[shape="record";label="{<b> | b} | {<d> | d}"];
{ edge[dir="back"];
A_w1:s -> A_right:b:n;
A_x1:s -> A_right:d:n;
}
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterC {
label="";
color="invis";
subgraph clusterSubC {
node[group="";shape="invtriangle";margin="0.01,0.01"];
A_u2[label="u"];
{ rank="same";
A_ut1[color="invis";shape="point";width="0.00000001"];
A_v2[label="v"];
A_ut2[color="invis";shape="point";width="0.00000001"];
}
A_ut1 -> A_v2 -> A_ut2[style="invisible",dir="none"];
A_u2 -> A_v2[style="invisible",dir="none"];
}
A_result[shape="record";label="{<a> | a} | {<b> | b} | {<d> | d}"];
{ edge[dir="back"];
A_u2:sw -> A_ut1:n;
A_v2:s -> A_result:b:n;
A_u2:se -> A_ut2:n;
A_v2:s -> A_result:n[style="invisible",dir="none"];
}
{ edge[dir="none";constraint="false"];
A_ut1:s -> A_result:a:n;
A_ut2:s -> A_result:d:n;
}
}
}

View File

@ -0,0 +1,35 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph clusterA {
color="invis";
{ node[shape="record"];
BasicBlockStartState[URL="../BasicBlockStartState.html";label="{BasicBlockStartState | {<alt1> alt 1 |<alt2> alt 2 | &#0183;\n&#0183;\n&#0183; |<altn> alt n}}"];
BlockEndState[URL="../BlockEndState.html"];
}
{ node[style="dashed"];
content1[label="alt 1"];
content2[label="alt 2"];
more[label="alt n"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> BasicBlockStartState[style="dashed"];
BlockEndState -> end[label="&#0949;"];
BasicBlockStartState:alt1 -> content1[label="&#0949;"];
content1 -> BlockEndState[style="dashed"];
BasicBlockStartState:alt2 -> content2[label="&#0949;"];
content2 -> BlockEndState[style="dashed"];
BasicBlockStartState:altn -> more[label="&#0949;"];
more -> BlockEndState[style="dashed"];
}

View File

@ -0,0 +1,46 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph cluster0 {
color="invis";
{ node[shape="record"];
StarLoopEntryState[URL="../StarLoopEntryState.html";label="{StarLoopEntryState | {<alt1> alt 1 |<alt2> alt 2}}"];
StarLoopbackState[URL="../StarLoopbackState.html"];
StarBlockStartState[URL="../StarBlockStartState.html";label="{StarBlockStartState | {<alt1> alt 1 |<alt2> alt 2 | &#0183;\n&#0183;\n&#0183; |<altn> alt n}}"];
BlockEndState[URL="../BlockEndState.html"];
{ rank="sink";
LoopEndState[URL="../LoopEndState.html";rank="max"];
}
}
{ node[style="dashed"];
content1[label="alt 1"];
content2[label="alt 2"];
more[label="alt n"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> StarLoopEntryState[style="dashed"];
LoopEndState -> end[label="&#0949;"];
StarBlockStartState:alt1 -> content1[label="&#0949;"];
content1 -> BlockEndState[style="dashed"];
StarBlockStartState:alt2 -> content2[label="&#0949;"];
content2 -> BlockEndState[style="dashed"];
StarBlockStartState:altn -> more[label="&#0949;"];
more -> BlockEndState[style="dashed"];
BlockEndState:e -> StarLoopbackState:w[label="&#0949;"];
StarLoopEntryState:alt1 -> StarBlockStartState[label="&#0949;"];
StarLoopEntryState:alt2 -> LoopEndState[label="&#0949;"];
StarLoopbackState:n -> StarLoopEntryState:n[label="&#0949;"];
}

View File

@ -0,0 +1,46 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph cluster0 {
color="invis";
{ node[shape="record"];
StarLoopEntryState[URL="../StarLoopEntryState.html";label="{StarLoopEntryState | {<alt1> alt 1 |<alt2> alt 2}}"];
StarLoopbackState[URL="../StarLoopbackState.html"];
StarBlockStartState[URL="../StarBlockStartState.html";label="{StarBlockStartState | {<alt1> alt 1 |<alt2> alt 2 | &#0183;\n&#0183;\n&#0183; |<altn> alt n}}"];
BlockEndState[URL="../BlockEndState.html"];
{ rank="sink";
LoopEndState[URL="../LoopEndState.html";rank="max"];
}
}
{ node[style="dashed"];
content1[label="alt 1"];
content2[label="alt 2"];
more[label="alt n"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> StarLoopEntryState[style="dashed"];
LoopEndState -> end[label="&#0949;"];
StarBlockStartState:alt1 -> content1[label="&#0949;"];
content1 -> BlockEndState[style="dashed"];
StarBlockStartState:alt2 -> content2[label="&#0949;"];
content2 -> BlockEndState[style="dashed"];
StarBlockStartState:altn -> more[label="&#0949;"];
more -> BlockEndState[style="dashed"];
BlockEndState:e -> StarLoopbackState:w[label="&#0949;"];
StarLoopEntryState:alt2 -> StarBlockStartState[label="&#0949;"];
StarLoopEntryState:alt1 -> LoopEndState[label="&#0949;"];
StarLoopbackState:s -> StarLoopEntryState:s[label="&#0949;"];
}

View File

@ -0,0 +1,46 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
lefttree[label=""];
left[label="$"];
lefttree -> left[style="invisible";dir="none"];
}
subgraph AB {
optree[shape="none";label=""];
temp1[shape="none";label="+"];
optree -> temp1[style="invisible";dir="none"];
}
subgraph R {
righttree[shape="none";label=""];
right[shape="invtriangle";label="x"];
righttree -> right[style="invisible";dir="none"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph Res {
node[shape="none"];
resulttree[shape="invtriangle";label=""];
result[shape="record";label="{ | $} | {<x> | x}"];
resulttree -> result:x:n[dir="back"];
}
}

View File

@ -0,0 +1,27 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
left[shape="none";label="$"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="none";label="$"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph Res {
result[shape="none";label="$"];
}
}

View File

@ -0,0 +1,52 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
lefttree[label=""];
left[label="$"];
leftroot[label="a"];
lefttree -> left[style="invisible";dir="none"];
left -> leftroot[dir="back"];
}
subgraph AB {
optree[shape="none";label=""];
temp1[shape="none";label="+"];
optree -> temp1[style="invisible";dir="none"];
}
subgraph R {
righttree[shape="none";label=""];
right[shape="invtriangle";label="x"];
rightroot[shape="none";label="a"];
righttree -> right[style="invisible";dir="none"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph Res {
node[shape="none"];
resulttree[shape="invtriangle";label=""];
result[shape="record";label="{ | $} | {<x> | x}"];
resultroot[label="a'"];
resulttree -> result:x:n[dir="back"];
result -> resultroot[dir="back"];
}
}

View File

@ -0,0 +1,39 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
left[label="$"];
leftroot[label="a"];
left -> leftroot[dir="back"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="invtriangle";label="x"];
rightroot[shape="none";label="b"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph Res {
node[shape="none"];
result[label="$"];
resultroot[shape="record";label="{<a> | a} | {<b> | b}"];
result -> resultroot:a:n[dir="back"];
result -> resultroot:b:n[dir="back"];
}
}

View File

@ -0,0 +1,38 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
left[label="$"];
leftroot[label="a"];
left -> leftroot[dir="back"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="invtriangle";label="x"];
rightroot[shape="none";label="a"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph Res {
node[shape="none"];
result[label="$"];
resultroot[label="a"];
result -> resultroot[dir="back"];
}
}

View File

@ -0,0 +1,27 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
left[shape="none";label="$"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="invtriangle";label="x"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph Res {
result[shape="none";label="$"];
}
}

View File

@ -0,0 +1,29 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph cluster0 {
color="invis";
{ node[shape="record"];
BasicBlockStartState[URL="../BasicBlockStartState.html";label="{BasicBlockStartState | {<alt1> alt 1 |<alt2> alt 2}}"];
BlockEndState[URL="../BlockEndState.html"];
}
{ node[style="dashed"];
content[label="alt"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> BasicBlockStartState[style="dashed"];
BlockEndState -> end[label="&#0949;"];
BasicBlockStartState:alt1 -> content[label="&#0949;"];
content -> BlockEndState[style="dashed"];
BasicBlockStartState:alt2 -> BlockEndState[label="&#0949;"];
}

View File

@ -0,0 +1,29 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph cluster0 {
color="invis";
{ node[shape="record"];
BasicBlockStartState[URL="../BasicBlockStartState.html";label="{BasicBlockStartState | {<alt1> alt 1 |<alt2> alt 2}}"];
BlockEndState[URL="../BlockEndState.html"];
}
{ node[style="dashed"];
content[label="alt"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> BasicBlockStartState[style="dashed"];
BlockEndState -> end[label="&#0949;"];
BasicBlockStartState:alt2 -> content[label="&#0949;"];
content -> BlockEndState[style="dashed"];
BasicBlockStartState:alt1 -> BlockEndState[label="&#0949;"];
}

View File

@ -0,0 +1,47 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph cluster0 {
color="invis";
{ node[shape="record"];
{ rank="source";
PlusBlockStartState[URL="../PlusBlockStartState.html";label="{PlusBlockStartState | {<alt1> alt 1 |<alt2> alt 2 | &#0183;\n&#0183;\n&#0183; |<altn> alt n}}"];
}
PlusLoopbackState[URL="../PlusLoopbackState.html";label="{PlusLoopbackState | {<alt1> alt 1 |<alt2> alt 2}}"];
BlockEndState[URL="../BlockEndState.html"];
{ rank="sink";
LoopEndState[URL="../LoopEndState.html"];
}
}
{ node[style="dashed"];
content1[label="alt 1"];
content2[label="alt 2"];
more[label="alt n"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> PlusBlockStartState[style="dashed"];
LoopEndState -> end[label="&#0949;"];
PlusBlockStartState:alt1 -> content1[label="&#0949;"];
content1 -> BlockEndState[style="dashed"];
PlusBlockStartState:alt2 -> content2[label="&#0949;"];
content2 -> BlockEndState[style="dashed"];
PlusBlockStartState:altn -> more[label="&#0949;"];
more -> BlockEndState[style="dashed"];
BlockEndState -> PlusLoopbackState[label="&#0949;"];
PlusLoopbackState:alt1:n -> PlusBlockStartState:n[label="&#0949;"];
PlusLoopbackState:alt2 -> LoopEndState[label="&#0949;"];
}

View File

@ -0,0 +1,47 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
subgraph cluster0 {
color="invis";
{ node[shape="record"];
{ rank="source";
PlusBlockStartState[URL="../PlusBlockStartState.html";label="{PlusBlockStartState | {<alt1> alt 1 |<alt2> alt 2 | &#0183;\n&#0183;\n&#0183; |<altn> alt n}}"];
}
PlusLoopbackState[URL="../PlusLoopbackState.html";label="{PlusLoopbackState | {<alt1> alt 1 |<alt2> alt 2}}"];
BlockEndState[URL="../BlockEndState.html"];
{ rank="sink";
LoopEndState[URL="../LoopEndState.html"];
}
}
{ node[style="dashed"];
content1[label="alt 1"];
content2[label="alt 2"];
more[label="alt n"];
}
}
{ node[style="dashed"];
begin;
end;
}
begin -> PlusBlockStartState[style="dashed"];
LoopEndState -> end[label="&#0949;"];
PlusBlockStartState:alt1 -> content1[label="&#0949;"];
content1 -> BlockEndState[style="dashed"];
PlusBlockStartState:alt2 -> content2[label="&#0949;"];
content2 -> BlockEndState[style="dashed"];
PlusBlockStartState:altn -> more[label="&#0949;"];
more -> BlockEndState[style="dashed"];
BlockEndState -> PlusLoopbackState[label="&#0949;"];
PlusLoopbackState:alt2:s -> PlusBlockStartState:s[label="&#0949;"];
PlusLoopbackState:alt1 -> LoopEndState[label="&#0949;"];
}

View File

@ -0,0 +1,15 @@
digraph "" {
graph[fontname="Courier New";rankdir="LR";pad="0.25"];
node[fontname="Courier New";target="_parent"];
edge[fontname="Courier New"];
{ node[shape="box"];
RuleStartState[URL="../RuleStartState.html"];
RuleStopState[URL="../RuleStopState.html"];
}
{ node[style="dashed"];
content;
}
RuleStartState -> content[label="&#0949;"];
content -> RuleStopState[style="dashed"];
}

View File

@ -0,0 +1,40 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
left[shape="invtriangle";label="x"];
leftroot[label="a"];
left -> leftroot[dir="back"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="invtriangle";label="y"];
rightroot[shape="none";label="b"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterRes {
color="invis";
resulttree1[shape="invtriangle";label="x"];
resulttree2[shape="invtriangle";label="y"];
result[shape="record";label="{<a> | a} | {<b> | b}"];
resulttree1:s -> result:a:n[dir="back"];
resulttree2:s -> result:b:n[dir="back"];
}
}

View File

@ -0,0 +1,39 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
left[shape="invtriangle";label="x"];
leftroot[label="a"];
left -> leftroot[dir="back"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="invtriangle";label="x"];
rightroot[shape="none";label="b"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterRes {
color="invis";
resulttree[shape="invtriangle";label="x"];
result[shape="record";label="{<a> | a} | {<b> | b}"];
resulttree -> result:a:n[dir="back"];
resulttree -> result:b:n[dir="back"];
}
}

View File

@ -0,0 +1,54 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
lefttree[label=""];
left[shape="invtriangle";label="x"];
leftroot[label="a"];
lefttree -> left[style="invisible";dir="none"];
left -> leftroot[dir="back"];
}
subgraph AB {
optree[shape="none";label=""];
temp1[shape="none";label="+"];
optree -> temp1[style="invisible";dir="none"];
}
subgraph R {
righttree[shape="none";label=""];
right[shape="invtriangle";label="y"];
rightroot[shape="none";label="a"];
righttree -> right[style="invisible";dir="none"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2a;
{ rank="same";
temp2b;
temp2c;
}
temp2a -> temp2b[style="invisible";dir="none"];
temp2a -> temp2c[style="invisible";dir="none"];
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterRes {
color="invis";
resulttree1[shape="invtriangle";label=""];
resulttree2[shape="invtriangle";label=""];
result[shape="record";label="{<x> | x} | {<y> | y}"];
resultroot[shape="none";label="a'"];
resulttree1:s -> result:x:n[dir="back"];
resulttree2:s -> result:y:n[dir="back"];
result -> resultroot[dir="back"];
}
}

View File

@ -0,0 +1,38 @@
digraph "" {
graph[dpi="60";compound="true"];
subgraph L {
node[shape="none"];
left[shape="invtriangle";label="x"];
leftroot[label="a"];
left -> leftroot[dir="back"];
}
subgraph AB {
temp1[shape="none";label="+"];
}
subgraph R {
right[shape="invtriangle";label="x"];
rightroot[shape="none";label="a"];
right -> rightroot[dir="back"];
}
subgraph BC {
node[color="invis";shape="point"];
temp2b;
temp2c;
temp2b -> temp2c[constraint="false";label="wwwwwww"];
}
subgraph clusterRes {
color="invis";
result[shape="invtriangle";label="x"];
resultroot[shape="none";label="a"];
result -> resultroot[dir="back"];
}
}

View File

@ -31,25 +31,27 @@
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.atn.ParserATNSimulator;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.BitSet;
/** How to emit recognition errors */
/** How to emit recognition errors. */
public interface ANTLRErrorListener {
/** Upon syntax error, notify any interested parties. This is not
* how to recover from errors or compute error messages. The
* parser ANTLRErrorStrategy specifies how to recover from syntax
* errors and how to compute error messages. This listener's job
* is simply to emit a computed message, though it has enough
* information to create its own message in many cases.
*
* The RecognitionException is non-null for all syntax errors
* except when we discover mismatched token errors that we can
* recover from in-line, without returning from the surrounding
* rule (via the single token insertion and deletion mechanism).
/**
* Upon syntax error, notify any interested parties. This is not how to
* recover from errors or compute error messages. {@link ANTLRErrorStrategy}
* specifies how to recover from syntax errors and how to compute error
* messages. This listener's job is simply to emit a computed message,
* though it has enough information to create its own message in many cases.
* <p/>
* The {@link RecognitionException} is non-null for all syntax errors except
* when we discover mismatched token errors that we can recover from
* in-line, without returning from the surrounding rule (via the single
* token insertion and deletion mechanism).
*
* @param recognizer
* What parser got the error. From this
@ -57,16 +59,15 @@ public interface ANTLRErrorListener {
* as the input stream.
* @param offendingSymbol
* The offending token in the input token
* stream, unless recognizer is a lexer (then it's null) If
* no viable alternative error, e has token at which we
* stream, unless recognizer is a lexer (then it's null). If
* no viable alternative error, {@code e} has token at which we
* started production for the decision.
* @param line
* At what line in input to the error occur? This always refers to
* stopTokenIndex
* The line number in the input where the error occurred.
* @param charPositionInLine
* At what character position within that line did the error occur.
* The character position within that line where the error occurred.
* @param msg
* The message to emit
* The message to emit.
* @param e
* The exception generated by the parser that led to
* the reporting of an error. It is null in the case where
@ -80,33 +81,102 @@ public interface ANTLRErrorListener {
String msg,
@Nullable RecognitionException e);
/** Called when the parser detects a true ambiguity: an input
* sequence can be matched literally by two or more pass through
* the grammar. ANTLR resolves the ambiguity in favor of the
* alternative appearing first in the grammar. The start and stop
* index are zero-based absolute indices into the token
* stream. ambigAlts is a set of alternative numbers that can
* match the input sequence. This method is only called when we
* are parsing with full context.
*/
void reportAmbiguity(@NotNull Parser recognizer,
DFA dfa, int startIndex, int stopIndex,
/**
* This method is called by the parser when a full-context prediction
* results in an ambiguity.
* <p/>
* When {@code exact} is {@code true}, <em>all</em> of the alternatives in
* {@code ambigAlts} are viable, i.e. this is reporting an exact ambiguity.
* When {@code exact} is {@code false}, <em>at least two</em> of the
* alternatives in {@code ambigAlts} are viable for the current input, but
* the prediction algorithm terminated as soon as it determined that at
* least the <em>minimum</em> alternative in {@code ambigAlts} is viable.
* <p/>
* When the {@link PredictionMode#LL_EXACT_AMBIG_DETECTION} prediction mode
* is used, the parser is required to identify exact ambiguities so
* {@code exact} will always be {@code true}.
* <p/>
* This method is not used by lexers.
*
* @param recognizer the parser instance
* @param dfa the DFA for the current decision
* @param startIndex the input index where the decision started
* @param stopIndex the input input where the ambiguity is reported
* @param exact {@code true} if the ambiguity is exactly known, otherwise
* {@code false}. This is always {@code true} when
* {@link PredictionMode#LL_EXACT_AMBIG_DETECTION} is used.
* @param ambigAlts the potentially ambiguous alternatives
* @param configs the ATN configuration set where the ambiguity was
* determined
*/
void reportAmbiguity(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex,
int stopIndex,
boolean exact,
@NotNull BitSet ambigAlts,
@NotNull ATNConfigSet configs);
/**
* This method is called when an SLL conflict occurs and the parser is about
* to use the full context information to make an LL decision.
* <p/>
* If one or more configurations in {@code configs} contains a semantic
* predicate, the predicates are evaluated before this method is called. The
* subset of alternatives which are still viable after predicates are
* evaluated is reported in {@code conflictingAlts}.
* <p/>
* This method is not used by lexers.
*
* @param recognizer the parser instance
* @param dfa the DFA for the current decision
* @param startIndex the input index where the decision started
* @param stopIndex the input index where the SLL conflict occurred
* @param conflictingAlts The specific conflicting alternatives. If this is
* {@code null}, the conflicting alternatives are all alternatives
* represented in {@code configs}.
* @param configs the ATN configuration set where the SLL conflict was
* detected
*/
void reportAttemptingFullContext(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
int startIndex,
int stopIndex,
@Nullable BitSet conflictingAlts,
@NotNull ATNConfigSet configs);
/** Called by the parser when it find a conflict that is resolved
* by retrying the parse with full context. This is not a
* warning; it simply notifies you that your grammar is more
* complicated than Strong LL can handle. The parser moved up to
* full context parsing for that input sequence.
*/
void reportContextSensitivity(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
@NotNull ATNConfigSet configs);
/**
* This method is called by the parser when a full-context prediction has a
* unique result.
* <p/>
* For prediction implementations that only evaluate full-context
* predictions when an SLL conflict is found (including the default
* {@link ParserATNSimulator} implementation), this method reports cases
* where SLL conflicts were resolved to unique full-context predictions,
* i.e. the decision was context-sensitive. This report does not necessarily
* indicate a problem, and it may appear even in completely unambiguous
* grammars.
* <p/>
* {@code configs} may have more than one represented alternative if the
* full-context prediction algorithm does not evaluate predicates before
* beginning the full-context prediction. In all cases, the final prediction
* is passed as the {@code prediction} argument.
* <p/>
* This method is not used by lexers.
*
* @param recognizer the parser instance
* @param dfa the DFA for the current decision
* @param startIndex the input index where the decision started
* @param stopIndex the input index where the context sensitivity was
* finally determined
* @param prediction the unambiguous result of the full-context prediction
* @param configs the ATN configuration set where the unambiguous prediction
* was determined
*/
void reportContextSensitivity(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex,
int stopIndex,
int prediction,
@NotNull ATNConfigSet configs);
}

View File

@ -31,112 +31,116 @@
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
/** The interface for defining strategies to deal with syntax errors
* encountered during a parse by ANTLR-generated parsers and tree parsers.
* We distinguish between three different kinds of errors:
/**
* The interface for defining strategies to deal with syntax errors encountered
* during a parse by ANTLR-generated parsers. We distinguish between three
* different kinds of errors:
*
* o The parser could not figure out which path to take in the ATN
* (none of the available alternatives could possibly match)
* o The current input does not match what we were looking for.
* o A predicate evaluated to false.
* <ul>
* <li>The parser could not figure out which path to take in the ATN (none of
* the available alternatives could possibly match)</li>
* <li>The current input does not match what we were looking for</li>
* <li>A predicate evaluated to false</li>
* </ul>
*
* The default implementation of this interface reports errors to any
* error listeners of the parser. It also handles single token insertion
* and deletion for mismatched elements.
*
* We pass in the parser to each function so that the same strategy
* can be shared between multiple parsers running at the same time.
* This is just for flexibility, not that we need it for the default system.
*
* TODO: To bail out upon first error, simply rethrow e?
*
* TODO: what to do about lexers
* Implementations of this interface report syntax errors by calling
* {@link Parser#notifyErrorListeners}.
* <p/>
* TODO: what to do about lexers
*/
public interface ANTLRErrorStrategy {
/** To create missing tokens, we need a factory */
public void setTokenFactory(TokenFactory<?> factory);
/** When matching elements within alternative, use this method
* to recover. The default implementation uses single token
* insertion and deletion. If you want to change the way ANTLR
* response to mismatched element errors within an alternative,
* implement this method.
*
* From the recognizer, we can get the input stream to get
* the current input symbol and we can get the current context.
* That context gives us the current state within the ATN.
* From that state, we can look at its transition to figure out
* what was expected.
*
* Because we can recover from a single token deletions by
* "inserting" tokens, we need to specify what that implicitly created
* token is. We use object, because it could be a tree node.
/**
* Reset the error handler state for the specified {@code recognizer}.
* @param recognizer the parser instance
*/
void reset(@NotNull Parser recognizer);
/**
* This method is called when an unexpected symbol is encountered during an
* inline match operation, such as {@link Parser#match}. If the error
* strategy successfully recovers from the match failure, this method
* returns the {@link Token} instance which should be treated as the
* successful result of the match.
* <p/>
* Note that the calling code will not report an error if this method
* returns successfully. The error strategy implementation is responsible
* for calling {@link Parser#notifyErrorListeners} as appropriate.
*
* @param recognizer the parser instance
* @throws RecognitionException if the error strategy was not able to
* recover from the unexpected input symbol
*/
@NotNull
Token recoverInline(@NotNull Parser recognizer)
throws RecognitionException;
/** Resynchronize the parser by consuming tokens until we find one
* in the resynchronization set--loosely the set of tokens that can follow
* the current rule. The exception contains info you might want to
* use to recover better.
/**
* This method is called to recover from exception {@code e}. This method is
* called after {@link #reportError} by the default exception handler
* generated for a rule method.
*
* @see #reportError
*
* @param recognizer the parser instance
* @param e the recognition exception to recover from
* @throws RecognitionException if the error strategy could not recover from
* the recognition exception
*/
void recover(@NotNull Parser recognizer,
@Nullable RecognitionException e);
@NotNull RecognitionException e)
throws RecognitionException;
/** Make sure that the current lookahead symbol is consistent with
* what were expecting at this point in the ATN. You can call this
* anytime but ANTLR only generates code to check before subrules/loops
* and each iteration.
/**
* This method provides the error handler with an opportunity to handle
* syntactic or semantic errors in the input stream before they result in a
* {@link RecognitionException}.
* <p/>
* The generated code currently contains calls to {@link #sync} after
* entering the decision state of a closure block ({@code (...)*} or
* {@code (...)+}).
* <p/>
* For an implementation based on Jim Idle's "magic sync" mechanism, see
* {@link DefaultErrorStrategy#sync}.
*
* Implements Jim Idle's magic sync mechanism in closures and optional
* subrules. E.g.,
* @see DefaultErrorStrategy#sync
*
* a : sync ( stuff sync )* ;
* sync : {consume to what can follow sync} ;
*
* Previous versions of ANTLR did a poor job of their recovery within
* loops. A single mismatch token or missing token would force the parser
* to bail out of the entire rules surrounding the loop. So, for rule
*
* classDef : 'class' ID '{' member* '}'
*
* input with an extra token between members would force the parser to
* consume until it found the next class definition rather than the
* next member definition of the current class.
*
* This functionality cost a little bit of effort because the parser
* has to compare token set at the start of the loop and at each
* iteration. If for some reason speed is suffering for you, you can
* turn off this functionality by simply overriding this method as
* a blank { }.
* @param recognizer the parser instance
* @throws RecognitionException if an error is detected by the error
* strategy but cannot be automatically recovered at the current state in
* the parsing process
*/
void sync(@NotNull Parser recognizer);
void sync(@NotNull Parser recognizer)
throws RecognitionException;
/** Notify handler that parser has entered an error state. The
* parser currently doesn't call this--the handler itself calls this
* in report error methods. But, for symmetry with endErrorCondition,
* this method is in the interface.
*/
void beginErrorCondition(@NotNull Parser recognizer);
/** Is the parser in the process of recovering from an error? Upon
* a syntax error, the parser enters recovery mode and stays there until
* the next successful match of a token. In this way, we can
* avoid sending out spurious error messages. We only want one error
* message per syntax error
/**
* Tests whether or not {@code recognizer} is in the process of recovering
* from an error. In error recovery mode, {@link Parser#consume} adds
* symbols to the parse tree by calling
* {@link ParserRuleContext#addErrorNode(Token)} instead of
* {@link ParserRuleContext#addChild(Token)}.
*
* @param recognizer the parser instance
* @return {@code true} if the parser is currently recovering from a parse
* error, otherwise {@code false}
*/
boolean inErrorRecoveryMode(@NotNull Parser recognizer);
/** Reset the error handler. Call this when the parser
* matches a valid token (indicating no longer in recovery mode)
* and from its own reset method.
/**
* This method is called by when the parser successfully matches an input
* symbol.
*
* @param recognizer the parser instance
*/
void endErrorCondition(@NotNull Parser recognizer);
void reportMatch(@NotNull Parser recognizer);
/** Report any kind of RecognitionException. */
/**
* Report any kind of {@link RecognitionException}. This method is called by
* the default exception handler generated for a rule method.
*
* @param recognizer the parser instance
* @param e the recognition exception to report
*/
void reportError(@NotNull Parser recognizer,
@Nullable RecognitionException e)
throws RecognitionException;
@NotNull RecognitionException e);
}

View File

@ -33,11 +33,11 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
/** This is an ANTLRInputStream that is loaded from a file
* all at once when you construct the object. This is a special case
* since we know the exact size of the object to load. We can avoid lots
* of data copying.
/**
* This is an {@link ANTLRInputStream} that is loaded from a file all at once
* when you construct the object.
*/
public class ANTLRFileStream extends ANTLRInputStream {
protected String fileName;
@ -69,7 +69,10 @@ public class ANTLRFileStream extends ANTLRInputStream {
}
try {
data = new char[size];
super.n = isr.read(data);
n = isr.read(data);
if (n < data.length) {
data = Arrays.copyOf(data, n);
}
}
finally {
isr.close();

View File

@ -35,11 +35,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
/** Vacuum all input from a Reader/InputStream and then treat it like a char[] buffer.
* Can also pass in a string or char[] to use.
*
* If you need encoding, pass in stream/reader with correct encoding.
/**
* Vacuum all input from a {@link Reader}/{@link InputStream} and then treat it
* like a {@code char[]} buffer. Can also pass in a {@link String} or
* {@code char[]} to use.
* <p/>
* If you need encoding, pass in stream/reader with correct encoding.
*/
public class ANTLRInputStream implements CharStream {
public static final int READ_BUFFER_SIZE = 1024;
@ -117,9 +120,7 @@ public class ANTLRInputStream implements CharStream {
do {
if ( p+readChunkSize > data.length ) { // overflow?
// System.out.println("### overflow p="+p+", data.length="+data.length);
char[] newdata = new char[data.length*2]; // resize
System.arraycopy(data, 0, newdata, 0, data.length);
data = newdata;
data = Arrays.copyOf(data, data.length * 2);
}
numRead = r.read(data, p, readChunkSize);
// System.out.println("read "+numRead+" chars; p was "+p+" is now "+(p+numRead));
@ -146,7 +147,7 @@ public class ANTLRInputStream implements CharStream {
@Override
public void consume() {
if (p >= n) {
assert LA(1) == CharStream.EOF;
assert LA(1) == IntStream.EOF;
throw new IllegalStateException("cannot consume EOF");
}

View File

@ -53,6 +53,7 @@ public class BaseErrorListener implements ANTLRErrorListener {
DFA dfa,
int startIndex,
int stopIndex,
boolean exact,
BitSet ambigAlts,
ATNConfigSet configs)
{
@ -63,6 +64,7 @@ public class BaseErrorListener implements ANTLRErrorListener {
DFA dfa,
int startIndex,
int stopIndex,
BitSet conflictingAlts,
ATNConfigSet configs)
{
}
@ -72,6 +74,7 @@ public class BaseErrorListener implements ANTLRErrorListener {
DFA dfa,
int startIndex,
int stopIndex,
int prediction,
ATNConfigSet configs)
{
}

View File

@ -38,36 +38,38 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Buffer all input tokens but do on-demand fetching of new tokens from
* lexer. Useful when the parser or lexer has to set context/mode info before
* proper lexing of future tokens. The ST template parser needs this,
* for example, because it has to constantly flip back and forth between
* inside/output templates. E.g., <names:{hi, <it>}> has to parse names
* as part of an expression but "hi, <it>" as a nested template.
*
* You can't use this stream if you pass whitespace or other off-channel
* tokens to the parser. The stream can't ignore off-channel tokens.
* (UnbufferedTokenStream is the same way.) Use CommonTokenStream.
*
* This is not a subclass of UnbufferedTokenStream because I don't want
* to confuse small moving window of tokens it uses for the full buffer.
/**
* Buffer all input tokens but do on-demand fetching of new tokens from lexer.
* Useful when the parser or lexer has to set context/mode info before proper
* lexing of future tokens. The ST template parser needs this, for example,
* because it has to constantly flip back and forth between inside/output
* templates. E.g., {@code <names:{hi, <it>}>} has to parse names as part of an
* expression but {@code "hi, <it>"} as a nested template.
* <p/>
* You can't use this stream if you pass whitespace or other off-channel tokens
* to the parser. The stream can't ignore off-channel tokens.
* ({@link UnbufferedTokenStream} is the same way.) Use
* {@link CommonTokenStream}.
*/
public class BufferedTokenStream implements TokenStream {
@NotNull
protected TokenSource tokenSource;
/** Record every single token pulled from the source so we can reproduce
* chunks of it later. The buffer in LookaheadStream overlaps sometimes
* as its moving window moves through the input. This list captures
* everything so we can access complete input text.
*/
/**
* Record every single token pulled from the source so we can reproduce
* chunks of it later. This list captures everything so we can access
* complete input text.
*/
protected List<Token> tokens = new ArrayList<Token>(100);
/** The index into the tokens list of the current token (next token
* to consume). tokens[p] should be LT(1). p=-1 indicates need
* to initialize with first token. The ctor doesn't get a token.
* First call to LT(1) or whatever gets the first token and sets p=0;
*/
/**
* The index into {@link #tokens} of the current token (next token to
* consume). {@link #tokens}{@code [}{@link #p}{@code ]} should be
* {@link #LT LT(1)}. {@link #p}{@code =-1} indicates need to initialize
* with first token. The constructor doesn't get a token. First call to
* {@link #LT LT(1)} or whatever gets the first token and sets
* {@link #p}{@code =0;}.
*/
protected int p = -1;
/**
@ -92,8 +94,6 @@ public class BufferedTokenStream implements TokenStream {
@Override
public int index() { return p; }
// public int range() { return range; }
@Override
public int mark() {
return 0;
@ -117,14 +117,12 @@ public class BufferedTokenStream implements TokenStream {
@Override
public int size() { return tokens.size(); }
/** Move the input pointer to the next incoming token. The stream
* must become active with LT(1) available. consume() simply
* moves the input pointer so that LT(1) points at the next
* input symbol. Consume at least one token, unless EOF has been reached.
*/
@Override
public void consume() {
lazyInit();
if (LA(1) == EOF) {
throw new IllegalStateException("cannot consume EOF");
}
if (sync(p + 1)) {
p = adjustSeekIndex(p + 1);
}
@ -224,7 +222,7 @@ public class BufferedTokenStream implements TokenStream {
* operation. The default implementation simply returns {@code i}. If an
* exception is thrown in this method, the current stream index should not be
* changed.
* <p>
* <p/>
* For example, {@link CommonTokenStream} overrides this method to ensure that
* the seek target is always an on-channel token.
*

View File

@ -35,23 +35,6 @@ import org.antlr.v4.runtime.misc.NotNull;
/** A source of characters for an ANTLR lexer. */
public interface CharStream extends IntStream {
/**
* The minimum allowed value for a character in a {@code CharStream}.
*/
public static final int MIN_CHAR = Character.MIN_VALUE;
/**
* The maximum allowed value for a character in a {@code CharStream}.
* <p/>
* This value is {@code Character.MAX_VALUE - 1}, which reserves the value
* {@code Character.MAX_VALUE} for special use within an implementing class.
* For some implementations, the data buffers required for supporting the
* marked ranges of {@link IntStream} are stored as {@code char[]} instead
* of {@code int[]}, with {@code Character.MAX_VALUE} being used instead of
* {@code -1} to mark the end of the stream internally.
*/
public static final int MAX_CHAR = Character.MAX_VALUE-1;
/**
* This method returns the text for a range of characters within this input
* stream. This method is guaranteed to not throw an exception if the

View File

@ -36,6 +36,9 @@ import org.antlr.v4.runtime.misc.Pair;
import java.io.Serializable;
public class CommonToken implements WritableToken, Serializable {
protected static final Pair<TokenSource, CharStream> EMPTY_SOURCE =
new Pair<TokenSource, CharStream>(null, null);
protected int type;
protected int line;
protected int charPositionInLine = -1; // set to invalid position
@ -78,6 +81,7 @@ public class CommonToken implements WritableToken, Serializable {
this.type = type;
this.channel = DEFAULT_CHANNEL;
this.text = text;
this.source = EMPTY_SOURCE;
}
public CommonToken(Token oldToken) {
@ -211,9 +215,9 @@ public class CommonToken implements WritableToken, Serializable {
}
String txt = getText();
if ( txt!=null ) {
txt = txt.replaceAll("\n","\\\\n");
txt = txt.replaceAll("\r","\\\\r");
txt = txt.replaceAll("\t","\\\\t");
txt = txt.replace("\n","\\n");
txt = txt.replace("\r","\\r");
txt = txt.replace("\t","\\t");
}
else {
txt = "<no text>";

View File

@ -38,11 +38,11 @@ package org.antlr.v4.runtime;
* from the tokens source on demand. In other words, until you ask for a
* token using consume(), LT(), etc. the stream does not pull from the lexer.
*
* The only difference between this stream and BufferedTokenStream superclass
* The only difference between this stream and {@link BufferedTokenStream} superclass
* is that this stream knows how to ignore off channel tokens. There may be
* a performance advantage to using the superclass if you don't pass
* whitespace and comments etc. to the parser on a hidden channel (i.e.,
* you set $channel instead of calling skip() in lexer rules.)
* you set {@code $channel} instead of calling {@code skip()} in lexer rules.)
*
* @see UnbufferedTokenStream
* @see BufferedTokenStream

View File

@ -32,26 +32,21 @@ package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.atn.BlockStartState;
import org.antlr.v4.runtime.atn.PlusBlockStartState;
import org.antlr.v4.runtime.atn.PlusLoopbackState;
import org.antlr.v4.runtime.atn.RuleTransition;
import org.antlr.v4.runtime.atn.StarLoopEntryState;
import org.antlr.v4.runtime.atn.StarLoopbackState;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.runtime.misc.Pair;
/** This is the default error handling mechanism for ANTLR parsers
* and tree parsers.
*/
public class DefaultErrorStrategy implements ANTLRErrorStrategy {
/** How to create token objects */
protected TokenFactory<?> _factory = CommonTokenFactory.DEFAULT;
/** This is true after we see an error and before having successfully
* matched a token. Prevents generation of more than one error message
* per error.
*
* @see #inErrorRecoveryMode
*/
protected boolean errorRecoveryMode = false;
@ -65,40 +60,86 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
protected IntervalSet lastErrorStates;
/**
* {@inheritDoc}
* <p/>
* The default implementation simply calls {@link #endErrorCondition} to
* ensure that the handler is not in error recovery mode.
*/
@Override
public void setTokenFactory(TokenFactory<?> factory) {
this._factory = factory;
public void reset(Parser recognizer) {
endErrorCondition(recognizer);
}
@Override
public void beginErrorCondition(Parser recognizer) {
/**
* This method is called to enter error recovery mode when a recognition
* exception is reported.
*
* @param recognizer the parser instance
*/
protected void beginErrorCondition(@NotNull Parser recognizer) {
errorRecoveryMode = true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean inErrorRecoveryMode(Parser recognizer) {
return errorRecoveryMode;
}
@Override
public void endErrorCondition(Parser recognizer) {
/**
* This method is called to leave error recovery mode after recovering from
* a recognition exception.
*
* @param recognizer
*/
protected void endErrorCondition(@NotNull Parser recognizer) {
errorRecoveryMode = false;
lastErrorStates = null;
lastErrorIndex = -1;
}
/**
* {@inheritDoc}
* <p/>
* The default implementation simply calls {@link #endErrorCondition}.
*/
@Override
public void reportMatch(Parser recognizer) {
endErrorCondition(recognizer);
}
/**
* {@inheritDoc}
* <p/>
* The default implementation returns immediately if the handler is already
* in error recovery mode. Otherwise, it calls {@link #beginErrorCondition}
* and dispatches the reporting task based on the runtime type of {@code e}
* according to the following table.
*
* <ul>
* <li>{@link NoViableAltException}: Dispatches the call to
* {@link #reportNoViableAlternative}</li>
* <li>{@link InputMismatchException}: Dispatches the call to
* {@link #reportInputMismatch}</li>
* <li>{@link FailedPredicateException}: Dispatches the call to
* {@link #reportFailedPredicate}</li>
* <li>All other types: calls {@link Parser#notifyErrorListeners} to report
* the exception</li>
* </ul>
*/
@Override
public void reportError(Parser recognizer,
RecognitionException e)
throws RecognitionException
{
// if we've already reported an error and have not matched a token
// yet successfully, don't report any errors.
if (errorRecoveryMode) {
if (inErrorRecoveryMode(recognizer)) {
// System.err.print("[SPURIOUS] ");
return; // don't count spurious errors
return; // don't report spurious errors
}
recognizer._syntaxErrors++;
beginErrorCondition(recognizer);
if ( e instanceof NoViableAltException ) {
reportNoViableAlternative(recognizer, (NoViableAltException) e);
@ -111,14 +152,16 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
}
else {
System.err.println("unknown recognition error type: "+e.getClass().getName());
if ( recognizer!=null ) {
recognizer.notifyErrorListeners(e.getOffendingToken(), e.getMessage(), e);
}
recognizer.notifyErrorListeners(e.getOffendingToken(), e.getMessage(), e);
}
}
/** Recover from NoViableAlt errors. Also there could be a mismatched
* token that the match() routine could not recover from.
/**
* {@inheritDoc}
* <p/>
* The default implementation resynchronizes the parser by consuming tokens
* until we find one in the resynchronization set--loosely the set of tokens
* that can follow the current rule.
*/
@Override
public void recover(Parser recognizer, RecognitionException e) {
@ -146,27 +189,60 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
consumeUntil(recognizer, followSet);
}
/** Make sure that the current lookahead symbol is consistent with
* what were expecting at this point in the ATN.
/**
* The default implementation of {@link ANTLRErrorStrategy#sync} makes sure
* that the current lookahead symbol is consistent with what were expecting
* at this point in the ATN. You can call this anytime but ANTLR only
* generates code to check before subrules/loops and each iteration.
* <p/>
* Implements Jim Idle's magic sync mechanism in closures and optional
* subrules. E.g.,
*
* At the start of a sub rule upon error, sync() performs single
* token deletion, if possible. If it can't do that, it bails
* on the current rule and uses the default error recovery,
* which consumes until the resynchronization set of the current rule.
* <pre>
* a : sync ( stuff sync )* ;
* sync : {consume to what can follow sync} ;
* </pre>
*
* If the sub rule is optional, ()? or ()* or optional alternative,
* then the expected set includes what follows the subrule.
* At the start of a sub rule upon error, {@link #sync} performs single
* token deletion, if possible. If it can't do that, it bails on the current
* rule and uses the default error recovery, which consumes until the
* resynchronization set of the current rule.
* <p/>
* If the sub rule is optional ({@code (...)?}, {@code (...)*}, or block
* with an empty alternative), then the expected set includes what follows
* the subrule.
* <p/>
* During loop iteration, it consumes until it sees a token that can start a
* sub rule or what follows loop. Yes, that is pretty aggressive. We opt to
* stay in the loop as long as possible.
* <p/>
* <strong>ORIGINS</strong>
* <p/>
* Previous versions of ANTLR did a poor job of their recovery within loops.
* A single mismatch token or missing token would force the parser to bail
* out of the entire rules surrounding the loop. So, for rule
*
* During loop iteration, it consumes until it sees a token that can
* start a sub rule or what follows loop. Yes, that is pretty aggressive.
* We opt to stay in the loop as long as possible.
*/
* <pre>
* classDef : 'class' ID '{' member* '}'
* </pre>
*
* input with an extra token between members would force the parser to
* consume until it found the next class definition rather than the next
* member definition of the current class.
* <p/>
* This functionality cost a little bit of effort because the parser has to
* compare token set at the start of the loop and at each iteration. If for
* some reason speed is suffering for you, you can turn off this
* functionality by simply overriding this method as a blank { }.
*/
@Override
public void sync(Parser recognizer) {
public void sync(Parser recognizer) throws RecognitionException {
ATNState s = recognizer.getInterpreter().atn.states.get(recognizer.getState());
// System.err.println("sync @ "+s.stateNumber+"="+s.getClass().getSimpleName());
// If already recovering, don't try to sync
if ( errorRecoveryMode ) return;
if (inErrorRecoveryMode(recognizer)) {
return;
}
TokenStream tokens = recognizer.getInputStream();
int la = tokens.LA(1);
@ -179,34 +255,49 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
return;
}
if ( s instanceof PlusBlockStartState ||
s instanceof StarLoopEntryState ||
s instanceof BlockStartState )
{
switch (s.getStateType()) {
case ATNState.BLOCK_START:
case ATNState.STAR_BLOCK_START:
case ATNState.PLUS_BLOCK_START:
case ATNState.STAR_LOOP_ENTRY:
// report error and recover if possible
if ( singleTokenDeletion(recognizer)!=null ) return;
if ( singleTokenDeletion(recognizer)!=null ) {
return;
}
throw new InputMismatchException(recognizer);
}
if ( s instanceof PlusLoopbackState ||
s instanceof StarLoopbackState )
{
case ATNState.PLUS_LOOP_BACK:
case ATNState.STAR_LOOP_BACK:
// System.err.println("at loop back: "+s.getClass().getSimpleName());
reportUnwantedToken(recognizer);
IntervalSet expecting = recognizer.getExpectedTokens();
IntervalSet whatFollowsLoopIterationOrRule =
expecting.or(getErrorRecoverySet(recognizer));
consumeUntil(recognizer, whatFollowsLoopIterationOrRule);
break;
default:
// do nothing if we can't identify the exact kind of ATN state
break;
}
// do nothing if we can't identify the exact kind of ATN state
}
public void reportNoViableAlternative(Parser recognizer,
NoViableAltException e)
throws RecognitionException
/**
* This is called by {@link #reportError} when the exception is a
* {@link NoViableAltException}.
*
* @see #reportError
*
* @param recognizer the parser instance
* @param e the recognition exception
*/
protected void reportNoViableAlternative(@NotNull Parser recognizer,
@NotNull NoViableAltException e)
{
TokenStream tokens = recognizer.getInputStream();
String input;
if (tokens instanceof TokenStream) {
if ( tokens!=null ) {
if ( e.getStartToken().getType()==Token.EOF ) input = "<EOF>";
else input = tokens.getText(e.getStartToken(), e.getOffendingToken());
}
@ -217,27 +308,63 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
recognizer.notifyErrorListeners(e.getOffendingToken(), msg, e);
}
public void reportInputMismatch(Parser recognizer,
InputMismatchException e)
throws RecognitionException
/**
* This is called by {@link #reportError} when the exception is an
* {@link InputMismatchException}.
*
* @see #reportError
*
* @param recognizer the parser instance
* @param e the recognition exception
*/
protected void reportInputMismatch(@NotNull Parser recognizer,
@NotNull InputMismatchException e)
{
String msg = "mismatched input "+getTokenErrorDisplay(e.getOffendingToken())+
" expecting "+e.getExpectedTokens().toString(recognizer.getTokenNames());
recognizer.notifyErrorListeners(e.getOffendingToken(), msg, e);
}
public void reportFailedPredicate(Parser recognizer,
FailedPredicateException e)
throws RecognitionException
/**
* This is called by {@link #reportError} when the exception is a
* {@link FailedPredicateException}.
*
* @see #reportError
*
* @param recognizer the parser instance
* @param e the recognition exception
*/
protected void reportFailedPredicate(@NotNull Parser recognizer,
@NotNull FailedPredicateException e)
{
String ruleName = recognizer.getRuleNames()[recognizer._ctx.getRuleIndex()];
String msg = "rule "+ruleName+" "+e.getMessage();
recognizer.notifyErrorListeners(e.getOffendingToken(), msg, e);
}
public void reportUnwantedToken(Parser recognizer) {
if (errorRecoveryMode) return;
recognizer._syntaxErrors++;
/**
* This method is called to report a syntax error which requires the removal
* of a token from the input stream. At the time this method is called, the
* erroneous symbol is current {@code LT(1)} symbol and has not yet been
* removed from the input stream. When this method returns,
* {@code recognizer} is in error recovery mode.
* <p/>
* This method is called when {@link #singleTokenDeletion} identifies
* single-token deletion as a viable recovery strategy for a mismatched
* input error.
* <p/>
* The default implementation simply returns if the handler is already in
* error recovery mode. Otherwise, it calls {@link #beginErrorCondition} to
* enter error recovery mode, followed by calling
* {@link Parser#notifyErrorListeners}.
*
* @param recognizer the parser instance
*/
protected void reportUnwantedToken(@NotNull Parser recognizer) {
if (inErrorRecoveryMode(recognizer)) {
return;
}
beginErrorCondition(recognizer);
Token t = recognizer.getCurrentToken();
@ -248,9 +375,28 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
recognizer.notifyErrorListeners(t, msg, null);
}
public void reportMissingToken(Parser recognizer) {
if (errorRecoveryMode) return;
recognizer._syntaxErrors++;
/**
* This method is called to report a syntax error which requires the
* insertion of a missing token into the input stream. At the time this
* method is called, the missing token has not yet been inserted. When this
* method returns, {@code recognizer} is in error recovery mode.
* <p/>
* This method is called when {@link #singleTokenInsertion} identifies
* single-token insertion as a viable recovery strategy for a mismatched
* input error.
* <p/>
* The default implementation simply returns if the handler is already in
* error recovery mode. Otherwise, it calls {@link #beginErrorCondition} to
* enter error recovery mode, followed by calling
* {@link Parser#notifyErrorListeners}.
*
* @param recognizer the parser instance
*/
protected void reportMissingToken(@NotNull Parser recognizer) {
if (inErrorRecoveryMode(recognizer)) {
return;
}
beginErrorCondition(recognizer);
Token t = recognizer.getCurrentToken();
@ -261,34 +407,55 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
recognizer.notifyErrorListeners(t, msg, null);
}
/** Attempt to recover from a single missing or extra token.
/**
* {@inheritDoc}
* <p/>
* The default implementation attempts to recover from the mismatched input
* by using single token insertion and deletion as described below. If the
* recovery attempt fails, this method throws an
* {@link InputMismatchException}.
* <p/>
* <strong>EXTRA TOKEN</strong> (single token deletion)
* <p/>
* {@code LA(1)} is not what we are looking for. If {@code LA(2)} has the
* right token, however, then assume {@code LA(1)} is some extra spurious
* token and delete it. Then consume and return the next token (which was
* the {@code LA(2)} token) as the successful result of the match operation.
* <p/>
* This recovery strategy is implemented by {@link #singleTokenDeletion}.
* <p/>
* <strong>MISSING TOKEN</strong> (single token insertion)
* <p/>
* If current token (at {@code LA(1)}) is consistent with what could come
* after the expected {@code LA(1)} token, then assume the token is missing
* and use the parser's {@link TokenFactory} to create it on the fly. The
* "insertion" is performed by returning the created token as the successful
* result of the match operation.
* <p/>
* This recovery strategy is implemented by {@link #singleTokenInsertion}.
* <p/>
* <strong>EXAMPLE</strong>
* <p/>
* For example, Input {@code i=(3;} is clearly missing the {@code ')'}. When
* the parser returns from the nested call to {@code expr}, it will have
* call chain:
*
* EXTRA TOKEN
* <pre>
* stat -> expr -> atom
* </pre>
*
* LA(1) is not what we are looking for. If LA(2) has the right token,
* however, then assume LA(1) is some extra spurious token. Delete it
* and LA(2) as if we were doing a normal match(), which advances the
* input.
* and it will be trying to match the {@code ')'} at this point in the
* derivation:
*
* MISSING TOKEN
* <pre>
* => ID '=' '(' INT ')' ('+' atom)* ';'
* ^
* </pre>
*
* If current token is consistent with what could come after
* ttype then it is ok to "insert" the missing token, else throw
* exception For example, Input "i=(3;" is clearly missing the
* ')'. When the parser returns from the nested call to expr, it
* will have call chain:
*
* stat -> expr -> atom
*
* and it will be trying to match the ')' at this point in the
* derivation:
*
* => ID '=' '(' INT ')' ('+' atom)* ';'
* ^
* match() will see that ';' doesn't match ')' and report a
* mismatched token error. To recover, it sees that LA(1)==';'
* is in the set of tokens that can follow the ')' token
* reference in rule atom. It can assume that you forgot the ')'.
* The attempt to match {@code ')'} will fail when it sees {@code ';'} and
* call {@link #recoverInline}. To recover, it sees that {@code LA(1)==';'}
* is in the set of tokens that can follow the {@code ')'} token reference
* in rule {@code atom}. It can assume that you forgot the {@code ')'}.
*/
@Override
public Token recoverInline(Parser recognizer)
@ -312,8 +479,24 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
throw new InputMismatchException(recognizer);
}
// if next token is what we are looking for then "delete" this token
public boolean singleTokenInsertion(Parser recognizer) {
/**
* This method implements the single-token insertion inline error recovery
* strategy. It is called by {@link #recoverInline} if the single-token
* deletion strategy fails to recover from the mismatched input. If this
* method returns {@code true}, {@code recognizer} will be in error recovery
* mode.
* <p/>
* This method determines whether or not single-token insertion is viable by
* checking if the {@code LA(1)} input symbol could be successfully matched
* if it were instead the {@code LA(2)} symbol. If this method returns
* {@code true}, the caller is responsible for creating and inserting a
* token with the correct type to produce this behavior.
*
* @param recognizer the parser instance
* @return {@code true} if single-token insertion is a viable recovery
* strategy for the current mismatched input, otherwise {@code false}
*/
protected boolean singleTokenInsertion(@NotNull Parser recognizer) {
int currentSymbolType = recognizer.getInputStream().LA(1);
// if current token is consistent with what could come after current
// ATN state, then we know we're missing a token; error recovery
@ -330,7 +513,27 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
return false;
}
public Token singleTokenDeletion(Parser recognizer) {
/**
* This method implements the single-token deletion inline error recovery
* strategy. It is called by {@link #recoverInline} to attempt to recover
* from mismatched input. If this method returns null, the parser and error
* handler state will not have changed. If this method returns non-null,
* {@code recognizer} will <em>not</em> be in error recovery mode since the
* returned token was a successful match.
* <p/>
* If the single-token deletion is successful, this method calls
* {@link #reportUnwantedToken} to report the error, followed by
* {@link Parser#consume} to actually "delete" the extraneous token. Then,
* before returning {@link #reportMatch} is called to signal a successful
* match.
*
* @param recognizer the parser instance
* @return the successfully matched {@link Token} instance if single-token
* deletion successfully recovers from the mismatched input, otherwise
* {@code null}
*/
@Nullable
protected Token singleTokenDeletion(@NotNull Parser recognizer) {
int nextTokenType = recognizer.getInputStream().LA(2);
IntervalSet expecting = getExpectedTokens(recognizer);
if ( expecting.contains(nextTokenType) ) {
@ -344,7 +547,7 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
recognizer.consume(); // simply delete extra token
// we want to return the token we're actually matching
Token matchedSymbol = recognizer.getCurrentToken();
endErrorCondition(recognizer); // we know current token is correct
reportMatch(recognizer); // we know current token is correct
return matchedSymbol;
}
return null;
@ -369,7 +572,8 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
* If you change what tokens must be created by the lexer,
* override this method to create the appropriate tokens.
*/
protected Token getMissingSymbol(Parser recognizer) {
@NotNull
protected Token getMissingSymbol(@NotNull Parser recognizer) {
Token currentSymbol = recognizer.getCurrentToken();
IntervalSet expecting = getExpectedTokens(recognizer);
int expectedTokenType = expecting.getMinElement(); // get any element
@ -382,13 +586,14 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
current = lookback;
}
return
_factory.create(new Pair<TokenSource, CharStream>(current.getTokenSource(), current.getTokenSource().getInputStream()), expectedTokenType, tokenText,
recognizer.getTokenFactory().create(new Pair<TokenSource, CharStream>(current.getTokenSource(), current.getTokenSource().getInputStream()), expectedTokenType, tokenText,
Token.DEFAULT_CHANNEL,
-1, -1,
current.getLine(), current.getCharPositionInLine());
}
public IntervalSet getExpectedTokens(Parser recognizer) {
@NotNull
protected IntervalSet getExpectedTokens(@NotNull Parser recognizer) {
return recognizer.getExpectedTokens();
}
@ -400,7 +605,7 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
* your token objects because you don't have to go modify your lexer
* so that it creates a new Java type.
*/
public String getTokenErrorDisplay(Token t) {
protected String getTokenErrorDisplay(Token t) {
if ( t==null ) return "<no token>";
String s = getSymbolText(t);
if ( s==null ) {
@ -422,11 +627,12 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
return symbol.getType();
}
protected String escapeWSAndQuote(String s) {
@NotNull
protected String escapeWSAndQuote(@NotNull String s) {
// if ( s==null ) return s;
s = s.replaceAll("\n","\\\\n");
s = s.replaceAll("\r","\\\\r");
s = s.replaceAll("\t","\\\\t");
s = s.replace("\n","\\n");
s = s.replace("\r","\\r");
s = s.replace("\t","\\t");
return "'"+s+"'";
}
@ -522,7 +728,8 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
* Like Grosch I implement context-sensitive FOLLOW sets that are combined
* at run-time upon error to avoid overhead during parsing.
*/
protected IntervalSet getErrorRecoverySet(Parser recognizer) {
@NotNull
protected IntervalSet getErrorRecoverySet(@NotNull Parser recognizer) {
ATN atn = recognizer.getInterpreter().atn;
RuleContext ctx = recognizer._ctx;
IntervalSet recoverSet = new IntervalSet();
@ -539,8 +746,8 @@ public class DefaultErrorStrategy implements ANTLRErrorStrategy {
return recoverSet;
}
/** Consume tokens until one matches the given token set */
public void consumeUntil(Parser recognizer, IntervalSet set) {
/** Consume tokens until one matches the given token set. */
protected void consumeUntil(@NotNull Parser recognizer, @NotNull IntervalSet set) {
// System.err.println("consumeUntil("+set.toString(recognizer.getTokenNames())+")");
int ttype = recognizer.getInputStream().LA(1);
while (ttype != Token.EOF && !set.contains(ttype) ) {

View File

@ -30,44 +30,151 @@
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.ATNConfig;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.BitSet;
/**
* This implementation of {@link ANTLRErrorListener} can be used to identify
* certain potential correctness and performance problems in grammars. "Reports"
* are made by calling {@link Parser#notifyErrorListeners} with the appropriate
* message.
*
* <ul>
* <li><b>Ambiguities</b>: These are cases where more than one path through the
* grammar can match the input.</li>
* <li><b>Weak context sensitivity</b>: These are cases where full-context
* prediction resolved an SLL conflict to a unique alternative which equaled the
* minimum alternative of the SLL conflict.</li>
* <li><b>Strong (forced) context sensitivity</b>: These are cases where the
* full-context prediction resolved an SLL conflict to a unique alternative,
* <em>and</em> the minimum alternative of the SLL conflict was found to not be
* a truly viable alternative. Two-stage parsing cannot be used for inputs where
* this situation occurs.</li>
* </ul>
*
* @author Sam Harwell
*/
public class DiagnosticErrorListener extends BaseErrorListener {
@Override
public void reportAmbiguity(@NotNull Parser recognizer,
DFA dfa, int startIndex, int stopIndex,
@NotNull BitSet ambigAlts,
/**
* When {@code true}, only exactly known ambiguities are reported.
*/
protected final boolean exactOnly;
/**
* Initializes a new instance of {@link DiagnosticErrorListener} which only
* reports exact ambiguities.
*/
public DiagnosticErrorListener() {
this(true);
}
/**
* Initializes a new instance of {@link DiagnosticErrorListener}, specifying
* whether all ambiguities or only exact ambiguities are reported.
*
* @param exactOnly {@code true} to report only exact ambiguities, otherwise
* {@code false} to report all ambiguities.
*/
public DiagnosticErrorListener(boolean exactOnly) {
this.exactOnly = exactOnly;
}
@Override
public void reportAmbiguity(@NotNull Parser recognizer,
DFA dfa,
int startIndex,
int stopIndex,
boolean exact,
@Nullable BitSet ambigAlts,
@NotNull ATNConfigSet configs)
{
recognizer.notifyErrorListeners("reportAmbiguity d=" + dfa.decision +
": ambigAlts=" + ambigAlts + ", input='" +
recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex)) + "'");
}
{
if (exactOnly && !exact) {
return;
}
String format = "reportAmbiguity d=%s: ambigAlts=%s, input='%s'";
String decision = getDecisionDescription(recognizer, dfa);
BitSet conflictingAlts = getConflictingAlts(ambigAlts, configs);
String text = recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex));
String message = String.format(format, decision, conflictingAlts, text);
recognizer.notifyErrorListeners(message);
}
@Override
public void reportAttemptingFullContext(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
int startIndex,
int stopIndex,
@Nullable BitSet conflictingAlts,
@NotNull ATNConfigSet configs)
{
recognizer.notifyErrorListeners("reportAttemptingFullContext d=" +
dfa.decision + ", input='" +
recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex)) + "'");
String format = "reportAttemptingFullContext d=%s, input='%s'";
String decision = getDecisionDescription(recognizer, dfa);
String text = recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex));
String message = String.format(format, decision, text);
recognizer.notifyErrorListeners(message);
}
@Override
public void reportContextSensitivity(@NotNull Parser recognizer,
@NotNull DFA dfa,
int startIndex, int stopIndex,
int startIndex,
int stopIndex,
int prediction,
@NotNull ATNConfigSet configs)
{
recognizer.notifyErrorListeners("reportContextSensitivity d=" +
dfa.decision + ", input='" +
recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex)) + "'");
}
{
String format = "reportContextSensitivity d=%s, input='%s'";
String decision = getDecisionDescription(recognizer, dfa);
String text = recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex));
String message = String.format(format, decision, text);
recognizer.notifyErrorListeners(message);
}
protected String getDecisionDescription(@NotNull Parser recognizer, @NotNull DFA dfa) {
int decision = dfa.decision;
int ruleIndex = dfa.atnStartState.ruleIndex;
String[] ruleNames = recognizer.getRuleNames();
if (ruleIndex < 0 || ruleIndex >= ruleNames.length) {
return String.valueOf(decision);
}
String ruleName = ruleNames[ruleIndex];
if (ruleName == null || ruleName.isEmpty()) {
return String.valueOf(decision);
}
return String.format("%d (%s)", decision, ruleName);
}
/**
* Computes the set of conflicting or ambiguous alternatives from a
* configuration set, if that information was not already provided by the
* parser.
*
* @param reportedAlts The set of conflicting or ambiguous alternatives, as
* reported by the parser.
* @param configs The conflicting or ambiguous configuration set.
* @return Returns {@code reportedAlts} if it is not {@code null}, otherwise
* returns the set of alternatives represented in {@code configs}.
*/
@NotNull
protected BitSet getConflictingAlts(@Nullable BitSet reportedAlts, @NotNull ATNConfigSet configs) {
if (reportedAlts != null) {
return reportedAlts;
}
BitSet result = new BitSet();
for (ATNConfig config : configs) {
result.set(config.alt);
}
return result;
}
}

View File

@ -35,6 +35,8 @@ import org.antlr.v4.runtime.atn.PredicateTransition;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.Locale;
/** A semantic predicate failed during validation. Validation of predicates
* occurs when normally parsing the alternative just like matching a token.
* Disambiguating predicate evaluation occurs when we test a predicate during
@ -93,6 +95,6 @@ public class FailedPredicateException extends RecognitionException {
return message;
}
return String.format("failed predicate: {%s}?", predicate);
return String.format(Locale.getDefault(), "failed predicate: {%s}?", predicate);
}
}

View File

@ -29,11 +29,13 @@
*/
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.misc.NotNull;
/** This signifies any kind of mismatched input exceptions such as
* when the current input does not match the expected token.
*/
public class InputMismatchException extends RecognitionException {
public InputMismatchException(Parser recognizer) {
public InputMismatchException(@NotNull Parser recognizer) {
super(recognizer, recognizer.getInputStream(), recognizer._ctx);
this.setOffendingToken(recognizer.getCurrentToken());
}

View File

@ -112,7 +112,7 @@ public interface IntStream {
* If {@code i} represents a position at or beyond the end of the stream,
* this method returns {@link #EOF}.
* <p/>
* The return value is unspecified if {@code i&lt;0} and fewer than {@code -i}
* The return value is unspecified if {@code i<0} and fewer than {@code -i}
* calls to {@link #consume consume()} have occurred from the beginning of
* the stream before calling this method.
*

View File

@ -224,6 +224,7 @@ public abstract class Lexer extends Recognizer<Integer, LexerATNSimulator>
this._factory = factory;
}
@Override
public TokenFactory<? extends Token> getTokenFactory() {
return _factory;
}

View File

@ -36,6 +36,8 @@ import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.runtime.misc.Utils;
import java.util.Locale;
public class LexerNoViableAltException extends RecognitionException {
/** Matching attempted at what input index? */
private final int startIndex;
@ -75,6 +77,6 @@ public class LexerNoViableAltException extends RecognitionException {
symbol = Utils.escapeWhitespace(symbol, false);
}
return String.format("%s('%s')", LexerNoViableAltException.class.getSimpleName(), symbol);
return String.format(Locale.getDefault(), "%s('%s')", LexerNoViableAltException.class.getSimpleName(), symbol);
}
}

View File

@ -37,6 +37,7 @@ import org.antlr.v4.runtime.atn.RuleTransition;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.IntegerStack;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTreeListener;
@ -44,6 +45,7 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** This is all the parsing support code essentially; most of it is error recovery stuff. */
@ -58,8 +60,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
@Override
public void visitTerminal(TerminalNode node) {
System.out.println("consume "+node.getSymbol()+" rule "+
getRuleNames()[_ctx.getRuleIndex()]+
" alt="+_ctx.altNum);
getRuleNames()[_ctx.getRuleIndex()]);
}
@Override
@ -93,8 +94,22 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
/**
* The error handling strategy for the parser. The default value is a new
* instance of {@link DefaultErrorStrategy}.
*
* @see #getErrorHandler
* @see #setErrorHandler
*/
@NotNull
protected ANTLRErrorStrategy _errHandler = new DefaultErrorStrategy();
/**
* The input stream.
*
* @see #getInputStream
* @see #setInputStream
*/
protected TokenStream _input;
protected final IntegerStack _precedenceStack;
@ -103,29 +118,44 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
_precedenceStack.push(0);
}
/** The RuleContext object for the currently executing rule. This
* must be non-null during parsing, but is initially null.
* When somebody calls the start rule, this gets set to the
* root context.
/**
* The {@link ParserRuleContext} object for the currently executing rule.
* This is always non-null during the parsing process.
*/
protected ParserRuleContext _ctx;
/**
* Specifies whether or not the parser should construct a parse tree during
* the parsing process. The default value is {@code true}.
*
* @see #getBuildParseTree
* @see #setBuildParseTree
*/
protected boolean _buildParseTrees = true;
protected TraceListener _tracer;
/**
* When {@link #setTrace}{@code (true)} is called, a reference to the
* {@link TraceListener} is stored here so it can be easily removed in a
* later call to {@link #setTrace}{@code (false)}. The listener itself is
* implemented as a parser listener so this field is not directly used by
* other parser methods.
*/
private TraceListener _tracer;
/** If the listener is non-null, trigger enter and exit rule events
* *during* the parse. This is typically done only when not building
* parse trees for later visiting. We either trigger events during
* the parse or during tree walks later. Both could be done.
* Not intended for average user!!! Most people should use
* ParseTreeListener with ParseTreeWalker.
* @see ParseTreeWalker
*/
protected List<ParseTreeListener> _parseListeners;
/**
* The list of {@link ParseTreeListener} listeners registered to receive
* events during the parse.
*
* @see #addParseListener
*/
@Nullable
protected List<ParseTreeListener> _parseListeners;
/** Did the recognizer encounter a syntax error? Track how many. */
protected int _syntaxErrors = 0;
/**
* The number of syntax errors reported during parsing. This value is
* incremented each time {@link #notifyErrorListeners} is called.
*/
protected int _syntaxErrors;
public Parser(TokenStream input) {
setInputStream(input);
@ -134,10 +164,10 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
/** reset the parser's state */
public void reset() {
if ( getInputStream()!=null ) getInputStream().seek(0);
_errHandler.endErrorCondition(this);
_errHandler.reset(this);
_ctx = null;
_syntaxErrors = 0;
_tracer = null;
setTrace(false);
_precedenceStack.clear();
_precedenceStack.push(0);
ATNSimulator interpreter = getInterpreter();
@ -146,14 +176,29 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
/** Match current input symbol against ttype. Attempt
* single token insertion or deletion error recovery. If
* that fails, throw MismatchedTokenException.
/**
* Match current input symbol against {@code ttype}. If the symbol type
* matches, {@link ANTLRErrorStrategy#reportMatch} and {@link #consume} are
* called to complete the match process.
* <p/>
* If the symbol type does not match,
* {@link ANTLRErrorStrategy#recoverInline} is called on the current error
* strategy to attempt recovery. If {@link #getBuildParseTree} is
* {@code true} and the token index of the symbol returned by
* {@link ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to
* the parse tree by calling {@link ParserRuleContext#addErrorNode}.
*
* @param ttype the token type to match
* @return the matched symbol
* @throws RecognitionException if the current input symbol did not match
* {@code ttype} and the error strategy could not recover from the
* mismatched symbol
*/
@NotNull
public Token match(int ttype) throws RecognitionException {
Token t = getCurrentToken();
if ( t.getType()==ttype ) {
_errHandler.endErrorCondition(this);
_errHandler.reportMatch(this);
consume();
}
else {
@ -167,10 +212,28 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return t;
}
/**
* Match current input symbol as a wildcard. If the symbol type matches
* (i.e. has a value greater than 0), {@link ANTLRErrorStrategy#reportMatch}
* and {@link #consume} are called to complete the match process.
* <p/>
* If the symbol type does not match,
* {@link ANTLRErrorStrategy#recoverInline} is called on the current error
* strategy to attempt recovery. If {@link #getBuildParseTree} is
* {@code true} and the token index of the symbol returned by
* {@link ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to
* the parse tree by calling {@link ParserRuleContext#addErrorNode}.
*
* @return the matched symbol
* @throws RecognitionException if the current input symbol did not match
* a wildcard and the error strategy could not recover from the mismatched
* symbol
*/
@NotNull
public Token matchWildcard() throws RecognitionException {
Token t = getCurrentToken();
if (t.getType() > 0) {
_errHandler.endErrorCondition(this);
_errHandler.reportMatch(this);
consume();
}
else {
@ -185,30 +248,32 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return t;
}
/** Track the RuleContext objects during the parse and hook them up
* using the children list so that it forms a parse tree.
* The RuleContext returned from the start rule represents the root
* of the parse tree.
*
* To built parse trees, all we have to do is put a hook in setState()
* and enterRule(). In setState(), we add tokens to the current context
* as children. By the time we get to enterRule(), we are already
* in an invoked rule so we add this context as a child of the parent
* (invoking) context. Simple and effective.
*
* Note that if we are not building parse trees, rule contexts
* only point upwards. When a rule exits, it returns the context
* but that gets garbage collected if nobody holds a reference.
* It points upwards but nobody points at it.
*
* When we build parse trees, we are adding all of these contexts to
* somebody's children list. Contexts are then not candidates
* for garbage collection.
/**
* Track the {@link ParserRuleContext} objects during the parse and hook
* them up using the {@link ParserRuleContext#children} list so that it
* forms a parse tree. The {@link ParserRuleContext} returned from the start
* rule represents the root of the parse tree.
* <p/>
* Note that if we are not building parse trees, rule contexts only point
* upwards. When a rule exits, it returns the context but that gets garbage
* collected if nobody holds a reference. It points upwards but nobody
* points at it.
* <p/>
* When we build parse trees, we are adding all of these contexts to
* {@link ParserRuleContext#children} list. Contexts are then not candidates
* for garbage collection.
*/
public void setBuildParseTree(boolean buildParseTrees) {
this._buildParseTrees = buildParseTrees;
}
/**
* Gets whether or not a complete parse tree will be constructed while
* parsing. This property is {@code true} for a newly constructed parser.
*
* @return {@code true} if a complete parse tree will be constructed while
* parsing, otherwise {@code false}
*/
public boolean getBuildParseTree() {
return _buildParseTrees;
}
@ -235,102 +300,142 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
* using the default {@link Parser.TrimToSizeListener} during the parse process.
*/
public boolean getTrimParseTree() {
if (_parseListeners == null) return false;
return _parseListeners.contains(TrimToSizeListener.INSTANCE);
return getParseListeners().contains(TrimToSizeListener.INSTANCE);
}
// public void setTraceATNStates(boolean traceATNStates) {
// this.traceATNStates = traceATNStates;
// }
//
// public boolean getTraceATNStates() {
// return traceATNStates;
// }
@NotNull
public List<ParseTreeListener> getParseListeners() {
List<ParseTreeListener> listeners = _parseListeners;
if (listeners == null) {
return Collections.emptyList();
}
public List<ParseTreeListener> getParseListeners() {
return _parseListeners;
}
return listeners;
}
/** Provide a listener that gets notified about token matches,
* and rule entry/exit events DURING the parse. It's a little bit
* weird for left recursive rule entry events but it's
* deterministic.
/**
* Registers {@code listener} to receive events during the parsing process.
* <p/>
* To support output-preserving grammar transformations (including but not
* limited to left-recursion removal, automated left-factoring, and
* optimized code generation), calls to listener methods during the parse
* may differ substantially from calls made by
* {@link ParseTreeWalker#DEFAULT} used after the parse is complete. In
* particular, rule entry and exit events may occur in a different order
* during the parse than after the parser. In addition, calls to certain
* rule entry methods may be omitted.
* <p/>
* With the following specific exceptions, calls to listener events are
* <em>deterministic</em>, i.e. for identical input the calls to listener
* methods will be the same.
*
* THIS IS ONLY FOR ADVANCED USERS. Please give your
* ParseTreeListener to a ParseTreeWalker instead of giving it to
* the parser!!!!
* <ul>
* <li>Alterations to the grammar used to generate code may change the
* behavior of the listener calls.</li>
* <li>Alterations to the command line options passed to ANTLR 4 when
* generating the parser may change the behavior of the listener calls.</li>
* <li>Changing the version of the ANTLR Tool used to generate the parser
* may change the behavior of the listener calls.</li>
* </ul>
*
* @param listener the listener to add
*
* @throws NullPointerException if {@code} listener is {@code null}
*/
public void addParseListener(ParseTreeListener listener) {
if ( listener==null ) return;
if ( _parseListeners==null ) {
public void addParseListener(@NotNull ParseTreeListener listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
if (_parseListeners == null) {
_parseListeners = new ArrayList<ParseTreeListener>();
}
this._parseListeners.add(listener);
}
public void removeParseListener(ParseTreeListener l) {
if ( l==null ) return;
if ( _parseListeners!=null ) {
_parseListeners.remove(l);
if (_parseListeners.isEmpty()) {
_parseListeners = null;
this._parseListeners.add(listener);
}
/**
* Remove {@code listener} from the list of parse listeners.
* <p/>
* If {@code listener} is {@code null} or has not been added as a parse
* listener, this method does nothing.
*
* @see #addParseListener
*
* @param listener the listener to remove
*/
public void removeParseListener(ParseTreeListener listener) {
if (_parseListeners != null) {
if (_parseListeners.remove(listener)) {
if (_parseListeners.isEmpty()) {
_parseListeners = null;
}
}
}
}
/**
* Remove all parse listeners.
*
* @see #addParseListener
*/
public void removeParseListeners() {
_parseListeners = null;
}
/** Notify any parse listeners (implemented as ParseTreeListener's)
* of an enter rule event. This is not involved with
* parse tree walking in any way; it's just reusing the
* ParseTreeListener interface. This is not for the average user.
/**
* Notify any parse listeners of an enter rule event.
*
* @see #addParseListener
*/
public void triggerEnterRuleEvent() {
for (ParseTreeListener l : _parseListeners) {
l.enterEveryRule(_ctx);
_ctx.enterRule(l);
protected void triggerEnterRuleEvent() {
for (ParseTreeListener listener : _parseListeners) {
listener.enterEveryRule(_ctx);
_ctx.enterRule(listener);
}
}
/** Notify any parse listeners (implemented as ParseTreeListener's)
* of an exit rule event. This is not involved with
* parse tree walking in any way; it's just reusing the
* ParseTreeListener interface. This is not for the average user.
/**
* Notify any parse listeners of an exit rule event.
*
* @see #addParseListener
*/
public void triggerExitRuleEvent() {
protected void triggerExitRuleEvent() {
// reverse order walk of listeners
for (int i = _parseListeners.size()-1; i >= 0; i--) {
ParseTreeListener l = _parseListeners.get(i);
_ctx.exitRule(l);
l.exitEveryRule(_ctx);
ParseTreeListener listener = _parseListeners.get(i);
_ctx.exitRule(listener);
listener.exitEveryRule(_ctx);
}
}
/** Get number of recognition errors (lexer, parser, tree parser). Each
* recognizer tracks its own number. So parser and lexer each have
* separate count. Does not count the spurious errors found between
* an error and next valid token match
/**
* Gets the number of syntax errors reported during parsing. This value is
* incremented each time {@link #notifyErrorListeners} is called.
*
* See also reportError()
* @see #notifyErrorListeners
*/
public int getNumberOfSyntaxErrors() {
return _syntaxErrors;
}
/** Tell our token source and error strategy about a new way to create tokens */
@Override
public TokenFactory<?> getTokenFactory() {
return _input.getTokenSource().getTokenFactory();
}
/** Tell our token source and error strategy about a new way to create tokens. */
@Override
public void setTokenFactory(TokenFactory<?> factory) {
_input.getTokenSource().setTokenFactory(factory);
_errHandler.setTokenFactory(factory);
}
@NotNull
public ANTLRErrorStrategy getErrorHandler() {
return _errHandler;
}
public void setErrorHandler(ANTLRErrorStrategy handler) {
public void setErrorHandler(@NotNull ANTLRErrorStrategy handler) {
this._errHandler = handler;
}
@ -346,7 +451,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return _input;
}
/** Set the token stream and reset the parser */
/** Set the token stream and reset the parser. */
public void setTokenStream(TokenStream input) {
this._input = null;
reset();
@ -360,13 +465,14 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return _input.LT(1);
}
public void notifyErrorListeners(String msg) {
public final void notifyErrorListeners(String msg) {
notifyErrorListeners(getCurrentToken(), msg, null);
}
public void notifyErrorListeners(Token offendingToken, String msg,
@Nullable RecognitionException e)
{
_syntaxErrors++;
int line = -1;
int charPositionInLine = -1;
line = offendingToken.getLine();
@ -376,22 +482,32 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
listener.syntaxError(this, offendingToken, line, charPositionInLine, msg, e);
}
/** Consume the current symbol and return it. E.g., given the following
* input with A being the current lookahead symbol:
/**
* Consume and return the {@linkplain #getCurrentToken current symbol}.
* <p/>
* E.g., given the following input with {@code A} being the current
* lookahead symbol, this function moves the cursor to {@code B} and returns
* {@code A}.
*
* A B
* ^
* <pre>
* A B
* ^
* </pre>
*
* this function moves the cursor to B and returns A.
*
* If the parser is creating parse trees, the current symbol
* would also be added as a child to the current context (node).
*
* Trigger listener events if there's a listener.
* If the parser is not in error recovery mode, the consumed symbol is added
* to the parse tree using {@link ParserRuleContext#addChild(Token)}, and
* {@link ParseTreeListener#visitTerminal} is called on any parse listeners.
* If the parser <em>is</em> in error recovery mode, the consumed symbol is
* added to the parse tree using
* {@link ParserRuleContext#addErrorNode(Token)}, and
* {@link ParseTreeListener#visitErrorNode} is called on any parse
* listeners.
*/
public Token consume() {
Token o = getCurrentToken();
getInputStream().consume();
if (o.getType() != EOF) {
getInputStream().consume();
}
boolean hasListener = _parseListeners != null && !_parseListeners.isEmpty();
if (_buildParseTrees || hasListener) {
if ( _errHandler.inErrorRecoveryMode(this) ) {
@ -422,14 +538,11 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
/** Always called by generated parsers upon entry to a rule.
* This occurs after the new context has been pushed. Access field
* _ctx get the current context.
*
* This is flexible because users do not have to regenerate parsers
* to get trace facilities.
/**
* Always called by generated parsers upon entry to a rule. Access field
* {@link #_ctx} get the current context.
*/
public void enterRule(ParserRuleContext localctx, int state, int ruleIndex) {
public void enterRule(@NotNull ParserRuleContext localctx, int state, int ruleIndex) {
setState(state);
_ctx = localctx;
_ctx.start = _input.LT(1);
@ -456,7 +569,6 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
_ctx = localctx;
_ctx.altNum = altNum;
}
public void enterRecursionRule(ParserRuleContext localctx, int ruleIndex, int precedence) {
@ -468,7 +580,9 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
/* like enterRule but for recursive rules */
/**
* Like {@link #enterRule} but for recursive rules.
*/
public void pushNewRecursionContext(ParserRuleContext localctx, int state, int ruleIndex) {
ParserRuleContext previous = _ctx;
previous.parent = localctx;
@ -501,9 +615,14 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
else {
_ctx = _parentctx;
}
// hook into tree
retctx.parent = _parentctx;
if (_buildParseTrees) _parentctx.addChild(retctx); // add return ctx into invoking rule's tree
if (_buildParseTrees && _parentctx != null) {
// add return ctx into invoking rule's tree
_parentctx.addChild(retctx);
}
}
public ParserRuleContext getInvokingContext(int ruleIndex) {
@ -529,6 +648,20 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return false;
}
/**
* Checks whether or not {@code symbol} can follow the current state in the
* ATN. The behavior of this method is equivalent to the following, but is
* implemented such that the complete context-sensitive follow set does not
* need to be explicitly constructed.
*
* <pre>
* return getExpectedTokens().contains(symbol);
* </pre>
*
* @param symbol the symbol type to check
* @return {@code true} if {@code symbol} can follow the current state in
* the ATN, otherwise {@code false}.
*/
public boolean isExpectedToken(int symbol) {
// return getInterpreter().atn.nextTokens(_ctx);
ATN atn = getInterpreter().atn;
@ -559,33 +692,19 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return false;
}
/** Compute the set of valid tokens reachable from the current
* position in the parse.
/**
* Computes the set of input symbols which could follow the current parser
* state and context, as given by {@link #getState} and {@link #getContext},
* respectively.
*
* @see ATN#getExpectedTokens(int, RuleContext)
*/
public IntervalSet getExpectedTokens() {
ATN atn = getInterpreter().atn;
ParserRuleContext ctx = _ctx;
ATNState s = atn.states.get(getState());
IntervalSet following = atn.nextTokens(s);
// System.out.println("following "+s+"="+following);
if ( !following.contains(Token.EPSILON) ) return following;
IntervalSet expected = new IntervalSet();
expected.addAll(following);
expected.remove(Token.EPSILON);
while ( ctx!=null && ctx.invokingState>=0 && following.contains(Token.EPSILON) ) {
ATNState invokingState = atn.states.get(ctx.invokingState);
RuleTransition rt = (RuleTransition)invokingState.transition(0);
following = atn.nextTokens(rt.followState);
expected.addAll(following);
expected.remove(Token.EPSILON);
ctx = (ParserRuleContext)ctx.parent;
}
if ( following.contains(Token.EPSILON) ) {
expected.add(Token.EOF);
}
return expected;
}
@NotNull
public IntervalSet getExpectedTokens() {
return getATN().getExpectedTokens(getState(), getContext());
}
@NotNull
public IntervalSet getExpectedTokensWithinCurrentRule() {
ATN atn = getInterpreter().atn;
ATNState s = atn.states.get(getState());
@ -604,7 +723,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
public ParserRuleContext getRuleContext() { return _ctx; }
/** Return List<String> of the rule names in your parser instance
/** Return List&lt;String&gt; of the rule names in your parser instance
* leading up to a call to the current rule. You could override if
* you want more details such as the file/line info of where
* in the ATN a rule is invoked.
@ -628,7 +747,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return stack;
}
/** For debugging and other purposes */
/** For debugging and other purposes. */
public List<String> getDFAStrings() {
synchronized (_interp.decisionToDFA) {
List<String> s = new ArrayList<String>();
@ -640,13 +759,13 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
/** For debugging and other purposes */
/** For debugging and other purposes. */
public void dumpDFA() {
synchronized (_interp.decisionToDFA) {
boolean seenOne = false;
for (int d = 0; d < _interp.decisionToDFA.length; d++) {
DFA dfa = _interp.decisionToDFA[d];
if ( dfa!=null ) {
if ( !dfa.states.isEmpty() ) {
if ( seenOne ) System.out.println();
System.out.println("Decision " + dfa.decision + ":");
System.out.print(dfa.toString(getTokenNames()));
@ -660,18 +779,6 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return _input.getSourceName();
}
/** A convenience method for use most often with template rewrites.
* Convert a List<Token> to List<String>
*/
public List<String> toStrings(List<? extends Token> tokens) {
if ( tokens==null ) return null;
List<String> strings = new ArrayList<String>(tokens.size());
for (int i=0; i<tokens.size(); i++) {
strings.add(tokens.get(i).getText());
}
return strings;
}
/** During a parse is sometimes useful to listen in on the rule entry and exit
* events as well as token matches. This is for quick and dirty debugging.
*/

View File

@ -96,9 +96,6 @@ public class ParserRuleContext extends RuleContext {
public Token start, stop;
/** Set during parsing to identify which alt of rule parser is in. */
public int altNum;
/**
* The exception which forced this rule to return. If the rule successfully
* completed, this is {@code null}.
@ -288,8 +285,7 @@ public class ParserRuleContext extends RuleContext {
List<String> rules = recognizer.getRuleInvocationStack(this);
Collections.reverse(rules);
return "ParserRuleContext"+rules+"{" +
"altNum=" + altNum +
", start=" + start +
"start=" + start +
", stop=" + stop +
'}';
}

View File

@ -36,12 +36,20 @@ import java.util.BitSet;
import java.util.Collection;
/**
* This implementation of {@link ANTLRErrorListener} dispatches all calls to a
* collection of delegate listeners. This reduces the effort required to support multiple
* listeners.
*
* @author Sam Harwell
*/
public class ProxyErrorListener implements ANTLRErrorListener {
private final Collection<? extends ANTLRErrorListener> delegates;
public ProxyErrorListener(Collection<? extends ANTLRErrorListener> delegates) {
if (delegates == null) {
throw new NullPointerException("delegates");
}
this.delegates = delegates;
}
@ -63,11 +71,12 @@ public class ProxyErrorListener implements ANTLRErrorListener {
DFA dfa,
int startIndex,
int stopIndex,
boolean exact,
BitSet ambigAlts,
ATNConfigSet configs)
{
for (ANTLRErrorListener listener : delegates) {
listener.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, ambigAlts, configs);
listener.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs);
}
}
@ -76,10 +85,11 @@ public class ProxyErrorListener implements ANTLRErrorListener {
DFA dfa,
int startIndex,
int stopIndex,
BitSet conflictingAlts,
ATNConfigSet configs)
{
for (ANTLRErrorListener listener : delegates) {
listener.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, configs);
listener.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs);
}
}
@ -88,10 +98,11 @@ public class ProxyErrorListener implements ANTLRErrorListener {
DFA dfa,
int startIndex,
int stopIndex,
int prediction,
ATNConfigSet configs)
{
for (ANTLRErrorListener listener : delegates) {
listener.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, configs);
listener.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs);
}
}
}

View File

@ -29,6 +29,7 @@
*/
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.Nullable;
@ -39,26 +40,27 @@ import org.antlr.v4.runtime.misc.Nullable;
* and what kind of problem occurred.
*/
public class RecognitionException extends RuntimeException {
/** Who threw the exception? */
private Recognizer<?, ?> recognizer;
/** The {@link Recognizer} where this exception originated. */
@Nullable
private final Recognizer<?, ?> recognizer;
// TODO: make a dummy recognizer for the interpreter to use?
// Next two (ctx,input) should be what is in recognizer, but
// won't work when interpreting
@Nullable
private final RuleContext ctx;
private RuleContext ctx;
@Nullable
private final IntStream input;
private IntStream input;
/** The current Token when an error occurred. Since not all streams
* can retrieve the ith Token, we have to track the Token object.
* For parsers. Even when it's a tree parser, token might be set.
/**
* The current {@link Token} when an error occurred. Since not all streams
* support accessing symbols by index, we have to track the {@link Token}
* instance itself.
*/
private Token offendingToken;
private int offendingState;
private int offendingState = -1;
public RecognitionException(@Nullable Recognizer<?, ?> recognizer, IntStream input,
public RecognitionException(@Nullable Recognizer<?, ?> recognizer,
@Nullable IntStream input,
@Nullable ParserRuleContext ctx)
{
this.recognizer = recognizer;
@ -67,7 +69,9 @@ public class RecognitionException extends RuntimeException {
if ( recognizer!=null ) this.offendingState = recognizer.getState();
}
public RecognitionException(String message, @Nullable Recognizer<?, ?> recognizer, IntStream input,
public RecognitionException(String message,
@Nullable Recognizer<?, ?> recognizer,
@Nullable IntStream input,
@Nullable ParserRuleContext ctx)
{
super(message);
@ -77,11 +81,14 @@ public class RecognitionException extends RuntimeException {
if ( recognizer!=null ) this.offendingState = recognizer.getState();
}
/** Where was the parser in the ATN when the error occurred?
* For No viable alternative exceptions, this is the decision state number.
* For others, it is the state whose emanating edge we couldn't match.
* This will help us tie into the grammar and syntax diagrams in
* ANTLRWorks v2.
/**
* Get the ATN state number the parser was in at the time the error
* occurred. For {@link NoViableAltException} and
* {@link LexerNoViableAltException} exceptions, this is the
* {@link DecisionState} number. For others, it is the state whose outgoing
* edge we couldn't match.
* <p/>
* If the state number is not known, this method returns -1.
*/
public int getOffendingState() {
return offendingState;
@ -91,30 +98,71 @@ public class RecognitionException extends RuntimeException {
this.offendingState = offendingState;
}
/**
* Gets the set of input symbols which could potentially follow the
* previously matched symbol at the time this exception was thrown.
* <p/>
* If the set of expected tokens is not known and could not be computed,
* this method returns {@code null}.
*
* @return The set of token types that could potentially follow the current
* state in the ATN, or {@code null} if the information is not available.
*/
@Nullable
public IntervalSet getExpectedTokens() {
// TODO: do we really need this type check?
if ( recognizer!=null && recognizer instanceof Parser) {
return ((Parser) recognizer).getExpectedTokens();
if (recognizer != null) {
return recognizer.getATN().getExpectedTokens(offendingState, ctx);
}
return null;
}
/**
* Gets the {@link RuleContext} at the time this exception was thrown.
* <p/>
* If the context is not available, this method returns {@code null}.
*
* @return The {@link RuleContext} at the time this exception was thrown.
* If the context is not available, this method returns {@code null}.
*/
@Nullable
public RuleContext getCtx() {
return ctx;
}
/**
* Gets the input stream which is the symbol source for the recognizer where
* this exception was thrown.
* <p/>
* If the input stream is not available, this method returns {@code null}.
*
* @return The input stream which is the symbol source for the recognizer
* where this exception was thrown, or {@code null} if the stream is not
* available.
*/
@Nullable
public IntStream getInputStream() {
return input;
}
@Nullable
public Token getOffendingToken() {
return offendingToken;
}
protected final void setOffendingToken(Token offendingToken) {
protected final void setOffendingToken(@Nullable Token offendingToken) {
this.offendingToken = offendingToken;
}
/**
* Gets the {@link Recognizer} where this exception occurred.
* <p/>
* If the recognizer is not available, this method returns {@code null}.
*
* @return The recognizer where this exception occurred, or {@code null} if
* the recognizer is not available.
*/
@Nullable
public Recognizer<?, ?> getRecognizer() {
return recognizer;
}

View File

@ -100,9 +100,9 @@ public abstract class Recognizer<Symbol, ATNInterpreter extends ATNSimulator> {
s = "<"+t.getType()+">";
}
}
s = s.replaceAll("\n","\\\\n");
s = s.replaceAll("\r","\\\\r");
s = s.replaceAll("\t","\\\\t");
s = s.replace("\n","\\n");
s = s.replace("\r","\\r");
s = s.replace("\t","\\t");
return "'"+s+"'";
}
@ -168,5 +168,8 @@ public abstract class Recognizer<Symbol, ATNInterpreter extends ATNSimulator> {
public abstract void setInputStream(IntStream input);
public abstract void setTokenFactory(TokenFactory<?> input);
@NotNull
public abstract TokenFactory<?> getTokenFactory();
public abstract void setTokenFactory(@NotNull TokenFactory<?> input);
}

View File

@ -38,9 +38,11 @@ import org.antlr.v4.runtime.tree.Trees;
import org.antlr.v4.runtime.tree.gui.TreeViewer;
import javax.print.PrintException;
import javax.swing.JDialog;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
/** A rule context is a record of a single rule invocation. It knows
* which context invoked it, if any. If there is no parent context, then
@ -152,32 +154,53 @@ public class RuleContext implements RuleNode {
public <T> T accept(ParseTreeVisitor<? extends T> visitor) { return visitor.visitChildren(this); }
/** Call this method to view a parse tree in a dialog box visually. */
public void inspect(Parser parser) {
TreeViewer viewer = new TreeViewer(parser, this);
viewer.open();
public Future<JDialog> inspect(@Nullable Parser parser) {
List<String> ruleNames = parser != null ? Arrays.asList(parser.getRuleNames()) : null;
return inspect(ruleNames);
}
public Future<JDialog> inspect(@Nullable List<String> ruleNames) {
TreeViewer viewer = new TreeViewer(ruleNames, this);
return viewer.open();
}
/** Save this tree in a postscript file */
public void save(Parser parser, String fileName)
public void save(@Nullable Parser parser, String fileName)
throws IOException, PrintException
{
// TreeViewer viewer = new TreeViewer(parser, this);
// viewer.save(fileName);
Trees.writePS(this, parser, fileName); // parrt routine
List<String> ruleNames = parser != null ? Arrays.asList(parser.getRuleNames()) : null;
save(ruleNames, fileName);
}
/** Save this tree in a postscript file using a particular font name and size */
public void save(Parser parser, String fileName,
public void save(@Nullable Parser parser, String fileName,
String fontName, int fontSize)
throws IOException
{
Trees.writePS(this, parser, fileName, fontName, fontSize);
List<String> ruleNames = parser != null ? Arrays.asList(parser.getRuleNames()) : null;
save(ruleNames, fileName, fontName, fontSize);
}
/** Save this tree in a postscript file */
public void save(@Nullable List<String> ruleNames, String fileName)
throws IOException, PrintException
{
Trees.writePS(this, ruleNames, fileName);
}
/** Save this tree in a postscript file using a particular font name and size */
public void save(@Nullable List<String> ruleNames, String fileName,
String fontName, int fontSize)
throws IOException
{
Trees.writePS(this, ruleNames, fileName, fontName, fontSize);
}
/** Print out a whole tree, not just a node, in LISP format
* (root child1 .. childN). Print just a node if this is a leaf.
* We have to know the recognizer so we can get rule names.
*/
@Override
public String toStringTree(@Nullable Parser recog) {
return Trees.toStringTree(this, recog);
}

View File

@ -30,6 +30,7 @@
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Pair;
/** The default mechanism for creating tokens. It's used by default in Lexer and
@ -41,10 +42,12 @@ public interface TokenFactory<Symbol extends Token> {
* error handling strategy. If text!=null, than the start and stop positions
* are wiped to -1 in the text override is set in the CommonToken.
*/
Symbol create(Pair<TokenSource, CharStream> source, int type, String text,
@NotNull
Symbol create(@NotNull Pair<TokenSource, CharStream> source, int type, String text,
int channel, int start, int stop,
int line, int charPositionInLine);
/** Generically useful */
@NotNull
Symbol create(int type, String text);
}

View File

@ -29,6 +29,8 @@
*/
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.misc.NotNull;
/** A source of tokens must provide a sequence of tokens via nextToken()
* and also must reveal it's source of characters; CommonToken's text is
* computed from a CharStream; it only store indices into the char stream.
@ -65,8 +67,9 @@ public interface TokenSource {
public String getSourceName();
/** Optional method that lets users set factory in lexer or other source */
public void setTokenFactory(TokenFactory<?> factory);
public void setTokenFactory(@NotNull TokenFactory<?> factory);
/** Gets the factory used for constructing tokens. */
@NotNull
public TokenFactory<?> getTokenFactory();
}

View File

@ -36,6 +36,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
/** Do not buffer up the entire char stream. It does keep a small buffer
* for efficiency and also buffers while a mark exists (set by the
@ -130,7 +131,7 @@ public class UnbufferedCharStream implements CharStream {
@Override
public void consume() {
if (LA(1) == CharStream.EOF) {
if (LA(1) == IntStream.EOF) {
throw new IllegalStateException("cannot consume EOF");
}
@ -168,7 +169,7 @@ public class UnbufferedCharStream implements CharStream {
*/
protected int fill(int n) {
for (int i=0; i<n; i++) {
if (this.n > 0 && data[this.n - 1] == CharStream.EOF) {
if (this.n > 0 && data[this.n - 1] == (char)IntStream.EOF) {
return i;
}
@ -194,9 +195,7 @@ public class UnbufferedCharStream implements CharStream {
protected void add(int c) {
if ( n>=data.length ) {
char[] newdata = new char[data.length*2]; // resize
System.arraycopy(data, 0, newdata, 0, data.length);
data = newdata;
data = Arrays.copyOf(data, data.length * 2);
}
data[n++] = (char)c;
}
@ -207,8 +206,8 @@ public class UnbufferedCharStream implements CharStream {
sync(i);
int index = p + i - 1;
if ( index < 0 ) throw new IndexOutOfBoundsException();
if ( index > n ) return IntStream.EOF;
int c = data[index];
if ( index >= n ) return IntStream.EOF;
char c = data[index];
if ( c==(char)IntStream.EOF ) return IntStream.EOF;
return c;
}
@ -316,7 +315,7 @@ public class UnbufferedCharStream implements CharStream {
if (interval.a < bufferStartIndex || interval.b >= bufferStartIndex + n) {
throw new UnsupportedOperationException("interval "+interval+" outside buffer: "+
bufferStartIndex+".."+(bufferStartIndex+n));
bufferStartIndex+".."+(bufferStartIndex+n-1));
}
// convert from absolute to local index
int i = interval.a - bufferStartIndex;

View File

@ -33,6 +33,8 @@ package org.antlr.v4.runtime;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.misc.NotNull;
import java.util.Arrays;
public class UnbufferedTokenStream<T extends Token> implements TokenStream {
protected TokenSource tokenSource;
@ -208,9 +210,7 @@ public class UnbufferedTokenStream<T extends Token> implements TokenStream {
protected void add(@NotNull Token t) {
if ( n>=tokens.length ) {
Token[] newtokens = new Token[tokens.length*2]; // resize
System.arraycopy(tokens, 0, newtokens, 0, tokens.length);
tokens = newtokens;
tokens = Arrays.copyOf(tokens, tokens.length * 2);
}
if (t instanceof WritableToken) {

View File

@ -30,7 +30,9 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
@ -44,10 +46,6 @@ import java.util.Map;
public class ATN {
public static final int INVALID_ALT_NUMBER = 0;
public static final int PARSER = 1;
public static final int LEXER = 2;
public static final int TREE_PARSER = 3;
@NotNull
public final List<ATNState> states = new ArrayList<ATNState>();
@ -58,45 +56,73 @@ public class ATN {
@NotNull
public final List<DecisionState> decisionToState = new ArrayList<DecisionState>();
/**
* Maps from rule index to starting state number.
*/
public RuleStartState[] ruleToStartState;
/**
* Maps from rule index to stop state number.
*/
public RuleStopState[] ruleToStopState;
@NotNull
public final Map<String, TokensStartState> modeNameToStartState =
new LinkedHashMap<String, TokensStartState>();
// runtime for parsers, lexers
public int grammarType; // ATN.LEXER, ...
public int maxTokenType;
/**
* The type of the ATN.
*/
public final ATNType grammarType;
// runtime for lexer only
/**
* The maximum value for any symbol recognized by a transition in the ATN.
*/
public final int maxTokenType;
/**
* For lexer ATNs, this maps the rule index to the resulting token type.
* <p/>
* This is {@code null} for parser ATNs.
*/
public int[] ruleToTokenType;
/**
* For lexer ATNs, this maps the rule index to the action which should be
* executed following a match.
* <p/>
* This is {@code null} for parser ATNs.
*/
public int[] ruleToActionIndex;
@NotNull
public final List<TokensStartState> modeToStartState = new ArrayList<TokensStartState>();
/** used during construction from grammar AST */
int stateNumber = 0;
/** Used for runtime deserialization of ATNs from strings */
public ATN() { }
public ATN(@NotNull ATNType grammarType, int maxTokenType) {
this.grammarType = grammarType;
this.maxTokenType = maxTokenType;
}
/** Compute the set of valid tokens that can occur starting in s.
* If ctx is null, the set of tokens will not include what can follow
* the rule surrounding s. In other words, the set will be
* restricted to tokens reachable staying within s's rule.
/** Compute the set of valid tokens that can occur starting in state {@code s}.
* If {@code ctx} is null, the set of tokens will not include what can follow
* the rule surrounding {@code s}. In other words, the set will be
* restricted to tokens reachable staying within {@code s}'s rule.
*/
@NotNull
public IntervalSet nextTokens(ATNState s, RuleContext ctx) {
LL1Analyzer anal = new LL1Analyzer(this);
IntervalSet next = anal.LOOK(s, ctx);
return next;
}
/** Compute the set of valid tokens that can occur starting in s and staying in same rule.
* EPSILON is in set if we reach end of rule.
/**
* Compute the set of valid tokens that can occur starting in {@code s} and
* staying in same rule. {@link Token#EPSILON} is in set if we reach end of
* rule.
*/
public IntervalSet nextTokens(ATNState s) {
@NotNull
public IntervalSet nextTokens(@NotNull ATNState s) {
if ( s.nextTokenWithinRule != null ) return s.nextTokenWithinRule;
s.nextTokenWithinRule = nextTokens(s, null);
s.nextTokenWithinRule.setReadonly(true);
@ -104,10 +130,12 @@ public class ATN {
}
public void addState(@Nullable ATNState state) {
if ( state==null ) { states.add(null); stateNumber++; return; }
state.atn = this;
if (state != null) {
state.atn = this;
state.stateNumber = states.size();
}
states.add(state);
state.stateNumber = stateNumber++;
}
public void removeState(@NotNull ATNState state) {
@ -130,4 +158,55 @@ public class ATN {
public int getNumberOfDecisions() {
return decisionToState.size();
}
/**
* Computes the set of input symbols which could follow ATN state number
* {@code stateNumber} in the specified full {@code context}. This method
* considers the complete parser context, but does not evaluate semantic
* predicates (i.e. all predicates encountered during the calculation are
* assumed true). If a path in the ATN exists from the starting state to the
* {@link RuleStopState} of the outermost context without matching any
* symbols, {@link Token#EOF} is added to the returned set.
* <p/>
* If {@code context} is {@code null}, it is treated as
* {@link ParserRuleContext#EMPTY}.
*
* @param stateNumber the ATN state number
* @param context the full parse context
* @return The set of potentially valid input symbols which could follow the
* specified state in the specified context.
* @throws IllegalArgumentException if the ATN does not contain a state with
* number {@code stateNumber}
*/
@NotNull
public IntervalSet getExpectedTokens(int stateNumber, @Nullable RuleContext context) {
if (stateNumber < 0 || stateNumber >= states.size()) {
throw new IllegalArgumentException("Invalid state number.");
}
RuleContext ctx = context;
ATNState s = states.get(stateNumber);
IntervalSet following = nextTokens(s);
if (!following.contains(Token.EPSILON)) {
return following;
}
IntervalSet expected = new IntervalSet();
expected.addAll(following);
expected.remove(Token.EPSILON);
while (ctx != null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) {
ATNState invokingState = states.get(ctx.invokingState);
RuleTransition rt = (RuleTransition)invokingState.transition(0);
following = nextTokens(rt.followState);
expected.addAll(following);
expected.remove(Token.EPSILON);
ctx = ctx.parent;
}
if (following.contains(Token.EPSILON)) {
expected.add(Token.EOF);
}
return expected;
}
}

View File

@ -31,6 +31,7 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
@ -159,13 +160,14 @@ public class ATNConfig {
@Override
public int hashCode() {
int hashCode = 7;
hashCode = 5 * hashCode + state.stateNumber;
hashCode = 5 * hashCode + alt;
hashCode = 5 * hashCode + (context != null ? context.hashCode() : 0);
hashCode = 5 * hashCode + semanticContext.hashCode();
return hashCode;
}
int hashCode = MurmurHash.initialize(7);
hashCode = MurmurHash.update(hashCode, state.stateNumber);
hashCode = MurmurHash.update(hashCode, alt);
hashCode = MurmurHash.update(hashCode, context);
hashCode = MurmurHash.update(hashCode, semanticContext);
hashCode = MurmurHash.finish(hashCode, 4);
return hashCode;
}
@Override
public String toString() {

View File

@ -33,6 +33,8 @@ package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.Array2DHashSet;
import org.antlr.v4.runtime.misc.DoubleKeyMap;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.ArrayList;
import java.util.BitSet;
@ -42,205 +44,22 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
/** Specialized OrderedHashSet that can track info about the set.
* Might be able to optimize later w/o affecting code that uses this set.
histogram of lexer DFA configset size:
206 30 <- 206 sets with size 30
47 1
17 31
12 2
10 3
7 32
4 4
3 35
2 9
2 6
2 5
2 34
1 7
1 33
1 29
1 12
1 119 <- max size
322 set size for SLL parser java.* in DFA states:
888 1
411 54
365 88
304 56
206 80
182 16
167 86
166 78
158 84
131 2
121 20
120 8
119 112
82 10
73 6
53 174
47 90
45 4
39 12
38 122
37 89
37 62
34 3
34 18
32 81
31 87
28 45
27 144
25 41
24 132
22 91
22 7
21 82
21 28
21 27
17 9
16 29
16 155
15 51
15 118
14 146
14 114
13 5
13 38
12 48
11 64
11 50
11 22
11 134
11 131
10 79
10 76
10 59
10 58
10 55
10 39
10 116
9 74
9 47
9 310
...
javalr, java.* configs with # preds histogram:
4569 0
57 1
27 27
5 76
4 28
3 72
3 38
3 30
2 6
2 32
1 9
1 2
javalr, java.* all atnconfigsets; max size = 322, num sets = 269088
114186 1 <-- optimize
35712 6
28081 78
15252 54
14171 56
13159 12
11810 88
6873 86
6158 80
5169 4
3773 118
2350 16
1002 112
915 28
898 44
734 2
632 62
575 8
566 59
474 20
388 84
343 48
333 55
328 47
311 41
306 38
277 81
263 79
255 66
245 90
245 87
234 50
224 10
220 60
194 64
186 32
184 82
150 18
125 7
121 132
116 30
103 51
95 114
84 36
82 40
78 22
77 89
55 9
53 174
48 152
44 67
44 5
42 115
41 58
38 122
37 134
34 13
34 116
29 45
29 3
29 24
27 144
26 146
25 91
24 113
20 27
...
number with 1-9 elements:
114186 1
35712 6
5169 4
734 2
575 8
125 7
55 9
44 5
29 3
Can cover 60% of sizes with size up to 6
Can cover 44% of sizes with size up to 4
Can cover 42% of sizes with size up to 1
/**
* Specialized {@link Set}{@code <}{@link ATNConfig}{@code >} that can track
* info about the set, with support for combining similar configurations using a
* graph-structured stack.
*/
public class ATNConfigSet implements Set<ATNConfig> {
/*
The reason that we need this is because we don't want the hash map to use
the standard hash code and equals. We need all configurations with the same
(s,i,_,semctx) to be equal. Unfortunately, this key effectively doubles
the number of objects associated with ATNConfigs. The other solution is to
use a hash table that lets us specify the equals/hashcode operation.
/**
* The reason that we need this is because we don't want the hash map to use
* the standard hash code and equals. We need all configurations with the same
* {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively doubles
* the number of objects associated with ATNConfigs. The other solution is to
* use a hash table that lets us specify the equals/hashcode operation.
*/
public static class ConfigHashSet extends Array2DHashSet<ATNConfig> {
public static class ConfigHashSet extends AbstractConfigHashSet {
public ConfigHashSet() {
super(ConfigEqualityComparator.INSTANCE,16,2);
super(ConfigEqualityComparator.INSTANCE);
}
}
@ -263,7 +82,6 @@ public class ATNConfigSet implements Set<ATNConfig> {
public boolean equals(ATNConfig a, ATNConfig b) {
if ( a==b ) return true;
if ( a==null || b==null ) return false;
if ( hashCode(a) != hashCode(b) ) return false;
return a.state.stateNumber==b.state.stateNumber
&& a.alt==b.alt
&& a.semanticContext.equals(b.semanticContext);
@ -278,10 +96,11 @@ public class ATNConfigSet implements Set<ATNConfig> {
*/
protected boolean readonly = false;
/** All configs but hashed by (s, i, _, pi) not incl context. Wiped out
* when we go readonly as this set becomes a DFA state.
/**
* All configs but hashed by (s, i, _, pi) not including context. Wiped out
* when we go readonly as this set becomes a DFA state.
*/
public Array2DHashSet<ATNConfig> configLookup;
public AbstractConfigHashSet configLookup;
/** Track the elements as they are added to the set; supports get(i) */
public final ArrayList<ATNConfig> configs = new ArrayList<ATNConfig>(7);
@ -302,6 +121,8 @@ public class ATNConfigSet implements Set<ATNConfig> {
*/
public final boolean fullCtx;
private int cachedHashCode = -1;
public ATNConfigSet(boolean fullCtx) {
configLookup = new ConfigHashSet();
this.fullCtx = fullCtx;
@ -318,24 +139,34 @@ public class ATNConfigSet implements Set<ATNConfig> {
}
@Override
public boolean add(ATNConfig config) {
public boolean add(@NotNull ATNConfig config) {
return add(config, null);
}
/** Adding a new config means merging contexts with existing configs for
* (s, i, pi, _)
* We use (s,i,pi) as key
/**
* Adding a new config means merging contexts with existing configs for
* {@code (s, i, pi, _)}, where {@code s} is the
* {@link ATNConfig#state}, {@code i} is the {@link ATNConfig#alt}, and
* {@code pi} is the {@link ATNConfig#semanticContext}. We use
* {@code (s,i,pi)} as key.
* <p/>
* This method updates {@link #dipsIntoOuterContext} and
* {@link #hasSemanticContext} when necessary.
*/
public boolean add(
ATNConfig config,
DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache)
@NotNull ATNConfig config,
@Nullable DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache)
{
if ( readonly ) throw new IllegalStateException("This set is readonly");
if ( config.semanticContext!=SemanticContext.NONE ) {
hasSemanticContext = true;
}
ATNConfig existing = configLookup.absorb(config);
if (config.reachesIntoOuterContext > 0) {
dipsIntoOuterContext = true;
}
ATNConfig existing = configLookup.getOrAdd(config);
if ( existing==config ) { // we added this new one
cachedHashCode = -1;
configs.add(config); // track order here
return true;
}
@ -375,14 +206,6 @@ public class ATNConfigSet implements Set<ATNConfig> {
public ATNConfig get(int i) { return configs.get(i); }
// TODO: very expensive, used in lexer to kill after wildcard config
public void remove(int i) {
if ( readonly ) throw new IllegalStateException("This set is readonly");
ATNConfig c = elements().get(i);
configLookup.remove(c);
configs.remove(c); // slow linear search. ugh but not worse than it was
}
public void optimizeConfigs(ATNSimulator interpreter) {
if ( readonly ) throw new IllegalStateException("This set is readonly");
if ( configLookup.isEmpty() ) return;
@ -395,6 +218,7 @@ public class ATNConfigSet implements Set<ATNConfig> {
}
}
@Override
public boolean addAll(Collection<? extends ATNConfig> coll) {
for (ATNConfig c : coll) add(c);
return false;
@ -402,6 +226,13 @@ public class ATNConfigSet implements Set<ATNConfig> {
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
else if (!(o instanceof ATNConfigSet)) {
return false;
}
// System.out.print("equals " + this + ", " + o+" = ");
ATNConfigSet other = (ATNConfigSet)o;
boolean same = configs!=null &&
@ -418,6 +249,14 @@ public class ATNConfigSet implements Set<ATNConfig> {
@Override
public int hashCode() {
if (isReadonly()) {
if (cachedHashCode == -1) {
cachedHashCode = configs.hashCode();
}
return cachedHashCode;
}
return configs.hashCode();
}
@ -433,10 +272,19 @@ public class ATNConfigSet implements Set<ATNConfig> {
@Override
public boolean contains(Object o) {
if ( o instanceof ATNConfig ) {
return configLookup.contains(o);
if (configLookup == null) {
throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
}
return false;
return configLookup.contains(o);
}
public boolean containsFast(ATNConfig obj) {
if (configLookup == null) {
throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
}
return configLookup.containsFast(obj);
}
@Override
@ -448,9 +296,14 @@ public class ATNConfigSet implements Set<ATNConfig> {
public void clear() {
if ( readonly ) throw new IllegalStateException("This set is readonly");
configs.clear();
cachedHashCode = -1;
configLookup.clear();
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
configLookup = null; // can't mod, no need for lookup cache
@ -470,7 +323,7 @@ public class ATNConfigSet implements Set<ATNConfig> {
// satisfy interface
@Override
public Object[] toArray() {
public ATNConfig[] toArray() {
return configLookup.toArray();
}
@ -498,4 +351,35 @@ public class ATNConfigSet implements Set<ATNConfig> {
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public static abstract class AbstractConfigHashSet extends Array2DHashSet<ATNConfig> {
public AbstractConfigHashSet(AbstractEqualityComparator<? super ATNConfig> comparator) {
this(comparator, 16, 2);
}
public AbstractConfigHashSet(AbstractEqualityComparator<? super ATNConfig> comparator, int initialCapacity, int initialBucketCapacity) {
super(comparator, initialCapacity, initialBucketCapacity);
}
@Override
protected final ATNConfig asElementType(Object o) {
if (!(o instanceof ATNConfig)) {
return null;
}
return (ATNConfig)o;
}
@Override
protected final ATNConfig[][] createBuckets(int capacity) {
return new ATNConfig[capacity][];
}
@Override
protected final ATNConfig[] createBucket(int capacity) {
return new ATNConfig[capacity];
}
}
}

View File

@ -30,6 +30,7 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.dfa.DFAState;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;
@ -39,13 +40,26 @@ import java.io.InvalidClassException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
public abstract class ATNSimulator {
public static final int SERIALIZED_VERSION;
static {
/* This value should never change. Updates following this version are
* reflected as change in the unique ID SERIALIZED_UUID.
*/
SERIALIZED_VERSION = 3;
}
public static final UUID SERIALIZED_UUID;
static {
/* WARNING: DO NOT MERGE THIS LINE. If UUIDs differ during a merge,
* resolve the conflict by generating a new ID!
*/
SERIALIZED_UUID = UUID.fromString("33761B2D-78BB-4A43-8B0B-4F5BEE8AACF3");
}
/** Must distinguish between missing edge and edge we know leads nowhere */
@NotNull
public static final DFAState ERROR;
@ -58,7 +72,7 @@ public abstract class ATNSimulator {
* to use only cached nodes/graphs in addDFAState(). We don't want to
* fill this during closure() since there are lots of contexts that
* pop up but are not used ever again. It also greatly slows down closure().
*
* <p/>
* This cache makes a huge difference in memory and a little bit in speed.
* For the Java grammar on java.*, it dropped the memory requirements
* at the end from 25M to 16M. We don't store any of the full context
@ -66,7 +80,7 @@ public abstract class ATNSimulator {
* but apparently there's a lot of repetition there as well. We optimize
* the config contexts before storing the config set in the DFA states
* by literally rebuilding them with cached subgraphs only.
*
* <p/>
* I tried a cache for use during closure operations, that was
* whacked after each adaptivePredict(). It cost a little bit
* more time I think and doesn't save on the overall footprint
@ -88,28 +102,46 @@ public abstract class ATNSimulator {
public abstract void reset();
public PredictionContextCache getSharedContextCache() {
return sharedContextCache;
}
public PredictionContext getCachedContext(PredictionContext context) {
if ( sharedContextCache==null ) return context;
IdentityHashMap<PredictionContext, PredictionContext> visited =
new IdentityHashMap<PredictionContext, PredictionContext>();
return PredictionContext.getCachedContext(context,
sharedContextCache,
visited);
synchronized (sharedContextCache) {
IdentityHashMap<PredictionContext, PredictionContext> visited =
new IdentityHashMap<PredictionContext, PredictionContext>();
return PredictionContext.getCachedContext(context,
sharedContextCache,
visited);
}
}
public static ATN deserialize(@NotNull char[] data) {
ATN atn = new ATN();
List<IntervalSet> sets = new ArrayList<IntervalSet>();
data = data.clone();
// don't adjust the first value since that's the version number
for (int i = 1; i < data.length; i++) {
data[i] = (char)(data[i] - 2);
}
int p = 0;
int version = toInt(data[p++]);
if (version != SERIALIZED_VERSION) {
String reason = String.format("Could not deserialize ATN with version %d (expected %d).", version, SERIALIZED_VERSION);
String reason = String.format(Locale.getDefault(), "Could not deserialize ATN with version %d (expected %d).", version, SERIALIZED_VERSION);
throw new UnsupportedOperationException(new InvalidClassException(ATN.class.getName(), reason));
}
atn.grammarType = toInt(data[p++]);
atn.maxTokenType = toInt(data[p++]);
UUID uuid = toUUID(data, p);
p += 8;
if (!uuid.equals(SERIALIZED_UUID)) {
String reason = String.format(Locale.getDefault(), "Could not deserialize ATN with UUID %s (expected %s).", uuid, SERIALIZED_UUID);
throw new UnsupportedOperationException(new InvalidClassException(ATN.class.getName(), reason));
}
ATNType grammarType = ATNType.values()[toInt(data[p++])];
int maxTokenType = toInt(data[p++]);
ATN atn = new ATN(grammarType, maxTokenType);
//
// STATES
@ -117,7 +149,7 @@ public abstract class ATNSimulator {
List<Pair<LoopEndState, Integer>> loopBackStateNumbers = new ArrayList<Pair<LoopEndState, Integer>>();
List<Pair<BlockStartState, Integer>> endStateNumbers = new ArrayList<Pair<BlockStartState, Integer>>();
int nstates = toInt(data[p++]);
for (int i=1; i<=nstates; i++) {
for (int i=0; i<nstates; i++) {
int stype = toInt(data[p++]);
// ignore bad type of states
if ( stype==ATNState.INVALID_TYPE ) {
@ -125,8 +157,12 @@ public abstract class ATNSimulator {
continue;
}
ATNState s = stateFactory(stype, i);
s.ruleIndex = toInt(data[p++]);
int ruleIndex = toInt(data[p++]);
if (ruleIndex == Character.MAX_VALUE) {
ruleIndex = -1;
}
ATNState s = stateFactory(stype, ruleIndex);
if ( stype == ATNState.LOOP_END ) { // special case
int loopBackStateNumber = toInt(data[p++]);
loopBackStateNumbers.add(new Pair<LoopEndState, Integer>((LoopEndState)s, loopBackStateNumber));
@ -163,7 +199,7 @@ public abstract class ATNSimulator {
// RULES
//
int nrules = toInt(data[p++]);
if ( atn.grammarType == ATN.LEXER ) {
if ( atn.grammarType == ATNType.LEXER ) {
atn.ruleToTokenType = new int[nrules];
atn.ruleToActionIndex = new int[nrules];
}
@ -172,10 +208,18 @@ public abstract class ATNSimulator {
int s = toInt(data[p++]);
RuleStartState startState = (RuleStartState)atn.states.get(s);
atn.ruleToStartState[i] = startState;
if ( atn.grammarType == ATN.LEXER ) {
if ( atn.grammarType == ATNType.LEXER ) {
int tokenType = toInt(data[p++]);
if (tokenType == 0xFFFF) {
tokenType = Token.EOF;
}
atn.ruleToTokenType[i] = tokenType;
int actionIndex = toInt(data[p++]);
if (actionIndex == 0xFFFF) {
actionIndex = -1;
}
atn.ruleToActionIndex[i] = actionIndex;
}
}
@ -203,13 +247,20 @@ public abstract class ATNSimulator {
//
// SETS
//
List<IntervalSet> sets = new ArrayList<IntervalSet>();
int nsets = toInt(data[p++]);
for (int i=1; i<=nsets; i++) {
for (int i=0; i<nsets; i++) {
int nintervals = toInt(data[p]);
p++;
IntervalSet set = new IntervalSet();
sets.add(set);
for (int j=1; j<=nintervals; j++) {
boolean containsEof = toInt(data[p++]) != 0;
if (containsEof) {
set.add(-1);
}
for (int j=0; j<nintervals; j++) {
set.add(toInt(data[p]), toInt(data[p + 1]));
p += 2;
}
@ -219,7 +270,7 @@ public abstract class ATNSimulator {
// EDGES
//
int nedges = toInt(data[p++]);
for (int i=1; i<=nedges; i++) {
for (int i=0; i<nedges; i++) {
int src = toInt(data[p]);
int trg = toInt(data[p+1]);
int ttype = toInt(data[p+2]);
@ -306,53 +357,88 @@ public abstract class ATNSimulator {
continue;
}
checkCondition(state.onlyHasEpsilonTransitions() || state.getNumberOfTransitions() <= 1);
if (state instanceof PlusBlockStartState) {
if (((PlusBlockStartState)state).loopBackState == null) {
throw new IllegalStateException();
}
checkCondition(((PlusBlockStartState)state).loopBackState != null);
}
if (state instanceof StarLoopEntryState) {
if (((StarLoopEntryState)state).loopBackState == null) {
StarLoopEntryState starLoopEntryState = (StarLoopEntryState)state;
checkCondition(starLoopEntryState.loopBackState != null);
checkCondition(starLoopEntryState.getNumberOfTransitions() == 2);
if (starLoopEntryState.transition(0).target instanceof StarBlockStartState) {
checkCondition(starLoopEntryState.transition(1).target instanceof LoopEndState);
checkCondition(!starLoopEntryState.nonGreedy);
}
else if (starLoopEntryState.transition(0).target instanceof LoopEndState) {
checkCondition(starLoopEntryState.transition(1).target instanceof StarBlockStartState);
checkCondition(starLoopEntryState.nonGreedy);
}
else {
throw new IllegalStateException();
}
}
if (state instanceof StarLoopbackState) {
checkCondition(state.getNumberOfTransitions() == 1);
checkCondition(state.transition(0).target instanceof StarLoopEntryState);
}
if (state instanceof LoopEndState) {
if (((LoopEndState)state).loopBackState == null) {
throw new IllegalStateException();
}
checkCondition(((LoopEndState)state).loopBackState != null);
}
if (state instanceof RuleStartState) {
if (((RuleStartState)state).stopState == null) {
throw new IllegalStateException();
}
checkCondition(((RuleStartState)state).stopState != null);
}
if (state instanceof BlockStartState) {
if (((BlockStartState)state).endState == null) {
throw new IllegalStateException();
}
checkCondition(((BlockStartState)state).endState != null);
}
if (state instanceof BlockEndState) {
if (((BlockEndState)state).startState == null) {
throw new IllegalStateException();
}
checkCondition(((BlockEndState)state).startState != null);
}
if (state instanceof DecisionState) {
DecisionState decisionState = (DecisionState)state;
if (decisionState.getNumberOfTransitions() > 1 && decisionState.decision < 0) {
throw new IllegalStateException();
}
checkCondition(decisionState.getNumberOfTransitions() <= 1 || decisionState.decision >= 0);
}
else {
checkCondition(state.getNumberOfTransitions() <= 1 || state instanceof RuleStopState);
}
}
}
public static void checkCondition(boolean condition) {
checkCondition(condition, null);
}
public static void checkCondition(boolean condition, String message) {
if (!condition) {
throw new IllegalStateException(message);
}
}
public static int toInt(char c) {
return c==65535 ? -1 : c;
return c;
}
public static int toInt32(char[] data, int offset) {
return (int)data[offset] | ((int)data[offset + 1] << 16);
}
public static long toLong(char[] data, int offset) {
long lowOrder = toInt32(data, offset) & 0x00000000FFFFFFFFL;
return lowOrder | ((long)toInt32(data, offset + 2) << 32);
}
public static UUID toUUID(char[] data, int offset) {
long leastSigBits = toLong(data, offset);
long mostSigBits = toLong(data, offset + 4);
return new UUID(mostSigBits, leastSigBits);
}
@NotNull
@ -364,7 +450,13 @@ public abstract class ATNSimulator {
ATNState target = atn.states.get(trg);
switch (type) {
case Transition.EPSILON : return new EpsilonTransition(target);
case Transition.RANGE : return new RangeTransition(target, arg1, arg2);
case Transition.RANGE :
if (arg3 != 0) {
return new RangeTransition(target, Token.EOF, arg2);
}
else {
return new RangeTransition(target, arg1, arg2);
}
case Transition.RULE :
RuleTransition rt = new RuleTransition((RuleStartState)atn.states.get(arg1), arg2, arg3, target);
return rt;
@ -373,7 +465,13 @@ public abstract class ATNSimulator {
return pt;
case Transition.PRECEDENCE:
return new PrecedencePredicateTransition(target, arg1);
case Transition.ATOM : return new AtomTransition(target, arg1);
case Transition.ATOM :
if (arg3 != 0) {
return new AtomTransition(target, Token.EOF);
}
else {
return new AtomTransition(target, arg1);
}
case Transition.ACTION :
ActionTransition a = new ActionTransition(target, arg1, arg2, arg3 != 0);
return a;
@ -385,13 +483,13 @@ public abstract class ATNSimulator {
throw new IllegalArgumentException("The specified transition type is not valid.");
}
public static ATNState stateFactory(int type, int stateNumber) {
public static ATNState stateFactory(int type, int ruleIndex) {
ATNState s;
switch (type) {
case ATNState.INVALID_TYPE: return null;
case ATNState.BASIC : s = new ATNState(); break;
case ATNState.BASIC : s = new BasicState(); break;
case ATNState.RULE_START : s = new RuleStartState(); break;
case ATNState.BLOCK_START : s = new BlockStartState(); break;
case ATNState.BLOCK_START : s = new BasicBlockStartState(); break;
case ATNState.PLUS_BLOCK_START : s = new PlusBlockStartState(); break;
case ATNState.STAR_BLOCK_START : s = new StarBlockStartState(); break;
case ATNState.TOKEN_START : s = new TokensStartState(); break;
@ -402,11 +500,11 @@ public abstract class ATNSimulator {
case ATNState.PLUS_LOOP_BACK : s = new PlusLoopbackState(); break;
case ATNState.LOOP_END : s = new LoopEndState(); break;
default :
String message = String.format("The specified state type %d for state %d is not valid.", type, stateNumber);
String message = String.format(Locale.getDefault(), "The specified state type %d is not valid.", type);
throw new IllegalArgumentException(message);
}
s.stateNumber = stateNumber;
s.ruleIndex = ruleIndex;
return s;
}

View File

@ -35,11 +35,70 @@ import org.antlr.v4.runtime.misc.IntervalSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Locale;
public class ATNState {
/**
* The following images show the relation of states and
* {@link ATNState#transitions} for various grammar constructs.
*
* <ul>
*
* <li>Solid edges marked with an &#0949; indicate a required
* {@link EpsilonTransition}.</li>
*
* <li>Dashed edges indicate locations where any transition derived from
* {@link Transition} might appear.</li>
*
* <li>Dashed nodes are place holders for either a sequence of linked
* {@link BasicState} states or the inclusion of a block representing a nested
* construct in one of the forms below.</li>
*
* <li>Nodes showing multiple outgoing alternatives with a {@code ...} support
* any number of alternatives (one or more). Nodes without the {@code ...} only
* support the exact number of alternatives shown in the diagram.</li>
*
* </ul>
*
* <h2>Basic Blocks</h2>
*
* <h3>Rule</h3>
*
* <embed src="images/Rule.svg" type="image/svg+xml"/>
*
* <h3>Block of 1 or more alternatives</h3>
*
* <embed src="images/Block.svg" type="image/svg+xml"/>
*
* <h2>Greedy Loops</h2>
*
* <h3>Greedy Closure: {@code (...)*}</h3>
*
* <embed src="images/ClosureGreedy.svg" type="image/svg+xml"/>
*
* <h3>Greedy Positive Closure: {@code (...)+}</h3>
*
* <embed src="images/PositiveClosureGreedy.svg" type="image/svg+xml"/>
*
* <h3>Greedy Optional: {@code (...)?}</h3>
*
* <embed src="images/OptionalGreedy.svg" type="image/svg+xml"/>
*
* <h2>Non-Greedy Loops</h2>
*
* <h3>Non-Greedy Closure: {@code (...)*?}</h3>
*
* <embed src="images/ClosureNonGreedy.svg" type="image/svg+xml"/>
*
* <h3>Non-Greedy Positive Closure: {@code (...)+?}</h3>
*
* <embed src="images/PositiveClosureNonGreedy.svg" type="image/svg+xml"/>
*
* <h3>Non-Greedy Optional: {@code (...)??}</h3>
*
* <embed src="images/OptionalNonGreedy.svg" type="image/svg+xml"/>
*/
public abstract class ATNState {
public static final int INITIAL_NUM_TRANSITIONS = 4;
// constants for serialization
@ -74,22 +133,6 @@ public class ATNState {
"LOOP_END"
));
public static final Map<Class<? extends ATNState>, Integer> serializationTypes =
Collections.unmodifiableMap(new HashMap<Class<? extends ATNState>, Integer>() {{
put(ATNState.class, BASIC);
put(RuleStartState.class, RULE_START);
put(BlockStartState.class, BLOCK_START);
put(PlusBlockStartState.class, PLUS_BLOCK_START);
put(StarBlockStartState.class, STAR_BLOCK_START);
put(TokensStartState.class, TOKEN_START);
put(RuleStopState.class, RULE_STOP);
put(BlockEndState.class, BLOCK_END);
put(PlusLoopbackState.class, PLUS_LOOP_BACK);
put(StarLoopbackState.class, STAR_LOOP_BACK);
put(StarLoopEntryState.class, STAR_LOOP_ENTRY);
put(LoopEndState.class, LOOP_END);
}});
public static final int INVALID_STATE_NUMBER = -1;
/** Which ATN are we in? */
@ -136,15 +179,19 @@ public class ATNState {
}
public void addTransition(Transition e) {
addTransition(transitions.size(), e);
}
public void addTransition(int index, Transition e) {
if (transitions.isEmpty()) {
epsilonOnlyTransitions = e.isEpsilon();
}
else if (epsilonOnlyTransitions != e.isEpsilon()) {
System.err.format("ATN state %d has both epsilon and non-epsilon transitions.\n", stateNumber);
System.err.format(Locale.getDefault(), "ATN state %d has both epsilon and non-epsilon transitions.\n", stateNumber);
epsilonOnlyTransitions = false;
}
transitions.add(e);
transitions.add(index, e);
}
public Transition transition(int i) { return transitions.get(i); }
@ -157,9 +204,7 @@ public class ATNState {
return transitions.remove(index);
}
public int getStateType() {
return serializationTypes.get(this.getClass());
}
public abstract int getStateType();
public final boolean onlyHasEpsilonTransitions() {
return epsilonOnlyTransitions;

View File

@ -0,0 +1,50 @@
/*
* [The "BSD license"]
* Copyright (c) 2013 Terence Parr
* Copyright (c) 2013 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.runtime.atn;
/**
* Represents the type of recognizer an ATN applies to.
*
* @author Sam Harwell
*/
public enum ATNType {
/**
* A lexer grammar.
*/
LEXER,
/**
* A parser grammar.
*/
PARSER,
}

View File

@ -31,17 +31,16 @@
package org.antlr.v4.runtime.atn;
import java.util.Arrays;
import java.util.Iterator;
public class ArrayPredictionContext extends PredictionContext {
/** Parent can be null only if full ctx mode and we make an array
* from EMPTY and non-empty. We merge EMPTY by using null parent and
* returnState == EMPTY_FULL_RETURN_STATE
* from {@link #EMPTY} and non-empty. We merge {@link #EMPTY} by using null parent and
* returnState == {@link #EMPTY_RETURN_STATE}.
*/
public final PredictionContext[] parents;
/** Sorted for merge, no duplicates; if present,
* EMPTY_FULL_RETURN_STATE is always first
* {@link #EMPTY_RETURN_STATE} is always last.
*/
public final int[] returnStates;
@ -58,73 +57,11 @@ public class ArrayPredictionContext extends PredictionContext {
this.returnStates = returnStates;
}
//ArrayPredictionContext(@NotNull PredictionContext[] parents, int[] returnStates, int parentHashCode, int returnStateHashCode) {
// super(calculateHashCode(parentHashCode, returnStateHashCode));
// assert parents.length == returnStates.length;
// assert returnStates.length > 1 || returnStates[0] != EMPTY_FULL_STATE_KEY : "Should be using PredictionContext.EMPTY instead.";
//
// this.parents = parents;
// this.returnStates = returnStates;
// }
//
//ArrayPredictionContext(@NotNull PredictionContext[] parents, int[] returnStates, int hashCode) {
// super(hashCode);
// assert parents.length == returnStates.length;
// assert returnStates.length > 1 || returnStates[0] != EMPTY_FULL_STATE_KEY : "Should be using PredictionContext.EMPTY instead.";
//
// this.parents = parents;
// this.returnStates = returnStates;
// }
protected static int calculateHashCode(PredictionContext[] parents, int[] returnStates) {
return calculateHashCode(calculateParentHashCode(parents),
calculateReturnStatesHashCode(returnStates));
}
protected static int calculateParentHashCode(PredictionContext[] parents) {
int hashCode = 1;
for (PredictionContext p : parents) {
if ( p!=null ) { // can be null for full ctx stack in ArrayPredictionContext
hashCode = hashCode * 31 ^ p.hashCode();
}
}
return hashCode;
}
protected static int calculateReturnStatesHashCode(int[] returnStates) {
int hashCode = 1;
for (int state : returnStates) {
hashCode = hashCode * 31 ^ state;
}
return hashCode;
}
@Override
public Iterator<SingletonPredictionContext> iterator() {
return new Iterator<SingletonPredictionContext>() {
int i = 0;
@Override
public boolean hasNext() { return i < parents.length; }
@Override
public SingletonPredictionContext next() {
SingletonPredictionContext ctx =
SingletonPredictionContext.create(parents[i], returnStates[i]);
i++;
return ctx;
}
@Override
public void remove() { throw new UnsupportedOperationException(); }
};
}
@Override
public boolean isEmpty() {
return size()==1 &&
returnStates[0]==EMPTY_RETURN_STATE;
// since EMPTY_RETURN_STATE can only appear in the last position, we
// don't need to verify that size==1
return returnStates[0]==EMPTY_RETURN_STATE;
}
@Override

View File

@ -0,0 +1,44 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* 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.runtime.atn;
/**
*
* @author Sam Harwell
*/
public final class BasicBlockStartState extends BlockStartState {
@Override
public int getStateType() {
return BLOCK_START;
}
}

View File

@ -28,11 +28,17 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.tree.gui;
package org.antlr.v4.runtime.atn;
public class CourierNew extends BasicFontMetrics {
{
maxCharHeight = 678;
for (int i=0; i<MAX_CHAR; i++) widths[i] = 600;
/**
*
* @author Sam Harwell
*/
public final class BasicState extends ATNState {
@Override
public int getStateType() {
return BASIC;
}
}

View File

@ -30,7 +30,12 @@
package org.antlr.v4.runtime.atn;
/** Terminal node of a simple (a|b|c) block */
public class BlockEndState extends ATNState {
/** Terminal node of a simple {@code (a|b|c)} block. */
public final class BlockEndState extends ATNState {
public BlockStartState startState;
@Override
public int getStateType() {
return BLOCK_END;
}
}

View File

@ -30,7 +30,7 @@
package org.antlr.v4.runtime.atn;
/** The start of a regular (...) block */
public class BlockStartState extends DecisionState {
/** The start of a regular {@code (...)} block. */
public abstract class BlockStartState extends DecisionState {
public BlockEndState endState;
}

View File

@ -30,7 +30,7 @@
package org.antlr.v4.runtime.atn;
public class DecisionState extends ATNState {
public abstract class DecisionState extends ATNState {
public int decision = -1;
public boolean nonGreedy;
}

View File

@ -35,6 +35,7 @@ public class EmptyPredictionContext extends SingletonPredictionContext {
super(null, EMPTY_RETURN_STATE);
}
@Override
public boolean isEmpty() { return true; }
@Override

View File

@ -30,19 +30,19 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.IntStream;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Set;
public class LL1Analyzer {
/** Special value added to the lookahead sets to indicate that we hit
* a predicate during analysis if seeThruPreds==false.
* a predicate during analysis if {@code seeThruPreds==false}.
*/
public static final int HIT_PRED = Token.INVALID_TYPE;
@ -51,21 +51,30 @@ public class LL1Analyzer {
public LL1Analyzer(@NotNull ATN atn) { this.atn = atn; }
/** From an ATN state, s, find the set of all labels reachable from s at
* depth k. Only for DecisionStates.
/**
* Calculates the SLL(1) expected lookahead set for each outgoing transition
* of an {@link ATNState}. The returned array has one element for each
* outgoing transition in {@code s}. If the closure from transition
* <em>i</em> leads to a semantic predicate before matching a symbol, the
* element at index <em>i</em> of the result will be {@code null}.
*
* @param s the ATN state
* @return the expected symbols for each outgoing transition of {@code s}.
*/
@Nullable
public IntervalSet[] getDecisionLookahead(@Nullable ATNState s) {
// System.out.println("LOOK("+s.stateNumber+")");
if ( s==null ) return null;
IntervalSet[] look = new IntervalSet[s.getNumberOfTransitions()+1];
for (int alt=1; alt<=s.getNumberOfTransitions(); alt++) {
if ( s==null ) {
return null;
}
IntervalSet[] look = new IntervalSet[s.getNumberOfTransitions()];
for (int alt = 0; alt < s.getNumberOfTransitions(); alt++) {
look[alt] = new IntervalSet();
Set<ATNConfig> lookBusy = new HashSet<ATNConfig>();
boolean seeThruPreds = false; // fail to get lookahead upon pred
_LOOK(s.transition(alt - 1).target,
PredictionContext.EMPTY,
look[alt], lookBusy, seeThruPreds, false);
_LOOK(s.transition(alt).target, null, PredictionContext.EMPTY,
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].size()==0 || look[alt].contains(HIT_PRED) ) {
@ -76,39 +85,106 @@ public class LL1Analyzer {
}
/**
* Get lookahead, using {@code ctx} if we reach end of rule. If {@code ctx}
* is {@code null} or {@link RuleContext#EMPTY EMPTY}, don't chase FOLLOW.
* If {@code ctx} is {@code null}, {@link Token#EPSILON EPSILON} is in set
* if we can reach end of rule. If {@code ctx} is
* {@link RuleContext#EMPTY EMPTY}, {@link IntStream#EOF EOF} is in set if
* we can reach end of rule.
* Compute set of tokens that can follow {@code s} in the ATN in the
* specified {@code ctx}.
* <p/>
* If {@code ctx} is {@code null} and the end of the rule containing
* {@code s} is reached, {@link Token#EPSILON} is added to the result set.
* If {@code ctx} is not {@code null} and the end of the outermost rule is
* reached, {@link Token#EOF} is added to the result set.
*
* @param s the ATN state
* @param ctx the complete parser context, or {@code null} if the context
* should be ignored
*
* @return The set of tokens that can follow {@code s} in the ATN in the
* specified {@code ctx}.
*/
@NotNull
public IntervalSet LOOK(@NotNull ATNState s, @Nullable RuleContext ctx) {
return LOOK(s, null, ctx);
}
/**
* Compute set of tokens that can follow {@code s} in the ATN in the
* specified {@code ctx}.
* <p/>
* If {@code ctx} is {@code null} and the end of the rule containing
* {@code s} is reached, {@link Token#EPSILON} is added to the result set.
* If {@code ctx} is not {@code null} and the end of the outermost rule is
* reached, {@link Token#EOF} is added to the result set.
*
* @param s the ATN state
* @param stopState the ATN state to stop at. This can be a
* {@link BlockEndState} to detect epsilon paths through a closure.
* @param ctx the complete parser context, or {@code null} if the context
* should be ignored
*
* @return The set of tokens that can follow {@code s} in the ATN in the
* specified {@code ctx}.
*/
@NotNull
public IntervalSet LOOK(@NotNull ATNState s, @Nullable ATNState stopState, @Nullable RuleContext ctx) {
IntervalSet r = new IntervalSet();
boolean seeThruPreds = true; // ignore preds; get all lookahead
PredictionContext lookContext = ctx != null ? PredictionContext.fromRuleContext(s.atn, ctx) : null;
_LOOK(s, lookContext,
r, new HashSet<ATNConfig>(), seeThruPreds, true);
_LOOK(s, stopState, lookContext,
r, new HashSet<ATNConfig>(), new BitSet(), seeThruPreds, true);
return r;
}
/** Compute set of tokens that can come next. If the context is EMPTY,
* then we don't go anywhere when we hit the end of the rule. We have
* the correct set. If the context is null, that means that we did not want
* any tokens following this rule--just the tokens that could be found within this
* rule. Add EPSILON to the set indicating we reached the end of the ruled out having
* to match a token.
*/
protected void _LOOK(@NotNull ATNState s, @Nullable PredictionContext ctx,
/**
* Compute set of tokens that can follow {@code s} in the ATN in the
* specified {@code ctx}.
* <p/>
* If {@code ctx} is {@code null} and {@code stopState} or the end of the
* rule containing {@code s} is reached, {@link Token#EPSILON} is added to
* the result set. If {@code ctx} is not {@code null} and {@code addEOF} is
* {@code true} and {@code stopState} or the end of the outermost rule is
* reached, {@link Token#EOF} is added to the result set.
*
* @param s the ATN state.
* @param stopState the ATN state to stop at. This can be a
* {@link BlockEndState} to detect epsilon paths through a closure.
* @param ctx The outer context, or {@code null} if the outer context should
* not be used.
* @param look The result lookahead set.
* @param lookBusy A set used for preventing epsilon closures in the ATN
* from causing a stack overflow. Outside code should pass
* {@code new HashSet<ATNConfig>} for this argument.
* @param calledRuleStack A set used for preventing left recursion in the
* ATN from causing a stack overflow. Outside code should pass
* {@code new BitSet()} for this argument.
* @param seeThruPreds {@code true} to true semantic predicates as
* implicitly {@code true} and "see through them", otherwise {@code false}
* to treat semantic predicates as opaque and add {@link #HIT_PRED} to the
* result if one is encountered.
* @param addEOF Add {@link Token#EOF} to the result if the end of the
* outermost context is reached. This parameter has no effect if {@code ctx}
* is {@code null}.
*/
protected void _LOOK(@NotNull ATNState s,
@Nullable ATNState stopState,
@Nullable PredictionContext ctx,
@NotNull IntervalSet look,
@NotNull Set<ATNConfig> lookBusy,
@NotNull BitSet calledRuleStack,
boolean seeThruPreds, boolean addEOF)
{
// System.out.println("_LOOK("+s.stateNumber+", ctx="+ctx);
ATNConfig c = new ATNConfig(s, 0, ctx);
if ( !lookBusy.add(c) ) return;
if (s == stopState) {
if (ctx == null) {
look.add(Token.EPSILON);
return;
} else if (ctx.isEmpty() && addEOF) {
look.add(Token.EOF);
return;
}
}
if ( s instanceof RuleStopState ) {
if ( ctx==null ) {
look.add(Token.EPSILON);
@ -120,10 +196,20 @@ public class LL1Analyzer {
if ( ctx != PredictionContext.EMPTY ) {
// run thru all possible stack tops in ctx
for (SingletonPredictionContext p : ctx) {
ATNState returnState = atn.states.get(p.returnState);
for (int i = 0; i < ctx.size(); i++) {
ATNState returnState = atn.states.get(ctx.getReturnState(i));
// System.out.println("popping back to "+retState);
_LOOK(returnState, p.parent, look, lookBusy, seeThruPreds, addEOF);
boolean removed = calledRuleStack.get(returnState.ruleIndex);
try {
calledRuleStack.clear(returnState.ruleIndex);
_LOOK(returnState, stopState, ctx.getParent(i), look, lookBusy, calledRuleStack, seeThruPreds, addEOF);
}
finally {
if (removed) {
calledRuleStack.set(returnState.ruleIndex);
}
}
}
return;
}
@ -133,20 +219,31 @@ public class LL1Analyzer {
for (int i=0; i<n; i++) {
Transition t = s.transition(i);
if ( t.getClass() == RuleTransition.class ) {
if (calledRuleStack.get(((RuleTransition)t).target.ruleIndex)) {
continue;
}
PredictionContext newContext =
SingletonPredictionContext.create(ctx, ((RuleTransition)t).followState.stateNumber);
_LOOK(t.target, newContext, look, lookBusy, seeThruPreds, addEOF);
try {
calledRuleStack.set(((RuleTransition)t).target.ruleIndex);
_LOOK(t.target, stopState, newContext, look, lookBusy, calledRuleStack, seeThruPreds, addEOF);
}
finally {
calledRuleStack.clear(((RuleTransition)t).target.ruleIndex);
}
}
else if ( t instanceof AbstractPredicateTransition ) {
if ( seeThruPreds ) {
_LOOK(t.target, ctx, look, lookBusy, seeThruPreds, addEOF);
_LOOK(t.target, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF);
}
else {
look.add(HIT_PRED);
}
}
else if ( t.isEpsilon() ) {
_LOOK(t.target, ctx, look, lookBusy, seeThruPreds, addEOF);
_LOOK(t.target, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF);
}
else if ( t.getClass() == WildcardTransition.class ) {
look.addAll( IntervalSet.of(Token.MIN_USER_TOKEN_TYPE, atn.maxTokenType) );

View File

@ -30,6 +30,7 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
@ -84,8 +85,13 @@ public class LexerATNConfig extends ATNConfig {
@Override
public int hashCode() {
int hashCode = super.hashCode();
hashCode = 35 * hashCode ^ (passedThroughNonGreedyDecision ? 1 : 0);
int hashCode = MurmurHash.initialize(7);
hashCode = MurmurHash.update(hashCode, state.stateNumber);
hashCode = MurmurHash.update(hashCode, alt);
hashCode = MurmurHash.update(hashCode, context);
hashCode = MurmurHash.update(hashCode, semanticContext);
hashCode = MurmurHash.update(hashCode, passedThroughNonGreedyDecision ? 1 : 0);
hashCode = MurmurHash.finish(hashCode, 5);
return hashCode;
}

View File

@ -41,7 +41,7 @@ import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.io.OutputStream;
import java.util.Locale;
/** "dup" of ParserInterpreter */
public class LexerATNSimulator extends ATNSimulator {
@ -53,12 +53,12 @@ public class LexerATNSimulator extends ATNSimulator {
/** When we hit an accept state in either the DFA or the ATN, we
* have to notify the character stream to start buffering characters
* via mark() and record the current state. The current sim state
* via {@link IntStream#mark} and record the current state. The current sim state
* includes the current index into the input, the current line,
* and current character position in that line. Note that the Lexer is
* tracking the starting line and characterization of the token. These
* variables track the "state" of the simulator when it hits an accept state.
*
* <p/>
* We track these variables separately for the DFA and ATN simulation
* because the DFA simulation often has to fail over to the ATN
* simulation. If the ATN simulation fails, we need the DFA to fall
@ -118,15 +118,6 @@ public class LexerATNSimulator extends ATNSimulator {
{
super(atn,sharedContextCache);
this.decisionToDFA = decisionToDFA;
if ( decisionToDFA[Lexer.DEFAULT_MODE]==null ) { // create all mode dfa
synchronized (this.decisionToDFA) {
if ( decisionToDFA[Lexer.DEFAULT_MODE]==null ) { // create all mode dfa
for (int i=0; i<atn.modeToStartState.size(); i++) {
this.decisionToDFA[i] = new DFA(atn.modeToStartState.get(i));
}
}
}
}
this.recog = recog;
}
@ -166,12 +157,11 @@ public class LexerATNSimulator extends ATNSimulator {
mode = Lexer.DEFAULT_MODE;
}
// only called from test code from outside
protected int matchATN(@NotNull CharStream input) {
ATNState startState = atn.modeToStartState.get(mode);
if ( debug ) {
System.out.format("matchATN mode %d start: %s\n", mode, startState);
System.out.format(Locale.getDefault(), "matchATN mode %d start: %s\n", mode, startState);
}
int old_mode = mode;
@ -188,7 +178,7 @@ public class LexerATNSimulator extends ATNSimulator {
int predict = execATN(input, next);
if ( debug ) {
System.out.format("DFA after matchATN: %s\n", decisionToDFA[old_mode].toLexerString());
System.out.format(Locale.getDefault(), "DFA after matchATN: %s\n", decisionToDFA[old_mode].toLexerString());
}
return predict;
@ -197,7 +187,7 @@ public class LexerATNSimulator extends ATNSimulator {
protected int execATN(@NotNull CharStream input, @NotNull DFAState ds0) {
//System.out.println("enter exec index "+input.index()+" from "+ds0.configs);
if ( debug ) {
System.out.format("start state closure=%s\n", ds0.configs);
System.out.format(Locale.getDefault(), "start state closure=%s\n", ds0.configs);
}
int t = input.LA(1);
@ -206,7 +196,7 @@ public class LexerATNSimulator extends ATNSimulator {
while ( true ) { // while more work
if ( debug ) {
System.out.format("execATN loop starting closure: %s\n", s.configs);
System.out.format(Locale.getDefault(), "execATN loop starting closure: %s\n", s.configs);
}
// As we move src->trg, src->trg, we keep track of the previous trg to
@ -226,41 +216,13 @@ public class LexerATNSimulator extends ATNSimulator {
// This optimization makes a lot of sense for loops within DFA.
// A character will take us back to an existing DFA state
// that already has lots of edges out of it. e.g., .* in comments.
ATNConfigSet closure = s.configs;
DFAState target = null;
if ( s.edges != null && t >= MIN_DFA_EDGE && t <= MAX_DFA_EDGE ) {
target = s.edges[t - MIN_DFA_EDGE];
if (target == ERROR) {
break;
}
if (debug && target != null) {
System.out.println("reuse state "+s.stateNumber+
" edge to "+target.stateNumber);
}
DFAState target = getExistingTargetState(s, t);
if (target == null) {
target = computeTargetState(input, s, t);
}
if (target == null) {
ATNConfigSet reach = new OrderedATNConfigSet();
// if we don't find an existing DFA state
// Fill reach starting from closure, following t transitions
getReachableConfigSet(input, closure, reach, t);
if ( reach.isEmpty() ) { // we got nowhere on t from s
// we reached state associated with closure for sure, so
// make sure it's defined. worst case, we define s0 from
// start state configs.
@NotNull
DFAState from = s != null ? s : addDFAState(closure);
// we got nowhere on t, don't throw out this knowledge; it'd
// cause a failover from DFA later.
addDFAEdge(from, t, ERROR);
break; // stop when we can't match any more char
}
// Add an edge from s to target DFA found/created for reach
target = addDFAEdge(s, t, reach);
if (target == ERROR) {
break;
}
if (target.isAcceptState) {
@ -281,6 +243,64 @@ public class LexerATNSimulator extends ATNSimulator {
return failOrAccept(prevAccept, input, s.configs, t);
}
/**
* Get an existing target state for an edge in the DFA. If the target state
* for the edge has not yet been computed or is otherwise not available,
* this method returns {@code null}.
*
* @param s The current DFA state
* @param t The next input symbol
* @return The existing target DFA state for the given input symbol
* {@code t}, or {@code null} if the target state for this edge is not
* already cached
*/
@Nullable
protected DFAState getExistingTargetState(@NotNull DFAState s, int t) {
if (s.edges == null || t < MIN_DFA_EDGE || t > MAX_DFA_EDGE) {
return null;
}
DFAState target = s.edges[t - MIN_DFA_EDGE];
if (debug && target != null) {
System.out.println("reuse state "+s.stateNumber+
" edge to "+target.stateNumber);
}
return target;
}
/**
* Compute a target state for an edge in the DFA, and attempt to add the
* computed state and corresponding edge to the DFA.
*
* @param input The input stream
* @param s The current DFA state
* @param t The next input symbol
*
* @return The computed target DFA state for the given input symbol
* {@code t}. If {@code t} does not lead to a valid DFA state, this method
* returns {@link #ERROR}.
*/
@NotNull
protected DFAState computeTargetState(@NotNull CharStream input, @NotNull DFAState s, int t) {
ATNConfigSet reach = new OrderedATNConfigSet();
// if we don't find an existing DFA state
// Fill reach starting from closure, following t transitions
getReachableConfigSet(input, s.configs, reach, t);
if ( reach.isEmpty() ) { // we got nowhere on t from s
// we got nowhere on t, don't throw out this knowledge; it'd
// cause a failover from DFA later.
addDFAEdge(s, t, ERROR);
// stop when we can't match any more char
return ERROR;
}
// Add an edge from s to target DFA found/created for reach
return addDFAEdge(s, t, reach);
}
protected int failOrAccept(SimState prevAccept, CharStream input,
ATNConfigSet reach, int t)
{
@ -316,7 +336,7 @@ public class LexerATNSimulator extends ATNSimulator {
}
if ( debug ) {
System.out.format("testing %s at %s\n", getTokenName(t), c.toString(recog, true));
System.out.format(Locale.getDefault(), "testing %s at %s\n", getTokenName(t), c.toString(recog, true));
}
int n = c.state.getNumberOfTransitions();
@ -339,7 +359,7 @@ public class LexerATNSimulator extends ATNSimulator {
int index, int line, int charPos)
{
if ( debug ) {
System.out.format("ACTION %s:%d\n", recog != null ? recog.getRuleNames()[ruleIndex] : ruleIndex, actionIndex);
System.out.format(Locale.getDefault(), "ACTION %s:%d\n", recog != null ? recog.getRuleNames()[ruleIndex] : ruleIndex, actionIndex);
}
if ( actionIndex>=0 && recog!=null ) recog.action(null, ruleIndex, actionIndex);
@ -354,8 +374,8 @@ public class LexerATNSimulator extends ATNSimulator {
}
@Nullable
public ATNState getReachableTarget(Transition trans, int t) {
if (trans.matches(t, Lexer.MIN_CHAR_VALUE, Lexer.MAX_CHAR_VALUE)) {
protected ATNState getReachableTarget(Transition trans, int t) {
if (trans.matches(t, Character.MIN_VALUE, Character.MAX_VALUE)) {
return trans.target;
}
@ -394,10 +414,10 @@ public class LexerATNSimulator extends ATNSimulator {
if ( config.state instanceof RuleStopState ) {
if ( debug ) {
if ( recog!=null ) {
System.out.format("closure at %s rule stop %s\n", recog.getRuleNames()[config.state.ruleIndex], config);
System.out.format(Locale.getDefault(), "closure at %s rule stop %s\n", recog.getRuleNames()[config.state.ruleIndex], config);
}
else {
System.out.format("closure at rule stop %s\n", config);
System.out.format(Locale.getDefault(), "closure at rule stop %s\n", config);
}
}
@ -413,20 +433,10 @@ public class LexerATNSimulator extends ATNSimulator {
}
if ( config.context!=null && !config.context.isEmpty() ) {
for (SingletonPredictionContext ctx : config.context) {
if ( !ctx.isEmpty() ) {
PredictionContext newContext = ctx.parent; // "pop" return state
if ( ctx.returnState==PredictionContext.EMPTY_RETURN_STATE ) {
// we have no context info. Don't pursue but
// record a config that indicates how we hit end
LexerATNConfig c = new LexerATNConfig(config, config.state, ctx);
if ( debug ) System.out.println("FALLING off token "+
recog.getRuleNames()[config.state.ruleIndex]+
" record "+c);
configs.add(c);
continue;
}
ATNState returnState = atn.states.get(ctx.returnState);
for (int i = 0; i < config.context.size(); i++) {
if (config.context.getReturnState(i) != PredictionContext.EMPTY_RETURN_STATE) {
PredictionContext newContext = config.context.getParent(i); // "pop" return state
ATNState returnState = atn.states.get(config.context.getReturnState(i));
LexerATNConfig c = new LexerATNConfig(returnState, config.alt, newContext);
currentAltReachedAcceptState = closure(input, c, configs, currentAltReachedAcceptState, speculative);
}
@ -457,7 +467,7 @@ public class LexerATNSimulator extends ATNSimulator {
// side-effect: can alter configs.hasSemanticContext
@Nullable
public LexerATNConfig getEpsilonTarget(@NotNull CharStream input,
protected LexerATNConfig getEpsilonTarget(@NotNull CharStream input,
@NotNull LexerATNConfig config,
@NotNull Transition t,
@NotNull ATNConfigSet configs,
@ -612,8 +622,7 @@ public class LexerATNSimulator extends ATNSimulator {
System.out.println("EDGE "+p+" -> "+q+" upon "+((char)t));
}
DFA dfa = decisionToDFA[mode];
synchronized (dfa) {
synchronized (p) {
if ( p.edges==null ) {
// make room for tokens 1..n and -1 masquerading as index 0
p.edges = new DFAState[MAX_DFA_EDGE-MIN_DFA_EDGE+1];
@ -652,7 +661,7 @@ public class LexerATNSimulator extends ATNSimulator {
}
DFA dfa = decisionToDFA[mode];
synchronized (dfa) {
synchronized (dfa.states) {
DFAState existing = dfa.states.get(proposed);
if ( existing!=null ) return existing;
@ -661,13 +670,13 @@ public class LexerATNSimulator extends ATNSimulator {
newState.stateNumber = dfa.states.size();
configs.setReadonly(true);
newState.configs = configs;
decisionToDFA[mode].states.put(newState, newState);
dfa.states.put(newState, newState);
return newState;
}
}
@Nullable
public DFA getDFA(int mode) {
@NotNull
public final DFA getDFA(int mode) {
return decisionToDFA[mode];
}

View File

@ -30,7 +30,12 @@
package org.antlr.v4.runtime.atn;
/** Mark the end of a * or + loop */
public class LoopEndState extends ATNState {
/** Mark the end of a * or + loop. */
public final class LoopEndState extends ATNState {
public ATNState loopBackState;
@Override
public int getStateType() {
return LOOP_END;
}
}

View File

@ -30,7 +30,6 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.Array2DHashSet;
import org.antlr.v4.runtime.misc.ObjectEqualityComparator;
/**
@ -43,9 +42,9 @@ public class OrderedATNConfigSet extends ATNConfigSet {
this.configLookup = new LexerConfigHashSet();
}
public static class LexerConfigHashSet extends Array2DHashSet<ATNConfig> {
public static class LexerConfigHashSet extends AbstractConfigHashSet {
public LexerConfigHashSet() {
super(ObjectEqualityComparator.INSTANCE, 16, 2);
super(ObjectEqualityComparator.INSTANCE);
}
}
}

View File

@ -1,175 +0,0 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* 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.runtime.atn;
public class ParserATNPathFinder /*extends ParserATNSimulator*/ {
// public ParserATNPathFinder(@Nullable Parser parser, @NotNull ATN atn, @NotNull DFA[] decisionToDFA) {
// super(parser, atn, decisionToDFA);
// }
//
// /** Given an input sequence, as a subset of the input stream, trace the path through the
// * ATN starting at s. The path returned includes s and the final target of the last input
// * symbol. If there are multiple paths through the ATN to the final state, it uses the first
// * method finds. This is used to figure out how input sequence is matched in more than one
// * way between the alternatives of a decision. It's only that decision we are concerned with
// * and so if there are ambiguous decisions further along, we will ignore them for the
// * purposes of computing the path to the final state. To figure out multiple paths for
// * decision, use this method on the left edge of the alternatives of the decision in question.
// *
// * TODO: I haven't figured out what to do with nongreedy decisions yet
// * TODO: preds. unless i create rule specific ctxs, i can't eval preds. also must eval args!
// */
// public TraceTree trace(@NotNull ATNState s, @Nullable RuleContext ctx,
// TokenStream input, int start, int stop)
// {
// System.out.println("REACHES "+s.stateNumber+" start state");
// List<TraceTree> leaves = new ArrayList<TraceTree>();
// HashSet<ATNState>[] busy = new HashSet[stop-start+1];
// for (int i = 0; i < busy.length; i++) {
// busy[i] = new HashSet<ATNState>();
// }
// TraceTree path = _trace(s, ctx, ctx, input, start, start, stop, leaves, busy);
// if ( path!=null ) path.leaves = leaves;
// return path;
// }
//
// /** Returns true if we found path */
// public TraceTree _trace(@NotNull ATNState s, RuleContext initialContext, RuleContext ctx,
// TokenStream input, int start, int i, int stop,
// List<TraceTree> leaves, @NotNull Set<ATNState>[] busy)
// {
// TraceTree root = new TraceTree(s);
// if ( i>stop ) {
// leaves.add(root); // track final states
// System.out.println("leaves=" + leaves);
// return root;
// }
//
// if ( !busy[i-start].add(s) ) {
// System.out.println("already visited "+s.stateNumber+" at input "+i+"="+input.get(i).getText());
// return null;
// }
// busy[i-start].add(s);
//
// System.out.println("TRACE "+s.stateNumber+" at input "+input.get(i).getText());
//
// if ( s instanceof RuleStopState) {
// // We hit rule end. If we have context info, use it
// if ( ctx!=null && !ctx.isEmpty() ) {
// System.out.println("stop state "+s.stateNumber+", ctx="+ctx);
// ATNState invokingState = atn.states.get(ctx.invokingState);
// RuleTransition rt = (RuleTransition)invokingState.transition(0);
// ATNState retState = rt.followState;
// return _trace(retState, initialContext, ctx.parent, input, start, i, stop, leaves, busy);
// }
// else {
// // else if we have no context info, just chase follow links (if greedy)
// System.out.println("FALLING off rule "+getRuleName(s.ruleIndex));
// }
// }
//
// int n = s.getNumberOfTransitions();
// boolean aGoodPath = false;
// TraceTree found;
// for (int j=0; j<n; j++) {
// Transition t = s.transition(j);
// if ( t.getClass() == RuleTransition.class ) {
// RuleContext newContext =
// new RuleContext(ctx, s.stateNumber);
// found = _trace(t.target, initialContext, newContext, input, start, i, stop, leaves, busy);
// if ( found!=null ) {aGoodPath=true; root.addChild(found);}
// continue;
// }
// if ( t instanceof PredicateTransition ) {
// found = predTransition(initialContext, ctx, input, start, i, stop, leaves, busy, root, t);
// if ( found!=null ) {aGoodPath=true; root.addChild(found);}
// continue;
// }
// if ( t.isEpsilon() ) {
// found = _trace(t.target, initialContext, ctx, input, start, i, stop, leaves, busy);
// if ( found!=null ) {aGoodPath=true; root.addChild(found);}
// continue;
// }
// if ( t.getClass() == WildcardTransition.class ) {
// System.out.println("REACHES " + t.target.stateNumber + " matching input " + input.get(i).getText());
// found = _trace(t.target, initialContext, ctx, input, start, i+1, stop, leaves, busy);
// if ( found!=null ) {aGoodPath=true; root.addChild(found);}
// continue;
// }
// IntervalSet set = t.label();
// if ( set!=null ) {
// if ( t instanceof NotSetTransition ) {
// if ( !set.contains(input.get(i).getType()) ) {
// System.out.println("REACHES " + t.target.stateNumber + " matching input " + input.get(i).getText());
// found = _trace(t.target, initialContext, ctx, input, start, i+1, stop, leaves, busy);
// if ( found!=null ) {aGoodPath=true; root.addChild(found);}
// }
// }
// else {
// if ( set.contains(input.get(i).getType()) ) {
// System.out.println("REACHES " + t.target.stateNumber + " matching input " + input.get(i).getText());
// found = _trace(t.target, initialContext, ctx, input, start, i+1, stop, leaves, busy);
// if ( found!=null ) {aGoodPath=true; root.addChild(found);}
// }
// }
// }
// }
// if ( aGoodPath ) return root; // found at least one transition leading to success
// return null;
// }
//
// public TraceTree predTransition(RuleContext initialContext, RuleContext ctx, TokenStream input, int start,
// int i, int stop, List<TraceTree> leaves, Set<ATNState>[] busy,
// TraceTree root, Transition t)
// {
// SemanticContext.Predicate pred = ((PredicateTransition) t).getPredicate();
// boolean pass;
// if ( pred.isCtxDependent ) {
// if ( ctx instanceof ParserRuleContext && ctx==initialContext ) {
// System.out.println("eval pred "+pred+"="+pred.eval(parser, ctx));
// pass = pred.eval(parser, ctx);
// }
// else {
// pass = true; // see thru ctx dependent when out of context
// }
// }
// else {
// System.out.println("eval pred "+pred+"="+pred.eval(parser, initialContext));
// pass = pred.eval(parser, ctx);
// }
// if ( pass ) {
// return _trace(t.target, initialContext, ctx, input, start, i, stop, leaves, busy);
// }
// return null;
// }
}

View File

@ -30,11 +30,16 @@
package org.antlr.v4.runtime.atn;
/** Start of (A|B|...)+ loop. Technically a decision state, but
/** Start of {@code (A|B|...)+} loop. Technically a decision state, but
* we don't use for code generation; somebody might need it, so I'm defining
* it for completeness. In reality, the PlusLoopbackState node is the
* real decision-making note for A+
* it for completeness. In reality, the {@link PlusLoopbackState} node is the
* real decision-making note for {@code A+}.
*/
public class PlusBlockStartState extends BlockStartState {
public final class PlusBlockStartState extends BlockStartState {
public PlusLoopbackState loopBackState;
@Override
public int getStateType() {
return PLUS_BLOCK_START;
}
}

View File

@ -30,8 +30,13 @@
package org.antlr.v4.runtime.atn;
/** Decision state for A+ and (A|B)+. It has two transitions:
/** Decision state for {@code A+} and {@code (A|B)+}. It has two transitions:
* one to the loop back to start of the block and one to exit.
*/
public class PlusLoopbackState extends DecisionState {
public final class PlusLoopbackState extends DecisionState {
@Override
public int getStateType() {
return PLUS_LOOP_BACK;
}
}

View File

@ -33,40 +33,67 @@ package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.misc.DoubleKeyMap;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public abstract class PredictionContext implements Iterable<SingletonPredictionContext>,
Comparable<PredictionContext> // to sort node lists by id
{
/** Represents $ in local ctx prediction, which means wildcard. *+x = *. */
public abstract class PredictionContext {
/**
* Represents {@code $} in local context prediction, which means wildcard.
* {@code *+x = *}.
*/
public static final EmptyPredictionContext EMPTY = new EmptyPredictionContext();
/** Represents $ in an array in full ctx mode, when $ doesn't mean wildcard:
* $ + x = [$,x]. Here, $ = EMPTY_RETURN_STATE.
/**
* Represents {@code $} in an array in full context mode, when {@code $}
* doesn't mean wildcard: {@code $ + x = [$,x]}. Here,
* {@code $} = {@link #EMPTY_RETURN_STATE}.
*/
public static final int EMPTY_RETURN_STATE = Integer.MAX_VALUE;
private static final int INITIAL_HASH = 1;
public static int globalNodeCount = 0;
public final int id = globalNodeCount++;
/**
* Stores the computed hash code of this {@link PredictionContext}. The hash
* code is computed in parts to match the following reference algorithm.
*
* <pre>
* private int referenceHashCode() {
* int hash = {@link MurmurHash#initialize}({@link #INITIAL_HASH});
*
* for (int i = 0; i < {@link #size()}; i++) {
* hash = {@link MurmurHash#update}(hash, {@link #getParent}(i));
* }
*
* for (int i = 0; i < {@link #size()}; i++) {
* hash = {@link MurmurHash#update}(hash, {@link #getReturnState}(i));
* }
*
* hash = {@link MurmurHash#finish}(hash, 2 * {@link #size()});
* return hash;
* }
* </pre>
*/
public final int cachedHashCode;
protected PredictionContext(int cachedHashCode) {
this.cachedHashCode = cachedHashCode;
}
/** Convert a RuleContext tree to a PredictionContext graph.
* Return EMPTY if outerContext is empty or null.
/** Convert a {@link RuleContext} tree to a {@link PredictionContext} graph.
* Return {@link #EMPTY} if {@code outerContext} is empty or null.
*/
public static PredictionContext fromRuleContext(@NotNull ATN atn, RuleContext outerContext) {
if ( outerContext==null ) outerContext = RuleContext.EMPTY;
@ -88,16 +115,13 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
return SingletonPredictionContext.create(parent, transition.followState.stateNumber);
}
@Override
public abstract Iterator<SingletonPredictionContext> iterator();
public abstract int size();
public abstract PredictionContext getParent(int index);
public abstract int getReturnState(int index);
/** This means only the EMPTY context is in set */
/** This means only the {@link #EMPTY} context is in set. */
public boolean isEmpty() {
return this == EMPTY;
}
@ -107,46 +131,41 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
}
@Override
public int compareTo(PredictionContext o) { // used for toDotString to print nodes in order
return id - o.id;
}
@Override
public int hashCode() {
public final int hashCode() {
return cachedHashCode;
}
protected static int calculateHashCode(int parentHashCode, int returnStateHashCode) {
return 5 * 5 * 7 + 5 * parentHashCode + returnStateHashCode;
@Override
public abstract boolean equals(Object obj);
protected static int calculateEmptyHashCode() {
int hash = MurmurHash.initialize(INITIAL_HASH);
hash = MurmurHash.finish(hash, 0);
return hash;
}
/** Two contexts conflict() if they are equals() or one is a stack suffix
* of the other. For example, contexts [21 12 $] and [21 9 $] do not
* conflict, but [21 $] and [21 12 $] do conflict. Note that I should
* probably not show the $ in this case. There is a dummy node for each
* stack that just means empty; $ is a marker that's all.
*
* This is used in relation to checking conflicts associated with a
* single NFA state's configurations within a single DFA state.
* If there are configurations s and t within a DFA state such that
* s.state=t.state && s.alt != t.alt && s.ctx conflicts t.ctx then
* the DFA state predicts more than a single alt--it's nondeterministic.
* Two contexts conflict if they are the same or if one is a suffix
* of the other.
*
* When comparing contexts, if one context has a stack and the other
* does not then they should be considered the same context. The only
* way for an NFA state p to have an empty context and a nonempty context
* is the case when closure falls off end of rule without a call stack
* and re-enters the rule with a context. This resolves the issue I
* discussed with Sriram Srinivasan Feb 28, 2005 about not terminating
* fast enough upon nondeterminism.
*
* UPDATE FOR GRAPH STACK; no suffix
*/
// public boolean conflictsWith(PredictionContext other) {
// return this.equals(other);
// }
protected static int calculateHashCode(PredictionContext parent, int returnState) {
int hash = MurmurHash.initialize(INITIAL_HASH);
hash = MurmurHash.update(hash, parent);
hash = MurmurHash.update(hash, returnState);
hash = MurmurHash.finish(hash, 2);
return hash;
}
protected static int calculateHashCode(PredictionContext[] parents, int[] returnStates) {
int hash = MurmurHash.initialize(INITIAL_HASH);
for (PredictionContext parent : parents) {
hash = MurmurHash.update(hash, parent);
}
for (int returnState : returnStates) {
hash = MurmurHash.update(hash, returnState);
}
hash = MurmurHash.finish(hash, 2 * parents.length);
return hash;
}
// dispatch
public static PredictionContext merge(
@ -154,8 +173,10 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
boolean rootIsWildcard,
DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache)
{
assert a!=null && b!=null; // must be empty context, never null
// share same graph if both same
if ( (a==null&&b==null) || a==b || (a!=null&&a.equals(b)) ) return a;
if ( a==b || a.equals(b) ) return a;
if ( a instanceof SingletonPredictionContext && b instanceof SingletonPredictionContext) {
return mergeSingletons((SingletonPredictionContext)a,
@ -181,7 +202,41 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
rootIsWildcard, mergeCache);
}
// http://www.antlr.org/wiki/download/attachments/32014352/singleton-merge.png
/**
* Merge two {@link SingletonPredictionContext} instances.
*
* <p/>
*
* Stack tops equal, parents merge is same; return left graph.<br/>
* <embed src="images/SingletonMerge_SameRootSamePar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Same stack top, parents differ; merge parents giving array node, then
* remainders of those graphs. A new root node is created to point to the
* merged parents.<br/>
* <embed src="images/SingletonMerge_SameRootDiffPar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Different stack tops pointing to same parent. Make array node for the
* root where both element in the root point to the same (original)
* parent.<br/>
* <embed src="images/SingletonMerge_DiffRootSamePar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Different stack tops pointing to different parents. Make array node for
* the root where each element points to the corresponding original
* parent.<br/>
* <embed src="images/SingletonMerge_DiffRootDiffPar.svg" type="image/svg+xml"/>
*
* @param a the first {@link SingletonPredictionContext}
* @param b the second {@link SingletonPredictionContext}
* @param rootIsWildcard {@code true} if this is a local-context merge,
* otherwise false to indicate a full-context merge
* @param mergeCache
*/
public static PredictionContext mergeSingletons(
SingletonPredictionContext a,
SingletonPredictionContext b,
@ -248,9 +303,56 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
}
}
// http://www.antlr.org/wiki/download/attachments/32014352/local-ctx-root-merge.png
// http://www.antlr.org/wiki/download/attachments/32014352/full-ctx-root-merge.png
/** Handle case where at least one of a or b is $ (EMPTY) */
/**
* 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}.
*
* <h2>Local-Context Merges</h2>
*
* These local-context merge operations are used when {@code rootIsWildcard}
* is true.
*
* <p/>
*
* {@link #EMPTY} is superset of any graph; return {@link #EMPTY}.<br/>
* <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/>
*
* <p/>
*
* {@link #EMPTY} and anything is {@code #EMPTY}, so merged parent is
* {@code #EMPTY}; return left graph.<br/>
* <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/>
*
* <p/>
*
* Special case of last merge if local context.<br/>
* <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/>
*
* <h2>Full-Context Merges</h2>
*
* These full-context merge operations are used when {@code rootIsWildcard}
* is false.
*
* <p/>
*
* <embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/>
*
* <p/>
*
* Must keep all contexts; {@link #EMPTY} in array is a special value (and
* null parent).<br/>
* <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/>
*
* <p/>
*
* <embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/>
*
* @param a the first {@link SingletonPredictionContext}
* @param b the second {@link SingletonPredictionContext}
* @param rootIsWildcard {@code true} if this is a local-context merge,
* otherwise false to indicate a full-context merge
*/
public static PredictionContext mergeRoot(SingletonPredictionContext a,
SingletonPredictionContext b,
boolean rootIsWildcard)
@ -279,7 +381,35 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
return null;
}
// http://www.antlr.org/wiki/download/attachments/32014352/array-merge.png
/**
* Merge two {@link ArrayPredictionContext} instances.
*
* <p/>
*
* Different tops, different parents.<br/>
* <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Shared top, same parents.<br/>
* <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Shared top, different parents.<br/>
* <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Shared top, all shared parents.<br/>
* <embed src="images/ArrayMerge_ShareTopSharePar.svg" type="image/svg+xml"/>
*
* <p/>
*
* Equal tops, merge parents and reduce top to
* {@link SingletonPredictionContext}.<br/>
* <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/>
*/
public static PredictionContext mergeArrays(
ArrayPredictionContext a,
ArrayPredictionContext b,
@ -389,7 +519,10 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
return M;
}
/** make pass over all M parents; merge any equals() ones */
/**
* Make pass over all <em>M</em> {@code parents}; merge any {@code equals()}
* ones.
*/
protected static void combineCommonParents(PredictionContext[] parents) {
Map<PredictionContext, PredictionContext> uniqueParents =
new HashMap<PredictionContext, PredictionContext>();
@ -413,7 +546,12 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
buf.append("rankdir=LR;\n");
List<PredictionContext> nodes = getAllContextNodes(context);
Collections.sort(nodes);
Collections.sort(nodes, new Comparator<PredictionContext>() {
@Override
public int compare(PredictionContext o1, PredictionContext o2) {
return o1.id - o2.id;
}
});
for (PredictionContext current : nodes) {
if ( current instanceof SingletonPredictionContext ) {
@ -472,12 +610,10 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
return existing;
}
synchronized (contextCache) {
existing = contextCache.get(context);
if (existing != null) {
visited.put(context, existing);
return existing;
}
existing = contextCache.get(context);
if (existing != null) {
visited.put(context, existing);
return existing;
}
boolean changed = false;
@ -499,9 +635,7 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
}
if (!changed) {
synchronized (contextCache) {
contextCache.add(context);
}
contextCache.add(context);
visited.put(context, context);
return context;
}
@ -518,9 +652,7 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
updated = new ArrayPredictionContext(parents, arrayPredictionContext.returnStates);
}
synchronized (contextCache) {
contextCache.add(updated);
}
contextCache.add(updated);
visited.put(updated, updated);
visited.put(context, updated);
@ -575,20 +707,6 @@ public abstract class PredictionContext implements Iterable<SingletonPredictionC
// return toString(recog, ParserRuleContext.EMPTY);
}
// recog null unless ParserRuleContext, in which case we use subclass toString(...)
public String toString(@Nullable Recognizer<?,?> recog, RuleContext stop) {
StringBuilder buf = new StringBuilder();
PredictionContext p = this;
buf.append("[");
// while ( p != null && p != stop ) {
// if ( !p.isEmpty() ) buf.append(p.returnState);
// if ( p.parent != null && !p.parent.isEmpty() ) buf.append(" ");
// p = p.parent;
// }
buf.append("]");
return buf.toString();
}
public String[] toStrings(Recognizer<?, ?> recognizer, int currentState) {
return toStrings(recognizer, EMPTY, currentState);
}

View File

@ -33,7 +33,7 @@ package org.antlr.v4.runtime.atn;
import java.util.HashMap;
import java.util.Map;
/** Used to cache PredictionContext objects. Its used for the shared
/** Used to cache {@link PredictionContext} objects. Its used for the shared
* context cash associated with contexts in DFA states. This cache
* can be used for both lexers and parsers.
*/

View File

@ -32,6 +32,7 @@ package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.FlexibleHashMap;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;
import java.util.BitSet;
@ -79,9 +80,10 @@ public enum PredictionMode {
/** Code is function of (s, _, ctx, _) */
@Override
public int hashCode(ATNConfig o) {
int hashCode = 7;
hashCode = 31 * hashCode + o.state.stateNumber;
hashCode = 31 * hashCode + o.context.hashCode();
int hashCode = MurmurHash.initialize(7);
hashCode = MurmurHash.update(hashCode, o.state.stateNumber);
hashCode = MurmurHash.update(hashCode, o.context);
hashCode = MurmurHash.finish(hashCode, 2);
return hashCode;
}
@ -89,100 +91,154 @@ public enum PredictionMode {
public boolean equals(ATNConfig a, ATNConfig b) {
if ( a==b ) return true;
if ( a==null || b==null ) return false;
if ( hashCode(a) != hashCode(b) ) return false;
return a.state.stateNumber==b.state.stateNumber
&& a.context.equals(b.context);
}
}
/**
SLL prediction termination.
There are two cases: the usual combined SLL+LL parsing and
pure SLL parsing that has no fail over to full LL.
COMBINED SLL+LL PARSING
SLL can decide to give up any point, even immediately,
failing over to full LL. To be as efficient as possible,
though, SLL should fail over only when it's positive it can't get
anywhere on more lookahead without seeing a conflict.
Assuming combined SLL+LL parsing, an SLL confg set with only
conflicting subsets should failover to full LL, even if the
config sets don't resolve to the same alternative like {1,2}
and {3,4}. If there is at least one nonconflicting set of
configs, SLL could continue with the hopes that more lookahead
will resolve via one of those nonconflicting configs.
Here's the prediction termination rule them: SLL (for SLL+LL
parsing) stops when it sees only conflicting config subsets.
In contrast, full LL keeps going when there is uncertainty.
HEURISTIC
As a heuristic, we stop prediction when we see any conflicting subset
unless we see a state that only has one alternative associated with
it. The single-alt-state thing lets prediction continue upon rules
like (otherwise, it would admit defeat too soon):
// [12|1|[], 6|2|[], 12|2|[]].
s : (ID | ID ID?) ';' ;
When the ATN simulation reaches the state before ';', it has a DFA
state that looks like: [12|1|[], 6|2|[], 12|2|[]]. Naturally 12|1|[]
and 12|2|[] conflict, but we cannot stop processing this node because
alternative to has another way to continue, via [6|2|[]].
It also let's us continue for this rule:
// [1|1|[], 1|2|[], 8|3|[]]
a : A | A | A B ;
After matching input A, we reach the stop state for rule A, state 1.
State 8 is the state right before B. Clearly alternatives 1 and 2
conflict and no amount of further lookahead will separate the two.
However, alternative 3 will be able to continue and so we do not stop
working on this state. In the previous example, we're concerned with
states associated with the conflicting alternatives. Here alt 3 is not
associated with the conflicting configs, but since we can continue
looking for input reasonably, don't declare the state done.
PURE SLL PARSING
To handle pure SLL parsing, all we have to do is make sure that we
combine stack contexts for configurations that differ only by semantic
predicate. From there, we can do the usual SLL termination heuristic.
PREDICATES IN SLL+LL PARSING
SLL decisions don't evaluate predicates until after they reach DFA
stop states because they need to create the DFA cache that
works in all (semantic) situations. (In contrast, full LL
evaluates predicates collected during start state computation
so it can ignore predicates thereafter.) This means that SLL
termination detection can totally ignore semantic predicates.
Of course, implementation-wise, ATNConfigSets combine stack
contexts but not semantic predicate contexts so we might see
two configs like this:
(s, 1, x, {}), (s, 1, x', {p})
Before testing these configurations against others, we have
to merge x and x' (w/o modifying the existing configs). For
example, we test (x+x')==x'' when looking for conflicts in
the following configs.
(s, 1, x, {}), (s, 1, x', {p}), (s, 2, x'', {})
If the configuration set has predicates, which we can test
quickly, this algorithm makes a copy of the configs and
strip out all of the predicates so that a standard
ATNConfigSet will merge everything ignoring
predicates.
*/
* Computes the SLL prediction termination condition.
*
* <p/>
*
* This method computes the SLL prediction termination condition for both of
* the following cases.
*
* <ul>
* <li>The usual SLL+LL fallback upon SLL conflict</li>
* <li>Pure SLL without LL fallback</li>
* </ul>
*
* <p/>
*
* <strong>COMBINED SLL+LL PARSING</strong>
*
* <p/>
*
* When LL-fallback is enabled upon SLL conflict, correct predictions are
* ensured regardless of how the termination condition is computed by this
* method. Due to the substantially higher cost of LL prediction, the
* prediction should only fall back to LL when the additional lookahead
* cannot lead to a unique SLL prediction.
*
* <p/>
*
* Assuming combined SLL+LL parsing, an SLL configuration set with only
* conflicting subsets should fall back to full LL, even if the
* configuration sets don't resolve to the same alternative (e.g.
* {@code {1,2}} and {@code {3,4}}. If there is at least one non-conflicting
* configuration, SLL could continue with the hopes that more lookahead will
* resolve via one of those non-conflicting configurations.
*
* <p/>
*
* Here's the prediction termination rule them: SLL (for SLL+LL parsing)
* stops when it sees only conflicting configuration subsets. In contrast,
* full LL keeps going when there is uncertainty.
*
* <p/>
*
* <strong>HEURISTIC</strong>
*
* <p/>
*
* As a heuristic, we stop prediction when we see any conflicting subset
* unless we see a state that only has one alternative associated with it.
* The single-alt-state thing lets prediction continue upon rules like
* (otherwise, it would admit defeat too soon):
*
* <p/>
*
* {@code [12|1|[], 6|2|[], 12|2|[]]. s : (ID | ID ID?) ';' ;}
*
* <p/>
*
* When the ATN simulation reaches the state before {@code ';'}, it has a
* DFA state that looks like: {@code [12|1|[], 6|2|[], 12|2|[]]}. Naturally
* {@code 12|1|[]} and {@code 12|2|[]} conflict, but we cannot stop
* processing this node because alternative to has another way to continue,
* via {@code [6|2|[]]}.
*
* <p/>
*
* It also let's us continue for this rule:
*
* <p/>
*
* {@code [1|1|[], 1|2|[], 8|3|[]] a : A | A | A B ;}
*
* <p/>
*
* After matching input A, we reach the stop state for rule A, state 1.
* State 8 is the state right before B. Clearly alternatives 1 and 2
* conflict and no amount of further lookahead will separate the two.
* However, alternative 3 will be able to continue and so we do not stop
* working on this state. In the previous example, we're concerned with
* states associated with the conflicting alternatives. Here alt 3 is not
* associated with the conflicting configs, but since we can continue
* looking for input reasonably, don't declare the state done.
*
* <p/>
*
* <strong>PURE SLL PARSING</strong>
*
* <p/>
*
* To handle pure SLL parsing, all we have to do is make sure that we
* combine stack contexts for configurations that differ only by semantic
* predicate. From there, we can do the usual SLL termination heuristic.
*
* <p/>
*
* <strong>PREDICATES IN SLL+LL PARSING</strong>
*
* <p/>
*
* SLL decisions don't evaluate predicates until after they reach DFA stop
* states because they need to create the DFA cache that works in all
* semantic situations. In contrast, full LL evaluates predicates collected
* during start state computation so it can ignore predicates thereafter.
* This means that SLL termination detection can totally ignore semantic
* predicates.
*
* <p/>
*
* Implementation-wise, {@link ATNConfigSet} combines stack contexts but not
* semantic predicate contexts so we might see two configurations like the
* following.
*
* <p/>
*
* {@code (s, 1, x, {}), (s, 1, x', {p})}
*
* <p/>
*
* Before testing these configurations against others, we have to merge
* {@code x} and {@code x'} (without modifying the existing configurations).
* For example, we test {@code (x+x')==x''} when looking for conflicts in
* the following configurations.
*
* <p/>
*
* {@code (s, 1, x, {}), (s, 1, x', {p}), (s, 2, x'', {})}
*
* <p/>
*
* If the configuration set has predicates (as indicated by
* {@link ATNConfigSet#hasSemanticContext}), this algorithm makes a copy of
* the configurations to strip out all of the predicates so that a standard
* {@link ATNConfigSet} will merge everything ignoring predicates.
*/
public static boolean hasSLLConflictTerminatingPrediction(PredictionMode mode, @NotNull ATNConfigSet configs) {
/* Configs in rule stop states indicate reaching the end of the decision
* rule (local context) or end of start rule (full context). If all
* configs meet this condition, then none of the configurations is able
* to match additional input so we terminate prediction.
*/
if (allConfigsInRuleStopStates(configs)) {
return true;
}
// pure SLL mode parsing
if ( mode == PredictionMode.SLL ) {
// Don't bother with combining configs from different semantic
@ -208,147 +264,252 @@ public enum PredictionMode {
return heuristic;
}
/**
* Checks if any configuration in {@code configs} is in a
* {@link RuleStopState}. Configurations meeting this condition have reached
* the end of the decision rule (local context) or end of start rule (full
* context).
*
* @param configs the configuration set to test
* @return {@code true} if any configuration in {@code configs} is in a
* {@link RuleStopState}, otherwise {@code false}
*/
public static boolean hasConfigInRuleStopState(ATNConfigSet configs) {
for (ATNConfig c : configs) {
if (c.state instanceof RuleStopState) {
return true;
}
}
return false;
}
/**
Full LL prediction termination.
Can we stop looking ahead during ATN simulation or is there some
uncertainty as to which alternative we will ultimately pick, after
consuming more input? Even if there are partial conflicts, we might
know that everything is going to resolve to the same minimum
alt. That means we can stop since no more lookahead will change that
fact. On the other hand, there might be multiple conflicts that
resolve to different minimums. That means we need more look ahead to
decide which of those alternatives we should predict.
The basic idea is to split the set of configurations, C, into
conflicting (s, _, ctx, _) subsets and singleton subsets with
non-conflicting configurations. Two config's conflict if they have
identical state and rule stack contexts but different alternative
numbers: (s, i, ctx, _), (s, j, ctx, _) for i!=j.
Reduce these config subsets to the set of possible alternatives. You
can compute the alternative subsets in one go as follows:
A_s,ctx = {i | (s, i, ctx, _) for in C holding s, ctx fixed}
Or in pseudo-code:
for c in C:
map[c] U= c.alt # map hash/equals uses s and x, not alt and not pred
Then map.values is the set of A_s,ctx sets.
If |A_s,ctx|=1 then there is no conflict associated with s and ctx.
Reduce the subsets to singletons by choosing a minimum of each subset.
If the union of these alternatives sets is a singleton, then no amount
of more lookahead will help us. We will always pick that
alternative. If, however, there is more than one alternative, then we
are uncertain which alt to predict and must continue looking for
resolution. We may or may not discover an ambiguity in the future,
even if there are no conflicting subsets this round.
The biggest sin is to terminate early because it means we've made a
decision but were uncertain as to the eventual outcome. We haven't
used enough lookahead. On the other hand, announcing a conflict too
late is no big deal; you will still have the conflict. It's just
inefficient. It might even look until the end of file.
Semantic predicates for full LL aren't involved in this decision
because the predicates are evaluated during start state computation.
This set of configurations was derived from the initial subset with
configurations holding false predicate stripped out.
CONFLICTING CONFIGS
Two configurations, (s, i, x) and (s, j, x'), conflict when i!=j but
x = x'. Because we merge all (s, i, _) configurations together, that
means that there are at most n configurations associated with state s
for n possible alternatives in the decision. The merged stacks
complicate the comparison of config contexts, x and x'. Sam checks to
see if one is a subset of the other by calling merge and checking to
see if the merged result is either x or x'. If the x associated with
lowest alternative i is the superset, then i is the only possible
prediction since the others resolve to min i as well. If, however, x
is associated with j>i then at least one stack configuration for j is
not in conflict with alt i. The algorithm should keep going, looking
for more lookahead due to the uncertainty.
For simplicity, I'm doing a equality check between x and x' that lets
the algorithm continue to consume lookahead longer than necessary.
The reason I like the equality is of course the simplicity but also
because that is the test you need to detect the alternatives that are
actually in conflict.
CONTINUE/STOP RULE
Continue if union of resolved alt sets from nonconflicting and
conflicting alt subsets has more than one alt. We are uncertain about
which alternative to predict.
The complete set of alternatives, [i for (_,i,_)], tells us
which alternatives are still in the running for the amount of input
we've consumed at this point. The conflicting sets let us to strip
away configurations that won't lead to more states (because we
resolve conflicts to the configuration with a minimum alternate for
given conflicting set.)
CASES:
* no conflicts & > 1 alt in set => continue
* (s, 1, x), (s, 2, x), (s, 3, z)
(s', 1, y), (s', 2, y)
yields nonconflicting set {3} U conflicting sets min({1,2}) U min({1,2}) = {1,3}
=> continue
* (s, 1, x), (s, 2, x),
(s', 1, y), (s', 2, y)
(s'', 1, z)
yields nonconflicting set you this {1} U conflicting sets min({1,2}) U min({1,2}) = {1}
=> stop and predict 1
* (s, 1, x), (s, 2, x),
(s', 1, y), (s', 2, y)
yields conflicting, reduced sets {1} U {1} = {1}
=> stop and predict 1, can announce ambiguity {1,2}
* (s, 1, x), (s, 2, x)
(s', 2, y), (s', 3, y)
yields conflicting, reduced sets {1} U {2} = {1,2}
=> continue
* (s, 1, x), (s, 2, x)
(s', 3, y), (s', 4, y)
yields conflicting, reduced sets {1} U {3} = {1,3}
=> continue
EXACT AMBIGUITY DETECTION
If all states report the same conflicting alt set, then we know we
have the real ambiguity set:
|A_i|>1 and A_i = A_j for all i, j.
In other words, we continue examining lookahead until all A_i have
more than one alt and all A_i are the same. If A={{1,2}, {1,3}}, then
regular LL prediction would terminate because the resolved set is
{1}. To determine what the real ambiguity is, we have to know whether
the ambiguity is between one and two or one and three so we keep
going. We can only stop prediction when we need exact ambiguity
detection when the sets look like A={{1,2}} or {{1,2},{1,2}} etc...
* Checks if all configurations in {@code configs} are in a
* {@link RuleStopState}. Configurations meeting this condition have reached
* the end of the decision rule (local context) or end of start rule (full
* context).
*
* @param configs the configuration set to test
* @return {@code true} if all configurations in {@code configs} are in a
* {@link RuleStopState}, otherwise {@code false}
*/
public static int resolvesToJustOneViableAlt(Collection<BitSet> altsets) {
public static boolean allConfigsInRuleStopStates(@NotNull ATNConfigSet configs) {
for (ATNConfig config : configs) {
if (!(config.state instanceof RuleStopState)) {
return false;
}
}
return true;
}
/**
* Full LL prediction termination.
*
* <p/>
*
* Can we stop looking ahead during ATN simulation or is there some
* uncertainty as to which alternative we will ultimately pick, after
* consuming more input? Even if there are partial conflicts, we might know
* that everything is going to resolve to the same minimum alternative. That
* means we can stop since no more lookahead will change that fact. On the
* other hand, there might be multiple conflicts that resolve to different
* minimums. That means we need more look ahead to decide which of those
* alternatives we should predict.
*
* <p/>
*
* The basic idea is to split the set of configurations {@code C}, into
* conflicting subsets {@code (s, _, ctx, _)} and singleton subsets with
* non-conflicting configurations. Two configurations conflict if they have
* identical {@link ATNConfig#state} and {@link ATNConfig#context} values
* but different {@link ATNConfig#alt} value, e.g. {@code (s, i, ctx, _)}
* and {@code (s, j, ctx, _)} for {@code i!=j}.
*
* <p/>
*
* Reduce these configuration subsets to the set of possible alternatives.
* You can compute the alternative subsets in one pass as follows:
*
* <p/>
*
* {@code A_s,ctx = {i | (s, i, ctx, _)}} for each configuration in
* {@code C} holding {@code s} and {@code ctx} fixed.
*
* <p/>
*
* Or in pseudo-code, for each configuration {@code c} in {@code C}:
*
* <pre>
* map[c] U= c.{@link ATNConfig#alt alt} # map hash/equals uses s and x, not
* alt and not pred
* </pre>
*
* <p/>
*
* The values in {@code map} are the set of {@code A_s,ctx} sets.
*
* <p/>
*
* If {@code |A_s,ctx|=1} then there is no conflict associated with
* {@code s} and {@code ctx}.
*
* <p/>
*
* Reduce the subsets to singletons by choosing a minimum of each subset. If
* the union of these alternative subsets is a singleton, then no amount of
* more lookahead will help us. We will always pick that alternative. If,
* however, there is more than one alternative, then we are uncertain which
* alternative to predict and must continue looking for resolution. We may
* or may not discover an ambiguity in the future, even if there are no
* conflicting subsets this round.
*
* <p/>
*
* The biggest sin is to terminate early because it means we've made a
* decision but were uncertain as to the eventual outcome. We haven't used
* enough lookahead. On the other hand, announcing a conflict too late is no
* big deal; you will still have the conflict. It's just inefficient. It
* might even look until the end of file.
*
* <p/>
*
* No special consideration for semantic predicates is required because
* predicates are evaluated on-the-fly for full LL prediction, ensuring that
* no configuration contains a semantic context during the termination
* check.
*
* <p/>
*
* <strong>CONFLICTING CONFIGS</strong>
*
* <p/>
*
* Two configurations {@code (s, i, x)} and {@code (s, j, x')}, conflict
* when {@code i!=j} but {@code x=x'}. Because we merge all
* {@code (s, i, _)} configurations together, that means that there are at
* most {@code n} configurations associated with state {@code s} for
* {@code n} possible alternatives in the decision. The merged stacks
* complicate the comparison of configuration contexts {@code x} and
* {@code x'}. Sam checks to see if one is a subset of the other by calling
* merge and checking to see if the merged result is either {@code x} or
* {@code x'}. If the {@code x} associated with lowest alternative {@code i}
* is the superset, then {@code i} is the only possible prediction since the
* others resolve to {@code min(i)} as well. However, if {@code x} is
* associated with {@code j>i} then at least one stack configuration for
* {@code j} is not in conflict with alternative {@code i}. The algorithm
* should keep going, looking for more lookahead due to the uncertainty.
*
* <p/>
*
* For simplicity, I'm doing a equality check between {@code x} and
* {@code x'} that lets the algorithm continue to consume lookahead longer
* than necessary. The reason I like the equality is of course the
* simplicity but also because that is the test you need to detect the
* alternatives that are actually in conflict.
*
* <p/>
*
* <strong>CONTINUE/STOP RULE</strong>
*
* <p/>
*
* Continue if union of resolved alternative sets from non-conflicting and
* conflicting alternative subsets has more than one alternative. We are
* uncertain about which alternative to predict.
*
* <p/>
*
* The complete set of alternatives, {@code [i for (_,i,_)]}, tells us which
* alternatives are still in the running for the amount of input we've
* consumed at this point. The conflicting sets let us to strip away
* configurations that won't lead to more states because we resolve
* conflicts to the configuration with a minimum alternate for the
* conflicting set.
*
* <p/>
*
* <strong>CASES</strong>
*
* <ul>
*
* <li>no conflicts and more than 1 alternative in set =&gt; continue</li>
*
* <li> {@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s, 3, z)},
* {@code (s', 1, y)}, {@code (s', 2, y)} yields non-conflicting set
* {@code {3}} U conflicting sets {@code min({1,2})} U {@code min({1,2})} =
* {@code {1,3}} =&gt; continue
* </li>
*
* <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 1, y)},
* {@code (s', 2, y)}, {@code (s'', 1, z)} yields non-conflicting set
* {@code {1}} U conflicting sets {@code min({1,2})} U {@code min({1,2})} =
* {@code {1}} =&gt; stop and predict 1</li>
*
* <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 1, y)},
* {@code (s', 2, y)} yields conflicting, reduced sets {@code {1}} U
* {@code {1}} = {@code {1}} =&gt; stop and predict 1, can announce
* ambiguity {@code {1,2}}</li>
*
* <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 2, y)},
* {@code (s', 3, y)} yields conflicting, reduced sets {@code {1}} U
* {@code {2}} = {@code {1,2}} =&gt; continue</li>
*
* <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 3, y)},
* {@code (s', 4, y)} yields conflicting, reduced sets {@code {1}} U
* {@code {3}} = {@code {1,3}} =&gt; continue</li>
*
* </ul>
*
* <strong>EXACT AMBIGUITY DETECTION</strong>
*
* <p/>
*
* If all states report the same conflicting set of alternatives, then we
* know we have the exact ambiguity set.
*
* <p/>
*
* <code>|A_<em>i</em>|&gt;1</code> and
* <code>A_<em>i</em> = A_<em>j</em></code> for all <em>i</em>, <em>j</em>.
*
* <p/>
*
* In other words, we continue examining lookahead until all {@code A_i}
* have more than one alternative and all {@code A_i} are the same. If
* {@code A={{1,2}, {1,3}}}, then regular LL prediction would terminate
* because the resolved set is {@code {1}}. To determine what the real
* ambiguity is, we have to know whether the ambiguity is between one and
* two or one and three so we keep going. We can only stop prediction when
* we need exact ambiguity detection when the sets look like
* {@code A={{1,2}}} or {@code {{1,2},{1,2}}}, etc...
*/
public static int resolvesToJustOneViableAlt(@NotNull Collection<BitSet> altsets) {
return getSingleViableAlt(altsets);
}
public static boolean allSubsetsConflict(Collection<BitSet> altsets) {
/**
* Determines if every alternative subset in {@code altsets} contains more
* than one alternative.
*
* @param altsets a collection of alternative subsets
* @return {@code true} if every {@link BitSet} in {@code altsets} has
* {@link BitSet#cardinality cardinality} &gt; 1, otherwise {@code false}
*/
public static boolean allSubsetsConflict(@NotNull Collection<BitSet> altsets) {
return !hasNonConflictingAltSet(altsets);
}
/** return (there exists len(A_i)==1 for some A_i in altsets A) */
public static boolean hasNonConflictingAltSet(Collection<BitSet> altsets) {
/**
* Determines if any single alternative subset in {@code altsets} contains
* exactly one alternative.
*
* @param altsets a collection of alternative subsets
* @return {@code true} if {@code altsets} contains a {@link BitSet} with
* {@link BitSet#cardinality cardinality} 1, otherwise {@code false}
*/
public static boolean hasNonConflictingAltSet(@NotNull Collection<BitSet> altsets) {
for (BitSet alts : altsets) {
if ( alts.cardinality()==1 ) {
return true;
@ -357,8 +518,15 @@ public enum PredictionMode {
return false;
}
/** return (there exists len(A_i)>1 for some A_i in altsets A) */
public static boolean hasConflictingAltSet(Collection<BitSet> altsets) {
/**
* Determines if any single alternative subset in {@code altsets} contains
* more than one alternative.
*
* @param altsets a collection of alternative subsets
* @return {@code true} if {@code altsets} contains a {@link BitSet} with
* {@link BitSet#cardinality cardinality} &gt; 1, otherwise {@code false}
*/
public static boolean hasConflictingAltSet(@NotNull Collection<BitSet> altsets) {
for (BitSet alts : altsets) {
if ( alts.cardinality()>1 ) {
return true;
@ -367,7 +535,14 @@ public enum PredictionMode {
return false;
}
public static boolean allSubsetsEqual(Collection<BitSet> altsets) {
/**
* Determines if every alternative subset in {@code altsets} is equivalent.
*
* @param altsets a collection of alternative subsets
* @return {@code true} if every member of {@code altsets} is equal to the
* others, otherwise {@code false}
*/
public static boolean allSubsetsEqual(@NotNull Collection<BitSet> altsets) {
Iterator<BitSet> it = altsets.iterator();
BitSet first = it.next();
while ( it.hasNext() ) {
@ -377,14 +552,28 @@ public enum PredictionMode {
return true;
}
public static int getUniqueAlt(Collection<BitSet> altsets) {
/**
* Returns the unique alternative predicted by all alternative subsets in
* {@code altsets}. If no such alternative exists, this method returns
* {@link ATN#INVALID_ALT_NUMBER}.
*
* @param altsets a collection of alternative subsets
*/
public static int getUniqueAlt(@NotNull Collection<BitSet> altsets) {
BitSet all = getAlts(altsets);
if ( all.cardinality()==1 ) return all.nextSetBit(0);
return ATN.INVALID_ALT_NUMBER;
}
public static BitSet getAlts(Collection<BitSet> altsets) {
/**
* Gets the complete set of represented alternatives for a collection of
* alternative subsets. This method returns the union of each {@link BitSet}
* in {@code altsets}.
*
* @param altsets a collection of alternative subsets
* @return the set of represented alternatives in {@code altsets}
*/
public static BitSet getAlts(@NotNull Collection<BitSet> altsets) {
BitSet all = new BitSet();
for (BitSet alts : altsets) {
all.or(alts);
@ -393,10 +582,15 @@ public enum PredictionMode {
}
/**
* This function gets the conflicting alt subsets from a configuration set.
* for c in configs:
* map[c] U= c.alt # map hash/equals uses s and x, not alt and not pred
*/
* This function gets the conflicting alt subsets from a configuration set.
* For each configuration {@code c} in {@code configs}:
*
* <pre>
* map[c] U= c.{@link ATNConfig#alt alt} # map hash/equals uses s and x, not
* alt and not pred
* </pre>
*/
@NotNull
public static Collection<BitSet> getConflictingAltSubsets(ATNConfigSet configs) {
AltAndContextMap configToAlts = new AltAndContextMap();
for (ATNConfig c : configs) {
@ -410,11 +604,16 @@ public enum PredictionMode {
return configToAlts.values();
}
/** Get a map from state to alt subset from a configuration set.
* for c in configs:
* map[c.state] U= c.alt
/**
* Get a map from state to alt subset from a configuration set. For each
* configuration {@code c} in {@code configs}:
*
* <pre>
* map[c.{@link ATNConfig#state state}] U= c.{@link ATNConfig#alt alt}
* </pre>
*/
public static Map<ATNState, BitSet> getStateToAltMap(ATNConfigSet configs) {
@NotNull
public static Map<ATNState, BitSet> getStateToAltMap(@NotNull ATNConfigSet configs) {
Map<ATNState, BitSet> m = new HashMap<ATNState, BitSet>();
for (ATNConfig c : configs) {
BitSet alts = m.get(c.state);
@ -427,7 +626,7 @@ public enum PredictionMode {
return m;
}
public static boolean hasStateAssociatedWithOneAlt(ATNConfigSet configs) {
public static boolean hasStateAssociatedWithOneAlt(@NotNull ATNConfigSet configs) {
Map<ATNState, BitSet> x = getStateToAltMap(configs);
for (BitSet alts : x.values()) {
if ( alts.cardinality()==1 ) return true;
@ -435,7 +634,7 @@ public enum PredictionMode {
return false;
}
public static int getSingleViableAlt(Collection<BitSet> altsets) {
public static int getSingleViableAlt(@NotNull Collection<BitSet> altsets) {
BitSet viableAlts = new BitSet();
for (BitSet alts : altsets) {
int minAlt = alts.nextSetBit(0);

View File

@ -30,7 +30,12 @@
package org.antlr.v4.runtime.atn;
public class RuleStartState extends ATNState {
public final class RuleStartState extends ATNState {
public RuleStopState stopState;
public boolean isPrecedenceRule;
@Override
public int getStateType() {
return RULE_START;
}
}

View File

@ -35,5 +35,11 @@ package org.antlr.v4.runtime.atn;
* references to all calls to this rule to compute FOLLOW sets for
* error handling.
*/
public class RuleStopState extends ATNState {
public final class RuleStopState extends ATNState {
@Override
public int getStateType() {
return RULE_STOP;
}
}

View File

@ -32,6 +32,7 @@ package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.misc.MurmurHash;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Utils;
@ -46,29 +47,29 @@ import java.util.Set;
/** A tree structure used to record the semantic context in which
* an ATN configuration is valid. It's either a single predicate,
* a conjunction p1&&p2, or a sum of products p1||p2.
*
* I have scoped the AND, OR, and Predicate subclasses of
* SemanticContext within the scope of this outer class.
* a conjunction {@code p1&&p2}, or a sum of products {@code p1||p2}.
* <p/>
* I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of
* {@link SemanticContext} within the scope of this outer class.
*/
public abstract class SemanticContext {
public static final SemanticContext NONE = new Predicate();
public SemanticContext parent;
/**
For context independent predicates, we evaluate them without a local
context (i.e., null context). That way, we can evaluate them without having to create
proper rule-specific context during prediction (as opposed to the parser,
which creates them naturally). In a practical sense, this avoids a cast exception
from RuleContext to myruleContext.
For context dependent predicates, we must pass in a local context so that
references such as $arg evaluate properly as _localctx.arg. We only capture
context dependent predicates in the context in which we begin prediction,
so we passed in the outer context here in case of context dependent predicate
evaluation.
*/
/**
* For context independent predicates, we evaluate them without a local
* context (i.e., null context). That way, we can evaluate them without
* having to create proper rule-specific context during prediction (as
* opposed to the parser, which creates them naturally). In a practical
* sense, this avoids a cast exception from RuleContext to myruleContext.
* <p/>
* For context dependent predicates, we must pass in a local context so that
* references such as $arg evaluate properly as _localctx.arg. We only
* capture context dependent predicates in the context in which we begin
* prediction, so we passed in the outer context here in case of context
* dependent predicate evaluation.
*/
public abstract boolean eval(Recognizer<?,?> parser, RuleContext outerContext);
public static class Predicate extends SemanticContext {
@ -96,10 +97,11 @@ public abstract class SemanticContext {
@Override
public int hashCode() {
int hashCode = 1;
hashCode = 31 * hashCode + ruleIndex;
hashCode = 31 * hashCode + predIndex;
hashCode = 31 * hashCode + (isCtxDependent ? 1 : 0);
int hashCode = MurmurHash.initialize();
hashCode = MurmurHash.update(hashCode, ruleIndex);
hashCode = MurmurHash.update(hashCode, predIndex);
hashCode = MurmurHash.update(hashCode, isCtxDependent ? 1 : 0);
hashCode = MurmurHash.finish(hashCode, 3);
return hashCode;
}
@ -197,7 +199,7 @@ public abstract class SemanticContext {
@Override
public int hashCode() {
return Arrays.hashCode(opnds);
return MurmurHash.hashCode(opnds, AND.class.hashCode());
}
@Override
@ -244,7 +246,7 @@ public abstract class SemanticContext {
@Override
public int hashCode() {
return Arrays.hashCode(opnds) + 1; // differ from AND slightly
return MurmurHash.hashCode(opnds, OR.class.hashCode());
}
@Override

View File

@ -35,7 +35,7 @@ import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
/** A transition containing a set of values */
/** A transition containing a set of values. */
public class SetTransition extends Transition {
@NotNull
public final IntervalSet set;

View File

@ -30,15 +30,12 @@
package org.antlr.v4.runtime.atn;
import java.util.Iterator;
public class SingletonPredictionContext extends PredictionContext {
public final PredictionContext parent;
public final int returnState;
SingletonPredictionContext(PredictionContext parent, int returnState) {
super(calculateHashCode(parent!=null ? 31 ^ parent.hashCode() : 1,
31 ^ returnState));
super(parent != null ? calculateHashCode(parent, returnState) : calculateEmptyHashCode());
assert returnState!=ATNState.INVALID_STATE_NUMBER;
this.parent = parent;
this.returnState = returnState;
@ -52,22 +49,6 @@ public class SingletonPredictionContext extends PredictionContext {
return new SingletonPredictionContext(parent, returnState);
}
@Override
public Iterator<SingletonPredictionContext> iterator() {
final SingletonPredictionContext self = this;
return new Iterator<SingletonPredictionContext>() {
int i = 0;
@Override
public boolean hasNext() { return i==0; }
@Override
public SingletonPredictionContext next() { i++; return self; }
@Override
public void remove() { throw new UnsupportedOperationException(); }
};
}
@Override
public int size() {
return 1;

View File

@ -31,5 +31,10 @@
package org.antlr.v4.runtime.atn;
/** The block that begins a closure loop. */
public class StarBlockStartState extends BlockStartState {
public final class StarBlockStartState extends BlockStartState {
@Override
public int getStateType() {
return STAR_BLOCK_START;
}
}

Some files were not shown because too many files have changed in this diff Show More