gpt4 book ai didi

c++ - Antlr4 c++ 访问者 API

转载 作者:行者123 更新时间:2023-11-30 05:01:33 28 4
gpt4 key购买 nike

我正在使用 Antlr4 的 C++ 访问者 api 来遍历解析树。但是,我正在努力使其正常运行。也就是说,我不确定如何使用 visitChildren(ParseTree *tree) 调用。

我为我定义的每条规则提供了上下文。我可以使用上下文遍历树:context->accept[RuleContext]([RuleContext]* rule)

但是,当我使用它们时,我会连续多次访问同一个节点。

例如:

program:
: nameRule
dateRule
( statements )*
EOF
;

nameRule
: NAME IDENTIFIER ;

dateRule
: DATE IDENTIFIER ;

statements:
: statementX
| statementY
| statementZ
;

statementX:
: // do something here

statementY:
: // do something here

statementZ:
: // do something here

IDENTIFIERDATENAME 是终端。

我通过以下方式构建 Antlr 解析结构:

void Parser::parse() {
ifstream file(FLAGS_c, ifstream::binary);
// Convert the file into ANTLR's format.
ANTLRInputStream stream = ANTLRInputStream(file);

// Give the input to the lexer.
MyLexer lexer = new MyLexer(&stream);
// Generate the tokens.
CommonTokenStream tokens(lexer);

file.close();

tokens.fill();

// Create the translation that will parse the input.
MyParser parser = new MyParser(&tokens);
parser->setBuildParseTree(true);
MyParser::ProgramContext *tree = parser->program();

auto *visitor = new MyVisitor();
visitor->visitProgram(tree);
}

因此,当我尝试遍历它时,它看起来与此类似,MyVisitor 类扩展了 MyParserVisitorMyVisitor 是我用来遍历生成的树的访问者类。

Any MyVisitor::visitProgram(ParserVisitor::ProgramContext *context) {
this->visitNameRule(context->nameRule());
this->visitDateRule(context->dateRule());

if (!this->statements.empty()) {
for (auto &it : this->statements) {
this->visitStatements(it);
}
}
return Any(context);
}

// Omitting name and date rules.

Any MyVisitor::visitStatements(ParserVisitor::StatementContext *context) {
this->visitStatementX(context->statementX());
this->visitStatementY(context->statementY());
this->visitStatementZ(context->statementZ());
return Any(context);
}

在这种情况下,每次访问语句时都会访问语句XYZ。即使它们不存在于输入程序中。

这是正确的使用方法吗?如果不是,那么我假设 visitChildren(ParseTree *tree) 是在每个访问者函数中使用的正确 API。但我不明白如何从 *Context 访问 ParseTree 数据结构。

最佳答案

这个问题与C++访问者没有直接关系,而是ANTLR4中的一般访问者问题。您正在做的是以您不打算做的方式让访客走捷径。不要显式地手动访问某些子树,而是调用 super 实现让它为您做,并在单独的 visitStatementXXX 函数中收集结果。看这个implementation of a (very simple) expression evaluator ,用于单元测试(用 C++ 编写)。以下是演示原理的部分拷贝:

class EvalParseVisitor : public MySQLParserBaseVisitor {
public:
std::vector<EvalValue> results; // One entry for each select item.

bool asBool(EvalValue in) {
if (!in.isNullType() && in.number != 0)
return true;
return false;
};

virtual Any visitSelectItem(MySQLParser::SelectItemContext *context) override {
Any result = visitChildren(context);
results.push_back(result.as<EvalValue>());
return result;
}

virtual Any visitExprNot(MySQLParser::ExprNotContext *context) override {
EvalValue value = visit(context->expr());
switch (value.type) {
case EvalValue::Null:
return EvalValue::fromNotNull();
case EvalValue::NotNull:
return EvalValue::fromNull();
default:
return EvalValue::fromBool(!asBool(value));
}
}

virtual Any visitExprAnd(MySQLParser::ExprAndContext *context) override {
EvalValue left = visit(context->expr(0));
EvalValue right = visit(context->expr(1));

if (left.isNullType() || right.isNullType())
return EvalValue::fromNull();
return EvalValue::fromBool(asBool(left) && asBool(right));

return visitChildren(context);
}
...

关键部分是对 visit() 的调用,它依次遍历给定上下文树的子节点并仅触发实际存在的元素的访问者函数。

关于c++ - Antlr4 c++ 访问者 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50263759/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com