add getTokenTypeMap(), getRuleIndexMap() to recognizer. Gen new fields for that an ATN with bypass alts. Then methods for that: getATNWithBypassAlts(). Big changes to interface for ParseTreeMatch; create Parser.compileParseTreePattern() method. Convert rule names to rule indexes.

This commit is contained in:
Terence Parr 2013-11-24 14:04:46 -08:00
parent 4c52a103e1
commit bd91dc166d
7 changed files with 227 additions and 112 deletions

View File

@ -1,8 +1,22 @@
ANTLR v4 Honey Badger ANTLR v4 Honey Badger
November 22, 2013 November 24, 2013
* Ter adds tree pattern matching. * Ter adds tree pattern matching. Preferred interface:
ParseTree t = parser.expr();
ParseTreePattern p = parser.compileParseTreePattern("<ID>+0", MyParser.RULE_expr);
ParseTreeMatch m = p.match(t);
String id = m.get("ID");
or
String xpath = "//blockStatement/*";
String treePattern = "int <Identifier> = <expression>;";
ParseTreePattern p =
parser.compileParseTreePattern(treePattern,
JavaParser.RULE_localVariableDeclarationStatement);
List<ParseTreeMatch> matches = p.findAll(tree, xpath);
November 20, 2013 November 20, 2013

View File

@ -30,6 +30,8 @@
package org.antlr.v4.runtime; package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.ATN; import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializationOptions;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.atn.ATNSimulator; import org.antlr.v4.runtime.atn.ATNSimulator;
import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.atn.ATNState;
import org.antlr.v4.runtime.atn.ParserATNSimulator; import org.antlr.v4.runtime.atn.ParserATNSimulator;
@ -43,6 +45,8 @@ import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTreeListener; import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.pattern.ParseTreePattern;
import org.antlr.v4.runtime.tree.pattern.ParseTreePatternMatcher;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -133,6 +137,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
*/ */
protected boolean _buildParseTrees = true; protected boolean _buildParseTrees = true;
/** /**
* When {@link #setTrace}{@code (true)} is called, a reference to the * When {@link #setTrace}{@code (true)} is called, a reference to the
* {@link TraceListener} is stored here so it can be easily removed in a * {@link TraceListener} is stored here so it can be easily removed in a
@ -430,6 +435,59 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
_input.getTokenSource().setTokenFactory(factory); _input.getTokenSource().setTokenFactory(factory);
} }
/** The ATN with bypass alternatives is expensive to create so we create it lazily.
* The actual generated parsers override this method. These are just
* setters/getters. createATNWithBypassAlts() does the creation.
*/
public void setATNWithBypassAlts(ATN atn) { }
public ATN getATNWithBypassAlts() { return null; }
/** Create and cache the ATN with bypass alternatives. This is not
* part of the typical API--use compileParseTreePattern().
*/
public void createATNWithBypassAlts() {
if ( getATNWithBypassAlts()==null ) {
synchronized (Parser.class) { // create just one pattern matcher
if ( getATNWithBypassAlts()==null ) { // double-check
String sATN = getSerializedATN();
ATNDeserializationOptions deserializationOptions = new ATNDeserializationOptions();
deserializationOptions.setGenerateRuleBypassTransitions(true);
setATNWithBypassAlts( new ATNDeserializer(deserializationOptions).deserialize(sATN.toCharArray()) );
}
}
}
}
/** The preferred method of getting a tree pattern. For example,
* here's a sample use:
*
* ParseTree t = parser.expr();
* ParseTreePattern p = parser.compileParseTreePattern("<ID>+0", MyParser.RULE_expr);
* ParseTreeMatch m = p.match(t);
* String id = m.get("ID");
*/
public ParseTreePattern compileParseTreePattern(String pattern, int patternRuleIndex) {
if ( getTokenStream()!=null ) {
TokenSource tokenSource = getTokenStream().getTokenSource();
if ( tokenSource instanceof Lexer ) {
Lexer lexer = (Lexer)tokenSource;
return compileParseTreePattern(pattern, patternRuleIndex, lexer);
}
}
throw new UnsupportedOperationException("Parser can't discover a lexer to use");
}
/** The same as compileParseTreePattern(pattern,patternRuleName) but
* specify a lexer rather than trying to deduce it from this parser.
*/
public ParseTreePattern compileParseTreePattern(String pattern, int patternRuleIndex,
Lexer lexer)
{
createATNWithBypassAlts();
ParseTreePatternMatcher m = new ParseTreePatternMatcher(lexer, this);
return m.compile(pattern, patternRuleIndex);
}
@NotNull @NotNull
public ANTLRErrorStrategy getErrorHandler() { public ANTLRErrorStrategy getErrorHandler() {
return _errHandler; return _errHandler;
@ -725,15 +783,12 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return atn.nextTokens(s); return atn.nextTokens(s);
} }
// /** Compute the set of valid tokens reachable from the current /** Get a rule's index (i.e., RULE_ruleName field) or -1 if not found. */
// * position in the parse. public int getRuleIndex(String ruleName) {
// */ Integer ruleIndex = getRuleIndexMap().get(ruleName);
// public IntervalSet nextTokens(@NotNull RuleContext ctx) { if ( ruleIndex!=null ) return ruleIndex;
// ATN atn = getInterpreter().atn; return -1;
// ATNState s = atn.states.get(ctx.s); }
// if ( s == null ) return null;
// return atn.nextTokens(s, ctx);
// }
public ParserRuleContext getRuleContext() { return _ctx; } public ParserRuleContext getRuleContext() { return _ctx; }

