diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatch.java b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatch.java index 6a14623e6..4235b8e54 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatch.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatch.java @@ -14,6 +14,9 @@ public class ParseTreeMatch { protected List> 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; diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatchFailed.java b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatchFailed.java deleted file mode 100644 index 5e0c4b537..000000000 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreeMatchFailed.java +++ /dev/null @@ -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; - } -} diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java index 37bf67e6a..2cc46bd6f 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/ParseTreePatternMatcher.java @@ -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 , 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 - 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) 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 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 to start of next String text = pattern.substring(stops.get(i) + stop.length(), starts.get(i + 1)); diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/RuleTagToken.java b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/RuleTagToken.java index a25c56c04..6a9a59c77 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/RuleTagToken.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/RuleTagToken.java @@ -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; diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/TokenTagToken.java b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/TokenTagToken.java index d3864e367..7bb948766 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/TokenTagToken.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/pattern/TokenTagToken.java @@ -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+">"; diff --git a/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java b/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java index 34f6de132..d2a4d0fad 100644 --- a/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java +++ b/tool/test/org/antlr/v4/test/TestParseTreeMatcher.java @@ -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 = ";"; + checkPatternMatch("X3.g4", grammar, "s", input, pattern, "X3"); + } + @Test public void testTokenAndRuleMatch() throws Exception { String grammar = "grammar X4;\n" +