C++ implementation of issue #1665 and issue #1674

This commit is contained in:
Mike Lischke 2017-02-24 08:44:21 +01:00
parent cd6bb1f2a0
commit 126112fbbc
9 changed files with 92 additions and 35 deletions

View File

@ -93,8 +93,9 @@ namespace antlr4 {
/// Tests whether or not {@code recognizer} is in the process of recovering
/// from an error. In error recovery mode, <seealso cref="Parser#consume"/> adds
/// symbols to the parse tree by calling
/// <seealso cref="ParserRuleContext#addErrorNode(Token)"/> instead of
/// <seealso cref="ParserRuleContext#addChild(Token)"/>.
/// {@link Parser#createErrorNode(ParserRuleContext, Token)} then
/// {@link ParserRuleContext#addErrorNode(ErrorNode)} instead of
/// {@link Parser#createTerminalNode(ParserRuleContext, Token)}.
/// </summary>
/// <param name="recognizer"> the parser instance </param>
/// <returns> {@code true} if the parser is currently recovering from a parse

View File

@ -8,6 +8,7 @@
#include "dfa/DFA.h"
#include "ParserRuleContext.h"
#include "tree/TerminalNode.h"
#include "tree/ErrorNodeImpl.h"
#include "Lexer.h"
#include "atn/ParserATNSimulator.h"
#include "misc/IntervalSet.h"
@ -111,7 +112,7 @@ Token* Parser::match(size_t ttype) {
if (_buildParseTrees && t->getTokenIndex() == INVALID_INDEX) {
// we must have conjured up a new token during single token insertion
// if it's not the current symbol
_ctx->addErrorNode(_tracker, t);
_ctx->addChild(createErrorNode(t));
}
}
return t;
@ -127,7 +128,7 @@ Token* Parser::matchWildcard() {
if (_buildParseTrees && t->getTokenIndex() == INVALID_INDEX) {
// we must have conjured up a new token during single token insertion
// if it's not the current symbol
_ctx->addErrorNode(_tracker, t);
_ctx->addChild(createErrorNode(t));
}
}
@ -293,17 +294,19 @@ Token* Parser::consume() {
if (o->getType() != EOF) {
getInputStream()->consume();
}
bool hasListener = _parseListeners.size() > 0 && !_parseListeners.empty();
if (_buildParseTrees || hasListener) {
if (_errHandler->inErrorRecoveryMode(this)) {
tree::ErrorNode* node = _ctx->addErrorNode(_tracker, o);
tree::ErrorNode *node = createErrorNode(o);
_ctx->addChild(node);
if (_parseListeners.size() > 0) {
for (auto listener : _parseListeners) {
listener->visitErrorNode(node);
}
}
} else {
tree::TerminalNode *node = _ctx->addChild(_tracker, o);
tree::TerminalNode *node = _ctx->addChild(createTerminalNode(o));
if (_parseListeners.size() > 0) {
for (auto listener : _parseListeners) {
listener->visitTerminal(node);
@ -617,6 +620,14 @@ bool Parser::isTrace() const {
return _tracer != nullptr;
}
tree::TerminalNode *Parser::createTerminalNode(Token *t) {
return _tracker.createInstance<tree::TerminalNodeImpl>(t);
}
tree::ErrorNode *Parser::createErrorNode(Token *t) {
return _tracker.createInstance<tree::ErrorNodeImpl>(t);
}
void Parser::InitializeInstanceFields() {
_errHandler = std::make_shared<DefaultErrorStrategy>();
_precedenceStack.clear();

View File

@ -54,13 +54,14 @@ namespace antlr4 {
/// Match current input symbol against {@code ttype}. If the symbol type
/// matches, <seealso cref="ANTLRErrorStrategy#reportMatch"/> and <seealso cref="#consume"/> are
/// called to complete the match process.
/// <p/>
///
/// If the symbol type does not match,
/// <seealso cref="ANTLRErrorStrategy#recoverInline"/> is called on the current error
/// strategy to attempt recovery. If <seealso cref="#getBuildParseTree"/> is
/// {@code true} and the token index of the symbol returned by
/// <seealso cref="ANTLRErrorStrategy#recoverInline"/> is -1, the symbol is added to
/// the parse tree by calling <seealso cref="ParserRuleContext#addErrorNode"/>.
/// the parse tree by calling {@link #createErrorNode(ParserRuleContext, Token)} then
/// {@link ParserRuleContext#addErrorNode(ErrorNode)}.
/// </summary>
/// <param name="ttype"> the token type to match </param>
/// <returns> the matched symbol </returns>
@ -258,11 +259,11 @@ namespace antlr4 {
/// </pre>
///
/// If the parser is not in error recovery mode, the consumed symbol is added
/// to the parse tree using <seealso cref="ParserRuleContext#addChild(Token)"/>, and
/// to the parse tree using <seealso cref="ParserRuleContext#addChild(TerminalNode)"/>, and
/// <seealso cref="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
/// <seealso cref="ParserRuleContext#addErrorNode(Token)"/>, and
/// added to the parse tree using {@link #createErrorNode(ParserRuleContext, Token)} then
/// {@link ParserRuleContext#addErrorNode(ErrorNode)} and
/// <seealso cref="ParseTreeListener#visitErrorNode"/> is called on any parse
/// listeners.
virtual Token* consume();
@ -376,6 +377,30 @@ namespace antlr4 {
tree::ParseTreeTracker& getTreeTracker() { return _tracker; };
/** 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
*/
tree::TerminalNode *createTerminalNode(Token *t);
/** 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
*/
tree::ErrorNode *createErrorNode(Token *t);
protected:
/// The ParserRuleContext object for the currently executing rule.
/// This is always non-null during the parsing process.

View File

@ -23,6 +23,7 @@
#include "Vocabulary.h"
#include "InputMismatchException.h"
#include "CommonToken.h"
#include "tree/ErrorNode.h"
#include "support/CPPUtils.h"
@ -288,14 +289,14 @@ void ParserInterpreter::recover(RecognitionException &e) {
_errorToken = getTokenFactory()->create({ tok->getTokenSource(), tok->getTokenSource()->getInputStream() },
expectedTokenType, tok->getText(), Token::DEFAULT_CHANNEL, INVALID_INDEX, INVALID_INDEX, // invalid start/stop
tok->getLine(), tok->getCharPositionInLine());
_ctx->addErrorNode(_tracker, _errorToken.get());
_ctx->addChild(createErrorNode(_errorToken.get()));
}
else { // NoViableAlt
Token *tok = e.getOffendingToken();
_errorToken = getTokenFactory()->create({ tok->getTokenSource(), tok->getTokenSource()->getInputStream() },
Token::INVALID_TYPE, tok->getText(), Token::DEFAULT_CHANNEL, INVALID_INDEX, INVALID_INDEX, // invalid start/stop
tok->getLine(), tok->getCharPositionInLine());
_ctx->addErrorNode(_tracker, _errorToken.get());
_ctx->addChild(createErrorNode(_errorToken.get()));
}
}
}

View File

@ -3,7 +3,8 @@
* can be found in the LICENSE.txt file in the project root.
*/
#include "tree/ErrorNodeImpl.h"
#include "tree/TerminalNode.h"
#include "tree/ErrorNode.h"
#include "misc/Interval.h"
#include "Parser.h"
#include "Token.h"
@ -34,6 +35,22 @@ void ParserRuleContext::copyFrom(ParserRuleContext *ctx) {
this->start = ctx->start;
this->stop = ctx->stop;
// copy any error nodes to alt label node
if (!ctx->children.empty()) {
for (auto child : ctx->children) {
auto errorNode = dynamic_cast<ErrorNode *>(child);
if (errorNode != nullptr) {
errorNode->setParent(this);
children.push_back(errorNode);
}
}
// Remove the just reparented error nodes from the source context.
ctx->children.erase(std::remove_if(ctx->children.begin(), ctx->children.end(), [this](tree::ParseTree *e) -> bool {
return std::find(children.begin(), children.end(), e) != children.end();
}), ctx->children.end());
}
}
void ParserRuleContext::enterRule(tree::ParseTreeListener * /*listener*/) {
@ -43,6 +60,7 @@ void ParserRuleContext::exitRule(tree::ParseTreeListener * /*listener*/) {
}
tree::TerminalNode* ParserRuleContext::addChild(tree::TerminalNode *t) {
t->setParent(this);
children.push_back(t);
return t;
}
@ -58,20 +76,6 @@ void ParserRuleContext::removeLastChild() {
}
}
tree::TerminalNode* ParserRuleContext::addChild(ParseTreeTracker &tracker, Token *matchedToken) {
auto t = tracker.createInstance<tree::TerminalNodeImpl>(matchedToken);
addChild(t);
t->parent = this;
return t;
}
tree::ErrorNode* ParserRuleContext::addErrorNode(ParseTreeTracker &tracker, Token *badToken) {
auto t = tracker.createInstance<tree::ErrorNodeImpl>(badToken);
addChild(t);
t->parent = this;
return t;
}
tree::TerminalNode* ParserRuleContext::getToken(size_t ttype, size_t i) {
if (i >= children.size()) {
return nullptr;

View File

@ -70,7 +70,8 @@ namespace antlr4 {
virtual ~ParserRuleContext() {}
/** COPY a ctx (I'm deliberately not using copy constructor) to avoid
* confusion with creating node with parent. Does not copy children.
* confusion with creating node with parent. Does not copy children
* (except error leaves).
*/
virtual void copyFrom(ParserRuleContext *ctx);
@ -80,7 +81,7 @@ namespace antlr4 {
virtual void enterRule(tree::ParseTreeListener *listener);
virtual void exitRule(tree::ParseTreeListener *listener);
/// Does not set parent link; other add methods do that.
/** Add a token leaf node child and force its parent to be this node. */
tree::TerminalNode* addChild(tree::TerminalNode *t);
RuleContext* addChild(RuleContext *ruleInvocation);
@ -89,9 +90,6 @@ namespace antlr4 {
/// generic ruleContext object.
virtual void removeLastChild();
virtual tree::TerminalNode* addChild(tree::ParseTreeTracker &tracker, Token *matchedToken);
virtual tree::ErrorNode* addErrorNode(tree::ParseTreeTracker &tracker, Token *badToken);
virtual tree::TerminalNode* getToken(size_t ttype, std::size_t i);
virtual std::vector<tree::TerminalNode *> getTokens(size_t ttype);
@ -132,14 +130,14 @@ namespace antlr4 {
* Note that the range from start to stop is inclusive, so for rules that do not consume anything
* (for example, zero length or error productions) this token may exceed stop.
*/
virtual Token*getStart();
virtual Token *getStart();
/**
* Get the final token in this context.
* Note that the range from start to stop is inclusive, so for rules that do not consume anything
* (for example, zero length or error productions) this token may precede start.
*/
virtual Token* getStop();
virtual Token *getStop();
/// <summary>
/// Used for rule context info debugging during parse-time, not so much for ATN debugging </summary>

View File

@ -13,6 +13,17 @@ namespace tree {
class ANTLR4CPP_PUBLIC TerminalNode : public ParseTree {
public:
virtual Token* getSymbol() = 0;
/** Set the parent for this leaf node.
*
* Technically, this is not backward compatible as it changes
* the interface but no one was able to create custom
* TerminalNodes anyway so I'm adding as it improves internal
* code quality.
*
* @since 4.6.1
*/
virtual void setParent(RuleContext *parent) = 0;
};
} // namespace tree

View File

@ -5,6 +5,7 @@
#include "misc/Interval.h"
#include "Token.h"
#include "RuleContext.h"
#include "tree/ParseTreeVisitor.h"
#include "tree/TerminalNodeImpl.h"
@ -19,6 +20,10 @@ Token* TerminalNodeImpl::getSymbol() {
return symbol;
}
void TerminalNodeImpl::setParent(RuleContext *parent) {
this->parent = parent;
}
misc::Interval TerminalNodeImpl::getSourceInterval() {
if (symbol == nullptr) {
return misc::Interval::INVALID;

View File

@ -17,6 +17,7 @@ namespace tree {
TerminalNodeImpl(Token *symbol);
virtual Token* getSymbol() override;
virtual void setParent(RuleContext *parent) override;
virtual misc::Interval getSourceInterval() override;
virtual antlrcpp::Any accept(ParseTreeVisitor *visitor) override;