c++ - Antlr4 C++ 访客 API

标签 c++ antlr antlr4

我正在使用 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/

相关文章:

c++ - 对 CGAL 类型使用基于范围的 for 循环

c++ - 为什么 vector 不循环更新?

java - ANTLR 解析未找到正确的词法分析器部分

antlr4:在创建解析树时忽略多余的标记

c++ - SendInput() 不等于在 C++ 中手动在键盘上按键?

c++ - 带参数和不带参数的函数之间的执行时间/C++

antlr - 在生成 C 代码时寻求非常简单的 ANTLR 错误处理示例

antlr - 编写 ANTLR 目标

c++ - 如何多次使用解析器?

java - 此 ANTLR 4 Lexer 文件中的 "parser rule ' Channels' not allowed in lexer"错误是什么?