View File

@ -36,6 +36,7 @@ import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.runtime.misc.Nullable;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
public abstract class Recognizer<Symbol, ATNInterpreter extends ATNSimulator> { public abstract class Recognizer<Symbol, ATNInterpreter extends ATNSimulator> {
@ -59,6 +60,22 @@ public abstract class Recognizer<Symbol, ATNInterpreter extends ATNSimulator> {
public abstract String[] getRuleNames(); public abstract String[] getRuleNames();
/** Used for xpath, tree pattern compilation */
public Map<String, Integer> getTokenTypeMap() {
throw new UnsupportedOperationException("recognizer implementation must implement this");
}
/** Used for xpath, tree pattern compilation */
public Map<String, Integer> getRuleIndexMap() {
throw new UnsupportedOperationException("recognizer implementation must implement this");
}
public int getTokenType(String tokenName) {
Integer ttype = getTokenTypeMap().get(tokenName);
if ( ttype!=null ) return ttype;
return Token.INVALID_TYPE;
}
/** If this recognizer was generated, it will have a serialized ATN /** If this recognizer was generated, it will have a serialized ATN
* representation of the grammar. * representation of the grammar.
* *

View File

@ -30,8 +30,6 @@
package org.antlr.v4.runtime.tree.pattern; package org.antlr.v4.runtime.tree.pattern;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.xpath.XPath; import org.antlr.v4.runtime.tree.xpath.XPath;
@ -43,28 +41,36 @@ import java.util.List;
* ParseTreePatternMatcher.compile(). * ParseTreePatternMatcher.compile().
*/ */
public class ParseTreePattern { public class ParseTreePattern {
public String patternRuleName; protected int patternRuleIndex;
public String pattern; protected String pattern;
public ParseTree patternTree; protected ParseTree patternTree;
public ParseTreePatternMatcher matcher;
public ParseTreePattern(String patternRuleName, String pattern, ParseTree patternTree) { public ParseTreePattern(ParseTreePatternMatcher matcher,
this.patternRuleName = patternRuleName; String pattern, int patternRuleIndex, ParseTree patternTree)
{
this.matcher = matcher;
this.patternRuleIndex = patternRuleIndex;
this.pattern = pattern; this.pattern = pattern;
this.patternTree = patternTree; this.patternTree = patternTree;
} }
/** Find all nodes in the tree that match xpath and also the tree public ParseTreeMatch match(ParseTree tree) {
* pattern. Return the list of ParseTreeMatch objects for all matches. return matcher.match(tree, this);
}
public boolean matches(ParseTree tree) {
return matcher.match(tree, this).succeeded();
}
/** Find all nodes using xpath and then try to match those subtrees
* against this tree pattern
*/ */
public static List<ParseTreeMatch> findAll(ParseTree tree, String xpath, public List<ParseTreeMatch> findAll(ParseTree tree, String xpath) {
String pattern, String patternRuleName, Collection<ParseTree> subtrees = XPath.findAll(tree, xpath, matcher.getParser());
Lexer lexer, Parser parser)
{
Collection<ParseTree> subtrees = XPath.findAll(tree, xpath, parser);
List<ParseTreeMatch> matches = new ArrayList<ParseTreeMatch>(); List<ParseTreeMatch> matches = new ArrayList<ParseTreeMatch>();
ParseTreePatternMatcher p = new ParseTreePatternMatcher(lexer, parser);
for (ParseTree t : subtrees) { for (ParseTree t : subtrees) {
ParseTreeMatch match = p.match(t, pattern, patternRuleName); ParseTreeMatch match = match(t);
if ( match.succeeded() ) { if ( match.succeeded() ) {
matches.add(match); matches.add(match);
} }
@ -72,10 +78,19 @@ public class ParseTreePattern {
return matches; return matches;
} }
/** Does the tree match the pattern matched as a patternRuleName? */ public ParseTreePatternMatcher getParseTreePattern() {
public static ParseTreeMatch match(ParseTree tree, String pattern, String patternRuleName, return matcher;
Lexer lexer, Parser parser) { }
ParseTreePatternMatcher p = new ParseTreePatternMatcher(lexer, parser);
return p.match(tree, pattern, patternRuleName); public String getPattern() {
return pattern;
}
public int getPatternRuleIndex() {
return patternRuleIndex;
}
public ParseTree getPatternTree() {
return patternTree;
} }
} }

View File

@ -38,10 +38,6 @@ import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserInterpreter; import org.antlr.v4.runtime.ParserInterpreter;
import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializationOptions;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.misc.Utils;
import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode; import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.v4.runtime.tree.TerminalNode;
@ -51,7 +47,6 @@ import java.io.StringReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
/** A tree pattern matching mechanism for ANTLR ParseTrees. /** A tree pattern matching mechanism for ANTLR ParseTrees.
* *
@ -79,9 +74,7 @@ import java.util.Map;
* not match. * not match.
* *
* For efficiency, you can compile a tree pattern in string form to a * For efficiency, you can compile a tree pattern in string form to a
* ParseTreePattern object. It is also expensive to create a * ParseTreePattern object.
* ParseTreePatternMatcher object, so create one of those and reuse
* it.
* *
* See TestParseTreeMatcher for lots of examples. ParseTreePattern * See TestParseTreeMatcher for lots of examples. ParseTreePattern
* has two static helper methods: findAll() and match() that are easy * has two static helper methods: findAll() and match() that are easy
@ -129,14 +122,6 @@ public class ParseTreePatternMatcher {
protected String start = "<", stop=">"; protected String start = "<", stop=">";
protected String escape = "\\"; // e.g., \< and \> must escape BOTH! protected String escape = "\\"; // e.g., \< and \> must escape BOTH!
/** This ATN has alternatives to match special imaginary tokens for rules like <expr> */
protected ATN atnWithBypassAlts;
/** Maps the rule name to rule index; computed during the constructor
* for efficient use later.
*/
protected Map<String, Integer> ruleToIndex;
public ParseTreePatternMatcher() { } // used for testing only public ParseTreePatternMatcher() { } // used for testing only
/** Constructs a pattern match or from a lecture and parser object. /** Constructs a pattern match or from a lecture and parser object.
@ -147,11 +132,9 @@ public class ParseTreePatternMatcher {
public ParseTreePatternMatcher(Lexer lexer, Parser parser) { public ParseTreePatternMatcher(Lexer lexer, Parser parser) {
this.lexer = lexer; this.lexer = lexer;
this.parser = parser; this.parser = parser;
String sATN = parser.getSerializedATN(); if ( parser!=null ) {
ATNDeserializationOptions deserializationOptions = new ATNDeserializationOptions(); parser.createATNWithBypassAlts();
deserializationOptions.setGenerateRuleBypassTransitions(true); }
atnWithBypassAlts = new ATNDeserializer(deserializationOptions).deserialize(sATN.toCharArray());
ruleToIndex = Utils.toMap(parser.getRuleNames());
} }
public void setDelimiters(String start, String stop, String escapeLeft) { public void setDelimiters(String start, String stop, String escapeLeft) {
@ -160,13 +143,13 @@ public class ParseTreePatternMatcher {
this.escape = escapeLeft; this.escape = escapeLeft;
} }
/** Does pattern matched as a patternRuleName match tree? */ /** Does pattern matched as rule patternRuleIndex match tree? */
public boolean matches(ParseTree tree, String pattern, String patternRuleName) { public boolean matches(ParseTree tree, String pattern, int patternRuleIndex) {
ParseTreePattern p = compile(pattern, patternRuleName); ParseTreePattern p = compile(pattern, patternRuleIndex);
return matches(tree, p); return matches(tree, p);
} }
/** Does pattern matched as a patternRuleName match tree? Pass in a /** Does pattern matched as rule patternRuleIndex match tree? Pass in a
* compiled pattern instead of a string representation of a tree pattern. * compiled pattern instead of a string representation of a tree pattern.
*/ */
public boolean matches(ParseTree tree, ParseTreePattern pattern) { public boolean matches(ParseTree tree, ParseTreePattern pattern) {
@ -175,16 +158,16 @@ public class ParseTreePatternMatcher {
return match.succeeded(); return match.succeeded();
} }
/** Compare pattern matched as a patternRuleName against tree and /** Compare pattern matched as rule patternRuleIndex against tree and
* return a ParseTreeMatch object that contains the matched elements, * return a ParseTreeMatch object that contains the matched elements,
* or the node at which the match failed. * or the node at which the match failed.
*/ */
public ParseTreeMatch match(ParseTree tree, String pattern, String patternRuleName) { public ParseTreeMatch match(ParseTree tree, String pattern, int patternRuleIndex) {
ParseTreePattern p = compile(pattern, patternRuleName); ParseTreePattern p = compile(pattern, patternRuleIndex);
return match(tree, p); return match(tree, p);
} }
/** Compare pattern matched as a patternRuleName against tree and /** Compare pattern matched against tree and
* return a ParseTreeMatch object that contains the matched elements, * return a ParseTreeMatch object that contains the matched elements,
* or the node at which the match failed. Pass in a compiled pattern * or the node at which the match failed. Pass in a compiled pattern
* instead of a string representation of a tree pattern. * instead of a string representation of a tree pattern.
@ -198,7 +181,7 @@ public class ParseTreePatternMatcher {
/** For repeated use of a tree pattern, compile it to a ParseTreePattern /** For repeated use of a tree pattern, compile it to a ParseTreePattern
* using this method. * using this method.
*/ */
public ParseTreePattern compile(String pattern, String patternRuleName) { public ParseTreePattern compile(String pattern, int patternRuleIndex) {
List<? extends Token> tokenList = tokenize(pattern); List<? extends Token> tokenList = tokenize(pattern);
ListTokenSource tokenSrc = new ListTokenSource(tokenList); ListTokenSource tokenSrc = new ListTokenSource(tokenList);
CommonTokenStream tokens = new CommonTokenStream(tokenSrc); CommonTokenStream tokens = new CommonTokenStream(tokenSrc);
@ -206,20 +189,19 @@ public class ParseTreePatternMatcher {
ParserInterpreter parserInterp = new ParserInterpreter(parser.getGrammarFileName(), ParserInterpreter parserInterp = new ParserInterpreter(parser.getGrammarFileName(),
Arrays.asList(parser.getTokenNames()), Arrays.asList(parser.getTokenNames()),
Arrays.asList(parser.getRuleNames()), Arrays.asList(parser.getRuleNames()),
atnWithBypassAlts, parser.getATNWithBypassAlts(),
tokens); tokens);
ParseTree tree = null; ParseTree tree = null;
try { try {
Integer ruleIndex = ruleToIndex.get(patternRuleName); tree = parserInterp.parse(patternRuleIndex);
tree = parserInterp.parse(ruleIndex);
// System.out.println("pattern tree = "+tree.toStringTree(parserInterp)); // System.out.println("pattern tree = "+tree.toStringTree(parserInterp));
} }
catch (Exception e) { catch (Exception e) {
throw new CannotInvokeStartRule(e); throw new CannotInvokeStartRule(e);
} }
return new ParseTreePattern(patternRuleName, pattern, tree); return new ParseTreePattern(this, pattern, patternRuleIndex, tree);
} }
public Lexer getLexer() { public Lexer getLexer() {
@ -319,10 +301,6 @@ public class ParseTreePatternMatcher {
} }
public List<? extends Token> tokenize(String pattern) { public List<? extends Token> tokenize(String pattern) {
// make maps for quick look up
Map<String, Integer> tokenNameToType = Utils.toMap(parser.getTokenNames());
Map<String, Integer> ruleNameToIndex = Utils.toMap(parser.getRuleNames());
// split pattern into chunks: sea (raw input) and islands (<ID>, <expr>) // split pattern into chunks: sea (raw input) and islands (<ID>, <expr>)
List<Chunk> chunks = split(pattern); List<Chunk> chunks = split(pattern);
@ -333,19 +311,19 @@ public class ParseTreePatternMatcher {
TagChunk tagChunk = (TagChunk)chunk; TagChunk tagChunk = (TagChunk)chunk;
// add special rule token or conjure up new token from name // add special rule token or conjure up new token from name
if ( Character.isUpperCase(tagChunk.tag.charAt(0)) ) { if ( Character.isUpperCase(tagChunk.tag.charAt(0)) ) {
Integer ttype = tokenNameToType.get(tagChunk.tag); Integer ttype = parser.getTokenType(tagChunk.tag);
if ( ttype==null ) { if ( ttype==Token.INVALID_TYPE ) {
throw new IllegalArgumentException("Unknown token "+tagChunk.tag+" in pattern: "+pattern); throw new IllegalArgumentException("Unknown token "+tagChunk.tag+" in pattern: "+pattern);
} }
TokenTagToken t = new TokenTagToken(tagChunk.tag, ttype, tagChunk.label); TokenTagToken t = new TokenTagToken(tagChunk.tag, ttype, tagChunk.label);
tokens.add(t); tokens.add(t);
} }
else if ( Character.isLowerCase(tagChunk.tag.charAt(0)) ) { else if ( Character.isLowerCase(tagChunk.tag.charAt(0)) ) {
Integer ruleIndex = ruleNameToIndex.get(tagChunk.tag); int ruleIndex = parser.getRuleIndex(tagChunk.tag);
if ( ruleIndex==null ) { if ( ruleIndex==-1 ) {
throw new IllegalArgumentException("Unknown rule "+tagChunk.tag+" in pattern: "+pattern); throw new IllegalArgumentException("Unknown rule "+tagChunk.tag+" in pattern: "+pattern);
} }
int ruleImaginaryTokenType = atnWithBypassAlts.ruleToTokenType[ruleIndex]; int ruleImaginaryTokenType = parser.getATNWithBypassAlts().ruleToTokenType[ruleIndex];
tokens.add(new RuleTagToken(tagChunk.tag, ruleImaginaryTokenType, tagChunk.label)); tokens.add(new RuleTagToken(tagChunk.tag, ruleImaginaryTokenType, tagChunk.label));
} }
else { else {

View File

@ -56,6 +56,7 @@ import org.antlr.v4.runtime.tree.*;
import java.util.List; import java.util.List;
import java.util.Iterator; import java.util.Iterator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map;
<parser> <parser>
>> >>
@ -224,11 +225,13 @@ public class <parser.name> extends <superClass> {
public static final String[] tokenNames = { public static final String[] tokenNames = {
<parser.tokenNames:{t | <t>}; null="\"\<INVALID>\"", separator=", ", wrap, anchor> <parser.tokenNames:{t | <t>}; null="\"\<INVALID>\"", separator=", ", wrap, anchor>
}; };
public static final Map\<String, Integer> tokenNameToType = Utils.toMap(tokenNames);
public static final int public static final int
<parser.rules:{r | RULE_<r.name> = <r.index>}; separator=", ", wrap, anchor>; <parser.rules:{r | RULE_<r.name> = <r.index>}; separator=", ", wrap, anchor>;
public static final String[] ruleNames = { public static final String[] ruleNames = {
<parser.ruleNames:{r | "<r>"}; separator=", ", wrap, anchor> <parser.ruleNames:{r | "<r>"}; separator=", ", wrap, anchor>
}; };
public static final Map\<String, Integer> ruleNameToIndex = Utils.toMap(ruleNames);
@Override @Override
public String getGrammarFileName() { return "<parser.grammarFileName; format="java-escape">"; } public String getGrammarFileName() { return "<parser.grammarFileName; format="java-escape">"; }
@ -244,6 +247,15 @@ public class <parser.name> extends <superClass> {
@Override @Override
public ATN getATN() { return _ATN; } public ATN getATN() { return _ATN; }
@Override
public ATN getATNWithBypassAlts() { return _ATNWithBypassAlts; }
@Override
public void setATNWithBypassAlts(ATN atn) { _ATNWithBypassAlts = atn; }
@Override
public Map\<String, Integer> getTokenTypeMap() { return tokenNameToType; }
@Override
public Map\<String, Integer> getRuleIndexMap() { return ruleNameToIndex; }
<namedActions.members> <namedActions.members>
<parser:(ctor)()> <parser:(ctor)()>
@ -841,6 +853,7 @@ import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*; import org.antlr.v4.runtime.misc.*;
import java.util.Map;
<lexer> <lexer>
>> >>
@ -862,9 +875,16 @@ public class <lexer.name> extends <superClass> {
"\<INVALID>", "\<INVALID>",
<lexer.tokenNames:{t | <t>}; separator=", ", wrap, anchor> <lexer.tokenNames:{t | <t>}; separator=", ", wrap, anchor>
}; };
public static final Map\<String, Integer> tokenNameToType = Utils.toMap(tokenNames);
public static final String[] ruleNames = { public static final String[] ruleNames = {
<lexer.ruleNames:{r | "<r>"}; separator=", ", wrap, anchor> <lexer.ruleNames:{r | "<r>"}; separator=", ", wrap, anchor>
}; };
public static final Map\<String, Integer> ruleNameToIndex = Utils.toMap(ruleNames);
@Override
public Map\<String, Integer> getTokenTypeMap() { return tokenNameToType; }
@Override
public Map\<String, Integer> getRuleIndexMap() { return ruleNameToIndex; }
<namedActions.members> <namedActions.members>
@ -915,6 +935,7 @@ public static final String _serializedATN =
<endif> <endif>
public static final ATN _ATN = public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray()); new ATNDeserializer().deserialize(_serializedATN.toCharArray());
public static ATN _ATNWithBypassAlts;
static { static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i \< _ATN.getNumberOfDecisions(); i++) { for (int i = 0; i \< _ATN.getNumberOfDecisions(); i++) {

View File

@ -1,6 +1,7 @@
package org.antlr.v4.test; package org.antlr.v4.test;
import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
@ -22,27 +23,27 @@ import static org.junit.Assert.assertTrue;
public class TestParseTreeMatcher extends BaseTest { public class TestParseTreeMatcher extends BaseTest {
@Test public void testChunking() throws Exception { @Test public void testChunking() throws Exception {
ParseTreePatternMatcher p = new ParseTreePatternMatcher(); ParseTreePatternMatcher m = new ParseTreePatternMatcher();
assertEquals("[ID, ' = ', expr, ' ;']", p.split("<ID> = <expr> ;").toString()); assertEquals("[ID, ' = ', expr, ' ;']", m.split("<ID> = <expr> ;").toString());
assertEquals("[' ', ID, ' = ', expr]", p.split(" <ID> = <expr>").toString()); assertEquals("[' ', ID, ' = ', expr]", m.split(" <ID> = <expr>").toString());
assertEquals("[ID, ' = ', expr]", p.split("<ID> = <expr>").toString()); assertEquals("[ID, ' = ', expr]", m.split("<ID> = <expr>").toString());
assertEquals("[expr]", p.split("<expr>").toString()); assertEquals("[expr]", m.split("<expr>").toString());
assertEquals("['<x> foo']", p.split("\\<x\\> foo").toString()); assertEquals("['<x> foo']", m.split("\\<x\\> foo").toString());
assertEquals("['foo <x> bar ', tag]", p.split("foo \\<x\\> bar <tag>").toString()); assertEquals("['foo <x> bar ', tag]", m.split("foo \\<x\\> bar <tag>").toString());
} }
@Test public void testDelimiters() throws Exception { @Test public void testDelimiters() throws Exception {
ParseTreePatternMatcher p = new ParseTreePatternMatcher(); ParseTreePatternMatcher m = new ParseTreePatternMatcher();
p.setDelimiters("<<", ">>", "$"); m.setDelimiters("<<", ">>", "$");
String result = p.split("<<ID>> = <<expr>> ;$<< ick $>>").toString(); String result = m.split("<<ID>> = <<expr>> ;$<< ick $>>").toString();
assertEquals("[ID, ' = ', expr, ' ;<< ick >>']", result); assertEquals("[ID, ' = ', expr, ' ;<< ick >>']", result);
} }
@Test public void testInvertedTags() throws Exception { @Test public void testInvertedTags() throws Exception {
ParseTreePatternMatcher p = new ParseTreePatternMatcher(); ParseTreePatternMatcher m= new ParseTreePatternMatcher();
String result = null; String result = null;
try { try {
p.split(">expr<"); m.split(">expr<");
} }
catch (IllegalArgumentException iae) { catch (IllegalArgumentException iae) {
result = iae.getMessage(); result = iae.getMessage();
@ -52,10 +53,10 @@ public class TestParseTreeMatcher extends BaseTest {
} }
@Test public void testUnclosedTag() throws Exception { @Test public void testUnclosedTag() throws Exception {
ParseTreePatternMatcher p = new ParseTreePatternMatcher(); ParseTreePatternMatcher m = new ParseTreePatternMatcher();
String result = null; String result = null;
try { try {
p.split("<expr hi mom"); m.split("<expr hi mom");
} }
catch (IllegalArgumentException iae) { catch (IllegalArgumentException iae) {
result = iae.getMessage(); result = iae.getMessage();
@ -65,10 +66,10 @@ public class TestParseTreeMatcher extends BaseTest {
} }
@Test public void testExtraClose() throws Exception { @Test public void testExtraClose() throws Exception {
ParseTreePatternMatcher p = new ParseTreePatternMatcher(); ParseTreePatternMatcher m = new ParseTreePatternMatcher();
String result = null; String result = null;
try { try {
p.split("<expr> >"); m.split("<expr> >");
} }
catch (IllegalArgumentException iae) { catch (IllegalArgumentException iae) {
result = iae.getMessage(); result = iae.getMessage();
@ -89,9 +90,9 @@ public class TestParseTreeMatcher extends BaseTest {
rawGenerateAndBuildRecognizer("X1.g4", grammar, "X1Parser", "X1Lexer", false); rawGenerateAndBuildRecognizer("X1.g4", grammar, "X1Parser", "X1Lexer", false);
assertTrue(ok); assertTrue(ok);
ParseTreePatternMatcher p = getMatcher("X1"); ParseTreePatternMatcher m = getPatternMatcher("X1");
List<? extends Token> tokens = p.tokenize("<ID> = <expr> ;"); List<? extends Token> tokens = m.tokenize("<ID> = <expr> ;");
String results = tokens.toString(); String results = tokens.toString();
String expected = "[ID:3, [@-1,1:1='=',<1>,1:1], expr:7, [@-1,1:1=';',<2>,1:1]]"; String expected = "[ID:3, [@-1,1:1='=',<1>,1:1], expr:7, [@-1,1:1=';',<2>,1:1]]";
assertEquals(expected, results); assertEquals(expected, results);
@ -110,10 +111,10 @@ public class TestParseTreeMatcher extends BaseTest {
rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false); rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false);
assertTrue(ok); assertTrue(ok);
ParseTreePatternMatcher p = getMatcher("X2"); ParseTreePatternMatcher m = getPatternMatcher("X2");
ParseTreePattern t = p.compile("<ID> = <expr> ;", "s"); ParseTreePattern t = m.compile("<ID> = <expr> ;", m.getParser().getRuleIndex("s"));
String results = t.patternTree.toStringTree(p.getParser()); String results = t.getPatternTree().toStringTree(m.getParser());
String expected = "(s <ID> = (expr <expr>) ;)"; String expected = "(s <ID> = (expr <expr>) ;)";
assertEquals(expected, results); assertEquals(expected, results);
} }
@ -131,10 +132,10 @@ public class TestParseTreeMatcher extends BaseTest {
rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false); rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false);
assertTrue(ok); assertTrue(ok);
ParseTreePatternMatcher p = getMatcher("X2"); ParseTreePatternMatcher m = getPatternMatcher("X2");
ParseTreePattern t = p.compile("<ID> = <expr> ;", "s"); ParseTreePattern t = m.compile("<ID> = <expr> ;", m.getParser().getRuleIndex("s"));
String results = t.patternTree.toStringTree(p.getParser()); String results = t.getPatternTree().toStringTree(m.getParser());
String expected = "(s <ID> = (expr <expr>) ;)"; String expected = "(s <ID> = (expr <expr>) ;)";
assertEquals(expected, results); assertEquals(expected, results);
} }
@ -150,10 +151,10 @@ public class TestParseTreeMatcher extends BaseTest {
rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false); rawGenerateAndBuildRecognizer("X2.g4", grammar, "X2Parser", "X2Lexer", false);
assertTrue(ok); assertTrue(ok);
ParseTreePatternMatcher p = getMatcher("X2"); ParseTreePatternMatcher m = getPatternMatcher("X2");
ParseTreePattern t = p.compile("<ID> = <ID> ;", "s"); ParseTreePattern t = m.compile("<ID> = <ID> ;", m.getParser().getRuleIndex("s"));
String results = t.patternTree.toStringTree(p.getParser()); String results = t.getPatternTree().toStringTree(m.getParser());
String expected = "(s <ID> = <ID> ;)"; String expected = "(s <ID> = <ID> ;)";
assertEquals(expected, results); assertEquals(expected, results);
} }
@ -326,24 +327,38 @@ public class TestParseTreeMatcher extends BaseTest {
ParseTree result = execParser(startRule, input, parserName, lexerName); ParseTree result = execParser(startRule, input, parserName, lexerName);
ParseTreePatternMatcher p = getMatcher(grammarName); ParseTreePattern p = getPattern(grammarName, pattern, startRule);
ParseTreeMatch match = p.match(result, pattern, startRule); ParseTreeMatch match = p.match(result);
boolean matched = match.succeeded(); boolean matched = match.succeeded();
if ( invertMatch ) assertFalse(matched); if ( invertMatch ) assertFalse(matched);
else assertTrue(matched); else assertTrue(matched);
return match; return match;
} }
public ParseTreePatternMatcher getMatcher(String name) throws Exception { public ParseTreePattern getPattern(String grammarName, String pattern, String ruleName)
Class<? extends Lexer> lexerClass = loadLexerClassFromTempDir(name+"Lexer"); throws Exception
Class<? extends Parser> parserClass = loadParserClassFromTempDir(name + "Parser"); {
Class<? extends Lexer> c = lexerClass.asSubclass(Lexer.class); Class<? extends Lexer> lexerClass = loadLexerClassFromTempDir(grammarName + "Lexer");
Constructor<? extends Lexer> ctor = c.getConstructor(CharStream.class); Constructor<? extends Lexer> ctor = lexerClass.getConstructor(CharStream.class);
Lexer lexer = ctor.newInstance((CharStream) null); Lexer lexer = ctor.newInstance((CharStream) null);
Class<? extends Parser> pc = parserClass.asSubclass(Parser.class); Class<? extends Parser> parserClass = loadParserClassFromTempDir(grammarName + "Parser");
Constructor<? extends Parser> pctor = pc.getConstructor(TokenStream.class); Constructor<? extends Parser> pctor = parserClass.getConstructor(TokenStream.class);
Parser parser = pctor.newInstance((TokenStream)null); Parser parser = pctor.newInstance(new CommonTokenStream(lexer));
return parser.compileParseTreePattern(pattern, parser.getRuleIndex(ruleName));
}
public ParseTreePatternMatcher getPatternMatcher(String grammarName)
throws Exception
{
Class<? extends Lexer> lexerClass = loadLexerClassFromTempDir(grammarName + "Lexer");
Constructor<? extends Lexer> ctor = lexerClass.getConstructor(CharStream.class);
Lexer lexer = ctor.newInstance((CharStream) null);
Class<? extends Parser> parserClass = loadParserClassFromTempDir(grammarName + "Parser");
Constructor<? extends Parser> pctor = parserClass.getConstructor(TokenStream.class);
Parser parser = pctor.newInstance(new CommonTokenStream(lexer));
return new ParseTreePatternMatcher(lexer, parser); return new ParseTreePatternMatcher(lexer, parser);
} }