回答
算後做一些研究,並根據ÖzhanDUZ,我意識到我需要什麼,需要兩種技術:
- 運算符,保留字和標點符號可以使用ANTLR4詞法分析器進行計數,因爲可以在源代碼中標識這些運算符,而不必將它們置於上下文中。
- 變量(也常數,方法,類...)可以使用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)創建一個詞法,一個解析器,一個分析樹和一個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();
}
您可以使用詞法分析器的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()));
}
}
}
您可以使用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()代替,你可以直接說「保留」鍵和存儲每個增量
- 1. 瞭解ANTLR4令牌
- 2. ANTLR4與停止令牌
- 3. antlr4令牌識別錯誤:'$'
- 4. 不一致令牌ANTLR4
- 5. ANTLR4:如何注入令牌
- 6. 令牌計數令牌長度
- 7. ANTLR4:Island語法,令牌匹配/跳過
- 8. antlr4類似的令牌定義
- 9. ANTLR4令牌化一大組關鍵字
- 10. C計數令牌
- 11. 在監聽器中使用ParserRuleContext遍歷令牌 - ANTLR4
- 12. 如何獲取antlr4中每個令牌的行數和列數?
- 13. 如何使antlr4完全令牌化終端節點
- 14. 令牌化和統計令牌
- 15. 如何使用設計驗證令牌
- 16. 有沒有辦法讓ANTLR4爲生成的令牌使用枚舉?
- 17. antlr4使用詞法分析器島語法給出令牌識別錯誤
- 18. csrf令牌使用
- 19. ANTLR4令牌圖像連接與混合中的註釋
- 20. 來自ANTLR4中列表的令牌的非貪婪匹配
- 21. antlr4:令牌識別錯誤「。」點和其他標點符號
- 22. ANTLR4 - 具有不可區分令牌的語言
- 23. 跳繩令牌這是ANTLR4語法的一部分
- 24. antlr4:創建ParseTrees時忽略超快令牌
- 25. ANTLR4令牌被取代時不被識別
- 26. ANTLR4規則和令牌屬性在嵌入式行動
- 27. Antlr4(java)試圖匹配所有輸入到第一個令牌
- 28. 計數令牌和字符在名單
- 29. 用令牌計算API調用
- 30. 預計初始化*令牌
這幾乎完成。在我繼續研究這個主題時,我發現使用ANTLR詞法分析器可以輕鬆地提取操作符,標點符號和保留字,就像您寫的一樣。變量名稱不能用詞法標識,因此它們需要使用解析器 – mittelmania