来自带有预处理程序指令的c代码的AST

如何从gcc C代码构建AST(抽象语法树)以进行一些转换,如下所示,然后再将代码重新生成(生成)到C语法?

if(condition_1){ //lines of code 1 } #ifdef expression_1 else if(condition_2){ //lines of code 2 } #endif 

 bool test = condition_1; if(teste){ //lines of code 1 } #ifdef expression_1 if(!(test) && condition_2){ //lines of code 2 } #endif 

GCC本身将构建AST,但在扩展预处理器指令之前不会。 因此预处理器条件消失了。 完成转换后重新安装它们将非常困难。 进行涉及条件本身的转换是不可能的。 所以GCC本身并不是获得你想要的AST的好方法。

如果你想解析你的代码示例( 如果非常好的 ,条件包含在其他地方 !),你需要一个重新设计的解析器。 这些是旨在支持重构的解析器。 这样的解析器需要比传统解析器捕获更多,例如,令牌的列号,词汇项的格式等,以使得能够从修改的树再生源文本。 对于C,这样的解析器也必须捕获proprocessor指令。 这些非常罕见。

一个这样的再造解析器是我们的DMS软件再造工具包及其C前端,它处理C的许多方言,包括GCC 2/3/4/5。 它专门用于捕获预处理器条件(包括您的特定示例)。 DMS还支持使用源到源转换执行转换。

对于OP的示例的更改为合法版本,放在test.c中

 void main () { if (condition_1) { x++; } #ifdef expression_1 else if (condition_2) { y++; } #endif } 

… DMS C~GCC4解析器(开箱即用)产生以下AST:

 C:\DMS\Domains\C\GCC4\Tools\Parser\Source>run ..\domainparser ++AST C:\temp\test.c C~GCC4 Domain Parser Version 3.0.1(28449) Copyright (C) 1996-2015 Semantic Designs, Inc; All Rights Reserved; SD Confidential Powered by DMS (R) Software Reengineering Toolkit AST Optimizations: remove constant tokens, remove unary productions, compact sequences Using encoding Unicode-UTF-8?ANSI +CRLF +1 /^I 28 tree nodes in tree. (translation_unit@C~GCC4=2#3cde920^0 Line 1 Column 1 File C:/temp/test.c (function_definition@C~GCC4=966#3cde740^1#3cde920:1 Line 1 Column 1 File C:/temp/test.c (function_head@C~GCC4=967#3047320^1#3cde740:1 Line 1 Column 1 File C:/temp/test.c (simple_type_specifier@C~GCC4=686#3047180^1#3047320:1 Line 1 Column 1 File C:/temp/test.c)simple_type_specifier (direct_declarator@C~GCC4=852#3047380^1#3047320:2 Line 1 Column 6 File C:/temp/test.c |(IDENTIFIER@C~GCC4=1531#3047160^1#3047380:1[`main'] Line 1 Column 6 File C:/temp/test.c)IDENTIFIER |(parameter_declaration_clause@C~GCC4=900#30473c0^1#3047380:2 Line 1 Column 12 File C:/temp/test.c)parameter_declaration_clause )direct_declarator#3047380 )function_head#3047320 (compound_statement@C~GCC4=507#3cde1e0^1#3cde740:2 Line 1 Column 14 File C:/temp/test.c (selection_statement@C~GCC4=539#3cde940^1#3cde1e0:1 Line 2 Column 3 File C:/temp/test.c |(if_head@C~GCC4=550#30476e0^1#3cde940:1 Line 2 Column 3 File C:/temp/test.c | (IDENTIFIER@C~GCC4=1531#30473e0^1#30476e0:1[`condition_1'] Line 2 Column 7 File C:/temp/test.c)IDENTIFIER |)if_head#30476e0 |(compound_statement@C~GCC4=507#3cde700^1#3cde940:2 Line 2 Column 20 File C:/temp/test.c | (expression_statement@C~GCC4=503#3047740^1#3cde700:1 Line 3 Column 6 File C:/temp/test.c | (postfix_expression@C~GCC4=205#3047720^1#3047740:1 Line 3 Column 6 File C:/temp/test.c | (IDENTIFIER@C~GCC4=1531#3047700^1#3047720:1[`x'] Line 3 Column 6 File C:/temp/test.c)IDENTIFIER | )postfix_expression#3047720 | )expression_statement#3047740 |)compound_statement#3cde700 |(if_directive@C~GCC4=1088#3cde7a0^1#3cde940:3 Line 5 Column 3 File C:/temp/test.c | ('#'@C~GCC4=1548#3cde820^1#3cde7a0:1[Keyword:0] Line 5 Column 3 File C:/temp/test.c)'#' | (IDENTIFIER@C~GCC4=1531#3cde1c0^1#3cde7a0:2[`expression_1'] Line 5 Column 10 File C:/temp/test.c)IDENTIFIER | (new_line@C~GCC4=1578#3cde800^1#3cde7a0:3[Keyword:0] Line 5 Column 22 File C:/temp/test.c)new_line |)if_directive#3cde7a0 |(selection_statement@C~GCC4=527#3cde840^1#3cde940:4 Line 6 Column 8 File C:/temp/test.c | (IDENTIFIER@C~GCC4=1531#3047340^1#3cde840:1[`condition_2'] Line 6 Column 12 File C:/temp/test.c)IDENTIFIER | (compound_statement@C~GCC4=507#3cde860^1#3cde840:2 Line 6 Column 25 File C:/temp/test.c | (expression_statement@C~GCC4=503#3cde8a0^1#3cde860:1 Line 7 Column 12 File C:/temp/test.c | (postfix_expression@C~GCC4=205#3cde880^1#3cde8a0:1 Line 7 Column 12 File C:/temp/test.c | |(IDENTIFIER@C~GCC4=1531#3cde780^1#3cde880:1[`y'] Line 7 Column 12 File C:/temp/test.c)IDENTIFIER | )postfix_expression#3cde880 | )expression_statement#3cde8a0 | )compound_statement#3cde860 |)selection_statement#3cde840 |(endif_directive@C~GCC4=1092#3cde8c0^1#3cde940:5 Line 9 Column 3 File C:/temp/test.c | ('#'@C~GCC4=1548#3cde900^1#3cde8c0:1[Keyword:0] Line 9 Column 3 File C:/temp/test.c)'#' | (new_line@C~GCC4=1578#3cde8e0^1#3cde8c0:2[Keyword:0] Line 9 Column 9 File C:/temp/test.c)new_line |)endif_directive#3cde8c0 )selection_statement#3cde940 )compound_statement#3cde1e0 )function_definition#3cde740 )translation_unit#3cde920 

编辑:OP询问如何进行转换。 如前所述,DMS允许源到源转换模式,forms为“如果你看到这个,用它替换它”,在被操纵的目标语言的表面语法中说明(在这种情况下,C的GCC4版本) 。 这种转换的价值在于它们比由过程调用完成的传统AST黑客代码更容易编写。

为了达到OP的效果,他需要以下DMS转换:

  default domain C~GCC4; // tells DMS to use C domain with GCC4 dialect rule transform_pp_conditional_else(c1: condition, c2: condition, s1: statements, s2: statements, pc1: preprocessor_condition): statement -> statement "if (\c1) { \s1 } #ifdef \pc1 else if (\c2) { \s2 } #endif" -> "{ bool test=\c1; if (test) { \s1 } #ifdef \pc1 if (!test && \c2) { \s2 } #endif }" 

默认域声明告诉DMS以下规则适用于GCC4。 转换在DMS中称为“规则”; 它由子树类型参数化。 metaquotes“…”用于区分DMS重写规则语法和C~GCC4语法。 我认为剩下的就足够了。