Merge pull request #1665 from parrt/factor-create-leaves

factor out the creation of error nodes and terminal nodes in the parser
This commit is contained in:
Terence Parr 2017-02-16 10:43:30 -08:00 committed by GitHub
commit 628aa8ff02
4 changed files with 122 additions and 50 deletions

View File

@ -6,6 +6,8 @@
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.tree.ErrorNode;
/**
* The interface for defining strategies to deal with syntax errors encountered
* during a parse by ANTLR-generated parsers. We distinguish between three
@ -89,8 +91,9 @@ public interface ANTLRErrorStrategy {
* Tests whether or not {@code recognizer} is in the process of recovering
* from an error. In error recovery mode, {@link Parser#consume} adds
* symbols to the parse tree by calling
* {@link ParserRuleContext#addErrorNode(Token)} instead of
* {@link ParserRuleContext#addChild(Token)}.
* {@link Parser#createErrorNode(ParserRuleContext, Token)} then
* {@link ParserRuleContext#addErrorNode(ErrorNode)} instead of
* {@link Parser#createTerminalNode(ParserRuleContext, Token)}.
*
* @param recognizer the parser instance
* @return {@code true} if the parser is currently recovering from a parse

View File

@ -19,9 +19,12 @@ import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.IntegerStack;
import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ErrorNodeImpl;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
import org.antlr.v4.runtime.tree.pattern.ParseTreePattern;
import org.antlr.v4.runtime.tree.pattern.ParseTreePatternMatcher;
@ -182,7 +185,8 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
* strategy to attempt recovery. If {@link #getBuildParseTree} is
* {@code true} and the token index of the symbol returned by
* {@link ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to
* the parse tree by calling {@link ParserRuleContext#addErrorNode}.</p>
* the parse tree by calling {@link #createErrorNode(ParserRuleContext, Token)} then
* {@link ParserRuleContext#addErrorNode(ErrorNode)}.</p>
*
* @param ttype the token type to match
* @return the matched symbol
@ -204,7 +208,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
if ( _buildParseTrees && t.getTokenIndex()==-1 ) {
// we must have conjured up a new token during single token insertion
// if it's not the current symbol
_ctx.addErrorNode(t);
_ctx.addErrorNode(createErrorNode(_ctx,t));
}
}
return t;
@ -220,7 +224,8 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
* strategy to attempt recovery. If {@link #getBuildParseTree} is
* {@code true} and the token index of the symbol returned by
* {@link ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to
* the parse tree by calling {@link ParserRuleContext#addErrorNode}.</p>
* the parse tree by calling {@link Parser#createErrorNode(ParserRuleContext, Token)}. then
* {@link ParserRuleContext#addErrorNode(ErrorNode)}</p>
*
* @return the matched symbol
* @throws RecognitionException if the current input symbol did not match
@ -238,7 +243,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
if (_buildParseTrees && t.getTokenIndex() == -1) {
// we must have conjured up a new token during single token insertion
// if it's not the current symbol
_ctx.addErrorNode(t);
_ctx.addErrorNode(createErrorNode(_ctx,t));
}
}
@ -553,11 +558,11 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
* </pre>
*
* If the parser is not in error recovery mode, the consumed symbol is added
* to the parse tree using {@link ParserRuleContext#addChild(Token)}, and
* to the parse tree using {@link ParserRuleContext#addChild(TerminalNode)}, and
* {@link ParseTreeListener#visitTerminal} is called on any parse listeners.
* If the parser <em>is</em> in error recovery mode, the consumed symbol is
* added to the parse tree using
* {@link ParserRuleContext#addErrorNode(Token)}, and
* added to the parse tree using {@link #createErrorNode(ParserRuleContext, Token)} then
* {@link ParserRuleContext#addErrorNode(ErrorNode)} and
* {@link ParseTreeListener#visitErrorNode} is called on any parse
* listeners.
*/
@ -569,7 +574,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
boolean hasListener = _parseListeners != null && !_parseListeners.isEmpty();
if (_buildParseTrees || hasListener) {
if ( _errHandler.inErrorRecoveryMode(this) ) {
ErrorNode node = _ctx.addErrorNode(o);
ErrorNode node = _ctx.addErrorNode(createErrorNode(_ctx,o));
if (_parseListeners != null) {
for (ParseTreeListener listener : _parseListeners) {
listener.visitErrorNode(node);
@ -577,7 +582,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
}
}
else {
TerminalNode node = _ctx.addChild(o);
TerminalNode node = _ctx.addChild(createTerminalNode(_ctx,o));
if (_parseListeners != null) {
for (ParseTreeListener listener : _parseListeners) {
listener.visitTerminal(node);
@ -588,6 +593,38 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return o;
}
/** How to create a token leaf node associated with a parent.
* Typically, the terminal node to create is not a function of the parent
* but this method must still set the parent pointer of the terminal node
* returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)}
* set the parent pointer, but the parent pointer is implementation dependent
* and currently there is no setParent() in {@link TerminalNode} (and can't
* add method in Java 1.7 without breaking backward compatibility).
*
* @since 4.6.1
*/
public TerminalNode createTerminalNode(ParserRuleContext parent, Token t) {
TerminalNodeImpl node = new TerminalNodeImpl(t);
node.parent = parent;
return node;
}
/** How to create an error node, given a token, associated with a parent.
* Typically, the error node to create is not a function of the parent
* but this method must still set the parent pointer of the terminal node
* returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)}
* set the parent pointer, but the parent pointer is implementation dependent
* and currently there is no setParent() in {@link ErrorNode} (and can't
* add method in Java 1.7 without breaking backward compatibility).
*
* @since 4.6.1
*/
public ErrorNode createErrorNode(ParserRuleContext parent, Token t) {
ErrorNodeImpl node = new ErrorNodeImpl(t);
node.parent = parent;
return node;
}
protected void addContextToParseTree() {
ParserRuleContext parent = (ParserRuleContext)_ctx.parent;
// add current context to parent if we have a parent
@ -925,3 +962,4 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return _tracer != null;
}
}

View File

@ -414,7 +414,7 @@ public class ParserInterpreter extends Parser {
Token.DEFAULT_CHANNEL,
-1, -1, // invalid start/stop
tok.getLine(), tok.getCharPositionInLine());
_ctx.addErrorNode(errToken);
_ctx.addErrorNode(createErrorNode(_ctx,errToken));
}
else { // NoViableAlt
Token tok = e.getOffendingToken();
@ -424,7 +424,7 @@ public class ParserInterpreter extends Parser {
Token.DEFAULT_CHANNEL,
-1, -1, // invalid start/stop
tok.getLine(), tok.getCharPositionInLine());
_ctx.addErrorNode(errToken);
_ctx.addErrorNode(createErrorNode(_ctx,errToken));
}
}
}
@ -445,3 +445,4 @@ public class ParserInterpreter extends Parser {
return rootContext;
}
}

