我有一个用c编程语言编写的文件,并使用CIL进行预处理。现在有一个函数调用foo()在这个文件中。我想修改此文件中的c代码,以便对foo()的所有调用都在#ifdef防护下。我只想要保护呼叫而不是功能体,这样我就可以更好地控制呼叫。调用可以在if条件或while循环中。宏名称的规则:name以MACRO_开头,以原始代码中函数调用foo()的行号结束。
这是在工具中自动化的,我正在寻找一个可以解析c代码的编译器。
示例:
输入源文件
void foo(int x){
// do something
}
int main(){
int a;
printf("doing something");
foo(a);
printf("doing something again");
foo(a);
return 0;
}
所需的输出
void foo(int x){
// do something
}
int main(){
int a;
printf("doing something");
#ifdef MACRO_1
foo(a);
#endif
printf("doing something again");
#ifdef MACRO_2
foo(a);
#endif
return 0;
}
答案 0 :(得分:1)
您可以自定义一些免费软件编译器。如果使用最近的GCC,您可以使用MELT(一种Lispy域特定语言来扩展gcc
& g++
等来自定义它。)。
您可能不想生成惯用的C代码。定制编译器(例如GCC - 或许Clang/LLVM ...)以获得所需的行为会更加简单。
转换一些内部编译器表示(例如,Gimple用于GCC)比输出C代码要简单一些。它可能仍然意味着数周的工作(因为C和C ++是非常复杂的语言,编译器具有相当复杂的内部表示)。
请注意,您的问题不考虑在foo
内部调用 某些宏(或某些C ++模板扩展内部,或者甚至是某些内联函数)时会发生什么。这表明为什么处理编译器的中间表示是值得的。
答案 1 :(得分:1)
对于SIMPLE源代码,显然你可以使用一个简单的脚本和一些你最喜欢的脚本语言(perl,php,awk,python等)的regexp。但是如果你开始决定支持if语句,成员函数调用等内部的函数调用[并希望最终得到实际编译为正确程序的输出代码],它确实变得越来越困难。
在这种情况下,您需要能够阅读(和#34;理解")C或C ++并生成一些中间形式的内容,然后您可以通过修改处理和重新发布源代码。无论从哪里开始,编写此类代码都远非易事。一种解决方案可能是使用Clang作为库。它具有从它的抽象语法树(AST)形式重写C或C ++代码的功能。此链接显示了此类重写器的示例:http://eli.thegreenplace.net/2012/06/08/basic-source-to-source-transformation-with-clang
如果你有以下代码,我不确定你想要做什么:
if (x)
foo();
bar();
显然,只需插入#if来调用foo();
,只会在bar()
为真时调用x
,这可能不是您想要的。 。
答案 2 :(得分:0)
如果代码的结构是这样的,那么用foo
调用来保护行只能被注释掉,并且不需要处理更复杂的表达式,例如bar(), foo(a)
,你可以像使用awk一样使用awk这样:
awk '/^\s*foo\(/ { print "#ifdef MACRO_" NR; print; print "#endif"; next } 1' filename.c
这将
/^\s*foo\(/ { # handle lines that begin with foo( preceded
# optionally by whitespaces specially by:
print "#ifdef MACRO_" NR # printing #ifdef MACRO_linenumber before
print
print "#endif" # and #endif after the line.
next
}
1 # all other lines are printed unchanged.
请注意这是一个脏的,脏的黑客,不会尝试正确解析C代码。有很多方法可以解决这个问题,其中包括
等if(something)
foo(a);
和
foo(
a
);
那就是
if(something)
#ifdef MACRO_foo
foo(a);
#endif
和
#ifdef MACRO_foo
foo(
#endif
a
);
分别。它可能适用于您的特定情况,但它不是一般的C代码处理工具。
答案 3 :(得分:0)
如果任务是在某些宏未定义(或定义)时从代码中排除调用foo(int)
,则以下方法可能会更好:
void foo(int x){
#ifdef MACRO_foo
// do something
#endif
}
int main(){
int a;
printf("doing something");
foo(a);
printf("doing something again");
foo(a);
return 0;
}
因此,您可以在整个程序中排除函数体并保留函数调用。
答案 4 :(得分:0)
我认为你要求CIL做CIL不能做的事情。由于它对预处理的源代码进行操作,因此它不代表预处理器指令,因此您无法将它们放入CIL表示中。要再生。你可能能够破解CIL实现本身在遇到你的特殊情况时吐出你的指令,但是很难相信这样的hack会以任何方式通用。
你说你正在寻找一个可以解析c代码来实现这个目标的编译器"。如果你坚持"这个"特别是涉及CIL,我认为你运气不好;只有CIL才能做到这一点。
如果你放弃CIL并考虑使用不同的工具,那么我认为我有一个答案,就像CIL一样,可以在表示中保留预处理器指令(和/或允许)您可以根据自定义规则插入它们,并可以重新生成有效的C源代码文本。
该工具是我们的DMS Software Reengineering Toolkit,一个通用程序转换引擎及其C Front End。 DMS将C代码解析为AST,并将它们解析回有效的源代码,包括保留注释。 它可以用于在AST操作库上使用过程调用的混合进行源到源转换,和/或表面语法源到源重写。
DMS将捕获该AST中的预处理器指令(它们只是#34;更多语法!)在大多数情况下没有问题;有时你需要稍微(永久地)修改源代码以使预处理器指令变得可口。 DMS为C提供符号表,以及控制和数据流分析;这些将需要一些修改来处理预处理器条件。
为了匹配您在CIL上所做的事情,您可以要求DMS进行预处理;现在你最终获得了一个免费预处理器的AST。现在,DMS的现有符号表,CF和DF机器直接处理这种情况。 因此,您可以使用其他信息以不同于CIL的方式对AST执行复杂的操作。此外,您仍然可以修改AST以插入预处理程序指令,这似乎是您的关键问题。
要实现特定于呼叫站点的条件的特定效果,您可以利用DMS的surface syntax source-to-source transformation功能。 以下DMS转换可以执行您想要的操作:
rule wrap_function_call(i: Identifier, a:arguments ):statement -> statement
" \i(\a); "
->
" #ifdef \generate_macro_name\(\i\)
\i(\a);
#endif
"
if want_to_wrap(i);
此规则查找与函数调用对应的任何语法树作为语句,并将其包装在条件中。 (如果函数调用是表达式的一部分,那么你没有说出你想要做什么;这种情况需要更多的转换,但也可以处理)。自定义辅助函数 generated_macro_name 使用与该函数名称匹配的标识符AST节点相关联的源位置信息来制造宏名称。转换以另一个自定义帮助函数 want_to_wrap 为条件,它检查每个匹配的名称以确定它是否应该被包装。
完成转换代码后,您可以调用DMS的prettyprinter机器将AST打印为源文本。