基于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结点的操做确实须要随时按需求修改的,须要将数据结构的抽象和对数据结构的操做进行分离,因此比较知足了访问者模式的基本要求。

有人说,访问者模式比较适用于对已有功能的重构,或者说对一个项目已经完成,它的元素类型、数据结构已经定的差很少,而对数据的操做还有可能后序会改变。这样,可使用访问者模式对原有的代码进行重构一遍。设计

相关文章
相关标签/搜索