Clang中包含了非常多的關於抽象語法樹(AST)的訪問和操作的類和接口。我們程序開發人員可以直接通過繼承其中的某些類,重寫其中的關鍵成員方法,從而形成我們自己的對抽象語法樹的操作。
那么,首先我們簡要介紹幾個概念:
抽象語法樹(AST):抽象語法樹是源代碼的抽象語法結構的樹狀表現形式。樹上的每個節點都表示源代碼中的一種結構。之所以說語法是“抽象”的,是因為這里的語法並不會表示出真實語法中出現的每個細節。一般的,在源代碼的翻譯和編譯過程中,語法分析之后會創建出抽象語法樹。一旦AST被創建出來,在后續的處理過程中,比如語義分析階段,會添加很多語義信息幫助進行后續的語義翻譯工作。AST其實就是一個程序的靜態模型,它抽象出了一個程序在靜態時結構狀態,我們可以通過對AST的分析從而了解一個程序的相關靜態信息。
Source to Source轉換:我所理解的源到源的轉換可以簡單的當做是從一種源代碼的形式轉換到另一種源代碼的形式。這其中,形式的定義很寬泛,包括了最簡單的源代碼風格、變量函數的命名到不同的編程語言。這些的轉換都可以成為source to source transformation。
那么,如果要進行源到源的轉換,最直接的思路和方式就是通過獲得一個程序代碼片段的抽象語法樹(AST),然后通過修改AST的若干子樹或若干結點,然后將AST轉換成源代碼,從而完成源到源的轉換。
既然,我們已經明白了我們的目標和途徑,那么接下來就介紹一下Clang中的基於AST的操作以及它們的設計模式。
Clang中的AST部分操作和表示的設計和實現比較類似於設計模式中的訪問者模式。
Stmt
Stmt是表示程序語言語法成分的最原始的抽象基類接口,而我們的其他各種語法類型則是繼承Stmt,如IfStmt,NullStmt,DeclStmt等等。
它們相當於是訪問者模式中的Element和ConcreteElement。元素類和抽象元素類。
RecursiveASTVisitor
RecursiveASTVisitor類似於訪問者模式中的訪問者。
我們在實現自己的操作AST的方法時需要繼承自RecursiveASTVisitor類,並重寫其中的多個方法,一般為bool VisitXXX(Stmt* stmt)方法。
每一個VisitXXX方法都是訪問某個具體對應類型的Stmt結點並對它進行操作的函數。 所以RecursiveASTVisitor和我們寫的Visitor類就相當於抽象訪問者類和訪問者類。
ASTConsumer
ASTConsumer類的主要功能是提供一種自頂向下的對抽象語法樹進行訪問的入口。
因為AST中包含了各種各樣的Stmt,所以也可以認為ASTConsumer類似於提供了訪問這個包含多種類型Stmt的容器的入口。
因此我們可以將它對應到訪問者模式中的ObjectStructure和Client。在這其中有多種方法來遍歷當前程序生成的抽象語法樹AST,從而獲得各種各樣類型的AST Node。
因此,我們需要自己實現一個繼承自ASTConsumer得類,並重寫其中的遍歷AST的方法,如:HandleTopLevelDecl,HandleTranslationUnit等等。
2、接着,調用對應的VisitXXX()方法,進行具體的操作;
3、最后,在遇到接下來的語句再調用對應的TraverseDecl、TraverseStmt等等,遞歸執行。
有人說,訪問者模式比較適用於對已有功能的重構,或者說對一個項目已經完成,它的元素類型、數據結構已經定的差不多,而對數據的操作還有可能后序會改變。這樣,可以使用訪問者模式對原有的代碼進行重構一遍。