背景
C/C++ コードから関数定義を抜き出したり, ちょっとした source-to-source translation とかしたい.
libclang(clang-index?)の python binding などもありますが, メソッド調べるとかめんどいです.
clang そのものに AST を JSON でダンプする機能があります!
ast-dump
-ast-dump=json
で JSON でダンプできます. (xml もあるよ)
-ast-dump
オプションは Clang 固有のため, Clang 専用コンパイルフラグである -Xclang
を先につけます.
もしくは, 条件によっては -cc1
(clang そのもの?を呼び出す) でもよいです.
-fsyntax-only
で構文処理だけするようにします(実際にコンパイルを行わない)
$ clang -Xclang -ast-dump=json -fsyntax-only test.c
$ clang -cc1 -ast-dump=json -fsyntax-only test.c
あとは python で読むなりして JSON 処理すればいけます!
JSON viewer 使って構成をチェックするとよいでしょう.
スクリプト例
JSON を python で処理する例です.
def eval_expr(expr):
s = ""
if expr['kind'] == 'ConstantExpr':
inner = expr['inner']
assert len(inner) == 1
s += eval_expr(inner[0])
elif expr['kind'] == 'UnaryOperator':
opcode = expr['opcode']
assert len(expr['inner']) == 1
assert opcode == '-'
s += "{}{}".format(opcode, eval_expr(expr['inner'][0]))
elif expr['kind'] == 'IntegerLiteral':
s += expr['value']
elif expr['kind'] == 'BinaryOperator':
assert len(expr['inner']) == 2
opcode = expr['opcode']
s += "{} {} {}".format(eval_expr(expr['inner'][0]), opcode, eval_expr(expr['inner'][1]))
else:
print("eval_expr: TODO:", expr)
raise
return s
制約
プリプロセッサ
#define
とかのプリプロセッサ関連の情報はダンプされません. プリプロセッサ情報とかも欲しい場合はなにかほかの手を考える必要があります. AST 情報吐いてくれそうな C preprocessor を探してみるとか.
libclang では pp-trace https://clang.llvm.org/extra/pp-trace.html がありこれを何やら活用する手もありそうです.
システムヘッダのファイル名はトラックされない場合がある?
AST はプリプロセス後のソースで生成されます.
たとえば cublas_v2.h
を処理すると FP_NAN
(math.h で定義) の EnumDecl
が生成されますが, これに対応するソースファイル情報(math.h
)は出てこないです.
以下のように, includedFrom で cuComplex.h
は残っていますが, math.h
はないです.
{
"id": "0xe21268",
"kind": "EnumDecl",
...
"end": {
"spellingLoc": {
"offset": 25890,
"line": 856,
"col": 17,
"tokLen": 1,
"includedFrom": {
"file": "/usr/local/cuda/include/cuComplex.h"
}
},
"expansionLoc": {
"offset": 25898,
"line": 857,
"col": 7,
"tokLen": 6,
"includedFrom": {
"file": "/usr/local/cuda/include/cuComplex.h"
}
}
}
},
"name": "FP_NAN",
"type": {
"qualType": "int"
},
...
システムヘッダの場合はうまくトラックされないようです.
ただ, TypedefDecl など一部はうまくトラックしてくれるようです.
関数ポインタの typedef
typedef の定義が関数ポインタの場合, ほかの typedef と切り分けが面倒かもです.