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; package org.antlr.v4.runtime;
import org.antlr.v4.runtime.tree.ErrorNode;
/** /**
* The interface for defining strategies to deal with syntax errors encountered * The interface for defining strategies to deal with syntax errors encountered
* during a parse by ANTLR-generated parsers. We distinguish between three * 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 * Tests whether or not {@code recognizer} is in the process of recovering
* from an error. In error recovery mode, {@link Parser#consume} adds * from an error. In error recovery mode, {@link Parser#consume} adds
* symbols to the parse tree by calling * symbols to the parse tree by calling
* {@link ParserRuleContext#addErrorNode(Token)} instead of * {@link Parser#createErrorNode(ParserRuleContext, Token)} then
* {@link ParserRuleContext#addChild(Token)}. * {@link ParserRuleContext#addErrorNode(ErrorNode)} instead of
* {@link Parser#createTerminalNode(ParserRuleContext, Token)}.
* *
* @param recognizer the parser instance * @param recognizer the parser instance
* @return {@code true} if the parser is currently recovering from a parse * @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.IntegerStack;
import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.v4.runtime.misc.IntervalSet;
import org.antlr.v4.runtime.tree.ErrorNode; 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.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode; 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.ParseTreePattern;
import org.antlr.v4.runtime.tree.pattern.ParseTreePatternMatcher; 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 * strategy to attempt recovery. If {@link #getBuildParseTree} is
* {@code true} and the token index of the symbol returned by * {@code true} and the token index of the symbol returned by
* {@link ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to * {@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 * @param ttype the token type to match
* @return the matched symbol * @return the matched symbol
@ -204,7 +208,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
if ( _buildParseTrees && t.getTokenIndex()==-1 ) { if ( _buildParseTrees && t.getTokenIndex()==-1 ) {
// we must have conjured up a new token during single token insertion // we must have conjured up a new token during single token insertion
// if it's not the current symbol // if it's not the current symbol
_ctx.addErrorNode(t); _ctx.addErrorNode(createErrorNode(_ctx,t));
} }
} }
return t; return t;
@ -220,7 +224,8 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
* strategy to attempt recovery. If {@link #getBuildParseTree} is * strategy to attempt recovery. If {@link #getBuildParseTree} is
* {@code true} and the token index of the symbol returned by * {@code true} and the token index of the symbol returned by
* {@link ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to * {@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 * @return the matched symbol
* @throws RecognitionException if the current input symbol did not match * @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) { if (_buildParseTrees && t.getTokenIndex() == -1) {
// we must have conjured up a new token during single token insertion // we must have conjured up a new token during single token insertion
// if it's not the current symbol // 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> * </pre>
* *
* If the parser is not in error recovery mode, the consumed symbol is added * 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. * {@link ParseTreeListener#visitTerminal} is called on any parse listeners.
* If the parser <em>is</em> in error recovery mode, the consumed symbol is * If the parser <em>is</em> in error recovery mode, the consumed symbol is
* added to the parse tree using * added to the parse tree using {@link #createErrorNode(ParserRuleContext, Token)} then
* {@link ParserRuleContext#addErrorNode(Token)}, and * {@link ParserRuleContext#addErrorNode(ErrorNode)} and
* {@link ParseTreeListener#visitErrorNode} is called on any parse * {@link ParseTreeListener#visitErrorNode} is called on any parse
* listeners. * listeners.
*/ */
@ -569,7 +574,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
boolean hasListener = _parseListeners != null && !_parseListeners.isEmpty(); boolean hasListener = _parseListeners != null && !_parseListeners.isEmpty();
if (_buildParseTrees || hasListener) { if (_buildParseTrees || hasListener) {
if ( _errHandler.inErrorRecoveryMode(this) ) { if ( _errHandler.inErrorRecoveryMode(this) ) {
ErrorNode node = _ctx.addErrorNode(o); ErrorNode node = _ctx.addErrorNode(createErrorNode(_ctx,o));
if (_parseListeners != null) { if (_parseListeners != null) {
for (ParseTreeListener listener : _parseListeners) { for (ParseTreeListener listener : _parseListeners) {
listener.visitErrorNode(node); listener.visitErrorNode(node);
@ -577,7 +582,7 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
} }
} }
else { else {
TerminalNode node = _ctx.addChild(o); TerminalNode node = _ctx.addChild(createTerminalNode(_ctx,o));
if (_parseListeners != null) { if (_parseListeners != null) {
for (ParseTreeListener listener : _parseListeners) { for (ParseTreeListener listener : _parseListeners) {
listener.visitTerminal(node); listener.visitTerminal(node);
@ -588,6 +593,38 @@ public abstract class Parser extends Recognizer<Token, ParserATNSimulator> {
return o; 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() { protected void addContextToParseTree() {
ParserRuleContext parent = (ParserRuleContext)_ctx.parent; ParserRuleContext parent = (ParserRuleContext)_ctx.parent;
// add current context to parent if we have a 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; return _tracer != null;
} }
} }

View File

@ -414,7 +414,7 @@ public class ParserInterpreter extends Parser {
Token.DEFAULT_CHANNEL, Token.DEFAULT_CHANNEL,
-1, -1, // invalid start/stop -1, -1, // invalid start/stop
tok.getLine(), tok.getCharPositionInLine()); tok.getLine(), tok.getCharPositionInLine());
_ctx.addErrorNode(errToken); _ctx.addErrorNode(createErrorNode(_ctx,errToken));
} }
else { // NoViableAlt else { // NoViableAlt
Token tok = e.getOffendingToken(); Token tok = e.getOffendingToken();
@ -424,7 +424,7 @@ public class ParserInterpreter extends Parser {
Token.DEFAULT_CHANNEL, Token.DEFAULT_CHANNEL,
-1, -1, // invalid start/stop -1, -1, // invalid start/stop
tok.getLine(), tok.getCharPositionInLine()); tok.getLine(), tok.getCharPositionInLine());
_ctx.addErrorNode(errToken); _ctx.addErrorNode(createErrorNode(_ctx,errToken));
} }
} }
} }
@ -445,3 +445,4 @@ public class ParserInterpreter extends Parser {
return rootContext; 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 * The parser setState() method updates field s and adds it to this list
* if we are debugging/tracing. * 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; // public List<Integer> states;
@ -118,27 +118,60 @@ public class ParserRuleContext extends RuleContext {
public void enterRule(ParseTreeListener listener) { } public void enterRule(ParseTreeListener listener) { }
public void exitRule(ParseTreeListener listener) { } public void exitRule(ParseTreeListener listener) { }
/** Does not set parent link; other add methods do that */ /** Add a parse tree node to this as a child. Works for
public TerminalNode addChild(TerminalNode t) { * internal and leaf nodes. Does not set parent link;
if ( children==null ) children = new ArrayList<ParseTree>(); * 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); children.add(t);
return t; return t;
} }
public RuleContext addChild(RuleContext ruleInvocation) { public RuleContext addChild(RuleContext ruleInvocation) {
if ( children==null ) children = new ArrayList<ParseTree>(); return addAnyChild(ruleInvocation);
children.add(ruleInvocation);
return ruleInvocation;
} }
/** Used by enterOuterAlt to toss out a RuleContext previously added as public TerminalNode addChild(TerminalNode t) {
* we entered a rule. If we have # label, we will need to remove return addAnyChild(t);
* generic ruleContext object. }
*/
public void removeLastChild() { /** Add an error node child. @since 4.6.1 */
if ( children!=null ) { public ErrorNode addErrorNode(ErrorNode errorNode) {
children.remove(children.size()-1); 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) { // public void trace(int s) {
@ -146,18 +179,14 @@ public class ParserRuleContext extends RuleContext {
// states.add(s); // states.add(s);
// } // }
public TerminalNode addChild(Token matchedToken) { /** Used by enterOuterAlt to toss out a RuleContext previously added as
TerminalNodeImpl t = new TerminalNodeImpl(matchedToken); * we entered a rule. If we have # label, we will need to remove
addChild(t); * generic ruleContext object.
t.parent = this; */
return t; public void removeLastChild() {
} if ( children!=null ) {
children.remove(children.size()-1);
public ErrorNode addErrorNode(Token badToken) { }
ErrorNodeImpl t = new ErrorNodeImpl(badToken);
addChild(t);
t.parent = this;
return t;
} }
@Override @Override
@ -290,13 +319,14 @@ public class ParserRuleContext extends RuleContext {
*/ */
public Token getStop() { return stop; } public Token getStop() { return stop; }
/** Used for rule context info debugging during parse-time, not so much for ATN debugging */ /** Used for rule context info debugging during parse-time, not so much for ATN debugging */
public String toInfoString(Parser recognizer) { public String toInfoString(Parser recognizer) {
List<String> rules = recognizer.getRuleInvocationStack(this); List<String> rules = recognizer.getRuleInvocationStack(this);
Collections.reverse(rules); Collections.reverse(rules);
return "ParserRuleContext"+rules+"{" + return "ParserRuleContext"+rules+"{" +
"start=" + start + "start=" + start +
", stop=" + stop + ", stop=" + stop +
'}'; '}';
} }
} }