繁体   English   中英

将C代码的中间表示解析为c

[英]unparse the intermediate representation of c code back to c

我有一个用C编程语言编写的文件,并使用CIL进行了预处理。 现在在此文件中调用了一个名为foo()的函数。 我想修改此文件中的C代码,以使对foo()的所有调用都在#ifdef保护之下。 我只希望保护调用而不是函数主体,以便对调用有更好的控制。 调用可以在if条件或while循环内。 宏名称的规则:名称以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;
}

您可以自定义一些免费的软件编译器。 如果使用最新的GCC ,则可以使用MELT (一种Lispy域专用语言来扩展gccg++等)自定义它。

您可能不想生成惯用的C代码。 定制您的编译器(例如,GCC-或Clang / LLVM ...)以具有所需的行为会容易得多。

转换某些内部编译器表示形式(例如,用于GCC的Gimple )比输出C代码要简单一些。 它可能仍然意味着需要花费数周的时间(因为C和C ++是相当复杂的语言,并且编译器具有非常复杂的内部表示形式)。

注意,您的问题没有考虑某个宏(或某些C ++模板扩展,甚至某些内联函数)中调用foo时发生的情况。 这说明了为什么值得在编译器的中间表示形式上进行工作。

顺便说一句,您可能会对coccinelle感兴趣, coccinelle是自由软件转换器的来源。

您原则上也可以使用Clang (将C或C ++代码编译为LLVM ),然后使用llvm-cbe (将LLVM转换为C的后端)

对于SIMPLE源代码,显然可以使用您喜欢的脚本语言(perl,php,awk,python等)的简单脚本和一些正则表达式来执行此操作。 但是,如果您开始决定在if语句中支持函数调用,成员函数调用等[并且希望最终得到实际编译为正确程序的输出代码],这确实变得越来越困难。

在这种情况下,您需要可以读取(并“理解”)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(); 将仅在x为true时才调用bar() ,这可能不是您想要的...

如果代码的结构使得可以简单地注释掉foo调用来保护行,并且不需要处理更复杂的表达式,例如bar(), foo(a) ,则可以使用如下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代码处理工具。

如果任务是在未定义(或定义)某个宏时从代码中排除对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;
}

因此,您可以只排除函数体,而在整个程序中保留函数调用。

我认为您是在要求CIL做CIL无法做的事情。 由于它对预处理的源代码进行操作,因此它不表示预处理器指令,因此您不能“将它们放入CIL表示形式”进行重新生成。 当遇到特殊情况时,您可能可以破解CIL实现本身以吐出指令,但是很难相信这样的hack在任何情况下都是普遍的。

您说您正在寻找“无法解析C代码的编译器来执行此操作”。 如果您坚持认为“ this”涉及CIL,我认为您很不幸。 只有CIL本身可以做到这一点。

如果您放弃CIL并考虑使用其他工具,那么我想我有一个答案,它将像CIL一样做, 可以在表示中保留预处理器指令(和/或允许您根据自定义规则插入它们),并可以重新生成有效的C源代码文本。

该工具是我们的DMS软件再造工具包 ,通用程序转换引擎及其C前端 DMS将C代码解析为AST,然后将其解析为有效的源代码,包括保留注释。 它可以用于混合使用其AST操纵库中的过程调用和/或使用表面语法进行源到源重写的源到源转换。

在大多数情况下,DMS会捕获AST中的预处理器指令(它们只是“更多语法!”);有时您需要(永久地)稍微修改源代码以使预处理器指令可口DMS为C提供符号表和控制和数据流分析;这些将需要进行一些修订以处理预处理程序的条件。

为了匹配您对CIL所做的工作,您可以要求DMS进行预处理。 现在,您最终获得了不含预处理程序的AST。 DMS现有的符号表,CF和DF机械现在可以直接处理这种情况。 因此,您可以使用该附加信息对AST进行复杂的操作,其方式不同于但等同于CIL。 此外,您仍然可以修改AST以插入预处理程序指令,这似乎是您的关键问题。

为了实现特定于呼叫站点的条件的特定效果,您可以利用DMS的表面语法从源到源的转换功能。 以下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);

该规则查找与函数调用相对应的任何语法树作为语句 ,并将其包装在条件语句中。 (如果函数调用是表达式的一部分,您没有说要做什么;这种情况需要更多的转换,但也可以处理)。 定制帮助器函数generate_macro_name使用与与该函数名称匹配的标识符AST节点关联的源位置信息来制造宏名称。 转换以另一个自定义帮助器函数want_to_wrap为条件,该函数检查每个匹配的名称以确定是否应该包装该名称。

完成代码转换后,您将调用DMS的prettyprinter机制将AST打印为源文本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM