antlr — API посетителя Antlr4 c ++

Я использую API-интерфейс посетителя C ++ Antlr4 для обхода дерева разбора. Однако я изо всех сил пытаюсь заставить это функционировать правильно. А именно, я не уверен, как использовать 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

IDENTIFIER, DATE, а также NAME являются терминалами.

Я строю структуру синтаксического анализа 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 для использования в каждой функции посетителя. Но я не понимаю, как получить доступ к ParseTree структура данных из *Context,

0

Решение

Этот вопрос не имеет прямого отношения к посетителю 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);
}
...

Важной частью является призыв к visit() который, в свою очередь, перебирает дочерние узлы данного дерева контекста и запускает только функции посетителя для элементов, которые действительно существуют.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector