2015-08-13 53 views
3

我需要編寫一個Java程序,使用ANTLR4,給定一個單一方法的源文件,可以計算變量,運算符,標點符號和保留字的數量。使用ANTLR4計數令牌

如何根據其類型使用ANTLR4來計算令牌?

回答

4

算後做一些研究,並根據ÖzhanDUZ,我意識到我需要什麼,需要兩種技術:

  • 運算符,保留字和標點符號可以使用ANTLR4詞法分析器進行計數,因爲可以在源代碼中標識這些運算符,而不必將它們置於上下文中。
  • 變量(也常數,方法,類...)可以使用ANTLR4解析器進行計數,因爲它們識別需要分析和理解,其中這些標識符出現在上下文。

對於爲了所有將來需要做類似工作的人,我正是這麼做的:

1)使用ANTLR命令行工具爲您的語言生成Lexer,Parser和BaseListener。 ANTLR官方網站上有關於如何操作的說明。在這個例子中,我創建了這些類來分析Java語言。

2)創建一個新的Java項目。將JavaLexer.java,JavaListener.java,JavaParser.javaJavaBaseListener.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)創建一個詞法,一個解析器,一個分析樹和一個ParseTreeWalker

  • 詞法 - 運行在你的代碼,從開始到結束,並將它分爲「TOK ens「 - 標識符,文字,操作符等。每個標記都有一個名稱和一個類型。類型列表可以在您的詞法分析器文件的開頭找到(在我們的例子中,JavaLexer.java
  • 解析器 - 使用詞法分析器的輸出來構建代表您的代碼的AST(抽象語法樹)。這樣做除了標記源代碼外,還可以瞭解每個標記在哪個上下文中出現。
  • 分析樹 - 無論你的整個代碼的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; 
} 

對於需要解析AST,如識別變量,方法,註釋和更多的任務,使用的解析器。使用此代碼來計算方法內變量聲明的數量:

/** 
* 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(); 
} 
1

您可以使用詞法分析器的nextToken方法迭代令牌。對於this簡單語法,以下代碼片段顯示如何使用nextToken方法。

import java.io.ByteArrayInputStream; 

import org.antlr.v4.runtime.ANTLRInputStream; 
import org.antlr.v4.runtime.CharStream; 
import org.antlr.v4.runtime.Token; 

public class Antlr4TokenItreator { 

    public static void main(String[] args) throws Exception { 
     CharStream stream = new ANTLRInputStream(new ByteArrayInputStream("hello world".getBytes())); 
     HelloLexer lexer = new HelloLexer(stream); 
     for (Token token = lexer.nextToken(); token.getType() != Token.EOF; token = lexer.nextToken()) { 
      System.out.println(HelloLexer.VOCABULARY.getSymbolicName(token.getType())); 
     } 
    } 
} 
+0

這幾乎完成。在我繼續研究這個主題時,我發現使用ANTLR詞法分析器可以輕鬆地提取操作符,標點符號和保留字,就像您寫的一樣。變量名稱不能用詞法標識,因此它們需要使用解析器 – mittelmania

0

您可以使用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)* 
    ; 

像這樣的軌道,在爲gettoken()代替,你可以直接說「保留」鍵和存儲每個增量