reorg to pass round a match object in matches_()
This commit is contained in:
parent
e836544d30
commit
1e83557819
|
@ -14,6 +14,9 @@ public class ParseTreeMatch {
|
||||||
|
|
||||||
protected List<Pair<String,? extends ParseTree>> labels;
|
protected List<Pair<String,? extends ParseTree>> labels;
|
||||||
|
|
||||||
|
/** Where in actual parse tree we failed if we can't match pattern */
|
||||||
|
ParseTree mismatchedNode;
|
||||||
|
|
||||||
public ParseTreeMatch(ParseTree tree, ParseTreePattern pattern) {
|
public ParseTreeMatch(ParseTree tree, ParseTreePattern pattern) {
|
||||||
this.tree = tree;
|
this.tree = tree;
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
|
|
|
@ -1,46 +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.tree.pattern;
|
|
||||||
|
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
|
||||||
|
|
||||||
/** Record where we failed in tree. */
|
|
||||||
public class ParseTreeMatchFailed extends ParseTreeMatch {
|
|
||||||
/** Where in actual parse tree we failed to match pattern */
|
|
||||||
ParseTree offendingNode;
|
|
||||||
|
|
||||||
public ParseTreeMatchFailed(ParseTree tree, ParseTree offendingNode,
|
|
||||||
ParseTreePattern pattern)
|
|
||||||
{
|
|
||||||
super(tree, pattern);
|
|
||||||
this.offendingNode = offendingNode;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -99,27 +99,39 @@ public class ParseTreePatternMatcher {
|
||||||
|
|
||||||
public boolean matches(ParseTree tree, String patternRuleName, String pattern) {
|
public boolean matches(ParseTree tree, String patternRuleName, String pattern) {
|
||||||
ParseTreePattern p = compile(patternRuleName, pattern);
|
ParseTreePattern p = compile(patternRuleName, pattern);
|
||||||
ParseTreeMatch m = matches_(tree, p.patternTree, p);
|
ParseTreeMatch match = new ParseTreeMatch(tree, p);
|
||||||
return !(m instanceof ParseTreeMatchFailed);
|
matches_(tree, p.patternTree, p, match);
|
||||||
|
return match.mismatchedNode==null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(ParseTree tree, ParseTreePattern pattern) {
|
public boolean matches(ParseTree tree, ParseTreePattern pattern) {
|
||||||
ParseTreeMatch m = matches_(tree, pattern.patternTree, pattern);
|
ParseTreeMatch match = new ParseTreeMatch(tree, pattern);
|
||||||
return !(m instanceof ParseTreeMatchFailed);
|
matches_(tree, pattern.patternTree, pattern, match);
|
||||||
|
return match.mismatchedNode==null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParseTreeMatch match(ParseTree tree, String patternRuleName, String pattern) {
|
public ParseTreeMatch match(ParseTree tree, String patternRuleName, String pattern) {
|
||||||
ParseTreePattern p = compile(patternRuleName, pattern);
|
ParseTreePattern p = compile(patternRuleName, pattern);
|
||||||
return matches_(tree, p.patternTree, p);
|
ParseTreeMatch match = new ParseTreeMatch(tree, p);
|
||||||
|
if ( matches_(tree, p.patternTree, p, match) ) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParseTreeMatch match(ParseTree tree, ParseTreePattern pattern) {
|
public ParseTreeMatch match(ParseTree tree, ParseTreePattern pattern) {
|
||||||
return matches_(tree, pattern.patternTree, pattern);
|
ParseTreeMatch match = new ParseTreeMatch(tree, pattern);
|
||||||
|
matches_(tree, pattern.patternTree, pattern, match);
|
||||||
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ParseTreeMatch matches_(ParseTree tree, ParseTree patternTree, ParseTreePattern pattern) {
|
protected boolean matches_(ParseTree tree,
|
||||||
|
ParseTree patternTree,
|
||||||
|
ParseTreePattern pattern,
|
||||||
|
ParseTreeMatch match)
|
||||||
|
{
|
||||||
if ( tree==null || patternTree==null ) {
|
if ( tree==null || patternTree==null ) {
|
||||||
return new ParseTreeMatchFailed(tree, null, pattern);
|
return false;
|
||||||
}
|
}
|
||||||
// x and <ID>, x and y, or x and x; or could be mismatched types
|
// x and <ID>, x and y, or x and x; or could be mismatched types
|
||||||
if ( tree instanceof TerminalNode && patternTree instanceof TerminalNode ) {
|
if ( tree instanceof TerminalNode && patternTree instanceof TerminalNode ) {
|
||||||
|
@ -129,19 +141,17 @@ public class ParseTreePatternMatcher {
|
||||||
// both are tokens and they have same type
|
// both are tokens and they have same type
|
||||||
if ( t1.getSymbol().getType() == t2.getSymbol().getType() ) {
|
if ( t1.getSymbol().getType() == t2.getSymbol().getType() ) {
|
||||||
if ( t2.getSymbol() instanceof TokenTagToken ) { // x and <ID>
|
if ( t2.getSymbol() instanceof TokenTagToken ) { // x and <ID>
|
||||||
m = new ParseTreeMatch(tree, pattern);
|
|
||||||
}
|
}
|
||||||
else if ( t1.getText().equals(t2.getText()) ) { // x and x
|
else if ( t1.getText().equals(t2.getText()) ) { // x and x
|
||||||
m = new ParseTreeMatch(tree, pattern);
|
|
||||||
}
|
}
|
||||||
else { // x and y
|
else { // x and y
|
||||||
m = new ParseTreeMatchFailed(tree, t1, pattern);
|
match.mismatchedNode = t1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m = new ParseTreeMatchFailed(tree, t1, pattern);
|
match.mismatchedNode = t1;
|
||||||
}
|
}
|
||||||
return m;
|
return match.mismatchedNode==null;
|
||||||
}
|
}
|
||||||
if ( tree instanceof ParserRuleContext && patternTree instanceof ParserRuleContext ) {
|
if ( tree instanceof ParserRuleContext && patternTree instanceof ParserRuleContext ) {
|
||||||
ParserRuleContext r1 = (ParserRuleContext)tree;
|
ParserRuleContext r1 = (ParserRuleContext)tree;
|
||||||
|
@ -150,29 +160,28 @@ public class ParseTreePatternMatcher {
|
||||||
if ( isRuleTag(r2) ) {
|
if ( isRuleTag(r2) ) {
|
||||||
ParseTreeMatch m = null;
|
ParseTreeMatch m = null;
|
||||||
if ( r1.getRuleContext().getRuleIndex() == r2.getRuleContext().getRuleIndex() ) {
|
if ( r1.getRuleContext().getRuleIndex() == r2.getRuleContext().getRuleIndex() ) {
|
||||||
m = new ParseTreeMatch(tree, pattern);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m = new ParseTreeMatchFailed(tree, r1, pattern);
|
match.mismatchedNode = r1;
|
||||||
}
|
}
|
||||||
return m;
|
return match.mismatchedNode==null;
|
||||||
}
|
}
|
||||||
// (expr ...) and (expr ...)
|
// (expr ...) and (expr ...)
|
||||||
if ( r1.getChildCount()!=r2.getChildCount() ) {
|
if ( r1.getChildCount()!=r2.getChildCount() ) {
|
||||||
return new ParseTreeMatchFailed(tree, r1, pattern);
|
match.mismatchedNode = r1;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
int n = r1.getChildCount();
|
int n = r1.getChildCount();
|
||||||
for (int i = 0; i<n; i++) {
|
for (int i = 0; i<n; i++) {
|
||||||
ParseTreeMatch childMatch =
|
boolean childMatch =
|
||||||
matches_(r1.getChild(i), patternTree.getChild(i), pattern);
|
matches_(r1.getChild(i), patternTree.getChild(i), pattern, match);
|
||||||
if ( childMatch instanceof ParseTreeMatchFailed ) {
|
if ( !childMatch ) return false;
|
||||||
return childMatch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new ParseTreeMatch(tree, pattern);
|
return true;
|
||||||
}
|
}
|
||||||
// if nodes aren't both tokens or both rule nodes, can't match
|
// if nodes aren't both tokens or both rule nodes, can't match
|
||||||
return new ParseTreeMatchFailed(tree, tree, pattern);
|
match.mismatchedNode = tree;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is t (expr <expr>) subtree? */
|
/** Is t (expr <expr>) subtree? */
|
||||||
|
@ -229,12 +238,14 @@ 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)) ) {
|
||||||
tokens.add(new TokenTagToken(tagChunk.tag, tokenNameToType.get(tagChunk.tag)));
|
Integer ttype = tokenNameToType.get(tagChunk.tag);
|
||||||
|
TokenTagToken t = new TokenTagToken(tagChunk.tag, ttype, tagChunk.label);
|
||||||
|
tokens.add(t);
|
||||||
}
|
}
|
||||||
else if ( Character.isLowerCase(tagChunk.tag.charAt(0)) ) {
|
else if ( Character.isLowerCase(tagChunk.tag.charAt(0)) ) {
|
||||||
int ruleIndex = ruleNameToIndex.get(tagChunk.tag);
|
int ruleIndex = ruleNameToIndex.get(tagChunk.tag);
|
||||||
int ruleImaginaryTokenType = atnWithBypassAlts.ruleToTokenType[ruleIndex];
|
int ruleImaginaryTokenType = atnWithBypassAlts.ruleToTokenType[ruleIndex];
|
||||||
tokens.add(new RuleTagToken(tagChunk.tag, ruleImaginaryTokenType));
|
tokens.add(new RuleTagToken(tagChunk.tag, ruleImaginaryTokenType, tagChunk.label));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
System.err.println("invalid tag: "+tagChunk.tag);
|
System.err.println("invalid tag: "+tagChunk.tag);
|
||||||
|
@ -341,7 +352,14 @@ public class ParseTreePatternMatcher {
|
||||||
for (int i=0; i<ntags; i++) {
|
for (int i=0; i<ntags; i++) {
|
||||||
// copy inside of <tag>
|
// copy inside of <tag>
|
||||||
String tag = pattern.substring(starts.get(i) + start.length(), stops.get(i));
|
String tag = pattern.substring(starts.get(i) + start.length(), stops.get(i));
|
||||||
chunks.add(new TagChunk(tag));
|
String ruleOrToken = tag;
|
||||||
|
String label = null;
|
||||||
|
int colon = tag.indexOf(':');
|
||||||
|
if ( colon >= 0 ) {
|
||||||
|
label = tag.substring(0,colon);
|
||||||
|
ruleOrToken = tag.substring(colon+1, tag.length());
|
||||||
|
}
|
||||||
|
chunks.add(new TagChunk(label, ruleOrToken));
|
||||||
if ( i+1 < ntags ) {
|
if ( i+1 < ntags ) {
|
||||||
// copy from end of <tag> to start of next
|
// copy from end of <tag> to start of next
|
||||||
String text = pattern.substring(stops.get(i) + stop.length(), starts.get(i + 1));
|
String text = pattern.substring(stops.get(i) + stop.length(), starts.get(i + 1));
|
||||||
|
|
|
@ -7,12 +7,18 @@ import org.antlr.v4.runtime.TokenSource;
|
||||||
public class RuleTagToken implements Token {
|
public class RuleTagToken implements Token {
|
||||||
protected String ruleName;
|
protected String ruleName;
|
||||||
protected int ruleImaginaryTokenType;
|
protected int ruleImaginaryTokenType;
|
||||||
|
protected String label;
|
||||||
|
|
||||||
public RuleTagToken(String ruleName, int ruleImaginaryTokenType) {
|
public RuleTagToken(String ruleName, int ruleImaginaryTokenType) {
|
||||||
this.ruleName = ruleName;
|
this.ruleName = ruleName;
|
||||||
this.ruleImaginaryTokenType = ruleImaginaryTokenType;
|
this.ruleImaginaryTokenType = ruleImaginaryTokenType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RuleTagToken(String ruleName, int ruleImaginaryTokenType, String label) {
|
||||||
|
this(ruleName, ruleImaginaryTokenType);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getChannel() {
|
public int getChannel() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -4,12 +4,18 @@ import org.antlr.v4.runtime.CommonToken;
|
||||||
|
|
||||||
public class TokenTagToken extends CommonToken {
|
public class TokenTagToken extends CommonToken {
|
||||||
protected String tokenName;
|
protected String tokenName;
|
||||||
|
protected String label;
|
||||||
|
|
||||||
public TokenTagToken(String tokenName, int type) {
|
public TokenTagToken(String tokenName, int type) {
|
||||||
super(type);
|
super(type);
|
||||||
this.tokenName = tokenName;
|
this.tokenName = tokenName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TokenTagToken(String tokenName, int type, String label) {
|
||||||
|
this(tokenName, type);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return "<"+tokenName+">";
|
return "<"+tokenName+">";
|
||||||
|
|
|
@ -181,6 +181,18 @@ public class TestParseTreeMatcher extends BaseTest {
|
||||||
checkPatternMatch("X3.g4", grammar, "s", input, pattern, "X3");
|
checkPatternMatch("X3.g4", grammar, "s", input, pattern, "X3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void testIDNodeWithLabelMatches() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar X3;\n" +
|
||||||
|
"s : ID ';' ;\n" +
|
||||||
|
"ID : [a-z]+ ;\n" +
|
||||||
|
"WS : [ \\r\\n\\t]+ -> skip ;\n";
|
||||||
|
|
||||||
|
String input = "x ;";
|
||||||
|
String pattern = "<id:ID>;";
|
||||||
|
checkPatternMatch("X3.g4", grammar, "s", input, pattern, "X3");
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void testTokenAndRuleMatch() throws Exception {
|
@Test public void testTokenAndRuleMatch() throws Exception {
|
||||||
String grammar =
|
String grammar =
|
||||||
"grammar X4;\n" +
|
"grammar X4;\n" +
|
||||||
|
|
Loading…
Reference in New Issue