从C代码构建AST

12

如何从gcc C代码构建AST(抽象语法树),以便进行一些修改,例如将一些int变量转换为float,并在此之后再次将代码生成为C语法。

实际上,目前我真正需要的功能只是从由几行组成的c程序中提取变量及其类型的表格......我认为有一个简单的解析器可以做到这一点。

我有一些变量,例如:

int  var_bss ;           
float var_f_bss;            
int var_data = 4;        
float var_f_data = 5;  

还有一个函数:

int Foo(){          
   some local variables;            
}    

这份代码在单个c文件中。

我想将所有变量介绍给最终用户,让他选择特定内存段中的源类型,例如.data中的int变量。然后用户可以将这些变量转换为浮点数。最后,我会为用户生成相同的代码,但使用他选择的新变量类型。


请详细描述您想要实现的目标。 - Basile Starynkevitch
大家好:我的回答被版主删除了。 - Ira Baxter
也许你可以使用预处理器技巧或生成简单的C代码(例如,使用awk脚本)... - Basile Starynkevitch
请参考以下链接获取有关替代方案的信息:https://dev59.com/65Lea4cB1Zd3GeqP23yM#34505654。 - Ira Baxter
3个回答

7
首先,这是一项困难的任务,因为C语言的抽象语法树比您想象的要复杂得多。请阅读C11标准n1570以获取详细信息,并查看this网站。还可以查看tinyCCnwcc(至少可以获得灵感)。
然后,如果您正在使用最新的GCC(例如4.7或4.8),我强烈建议自定义GCC,例如使用MELT扩展(或您的GCC插件)。

我不认为这是一个简单的任务,因为你很可能需要了解GCC内部表示的细节(至少GIMPLE

顺便说一下,MELT是一个专门用于扩展GCC的领域特定语言,它正是为你梦寐以求的任务而设计的。使用MELT,你将能够转换内部GCC表示(Gimple和Tree-s)。到2020年,由于缺乏资金,MELT已经没有人在开发。

在GCC内部工作(或在其他编译器中,如Clang/LLVM)的优点是,你不必返回一些C代码(实际上比你想象的要困难得多);你只需转换内部编译器表示,并且,也许最重要的是,你可以“免费”利用编译器总是完成的各种优化:如常量折叠、内联、公共子表达式消除等等。

在2020年,您也可以考虑在最新的GCC 10中使用libgccjit框架,并阅读this draft报告(与Bismon相关;但也请参阅RefPerSys,它与Bismon分享了一些想法但没有代码)。也可以尝试Clang静态分析器和/或Frama-C


1
GCC对于这个任务来说相当不友好,这就是为什么MELT存在的原因。 - Ira Baxter
@irabaxter,比较GCC+MELT和LLVM是合适的吗? - happy_marmoset
@happy_marmoset:当然。我认为LLVM是这个问题的完美答案。请回答吧。 - Ira Baxter

5
Eli Bendersky的pycparser是一种用Python编写的C源代码工具,可进行源代码到源代码的转换: https://github.com/eliben/pycparser。它将解析C99,并且可以构建一个详细的解析树,其中的节点匹配K&R《C程序设计语言》附录A第13章“语法”的语法。它是基于一个名为PLY的Python模拟实现的lex/yacc、flex/bison的工具构建的。它有示例,并且非常容易上手。就像其他帖子中所说的那样,将解析树缩减为只留下所有无关细节的最小AST是一个复杂的任务。
这个项目也可以进行源代码到源代码的转换:https://github.com/axw/cmonster/ CMonster采用Python编写,包装了Clang API。
如果你想使用GCC来完成这个任务,你应该看看MELT。还有另一个项目使用JavaScript作为脚本语言,但我现在记不起来它的名称了。
编辑:回应评论
是的,处理中间表示的框架被称为TreeHydra,虽然已经被废弃,但据我所知仍然可用。有一个视频教程在线上某个地方,年轻的博士设计了TreeHydra并解释了他选择JS作为接口语言的原因,因为它很受欢迎等等。他显得很有知识和魅力,我想这就是那个特定项目让我印象深刻的原因:)不过我自己还没有尝试过。我本人正在使用Eli Bendersky的框架作为构建块,开发一种业余的控制流图和数据流分析工具。在我尝试过的工具包中,Eli的工具包似乎最有前途。结合来自这个特殊的酷项目的灵感:Atul's Mini-C Compiler,它使用相同的Lex/Yacc Python端口(PLY)。我还没做太多,但开始起来比学习libclang容易,尽管我认为那也是一条非常有前途的路线。

这是Mozilla的TreeHydra(JavaScript和一些GCC内部的粘合剂),但据我所知,他们已经放弃了它。 - Basile Starynkevitch
值得注意的是,pycparser是Clang(LLVM的一部分)的包装器。据我所知,Clang中的源到源支持受到限制,至少对于C ++而言是如此,但如果它适用于C99,则这可能是最不痛苦的选择。编辑:我看错了,请参见Morten在下面的回复。 - creichen
2
@creichen pycparser不是Clang的包装器,而是基于PLY构建的。另一方面,cmonster则是使用Clang API封装的。 - Morten Jensen

4
你需要的是一个C源代码转换器。这样的工具非常难建立,部分原因是C语言本身的复杂性,部分原因是C预处理器:抽象语法树可能包含来自系统头文件等的片段,你需要在重新生成C代码时正确处理它们。
你可以尝试Robert Grimm的SuperC:https://cs.nyu.edu/rgrimm/xtc/。这种特殊的解析器应该能够处理C语言的所有部分(包括预处理器)。我不知道它是否能够处理重新生成C代码,但这应该相对容易做到(也就是说,仍然需要大量的工作)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接