C 如何使用grep查找所有内部循环?

C 如何使用grep查找所有内部循环?,c,regex,grep,code-analysis,C,Regex,Grep,Code Analysis,我有一个巨大的C项目,有许多C文件。我必须找到所有的内部循环。我确信项目中没有任何O(n³)块,因此只能找到O(n²)-互补块(一个循环中的一个循环) 是否可以使用grep查找所有内部循环?如果是,我可以使用什么正则表达式来查找所有类型的内部循环,如{for,for},{while,for},{for,while},{do,while},等等?如果没有,是否有任何简单的unix方式方法(可能是多个grep或一种awk)?正则表达式适用于正则语言,您所描述的内容似乎与上下文无关,我很确定不能使用正

我有一个巨大的C项目,有许多C文件。我必须找到所有的内部循环。我确信项目中没有任何O(n³)块,因此只能找到O(n²)-互补块(一个循环中的一个循环)


是否可以使用grep查找所有内部循环?如果是,我可以使用什么正则表达式来查找所有类型的内部循环,如{for,for},{while,for},{for,while},{do,while},等等?如果没有,是否有任何简单的unix方式方法(可能是多个grep或一种awk)?

正则表达式适用于正则语言,您所描述的内容似乎与上下文无关,我很确定不能使用正则表达式来完成。请参见类似问题的答案。您应该寻找其他类型的自动机,如脚本语言(python或其他语言)。

如果没有C解析器,您最多只能得到一个启发式解决方案

如果您可以依赖代码中始终遵循的某些规则(例如,no
goto
,通过递归没有循环,…),则可以使用regexp扫描预处理的C代码。当然,grep还不够复杂,但只要使用几行Perl或类似的代码就可以了


但是技术上更好、更可靠的方法是使用真正的C解析器

对于特定的编译器扩展,这是一个很好的例子。最新的GCC编译器(即GCC的4.6版)可以通过插件(痛苦地用C编码)或扩展进行扩展;MELT是一种高级领域特定语言,用于编写GCC扩展,而且MELT比C更易于使用

然而,我承认,编写GCC扩展并不完全是一件小事:您必须部分了解GCC是如何工作的,以及它的主要内部表示(Gimple、Tree等)是什么。在扩展GCC时,基本上您可以添加自己的编译器过程,它可以做任何您想做的事情(包括检测嵌套循环)。编写GCC扩展通常需要一周以上的工作。(最难的部分是理解GCC是如何工作的)

在GCC框架内工作(通过C中的插件或MELT中的扩展)的最大优点是,您的扩展与编译器使用相同的数据


回到寻找嵌套循环的问题,不要认为它只是纯句法(这就是为什么代码> GRP不能工作)。在GCC编译器中,在某种内部表示级别上,由

for
,或
while
,或
do
,甚至使用
goto
-s实现的循环仍然被视为循环,对于GCC来说,这些东西可以嵌套(GCC知道嵌套!)。

C中有三种循环:

  • “结构化语法”(whilefor,…) [注意GCC,它可以使用(stmt;exp)语法在表达式中隐藏语句循环!]
  • 使用“转到”的即席循环;它们与结构化语法交互
  • 递归
要找到第一种,必须找到结构化语法和嵌套

Grep当然可以找到关键字(如果忽略注释和字符串中的误报),但它找不到嵌套结构。当然,您可以使用grep查找所有循环语法,然后简单地检查发生在同一文件中的那些语法,看看它们是否嵌套。 (如果您想在没有误报价格的情况下实现这一点,您可以使用我们的,它知道C的词汇语法,并且从不混淆字符串何时是关键字、数字、字符串等。)

如果您想自动找到这些循环,您几乎需要一个完整的C解析器,并完成扩展的预处理。(否则,某些宏可能会隐藏关键的循环语法)。一旦有了C语言的语法树,就可以直接(尽管可能有点不方便)编写一些在树上爬行的代码,检测循环语法节点,并计算子树中循环的嵌套。您可以使用任何解析C并提供抽象sytnax树的工具来实现这一点。我们很可能做到这一点;我认为可以为ANTLR获得一个C语法,它可以很好地处理C,但是在使用ANTLR之前必须运行预处理器

你也可以用我们的智能手机来实现。我们的C前端内置了一个完整的预处理器,因此它可以直接读取代码并在解析时进行扩展;它还处理相对广泛的C方言和字符编码(是否处理过包含日语文本的C?)。DMS提供了一个额外的优势:给定一种语言(例如C)前端,您可以直接使用语言语法为该语言编写模式。因此,我们可以很容易地表达我们想要找到的东西的片段:

 pattern block_for_loop(t:expression,l:expression,i:expression, s: statements): statement
     " for(\t,\l\,\i) { \s } ";

 pattern statement_for_loop(t:expression,l:expression,i:expression, s: statement): statement
     " for(\t,\l\,\i)  \s ";

 pattern block_while_loop(c:expression, s: statements): statement
     " while(\c) { \s } ";

 pattern statement_while_loop(c:expression): statement
     " for(\c)  \s ";

 ...
并将其分组:

 pattern_set syntactic_loops
     { block_for_loop,
       statement_for_loop,
       block_while_loop,
       block_statement_loop,
       ...
     }
给定模式集,DMS可以扫描语法树并找到与任何集合成员的匹配项,而无需编码任何特定的树爬行机制,也无需知道树结构的大量细节。(对于一个真正的C解析器来说,AST中有很多节点类型!)用这种方法查找嵌套循环非常简单:从上到下扫描树中的循环(使用模式集);任何命中必须是顶级循环。扫描找到的循环AST节点的子树(当您知道外部循环的树在哪里时很容易)以查找其他循环;任何点击都必须是嵌套循环;如有必要,递归以拾取具有任意嵌套的循环。这也适用于带有语句的GCC循环。树节点上印有精确的文件/行/列信息,因此很容易生成位置报告

对于使用goto(什么,您的代码没有?)构建的特殊循环,您需要能够生成实际控制流图的东西,然后将该图构造成嵌套的控制流区域。这里的要点是一个while循环,它包含一个无条件的gotopublic void do(){ for(...){ somethingElse(); } } ... Insert other code... public void somethingElse(){ for(.....){ print(....); } }