ANTLR文法ファイルとJavaソースコードを使用してJavaソースを分析するための完全な実装(おそらくgithubまたはgooglecode)を持っている人はいますか?たとえば、変数の数やメソッドなどを簡単に数えたいと思っています。
また、ANTLRの最新バージョンを使用しています。
昼休みにこれに挑戦しようと思いました。これで問題が完全に解決するわけではありませんが、出発点になる可能性があります。この例では、すべてを同じディレクトリで実行していると想定しています。
GitHub からANTLR ソースをダウンロードします。ANTLR サイトからのコンパイル済みの「完全な」JAR には、既知のバグが含まれています。GitHub リポジトリに修正があります。
ANTLR tarball を抽出します。
% tar xzf antlr-antlr3-release-3.4-150-g8312471.tar.gz
ANTLR の「完全な」JAR をビルドします。
% cd antlr-antlr3-8312471
% mvn -N install
% mvn -Dmaven.test.skip=true
% mvn -Dmaven.test.skip=true package assembly:assembly
% cd -
Java 文法をダウンロードします。他にもありますが、私はこれが機能することを知っています。
文法を Java ソースにコンパイルします。
% mkdir com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated
% mv *.g com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated
% java -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar org.antlr.Tool -o com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated Java.g
Java ソースをコンパイルします。
% javac -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/*.java
次のソース ファイル Main.java を追加します。
import java.io.IOException;
import java.util.List;
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import com.habelitz.jsobjectizer.unmarshaller.antlrbridge.generated.*;
public class Main {
public static void main(String... args) throws NoSuchFieldException, IllegalAccessException, IOException, RecognitionException {
JavaLexer lexer = new JavaLexer(new ANTLRFileStream(args[1], "UTF-8"));
JavaParser parser = new JavaParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)(parser.javaSource().getTree());
int type = ((Integer)(JavaParser.class.getDeclaredField(args[0]).get(null))).intValue();
System.out.println(count(tree, type));
}
private static int count(CommonTree tree, int type) {
int count = 0;
List children = tree.getChildren();
if (children != null) {
for (Object child : children) {
count += count((CommonTree)(child), type);
}
}
return ((tree.getType() != type) ? count : count + 1);
}
}
コンパイル。
% javac -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main.java
カウントする Java ソースのタイプを選択します。たとえば、VAR_DECLARATOR
、FUNCTION_METHOD_DECL
、またはVOID_METHOD_DECL
。
% cat com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/Java.tokens
最近作成された Main.java を含む任意のファイルで実行します。
% java -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main VAR_DECLARATOR Main.java
6
もちろん、これは不完全です。よく見ると、拡張for
ステートメントのローカル変数がカウントされていないことに気付いたかもしれません。そのためにはFOR_EACH
、 ではなくtype を使用する必要がありますVAR_DECLARATOR
。
Java ソースの要素を十分に理解し、それらがこの特定の文法の定義にどのように一致するかを合理的に推測できる必要があります。また、参照のカウントを行うこともできません。宣言は簡単ですが、たとえば、フィールドの使用をカウントするには、参照の解決が必要です。パッケージ内のクラスp.C.f
の静的フィールドを参照しますか、それともクラスの静的フィールドによって格納されたオブジェクトのインスタンス フィールドを参照しますか? 基本的なパーサーは、Java のような複雑な言語の参照を解決しません。これは、一般的なケースが非常に困難になる可能性があるためです。このレベルの制御が必要な場合は、コンパイラ (またはそれに近いもの) を使用する必要があります。Eclipse コンパイラーは一般的な選択肢です。f
C
p
f
C
p
ANTLR以外にもオプションがあることにも言及する必要があります。JavaCC は別のパーサー ジェネレーターです。JavaCC をパーサー ジェネレーターとして使用する静的解析ツール PMD を使用すると、指定した種類のカウントに使用できるカスタム ルールを作成できます。