forked from jasder/antlr
Merge branch 'master' into precedence-predicates
This commit is contained in:
commit
b14ca56441
|
@ -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/
|
||||
|
|
292
CHANGES.txt
292
CHANGES.txt
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
96
README.txt
96
README.txt
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>**/*.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
|
||||
*/
|
||||
|
|
|
@ -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>>>
|
||||
|
|
|
@ -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>
|
|
@ -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.
|
||||
|
|
@ -0,0 +1 @@
|
|||
FAQ
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
version=4.0b4
|
||||
|
||||
antlr3.jar=/usr/local/lib/antlr-3.5-complete.jar
|
||||
build.sysclasspath=ignore
|
236
build.xml
236
build.xml
|
@ -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}">
|
||||
|
|
|
@ -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
126
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 | ·\n·\n· |<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="ε"];
|
||||
|
||||
BasicBlockStartState:alt1 -> content1[label="ε"];
|
||||
content1 -> BlockEndState[style="dashed"];
|
||||
|
||||
BasicBlockStartState:alt2 -> content2[label="ε"];
|
||||
content2 -> BlockEndState[style="dashed"];
|
||||
|
||||
BasicBlockStartState:altn -> more[label="ε"];
|
||||
more -> BlockEndState[style="dashed"];
|
||||
}
|
|
@ -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 | ·\n·\n· |<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="ε"];
|
||||
|
||||
StarBlockStartState:alt1 -> content1[label="ε"];
|
||||
content1 -> BlockEndState[style="dashed"];
|
||||
|
||||
StarBlockStartState:alt2 -> content2[label="ε"];
|
||||
content2 -> BlockEndState[style="dashed"];
|
||||
|
||||
StarBlockStartState:altn -> more[label="ε"];
|
||||
more -> BlockEndState[style="dashed"];
|
||||
|
||||
BlockEndState:e -> StarLoopbackState:w[label="ε"];
|
||||
StarLoopEntryState:alt1 -> StarBlockStartState[label="ε"];
|
||||
StarLoopEntryState:alt2 -> LoopEndState[label="ε"];
|
||||
StarLoopbackState:n -> StarLoopEntryState:n[label="ε"];
|
||||
}
|
|
@ -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 | ·\n·\n· |<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="ε"];
|
||||
|
||||
StarBlockStartState:alt1 -> content1[label="ε"];
|
||||
content1 -> BlockEndState[style="dashed"];
|
||||
|
||||
StarBlockStartState:alt2 -> content2[label="ε"];
|
||||
content2 -> BlockEndState[style="dashed"];
|
||||
|
||||
StarBlockStartState:altn -> more[label="ε"];
|
||||
more -> BlockEndState[style="dashed"];
|
||||
|
||||
BlockEndState:e -> StarLoopbackState:w[label="ε"];
|
||||
StarLoopEntryState:alt2 -> StarBlockStartState[label="ε"];
|
||||
StarLoopEntryState:alt1 -> LoopEndState[label="ε"];
|
||||
StarLoopbackState:s -> StarLoopEntryState:s[label="ε"];
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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="$"];
|
||||
}
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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="$"];
|
||||
}
|
||||
}
|
|
@ -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="ε"];
|
||||
|
||||
BasicBlockStartState:alt1 -> content[label="ε"];
|
||||
content -> BlockEndState[style="dashed"];
|
||||
|
||||
BasicBlockStartState:alt2 -> BlockEndState[label="ε"];
|
||||
}
|
|
@ -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="ε"];
|
||||
|
||||
BasicBlockStartState:alt2 -> content[label="ε"];
|
||||
content -> BlockEndState[style="dashed"];
|
||||
|
||||
BasicBlockStartState:alt1 -> BlockEndState[label="ε"];
|
||||
}
|
|
@ -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 | ·\n·\n· |<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="ε"];
|
||||
|
||||
PlusBlockStartState:alt1 -> content1[label="ε"];
|
||||
content1 -> BlockEndState[style="dashed"];
|
||||
|
||||
PlusBlockStartState:alt2 -> content2[label="ε"];
|
||||
content2 -> BlockEndState[style="dashed"];
|
||||
|
||||
PlusBlockStartState:altn -> more[label="ε"];
|
||||
more -> BlockEndState[style="dashed"];
|
||||
|
||||
BlockEndState -> PlusLoopbackState[label="ε"];
|
||||
PlusLoopbackState:alt1:n -> PlusBlockStartState:n[label="ε"];
|
||||
PlusLoopbackState:alt2 -> LoopEndState[label="ε"];
|
||||
}
|
|
@ -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 | ·\n·\n· |<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="ε"];
|
||||
|
||||
PlusBlockStartState:alt1 -> content1[label="ε"];
|
||||
content1 -> BlockEndState[style="dashed"];
|
||||
|
||||
PlusBlockStartState:alt2 -> content2[label="ε"];
|
||||
content2 -> BlockEndState[style="dashed"];
|
||||
|
||||
PlusBlockStartState:altn -> more[label="ε"];
|
||||
more -> BlockEndState[style="dashed"];
|
||||
|
||||
BlockEndState -> PlusLoopbackState[label="ε"];
|
||||
PlusLoopbackState:alt2:s -> PlusBlockStartState:s[label="ε"];
|
||||
PlusLoopbackState:alt1 -> LoopEndState[label="ε"];
|
||||
}
|
|
@ -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="ε"];
|
||||
content -> RuleStopState[style="dashed"];
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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"];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) ) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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<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.
|
||||
*
|
||||
|
|
|
@ -224,6 +224,7 @@ public abstract class Lexer extends Recognizer<Integer, LexerATNSimulator>
|
|||
this._factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenFactory<? extends Token> getTokenFactory() {
|
||||
return _factory;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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.
|
||||
*/
|
||||
|
|
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ε 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;
|
||||
|
|
|
@ -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,
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ public class EmptyPredictionContext extends SingletonPredictionContext {
|
|||
super(null, EMPTY_RETURN_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() { return true; }
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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) );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
// }
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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 => 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}} => 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}} => 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}} => 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}} => 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}} => 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>|>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} > 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} > 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue