reorg to pass round a match object in matches_()

This commit is contained in:
Terence Parr 2013-11-21 10:30:37 -08:00
parent e836544d30
commit 1e83557819
6 changed files with 72 additions and 73 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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));

View File

@ -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;

View File

@ -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+">";

View File

@ -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" +