Antlr4 c++;访客API 我使用ANTLR4的C++访问者API来遍历解析树。然而,我正在努力让它正常工作。也就是说,我不知道如何使用visitChildren(ParseTree*tree)调用

Antlr4 c++;访客API 我使用ANTLR4的C++访问者API来遍历解析树。然而,我正在努力让它正常工作。也就是说,我不知道如何使用visitChildren(ParseTree*tree)调用,c++,antlr,antlr4,C++,Antlr,Antlr4,我得到了我定义的每个规则的上下文。我可以使用上下文遍历树:context->accept[RuleContext]([RuleContext]*rule) 但是,当我使用它们时,我会连续多次访问同一个节点 例如: program: : nameRule dateRule ( statements )* EOF ; nameRule : NAME IDENTIFIER ; dateRule : DATE IDENTIFIER

我得到了我定义的每个规则的上下文。我可以使用上下文遍历树:
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
标识符
日期
名称
是终端

我通过以下方式构建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
扩展了
MyParserVisitor
MyVisitor
是我用来遍历生成的树的访问者类

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);
}
在这种情况下,每次访问语句时都会访问语句
X
Y
Z
。即使它们不在输入程序中


这是正确的使用方法吗?如果不是,那么我假设
visitChildren(ParseTree*tree)
是每个访问者函数使用的正确api。但是我不理解如何从*上下文< /C> > < /P> > P>访问<代码> PARSETRE < /COD>数据结构。这个问题与C++访问者没有直接关系,而是ANTLR4中的一般访问者问题。你所做的是以一种你不想做的方式为访客走捷径。不要显式地手动访问某些子树,而是调用超级实现,让它为您执行,并在单个
visitStatementXXX
函数中收集结果。看看这个,用于单元测试(用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);
  }
...
class EvalParseVisitor:public MySQLParserBaseVisitor{
公众:
std::vector results;//每个选择项有一个条目。
bool asBool(中的评估值){
如果(!in.isNullType()&&in.number!=0)
返回true;
返回false;
};
虚拟任意visitSelectItem(MySQLParser::SelectItemContext*context)覆盖{
任何结果=访问儿童(上下文);
results.push_back(result.as());
返回结果;
}
虚拟任意visitExprNot(MySQLParser::ExprNotContext*context)覆盖{
EvalValue value=visit(context->expr());
开关(值.类型){
case EvalValue::Null:
返回EvalValue::fromNotNull();
case EvalValue::NotNull:
返回EvalValue::fromNull();
违约:
返回EvalValue::fromBool(!asBool(value));
}
}
虚拟任意visitExprAnd(MySQLParser::ExprAndContext*context)重写{
左评估值=访问(上下文->表达式(0));
EvalValue right=访问(上下文->表达式(1));
if(left.isNullType()| | right.isNullType())
返回EvalValue::fromNull();
返回EvalValue::fromBool(asBool(左)和&asBool(右));
返回visitChildren(上下文);
}
...

基本部分是调用
visit()
它反过来迭代给定上下文树的子节点,只触发实际存在的元素的访问者函数。

我觉得自己是世界上最大的白痴。我在阅读这方面的源代码,并认为这就是你要做的。但是我的IDE有红色的曲线,所以我从未尝试编译它;(不要把我看得太坏)。我改成了那样,效果很好。谢谢。我也很欣赏这个例子。