使用Antlr解析代码后如何检测循环块?

问题描述

我正在与Antlr4合作,我想解析和分析任何c ++代码以检测任何c ++源代码中的任何循环以对其进行依赖关系分析,我想检测外部循环,如果它具有内部循环,我想将其检测为内部循环,但是我无法在源代码中检测到任何循环。

我做了什么并尝试过:

这是我在Antlr中用于“ for循环”的规则:

forBlock: 'for' '(' (classicFor | forEach) ')' controlStructureBody ;
forExpression: primaryExpression (',' primaryExpression)* ;

我用以下代码打印出令牌:

#include <iostream>
using namespace std;
int main()
{
    for (int i = 0; i <= 5; i++) {
        for (int j = 0; j <= 5; j++) {
            cout << i << j << " \t";
        }
        cout << "\n";
    }
    return 0;
}

将此代码与jave一起使用

public void printToken(String inputFile) throws FileNotFoundException,IOException {
        System.out.println("The tokens of the source code is: \n");
        CharStream inputStream = CharStreams.fromFileName(inputFile);
        TokensLexer tokensLexer = new TokensLexer(inputStream);
        CommonTokenStream tokenStream = new CommonTokenStream(tokensLexer);
        tokenStream.fill();
        for (Token token : tokenStream.getTokens()) {
            System.out.println("<" + token.getText() + "> " + "<" + token.getType() + ">");
        }
    }

它给了我每个for循环的类型:

<for> <45>
<for> <45>

我尝试了以下代码

CharStream inputStream = CharStreams.fromFileName(inputFile);
        // lexing the code
        TokensLexer tokensLexer = new TokensLexer(inputStream);
        CommonTokenStream tokenStream = new CommonTokenStream(tokensLexer);
        // parsing the code
        TokensParser tokensParser = new TokensParser(tokenStream);
        tokenStream.fill();
        for (Token token : tokenStream.getTokens()) {
            if (token.getType() == 45)
                System.out.println("loop is found");
        }
}

当我输入“ 45”时,它会打印出“找到回路” 。当我将数字更改为“ 39”时,它会打印出“找到回路” 只有一次。 我尝试使用“ 39”,因为我从Antlr语法生成文件中具有此值。

Tokens.tokens-> for = 45。

Tokens.lexer.tokens-> for = 45

TokensParser.java-> Rule_forBlock = 39; Rule_forExpression = 40;

,当我尝试添加更多循环时:

#include <iostream>
using namespace std;
int main()
{
    for (int i = 0; i <= 5; i++)
    {
        for (int j = 0; j <= 5; j++) {
            cout << i << j << " \t";
        }
        cout << "\n";
    }
    for (int  i = 0; i < 100; i++)
    {
       cout<<"Test"<<endl;
    }    return 0;
}

使用数字39,仍然只检测到一个循环。

是否可以使用Antlr检测源代码中的循环并区分外部循环和内部循环?

解决方法

您没有提供足够的代码/语法来重现您所描述的内容。因此,我将给您演示如何使用the C++ grammar in the ANTLR repository解决该问题的示例。

我只更改了1条规则。代替:

iterationstatement
   : While '(' condition ')' statement
   | Do statement While '(' expression ')' ';'
   | For '(' forinitstatement condition? ';' expression? ')' statement
   | For '(' forrangedeclaration ':' forrangeinitializer ')' statement
   ;

我在其中添加了labels

iterationstatement
   : While '(' condition ')' statement                                 #WhileStatement
   | Do statement While '(' expression ')' ';'                         #DoWhileStatement
   | For '(' forinitstatement condition? ';' expression? ')' statement #ClassicForStatement
   | For '(' forrangedeclaration ':' forrangeinitializer ')' statement #ForEachStatement
   ;

进行了这些更改之后,下面的演示类:

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

public class Main {

    public static void main(String[] args) throws Exception {

        String source = "#include <iostream>\n" +
                "using namespace std;\n" +
                "int main()\n" +
                "{\n" +
                "    for (int i = 0; i <= 5; i++)\n" +
                "    {\n" +
                "        for (int j = 0; j <= 5; j++) {\n" +
                "            cout << i << j << \" \\t\";\n" +
                "        }\n" +
                "        cout << \"\\n\";\n" +
                "    }\n" +
                "    for (int  i = 0; i < 100; i++)\n" +
                "    {\n" +
                "       cout<<\"Test\"<<endl;\n" +
                "    }    return 0;\n" +
                "}";

        CPP14Lexer lexer = new CPP14Lexer(CharStreams.fromString(source));
        CPP14Parser parser = new CPP14Parser(new CommonTokenStream(lexer));
        ParseTreeWalker.DEFAULT.walk(new ForListener(),parser.translationunit());
    }
}

class ForListener extends CPP14BaseListener {

    private int forLevel = 0;

    @Override
    public void enterClassicForStatement(CPP14Parser.ClassicForStatementContext ctx) {
        this.forLevel++;
        printInfo("classic",ctx);
    }

    @Override
    public void exitClassicForStatement(CPP14Parser.ClassicForStatementContext ctx) {
        this.forLevel--;
    }

    @Override
    public void enterForEachStatement(CPP14Parser.ForEachStatementContext ctx) {
        this.forLevel++;
        printInfo("each",ctx);
    }

    @Override
    public void exitForEachStatement(CPP14Parser.ForEachStatementContext ctx) {
        this.forLevel--;
    }

    private void printInfo(String forType,ParserRuleContext ctx) {
        System.out.printf("%s %s for-statement on line %d.%n",this.forLevel == 1 ? "OUTER" : "INNER",forType,ctx.getStart().getLine());
    }
}

将提供以下输出:

OUTER classic for-statement on line 5.
INNER classic for-statement on line 7.
OUTER classic for-statement on line 12.