отступ в теле функции antlr

#antlr #antlr4

#antlr #antlr4

Вопрос:

Используя это https://github.com/antlr/grammars-v4/tree/master/cpp грамматика antlr Я пытаюсь разобрать код на C . Я хочу получить код для каждой функции, поэтому я решил сделать visit visitFunctionBody , и вы можете увидеть код ниже,

 #include <iostream>
#include <antlr4-runtime.h>

#include "parser/CPP14Lexer.h"
#include "parser/CPP14BaseVisitor.h"
#include "parser/CPP14Parser.h"
#include "parser/CPP14Visitor.h"


class TREEVisitor : public CPP14BaseVisitor {
    public:
        virtual antlrcpp::Any TREEVisitor::visitFunctionBody(
            CPP14Parser::FunctionBodyContext *ctx) override
        {
            std::cout << ctx->getText() << std::endl;
            return visitChildren(ctx);
        }
};


int main(int argc, char *argv[]) {

    std::ifstream stream;
    stream.open(argv[1]);
    antlr4::ANTLRInputStream input(stream);
    CPP14Lexer lexer(amp;input);
    antlr4::CommonTokenStream tokens(amp;lexer);
    CPP14Parser parser(amp;tokens);
    antlr4::tree::ParseTree *tree = parser.translationunit();

    // Visitor
    auto *visitor = new TREEVisitor();
    visitor->visit(tree);

    return 0;
}
 

и я попытался разобрать этот очень простой код на c ,

 void foo()
{
    char buf[10];
    int i = 10;
    int b = i * 2;
    return b * i;
}
 

Результат моей функции посетителя antlr — это код примерной функции, но без перевода строки и отступа, как показано ниже,

 {charbuf[10];inti=10;intb=i*2;returnb*i;}
 

Как я могу получить исходный код функции, которую я анализирую, как в исходном файле?

В моем случае я анализирую большой файл C и хочу сопоставить результат моего синтаксического анализа с фактическим исходным кодом.

Спасибо

Ответ №1:

Это один из способов, но есть и другие. В CPP14Lexer.g4 измените «-> пропустить» на «-> канал (СКРЫТЫЙ)». Затем в visitFunctionBody() измените вызов «getText ()» на «myGetText (ctx)» и определите процедуру myGetText() * вот так, но для C «. Этот код написан на Java.

 public String myGetText(ParseTree node) {
    if (node.getChildCount() == 0) {
        Token t = ((TerminalNodeImpl)node).getSymbol();
        List<Token> tokensBefore = tokens.getHiddenTokensToLeft(t.getTokenIndex(), Token.HIDDEN_CHANNEL);
        String pre = "";
        if (tokensBefore != null) {
            StringBuilder builder2 = new StringBuilder();
            for (Token token : tokensBefore) {
                CharStream input = token.getInputStream();
                String s = input.getText(Interval.of(token.getStartIndex(),token.getStopIndex()));
                builder2.append(s);
            }
            pre = builder2.toString();
        }
        String s2 = node.getText();
        String ss = pre   s2;
        return ss;
    }
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < node.getChildCount(); i  ) {
        String s = myGetText(node.getChild(i));
        builder.append(s);
    }
    return builder.toString();
}
 

Вы также можете восстановить текст, не меняя «пропустить» на «СКРЫТЫЙ», напрямую запрашивая в потоке символов символы, задействованные между конечными узлами дерева.

 static void Reconstruct(ParseTree node, Parser parser)
{
    var ct = (ParserRuleContext)node;
    Token ta = ct.getStart();
    Token tb = ct.getStop();
    var input_stream = ta.getInputStream();
    var start = ta.getStartIndex();
    var stop = tb.getStopIndex();
    System.out.println(input_stream.getText(new Interval(start, stop)));
}
 

Комментарии:

1. В Reconstruct() функции ParseTree node аргумент может быть любого типа? например, могу ли я передать CPP14Parser::DeclarationContext ? или измените прототип на, static void Reconstruct(CPP14Parser::DeclarationContext, Parser parser){...}

2. Да, проверки проверки отсутствуют.

Ответ №2:

Вам просто нужно сохранить маркеры пробелов (отправить их в HIDDEN канал).

Затем вам потребуется доступ к вашему потоку токенов в вашем слушателе / посетителе. Затем вы можете создать интервал для вашего контекста и getText(interval) . Это будет включать токены на HIDDEN канале.

Пример:

В вашем лексере измените -> skip на -> channel(HIDDEN) :

 Whitespace: [ t]  -> channel(HIDDEN);

Newline: ('r' 'n'? | 'n') -> channel(HIDDEN);

BlockComment: '/*' .*? '*/' -> channel(HIDDEN);

LineComment: '//' ~ [rn]* -> channel(HIDDEN);
 

После анализа ваших входных данных передайте свой токенстрим вашему слушателю.

 ...
  CommonTokenStream tokens = new CommonTokenStream(lexer);
...
  ParseTree tree = parser.translationUnit(); 
...
  CPPListener listener = new CPPListener(tokens); 
  ParseTreeWalker walker = new​ ParseTreeWalker();
  walker.walk(listener, tree);
 

Затем в вашем слушателе:

 class CPPListener extends CPP14ParserBaseListener {
    TokenStream tokenStream;
    
    CPPListener(TokenStream tokenStream) {
        this.tokenStream = tokenStream;
    }

    @Override
    public void exitFunctionDefinition(CPP14Parser.FunctionDefinitionContext ctx) {
        Interval interval = new Interval(
           ctx.start.getTokenIndex(),
           ctx.stop.getTokenIndex()
        );
        String source = tokenStream.getText(interval);
        System.out.println(source);
    }
}
 

Вывод на вашем примере:

 void foo()
{
    char buf[10];
    int i = 10;
    int b = i * 2;
    return b * i;
}
 

Комментарии:

1. Привет, Майк, ссылка была полезной, но я не уверен, что смогу реализовать ее, просто прочитав этот документ, я был бы признателен, если бы вы могли поделиться некоторым кодом. Большое вам спасибо

2. Хотя я немного «помешан на языках», я лишь ОЧЕНЬ недолго занимался C около 20-30 лет назад. Я мог бы собрать что-нибудь вместе на Java. Если я найду несколько минут, я опубликую это сообщение. Основная идея заключалась бы в создании TokenStremRewriter из вашего потока токенов. Затем, когда вы посещаете дерево и получаете ParserRuleContext для интересующего вас узла, вы создаете объект Interval, используя токены start и stop этого контекста. Затем вы можете вызвать rewriter.getText(интервал), чтобы вернуть исходный код. Такова теория. Я обновлю сообщение, если у меня будет возможность закодировать его на Java.

3. Хм… у меня возникла дополнительная мысль, и я проверил ее. Вам не нужен рерайтер… обновит ответ.