Java 使用ANTLR4计算令牌数

Java 使用ANTLR4计算令牌数,java,antlr,antlr4,Java,Antlr,Antlr4,我需要编写一个Java程序,使用ANTLR4,给定一个源文件,使用一个方法,可以计算变量、运算符、标点符号和保留字的数量 如何使用ANTLR4根据标记的类型对其进行计数?您可以像这样使用hashmap来跟踪所有的单词类型 @header { import java.util.HashMap; } @members { // Map variable name to Integer object holding value HashMap memory = new HashMap(); }

我需要编写一个Java程序,使用
ANTLR4
,给定一个源文件,使用一个方法,可以计算变量、运算符、标点符号和保留字的数量


如何使用
ANTLR4
根据标记的类型对其进行计数?

您可以像这样使用hashmap来跟踪所有的单词类型

@header {
import java.util.HashMap;
}

@members {
// Map variable name to Integer object holding value 
HashMap memory = new HashMap();
}

Identifier 
:   IdentifierNondigit(  IdentifierNondigit |   Digit )* {
    if(memory.containsKey(getText())){
        memory.put(getText(),(((Integer)memory.get(getText()))+1));     
    }
    else {
        memory.put(getText(),1);
    }
    System.out.println(getText()+" : "+memory.get(getText()));
} 
// { getText().length()<=3}?{ String str=getText(); while(str.length()<=3){ str=str+str;} setText(str);}
    |   IdentifierNondigit (   IdentifierNondigit |   Digit)* 
    ;
@标题{
导入java.util.HashMap;
}
@成员{
//将变量名映射到整型对象保持值
HashMap内存=新的HashMap();
}
标识符
:IdentifierNondigit(IdentifierNondigit|Digit)*{
if(memory.containsKey(getText())){
memory.put(getText(),(((整数)memory.get(getText())+1));
}
否则{
put(getText(),1);
}
System.out.println(getText()+“:”+memory.get(getText());
} 

//{getText().length()在做了一些研究之后,基于Özhan Düz,我意识到我需要两种技术:

  • 运算符、保留字和标点符号可以使用ANTLR4 lexer计数,因为它们可以在源代码中识别,而无需将它们放入上下文中
  • 变量(以及常量、方法、类……)可以使用ANTLR4解析器进行计数,因为识别它们需要解析和理解这些标识符出现在其中的上下文
为了所有将来需要做类似事情的人,我就是这样做的:

1) 使用ANTLR命令行工具为您的语言生成Lexer、Parser和BaseListener。有关如何生成的说明可以在ANTLR官方网站上找到。在本例中,我创建了这些类来分析Java语言

2) 创建一个新的Java项目。将
JavaLexer.Java
JavaListener.Java
JavaParser.Java
JavaBaseListener.Java
添加到项目中,并将ANTLR库添加到项目的构建路径中

3) 创建一个扩展
JavaBaseListener
基类的新类。查看
JavaBaseListener.java
文件中可以覆盖的所有方法。扫描源代码的AST时,将在发生相应事件时调用每个方法(例如-
enterMethodDeclaration()
将在解析器每次到达新方法声明时调用)

例如,此侦听器将在每次找到新方法时将计数器提高1:

public static final AtomicInteger count = new AtomicInteger();

/**
 * Implementation of the abstract base listener
 */
public static class MyListener extends JavaBaseListener {
    /**
     * Overrides the default callback called whenever the walker has entered a method declaration.
     * This raises the count every time a new method is found
     */
    @Override
    public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) {
        count.incrementAndGet();
    }
}
4) 创建一个词法分析器、一个语法分析器、一个语法树和一个语法树浏览器

  • Lexer—从头到尾运行您的代码,并将其拆分为“标记”-标识符、文本、运算符等。每个标记都有一个名称和一个类型。类型列表可以在Lexer文件的开头找到(在我们的示例中,
    JavaLexer.java
  • 解析器-使用lexer的输出构建一个表示代码的AST(抽象语法树)。这将允许,除了标记源代码之外,还可以理解每个标记出现在哪个上下文中
  • ParseTree—整个代码的AST或其子树
  • ParseTreeWalker-一个允许“遍历”树的对象,这基本上意味着以层次结构而不是从头到尾扫描代码
然后,最后,实例化您的侦听器并遍历ParseTree

例如:

public static void main(String... args) throws IOException {
    JavaLexer lexer = new JavaLexer(new ANTLRFileStream(sourceFile, "UTF-8"));
    JavaParser parser = new JavaParser(new CommonTokenStream(lexer));
    ParseTree tree = parser.compilationUnit();

    ParseTreeWalker walker = new ParseTreeWalker();
    MyListener listener = new MyListener();
    walker.walk(listener, tree);
}
这是基础。接下来的步骤取决于您想要实现什么,这让我回到使用词法分析器和解析器之间的区别:

对于代码的基本词法分析,如识别运算符和保留字,请使用词法分析器对标记进行迭代,并通过检查Token.type字段确定其类型。使用此代码计算方法中保留字的数量:

private List<Token> tokenizeMethod(String method) {
    JavaLexer lex = new JavaLexer(new ANTLRInputStream(method));
    CommonTokenStream tokStream = new CommonTokenStream(lex);
    tokStream.fill();

    return tokStream.getTokens();
}


/**
 * Returns the number of reserved words inside the given method, using lexical analysis
 * @param method The method text
 */
private int countReservedWords(String method) {
    int count = 0;

    for(Token t : tokenizeMethod(method)) {
        if(t.getType() <= JavaLexer.WHILE) {
            count++;
        }
    }

    return count;
}
/**
 * Returns the number of variable declarations inside the given method, by parsing the method's AST
 * @param method The method text
 */
private int countVariableDeclarations(String method) {
    JavaLexer lex = new JavaLexer(new ANTLRInputStream(method));
    JavaParser parse = new JavaParser(new CommonTokenStream(lex));
    ParseTree tree = parse.methodDeclaration();

    ParseTreeWalker walker = new ParseTreeWalker();
    final AtomicInteger count = new AtomicInteger();
    walker.walk(new JavaBaseListener() {
        @Override public void enterLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) {
            count.incrementAndGet();
        }
    }, tree);

    return count.get();
}