View File

@ -63,8 +63,8 @@ public class ParserRuleContext extends RuleContext {
*
* The parser setState() method updates field s and adds it to this list
* if we are debugging/tracing.
*
* This does not trace states visited during prediction.
*
* This does not trace states visited during prediction.
*/
// public List<Integer> states;
@ -118,27 +118,60 @@ public class ParserRuleContext extends RuleContext {
public void enterRule(ParseTreeListener listener) { }
public void exitRule(ParseTreeListener listener) { }
/** Does not set parent link; other add methods do that */
public TerminalNode addChild(TerminalNode t) {
if ( children==null ) children = new ArrayList<ParseTree>();
/** Add a parse tree node to this as a child. Works for
* internal and leaf nodes. Does not set parent link;
* other add methods must do that. Other addChild methods
* call this.
*
* We cannot set the parent pointer of the incoming node
* because the existing interfaces do not have a setParent()
* method and I don't want to break backward compatibility for this.
*
* @since 4.6.1
*/
public <T extends ParseTree> T addAnyChild(T t) {
if ( children==null ) children = new ArrayList<>();
children.add(t);
return t;
}
public RuleContext addChild(RuleContext ruleInvocation) {
if ( children==null ) children = new ArrayList<ParseTree>();
children.add(ruleInvocation);
return ruleInvocation;
return addAnyChild(ruleInvocation);
}
/** Used by enterOuterAlt to toss out a RuleContext previously added as
* we entered a rule. If we have # label, we will need to remove
* generic ruleContext object.
*/
public void removeLastChild() {
if ( children!=null ) {
children.remove(children.size()-1);
}
public TerminalNode addChild(TerminalNode t) {
return addAnyChild(t);
}
/** Add an error node child. @since 4.6.1 */
public ErrorNode addErrorNode(ErrorNode errorNode) {
return addAnyChild(errorNode);
}
/** Add a child to this node based upon matchedToken. It
* creates a TerminalNodeImpl rather than using
* {@link Parser#createTerminalNode(ParserRuleContext, Token)}. I'm leaving this
* in for compatibility but the parser doesn't use this anymore.
*/
@Deprecated
public TerminalNode addChild(Token matchedToken) {
TerminalNodeImpl t = new TerminalNodeImpl(matchedToken);
addAnyChild(t);
t.parent = this;
return t;
}
/** Add a child to this node based upon badToken. It
* creates a ErrorNodeImpl rather than using
* {@link Parser#createErrorNode(ParserRuleContext, Token)}. I'm leaving this
* in for compatibility but the parser doesn't use this anymore.
*/
@Deprecated
public ErrorNode addErrorNode(Token badToken) {
ErrorNodeImpl t = new ErrorNodeImpl(badToken);
addAnyChild(t);
t.parent = this;
return t;
}
// public void trace(int s) {
@ -146,18 +179,14 @@ public class ParserRuleContext extends RuleContext {
// states.add(s);
// }
public TerminalNode addChild(Token matchedToken) {
TerminalNodeImpl t = new TerminalNodeImpl(matchedToken);
addChild(t);
t.parent = this;
return t;
}
public ErrorNode addErrorNode(Token badToken) {
ErrorNodeImpl t = new ErrorNodeImpl(badToken);
addChild(t);
t.parent = this;
return t;
/** Used by enterOuterAlt to toss out a RuleContext previously added as
* we entered a rule. If we have # label, we will need to remove
* generic ruleContext object.
*/
public void removeLastChild() {
if ( children!=null ) {
children.remove(children.size()-1);
}
}
@Override
@ -290,13 +319,14 @@ public class ParserRuleContext extends RuleContext {
*/
public Token getStop() { return stop; }
/** Used for rule context info debugging during parse-time, not so much for ATN debugging */
public String toInfoString(Parser recognizer) {
List<String> rules = recognizer.getRuleInvocationStack(this);
Collections.reverse(rules);
return "ParserRuleContext"+rules+"{" +
"start=" + start +
", stop=" + stop +
'}';
}
/** Used for rule context info debugging during parse-time, not so much for ATN debugging */
public String toInfoString(Parser recognizer) {
List<String> rules = recognizer.getRuleInvocationStack(this);
Collections.reverse(rules);
return "ParserRuleContext"+rules+"{" +
"start=" + start +
", stop=" + stop +
'}';
}
}