1. 程式人生 > >(原創)clang的python介面教程(二)

(原創)clang的python介面教程(二)

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

參考資料