Clangのpython bindingsを使う
研究全然進まないのに時間だけは過ぎていく. そんでずっと環境構築している気がする….
環境構築
clang ASTの情報が欲しかったので,python bindingsを使おうとしたらまたつまりました. 教えてもらったのをメモ!
homebrewでllvmインストール.
% brew install llvm --with-clang --with-python
この場合,brewから入れたpythonを使うので,pythonもhomebrewで入れる.
% brew install python
python bindingsを使うためには,LD_LIBRARY_PATHを設定する必要がある.
% export LD_LIBRARY_PATH=$(llvm-config --libdir):$LD_LIBRARY_PATH
(ちなみに,ソースコードからコンパイルした場合にはPYTHONPATHも設定しないといけない)
これで,無事使えるようになりました!
動かしたサンプルはこれ.
[main.py]
import sys import clang.cindex from clang.cindex import Index from clang.cindex import Config def print_node_tree(node): print "%s : %s" % (node.kind.name, node.displayname) for child in node.get_children(): print_node_tree(child) index = Index.create() tu = index.parse("test.cpp") print_node_tree(tu.cursor)
libclang の Python binding を使用する 〜導入編〜 - C++でゲームプログラミングこのサイトのを参考にしました.
Clang's AST
無事結果は出力されましたが,なにをやってるか全然わからない…
Clang ASTのこと知らないから当然わかるはずないよな,と思ってドキュメントを読みました.
Introduction to the Clang AST — Clang 3.5 documentation
clangのASTをダンプするには,-ast-dumpのオプションを使って
% clang -Xclang -ast-dump -fsyntax-only sample.c
とすればよい.
解析したコードと出力結果は以下のようになりました.
[sample.c]
int f() { int a = 1; int b = 2; int c = 0; c = a + b; return c; }
[出力結果]
TranslationUnitDecl 0x7fb55b8012c0 <<invalid sloc>> <invalid sloc> |-TypedefDecl 0x7fb55b8017c0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128' |-TypedefDecl 0x7fb55b801820 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128' |-TypedefDecl 0x7fb55b801b70 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]' `-FunctionDecl 0x7fb55b801c10 <a.c:1:1, line:10:1> line:1:5 f 'int ()' `-CompoundStmt 0x7fb55b801fe8 <line:2:1, line:10:1> |-DeclStmt 0x7fb55b801d38 <line:3:2, col:11> | `-VarDecl 0x7fb55b801cc0 <col:2, col:10> col:6 used a 'int' cinit | `-IntegerLiteral 0x7fb55b801d18 <col:10> 'int' 1 |-DeclStmt 0x7fb55b801dd8 <line:4:2, col:11> | `-VarDecl 0x7fb55b801d60 <col:2, col:10> col:6 used b 'int' cinit | `-IntegerLiteral 0x7fb55b801db8 <col:10> 'int' 2 |-DeclStmt 0x7fb55b801e78 <line:5:2, col:11> | `-VarDecl 0x7fb55b801e00 <col:2, col:10> col:6 used c 'int' cinit | `-IntegerLiteral 0x7fb55b801e58 <col:10> 'int' 0 |-BinaryOperator 0x7fb55b801f60 <line:7:2, col:10> 'int' '=' | |-DeclRefExpr 0x7fb55b801e90 <col:2> 'int' lvalue Var 0x7fb55b801e00 'c' 'int' | `-BinaryOperator 0x7fb55b801f38 <col:6, col:10> 'int' '+' | |-ImplicitCastExpr 0x7fb55b801f08 <col:6> 'int' <LValueToRValue> | | `-DeclRefExpr 0x7fb55b801eb8 <col:6> 'int' lvalue Var 0x7fb55b801cc0 'a' 'int' | `-ImplicitCastExpr 0x7fb55b801f20 <col:10> 'int' <LValueToRValue> | `-DeclRefExpr 0x7fb55b801ee0 <col:10> 'int' lvalue Var 0x7fb55b801d60 'b' 'int' `-ReturnStmt 0x7fb55b801fc8 <line:9:2, col:9> `-ImplicitCastExpr 0x7fb55b801fb0 <col:9> 'int' <LValueToRValue> `-DeclRefExpr 0x7fb55b801f88 <col:9> 'int' lvalue Var 0x7fb55b801e00 'c' 'int'
- AST Context
translation unitの最上位の宣言は常にtranslation unit declatationで,ユーザが書いたコードの最初の宣言はfunction declaration.
そのbodyはcompound statementで,さらにその子ノードはdeclaration statement.
最後に,return statementがある.
※translation unitは,コンパイルにおける基本単位
- AST Nodes
重要なASTのノードはType(型),Decl(宣言),DeclContext(宣言コンテキスト),Stmt(文).
ASTを探索するにはTranslationUnitDeclからスタートして,そこから再起的に到達しうるノードをたどる.
Clang's ASTの基本的な2つのノードはstatement(Stmt)とdeclatations(Decl).
Clang's ASTではexpressions(Expr)はstatementであることに注意.
なんとなくClang's ASTのことがわかったような気がする…!
python bindings
で,python bindingsってどうやって使うんだ!と思って調べると,python bindingsのソースコードを読めばいいですよって書いてありました.
これがある場所は,
/usr/local/Cellar/llvm/3.5.0_2/lib/python2.7/site-packages/clang/cindex.py
でも3500行ぐらいあるので,どっから読めばいいのかわからないし,ネットで親切な記事を探しました.そしたらドキュメントっぽいものが見つかりました.
Parsing C++ in Python with Clang
- Creating the index and parsing the source
index = clang.cindex.Index.create() tu = index.parse(<ファイル名>)
1行目のindexはtranslation unitを表す.
2行目のparceメソッドはファイルから1つのtranslation unitをパースする.
python bindingsはClang C APIのCXTranslationUnitオブジェクトをTranslationUnitとしている.
cursorはlibclangのキーになる概念で,これはパースされたtranslation unitのASTの中のノードを表す.
TranslationUnitl.cursorはtranslation unitの最上位のcursolを返す.これは,ASTの探索のスタート地点を提供する.
- Working with cursors
python bindingsはlibclangのcursorをCursorオブジェクトとしている. Cursorはたくさんのアトリビュートを持っているが,その中で興味深いものは
- kind:cursorがさしているAStのノードの種類の一覧
- spelling:ノードのソースコードの名前
- location:ノートがパースされたソースコードの場所
- get_children:子ノード
get_childrenを使って次のようにして再起的に,与えられたノードのすべての子ノードを探索することができる.
for c in node.get_children(): find_typerefs(c, typename)
とりあえず今まで調べたことのメモでした.
なんだか少しだけわかってきた気がする!!