This commit is contained in:
Terence Parr 2013-09-09 14:45:46 -07:00
parent 0df4411ea7
commit 90187039f0
5 changed files with 68 additions and 41 deletions

View File

@ -37,6 +37,7 @@ import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
import org.antlr.v4.runtime.tree.pattern.RuleTagToken;
import java.util.ArrayList;
import java.util.Collections;
@ -96,8 +97,17 @@ public class ParserRuleContext extends RuleContext {
public Token start, stop;
/** Used when parsing <expr> tags in patterns. If non-null, then this
* node is not just expr but <expr> to match any expr rule subtree.
*
* // found a <expr> tag in pattern. Just consume it and store in
// rule node to indicate it matches entire subtree. A bit ugly
// but better than replacing rule node with diff kind of object
*/
public RuleTagToken patternRuleTag;
/**
* The exception which forced this rule to return. If the rule successfully
* The exception that forced this rule to return. If the rule successfully
* completed, this is {@code null}.
*/
public RecognitionException exception;

View File

@ -33,6 +33,7 @@ package org.antlr.v4.runtime.tree.pattern;
import org.antlr.v4.runtime.DefaultErrorStrategy;
import org.antlr.v4.runtime.InputMismatchException;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
@ -70,9 +71,15 @@ public class ParseTreePatternErrorStrategy extends DefaultErrorStrategy {
@Override
public void recover(Parser recognizer, RecognitionException e) {
if (e.getOffendingToken() instanceof RuleTagToken) {
recognizer.consume(); // match <tag> as if it matches rule, continue
// leaves <expr> as (expr <expr>) tree; can shrink later. this
// is simplest mechanism to get <expr> into tree.
ParserRuleContext ctx = recognizer.getContext();
// found a <expr> tag in pattern. Just consume it and store in
// rule node to indicate it matches entire subtree. A bit ugly
// but better than replacing rule node with diff kind of object
Token o = recognizer.getCurrentToken();
ctx.patternRuleTag = (RuleTagToken)o;
if (o.getType() != Token.EOF) {
recognizer.getInputStream().consume();
}
}
else {
super.recover(recognizer, e);

View File

@ -144,11 +144,11 @@ public class ParseTreePatternMatcher {
}
return m;
}
if ( tree instanceof RuleNode && patternTree instanceof RuleNode ) {
RuleNode r1 = (RuleNode)tree;
RuleNode r2 = (RuleNode)patternTree;
if ( tree instanceof ParserRuleContext && patternTree instanceof ParserRuleContext ) {
ParserRuleContext r1 = (ParserRuleContext)tree;
ParserRuleContext r2 = (ParserRuleContext)patternTree;
// (expr ...) and <expr>
if ( r2 instanceof RuleSubtreeNode ) {
if ( r2.patternRuleTag!=null ) {
ParseTreeMatch m = null;
if ( r1.getRuleContext().getRuleIndex() == r2.getRuleContext().getRuleIndex() ) {
m = new ParseTreeMatch(tree, pattern);
@ -184,48 +184,28 @@ public class ParseTreePatternMatcher {
parser.setErrorHandler(new ParseTreePatternErrorStrategy());
ParseTree tree = null;
try {
Method startRule = parserClass.getMethod(patternRuleName);
tree = (ParserRuleContext)startRule.invoke(parser, (Object[])null);
Method startRule = null;
Object[] args = null;
try {
startRule = parserClass.getMethod(patternRuleName);
}
catch (NoSuchMethodException nsme) {
// try with int _p arg for recursive func
startRule = parserClass.getMethod(patternRuleName, int.class);
args = new Integer[] {0};
}
tree = (ParseTree)startRule.invoke(parser, args);
System.out.println("pattern tree = "+tree.toStringTree(parser));
}
catch (Exception e) {
throw new CannotInvokeStartRule(e);
}
shrinkRuleTagSubtreesToSingleNode(tree);
System.out.println("after optimize pattern tree = " + tree.toStringTree(parser));
return new ParseTreePattern(patternRuleName, pattern, tree);
}
// replace (expr <expr>) where <expr> is a TerminalNode with RuleTagToken
// symbol to a single RuleSubtreeNode for <expr>
protected void shrinkRuleTagSubtreesToSingleNode(ParseTree t) {
if ( t instanceof RuleNode ) {
RuleNode r = (RuleNode)t;
if ( r.getChildCount()==1 && r.getChild(0) instanceof TerminalNode ) {
TerminalNode c = (TerminalNode)r.getChild(0);
if ( c.getSymbol() instanceof RuleTagToken ) {
System.out.println("rule tag subtree "+t.toStringTree(parser));
ParserRuleContext parent = (ParserRuleContext)r.getParent();
int i = parent.children.indexOf(r);
if ( i==-1 ) {
System.out.printf("eh?-------------------");
}
RuleSubtreeNode sub = new RuleSubtreeNode((ParserRuleContext)r.getRuleContext());
parent.children.set(i, sub);
}
}
}
if ( t instanceof RuleNode) {
RuleNode r = (RuleNode)t;
int n = r.getChildCount();
for (int i = 0; i<n; i++) {
shrinkRuleTagSubtreesToSingleNode(r.getChild(i));
}
}
}
public List<? extends Token> tokenize(String pattern) {
lazyInit();
// make maps for quick look up

View File

@ -524,8 +524,17 @@ public abstract class BaseTest {
CommonTokenStream tokens = new CommonTokenStream(lexer);
Parser parser = pctor.newInstance(tokens);
Method startRule = parserClass.getMethod(startRuleName);
ParseTree result = (ParseTree)startRule.invoke(parser, (Object[])null);
Method startRule = null;
Object[] args = null;
try {
startRule = parserClass.getMethod(startRuleName);
}
catch (NoSuchMethodException nsme) {
// try with int _p arg for recursive func
startRule = parserClass.getMethod(startRuleName, int.class);
args = new Integer[] {0};
}
ParseTree result = (ParseTree)startRule.invoke(parser, args);
System.out.println("parse tree = "+result.toStringTree(parser));
return result;
}

View File

@ -160,6 +160,27 @@ public class TestParseTreeMatcher extends BaseTest {
checkPatternMatch("X5.g4", grammar, "s", input, pattern, "X5Parser", "X5Lexer");
}
@Test public void testLRecursiveExpr() throws Exception {
String grammar =
"grammar X6;\n" +
"s : expr ';'\n" +
//" | 'return' expr ';'\n" +
" ;\n" +
"expr: expr '.' ID\n" +
" | expr '*' expr\n" +
" | expr '=' expr\n" +
" | ID\n" +
" | INT\n" +
" ;\n" +
"ID : [a-z]+ ;\n" +
"INT : [0-9]+ ;\n" +
"WS : [ \\r\\n\\t]+ -> skip ;\n";
String input = "3*4*5";
String pattern = "<expr> * <expr> * <expr>";
checkPatternMatch("X6.g4", grammar, "expr", input, pattern, "X6Parser", "X6Lexer");
}
public void checkPatternMatch(String grammarName, String grammar, String startRule,
String input, String pattern,
String parserName, String lexerName)