clang的python接口(二)
N久之前的一个坑了,今天来为大家填上。(果然需求是第一生产力)
常用类
- Index:
这个类是clang的核心类。具有构建语法树的主类。
常用方法:
create()
'''
初始化Index类。
'''
parse(self, path, args=None, unsaved_files=None, options = 0)
'''
构建语法树,同时返回AST的根节点。前两个参数比较常用,
path:要进行构建的源文件的路径
args:编译选项例如-DUSE_LIBPNG1等
'''
- TranslationUnit:
编译单元,一般来说指的是进行编译的文件。 - CursorKind
语法树的的索引结点的类别。
常用方法:
get_children()
"""
这个方法用来获取其子节点列表的迭代器。
"""
get_tokens()
'''
得到了其每个分词的列表的迭代器。
'''
@property
translation_unit()
"""返回这个节点索引所在的源文件"""
- TypeKind
这个是每个节点的语义类别。(后文会具体区分和TypeKind的区别) - Config
clang的配置类
AST的构建
抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。 —— [ 维基百科 ]
首先说明一下AST的定义,之后通过代码具体讲解一下clang的工作过程。
from clang.cindex import Config
from clang.cindex import TypeKind
from clang.cindex import CursorKind
from clang.cindex import Index
#首先需要导入需要的类型包。
#clang的python绑定的具体配置方法看博主的另一篇博客clang的python接口(-)
libclangPath = '/usr/lib/llvm-4.0/lib/libclang-4.0.so.1'
if Config.loaded==True:
pass
else:
Config.set_library_file(libclangPath)
#libclangPath是libclang.so的具体位置。
index = Index.create()
tu = index.parse(file_path,commands)
构建语法树的过程十分简单,接下来根据需要进行遍历,常用的便利方法一般都是先序遍历或者层次遍历。
(关于树的遍历可以查询《算法导论》之类的有关数据结构的书籍或资料,这里不进行赘述)
前序遍历AST
这里主要讲解通过前序遍历AST来进行提取各个语法单元的过程,
def iterAST(cursor):
'''
前序遍历严格来说是一个二叉树才有的概念。这里指的是对于每个节点,先遍历本节点,再遍历子节点的过程。
'''
for cur in cursor.get_children():
#do something
iterAST(cur)
语法单元提取
这里需要通过识别CursorKind以及TypeKind来进行语法单元的识别。
def iterAST(cursor):
'''
在遍历过程中,遇到了一个节点就进行检查。
CursorKind指的是这个节点在AST中的位置例如(函数,类,参数定义等)
TypeKind指的是这个节点的语义类别,例如这个参数的类别是const char,int等类别。
'''
for cur in cursor.get_children():
if cur.CursorKind==CursorKind.FUNCTION_DECL:
#do something
for cur_sub in cur.get_children():
if cur_sub .kind == CursorKind.CALL_EXPR:
#do something
#这一段代码分析的是函数定义调用的其他函数。
elif cur.kind == CursorKind.FIELD_DECL:
#do something
elif cur.type.kind == TypeKind.UCHAR:
#do something
iterAST(cur)
分词的提取
这里讲解一下如何提取分词,也就是Token.
def iter_cursor_content(self,cur):
'''
这里展示的是一个提取每个分词的方法。
'''
cursor_content=""
for token in cur.get_tokens():
#针对一个节点,调用get_tokens的方法。
str_token = token.spelling+" "
cursor_content = cursor_content+str_token
return cursor_content