基於Clang的Source to Source源代碼轉換(一)

Clang中包含了很是多的關於抽象語法樹(AST)的訪問和操做的類和接口。咱們程序開發人員能夠直接經過繼承其中的某些類,重寫其中的關鍵成員方法,從而造成咱們本身的對抽象語法樹的操做。編程

那麼,首先咱們簡要介紹幾個概念:設計模式

抽象語法樹(AST):抽象語法樹是源代碼的抽象語法結構的樹狀表現形式。樹上的每一個節點都表示源代碼中的一種結構。之因此說語法是「抽象」的,是由於這裏的語法並不會表示出真實語法中出現的每一個細節。通常的,在源代碼的翻譯和編譯過程當中,語法分析以後會建立出抽象語法樹。一旦AST被建立出來,在後續的處理過程當中,好比語義分析階段,會添加不少語義信息幫助進行後續的語義翻譯工做。AST其實就是一個程序的靜態模型,它抽象出了一個程序在靜態時結構狀態,咱們能夠經過對AST的分析從而瞭解一個程序的相關靜態信息。數據結構

Source to Source轉換:我所理解的源到源的轉換能夠簡單的當作是從一種源代碼的形式轉換到另外一種源代碼的形式。這其中,形式的定義很寬泛,包括了最簡單的源代碼風格、變量函數的命名到不一樣的編程語言。這些的轉換均可以成爲source to source transformation。編程語言

那麼,若是要進行源到源的轉換,最直接的思路和方式就是經過得到一個程序代碼片斷的抽象語法樹(AST),而後經過修改AST的若干子樹或若干結點,而後將AST轉換成源代碼,從而完成源到源的轉換。函數

既然,咱們已經明白了咱們的目標和途徑,那麼接下來就介紹一下Clang中的基於AST的操做以及它們的設計模式。spa

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等等。

 

最後,咱們還能夠加入了FrontendAction等類,讓咱們綁定相關的編譯器CompilerInstance信息等等,這些具體的部分咱們放在下一篇文章中進行分析。
 

 

所以,咱們在使用Clang進行AST操做時的主要流程是:分別繼承RecursiveASTVisitor類和ASTConsumer類並重寫其中對應的方法,如圖中紅色標識的類。
 
整個過程的表示以下:
根據咱們設定好的前序或後序深度優先的訪問方式,對於一個已經構建好的AST,完成如下工做:
一、從抽象語法樹的根節點開始,當遇到一個 AST節點時,根據它的類型調用對應的TraverseXXX()方法;
二、接着,調用對應的VisitXXX()方法,進行具體的操做;
三、最後,在遇到接下來的語句再調用對應的TraverseDecl、TraverseStmt等等,遞歸執行。
 
由於基於C++語言的基本的語法類型已經不會怎麼變更了(如if語句、class聲明語句、循環語句等等),而對於每個AST結點的操做確實須要隨時按需求修改的,須要將數據結構的抽象和對數據結構的操做進行分離,因此比較知足了訪問者模式的基本要求。

有人說,訪問者模式比較適用於對已有功能的重構,或者說對一個項目已經完成,它的元素類型、數據結構已經定的差很少,而對數據的操做還有可能後序會改變。這樣,可使用訪問者模式對原有的代碼進行重構一遍。設計

相關文章
相關標籤/搜索