GCC本身可以构建AST,但在扩展预处理器指令之前不会这样做。因此,预处理器条件已经不存在了。在进行转换后重新安装它们将非常困难。进行涉及条件本身的转换将是不可能的。因此,GCC本身不是获取所需AST的好方法。
如果您想解析代码示例(包裹在“else if”周围的条件非常好!),则需要一个重构解析器。这些解析器旨在支持重构。这种解析器需要捕获比传统解析器更多的内容,例如标记的列号、词法项的格式等,以便从修改后的树中重新生成源文本。对于C语言,这样的解析器必须捕获预处理器指令。这些相当罕见。
我们的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
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
(function_definition@C~GCC4=966
(function_head@C~GCC4=967
(simple_type_specifier@C~GCC4=686
(direct_declarator@C~GCC4=852
|(IDENTIFIER@C~GCC4=1531
|(parameter_declaration_clause@C~GCC4=900
)direct_declarator
)function_head
(compound_statement@C~GCC4=507
(selection_statement@C~GCC4=539
|(if_head@C~GCC4=550
| (IDENTIFIER@C~GCC4=1531
|)if_head
|(compound_statement@C~GCC4=507
| (expression_statement@C~GCC4=503
| (postfix_expression@C~GCC4=205
| (IDENTIFIER@C~GCC4=1531
| )postfix_expression
| )expression_statement
|)compound_statement
|(if_directive@C~GCC4=1088
| ('
| (IDENTIFIER@C~GCC4=1531
| (new_line@C~GCC4=1578
|)if_directive
|(selection_statement@C~GCC4=527
| (IDENTIFIER@C~GCC4=1531
| (compound_statement@C~GCC4=507
| (expression_statement@C~GCC4=503
| (postfix_expression@C~GCC4=205
| |(IDENTIFIER@C~GCC4=1531
| )postfix_expression
| )expression_statement
| )compound_statement
|)selection_statement
|(endif_directive@C~GCC4=1092
| ('
| (new_line@C~GCC4=1578
|)endif_directive
)selection_statement
)compound_statement
)function_definition
)translation_unit
编辑:OP要求提供如何进行转换的示例。 正如先前所述,DMS允许源代码到源代码的转换模式,形式为“如果你看到这个,就用目标语言(在本例中为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中,转换被称为“规则”;它通过子树类型进行参数化。元引号“...”用于区分DMS重写规则语法和C~GCC4语法。我认为其余部分足够清晰。