首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用ANTLR4计数令牌

使用ANTLR4计数令牌
EN

Stack Overflow用户
提问于 2015-08-13 09:22:16
回答 2查看 2.3K关注 0票数 4

我需要使用ANTLR4编写一个Java程序,该程序可以计算变量、操作符、标点符号和保留单词的数量,给出一个具有单一方法的源文件。

如何使用ANTLR4 来根据令牌的类型进行计数?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-08-15 09:39:46

在做了一些研究之后,基于詹杜兹,我意识到我所需要的需要两种技术:

  • 运算符、保留的单词和标点符号可以使用ANTLR4 lexer进行计数,因为它们可以在源代码中识别,而无需将它们放入上下文中。
  • 变量(还有常量、方法、类.)可以使用ANTLR4解析器进行计数,因为识别它们需要解析和理解这些标识符出现在其中的上下文。

为了所有将来需要做类似事情的人,我就是这样做的:

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

2)创建一个新的Java项目。将JavaLexer.javaJavaListener.javaJavaParser.javaJavaBaseListener.java添加到项目中,并将ANTLR库添加到项目的构建路径。

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

例如,此侦听器在每次发现新方法时将引发计数器1:

代码语言:javascript
复制
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)创建一个ParseTreeWalker ,一个解析器,一个ParseTree和一个

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

然后,最后实例化侦听器并遍历ParseTree。

例如:

代码语言:javascript
复制
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);
}

这就是基础。接下来的步骤取决于您想要实现什么,这使我回到使用LexerParser之间的区别。

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

代码语言:javascript
复制
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;
}

对于需要解析AST的任务,如标识变量、方法、注释等,请使用Parser。使用此代码计算方法中变量声明的数量:

代码语言:javascript
复制
/**
 * 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();
}
票数 5
EN

Stack Overflow用户

发布于 2015-08-15 00:34:34

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

代码语言:javascript
复制
@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)* 
    ;

像这样,您可以直接说“保留”键而不是getToken(),并在每次增量后存储计数

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31984268

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档