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;
|
||||
|
||||
/** Where in actual parse tree we failed if we can't match pattern */
|
||||
ParseTree mismatchedNode;
|
||||
|
||||
public ParseTreeMatch(ParseTree tree, ParseTreePattern pattern) {
|
||||
this.tree = tree;
|
||||
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) {
|
||||
ParseTreePattern p = compile(patternRuleName, pattern);
|
||||
ParseTreeMatch m = matches_(tree, p.patternTree, p);
|
||||
return !(m instanceof ParseTreeMatchFailed);
|
||||
ParseTreeMatch match = new ParseTreeMatch(tree, p);
|
||||
matches_(tree, p.patternTree, p, match);
|
||||
return match.mismatchedNode==null;
|
||||
}
|
||||
|
||||
public boolean matches(ParseTree tree, ParseTreePattern pattern) {
|
||||
ParseTreeMatch m = matches_(tree, pattern.patternTree, pattern);
|
||||
return !(m instanceof ParseTreeMatchFailed);
|
||||
ParseTreeMatch match = new ParseTreeMatch(tree, pattern);
|
||||
matches_(tree, pattern.patternTree, pattern, match);
|
||||
return match.mismatchedNode==null;
|
||||
}
|
||||
|
||||
public ParseTreeMatch match(ParseTree tree, String patternRuleName, String 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) {
|
||||
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 ) {
|
||||
return new ParseTreeMatchFailed(tree, null, pattern);
|
||||
return false;
|
||||
}
|
||||
// x and <ID>, x and y, or x and x; or could be mismatched types
|
||||
if ( tree instanceof TerminalNode && patternTree instanceof TerminalNode ) {
|
||||
|
@ -129,19 +141,17 @@ public class ParseTreePatternMatcher {
|
|||
// both are tokens and they have same type
|
||||
if ( t1.getSymbol().getType() == t2.getSymbol().getType() ) {
|
||||
if ( t2.getSymbol() instanceof TokenTagToken ) { // x and <ID>
|
||||
m = new ParseTreeMatch(tree, pattern);
|
||||
}
|
||||
else if ( t1.getText().equals(t2.getText()) ) { // x and x
|
||||
m = new ParseTreeMatch(tree, pattern);
|
||||
}
|
||||
else { // x and y
|
||||
m = new ParseTreeMatchFailed(tree, t1, pattern);
|
||||
match.mismatchedNode = t1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m = new ParseTreeMatchFailed(tree, t1, pattern);
|
||||
match.mismatchedNode = t1;
|
||||
}
|
||||
return m;
|
||||
return match.mismatchedNode==null;
|
||||
}
|
||||
if ( tree instanceof ParserRuleContext && patternTree instanceof ParserRuleContext ) {
|
||||
ParserRuleContext r1 = (ParserRuleContext)tree;
|
||||
|
@ -150,29 +160,28 @@ public class ParseTreePatternMatcher {
|
|||
if ( isRuleTag(r2) ) {
|
||||
ParseTreeMatch m = null;
|
||||
if ( r1.getRuleContext().getRuleIndex() == r2.getRuleContext().getRuleIndex() ) {
|
||||
m = new ParseTreeMatch(tree, pattern);
|
||||
}
|
||||
else {
|
||||
m = new ParseTreeMatchFailed(tree, r1, pattern);
|
||||
match.mismatchedNode = r1;
|
||||
}
|
||||
return m;
|
||||
return match.mismatchedNode==null;
|
||||
}
|
||||
// (expr ...) and (expr ...)
|
||||
if ( r1.getChildCount()!=r2.getChildCount() ) {
|
||||
return new ParseTreeMatchFailed(tree, r1, pattern);
|
||||
match.mismatchedNode = r1;
|
||||
return false;
|
||||
}
|
||||
int n = r1.getChildCount();
|
||||
for (int i = 0; i<n; i++) {
|
||||
ParseTreeMatch childMatch =
|
||||
matches_(r1.getChild(i), patternTree.getChild(i), pattern);
|
||||
if ( childMatch instanceof ParseTreeMatchFailed ) {
|
||||
return childMatch;
|
||||
}
|
||||
boolean childMatch =
|
||||
matches_(r1.getChild(i), patternTree.getChild(i), pattern, match);
|
||||
if ( !childMatch ) return false;
|
||||
}
|
||||
return new ParseTreeMatch(tree, pattern);
|
||||
return true;
|
||||
}
|
||||
// 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? */
|
||||
|
@ -229,12 +238,14 @@ public class ParseTreePatternMatcher {
|
|||
TagChunk tagChunk = (TagChunk)chunk;
|
||||
// add special rule token or conjure up new token from name
|
||||
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)) ) {
|
||||
int ruleIndex = ruleNameToIndex.get(tagChunk.tag);
|
||||
int ruleImaginaryTokenType = atnWithBypassAlts.ruleToTokenType[ruleIndex];
|
||||
tokens.add(new RuleTagToken(tagChunk.tag, ruleImaginaryTokenType));
|
||||
tokens.add(new RuleTagToken(tagChunk.tag, ruleImaginaryTokenType, tagChunk.label));
|
||||
}
|
||||
else {
|
||||
System.err.println("invalid tag: "+tagChunk.tag);
|
||||
|
@ -341,7 +352,14 @@ public class ParseTreePatternMatcher {
|
|||
for (int i=0; i<ntags; i++) {
|
||||
// copy inside of <tag>
|
||||
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 ) {
|
||||
// copy from end of <tag> to start of next
|
||||
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 {
|
||||
protected String ruleName;
|
||||
protected int ruleImaginaryTokenType;
|
||||
protected String label;
|
||||
|
||||
public RuleTagToken(String ruleName, int ruleImaginaryTokenType) {
|
||||
this.ruleName = ruleName;
|
||||
this.ruleImaginaryTokenType = ruleImaginaryTokenType;
|
||||
}
|
||||
|
||||
public RuleTagToken(String ruleName, int ruleImaginaryTokenType, String label) {
|
||||
this(ruleName, ruleImaginaryTokenType);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannel() {
|
||||
return 0;
|
||||
|
|
|
@ -4,12 +4,18 @@ import org.antlr.v4.runtime.CommonToken;
|
|||
|
||||
public class TokenTagToken extends CommonToken {
|
||||
protected String tokenName;
|
||||
protected String label;
|
||||
|
||||
public TokenTagToken(String tokenName, int type) {
|
||||
super(type);
|
||||
this.tokenName = tokenName;
|
||||
}
|
||||
|
||||
public TokenTagToken(String tokenName, int type, String label) {
|
||||
this(tokenName, type);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return "<"+tokenName+">";
|
||||
|
|
|
@ -181,6 +181,18 @@ public class TestParseTreeMatcher extends BaseTest {
|
|||
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 {
|
||||
String grammar =
|
||||
"grammar X4;\n" +
|
||||
|
|
Loading…
Reference in New Issue