4

我想对 C 文件进行简单分析(例如,如果您foo使用作为参数调用宏INT_TYPE,然后将响应转换为int*),我不想预处理文件,我只想解析它(例如,我'会有正确的行号)。

即,我想从

#include <a.h>

#define FOO(f)

int f() {FOO(1);}

令牌列表,例如

<include_directive value="a.h"/>
<macro name="FOO"><param name="f"/><result/></macro>
<function name="f">
    <return>int</return>
    <body>
        <macro_call name="FOO"><param>1</param></macro_call>
    </body>
</function>

无需设置包含路径等。

是否有任何预先存在的解析器可以做到这一点?我知道的所有解析器都假设 C 已经过预处理。我想访问宏和实际包含指令。

4

3 回答 3

1

我们的C 前端可以解析包含预处理器元素的代码,可以在很大程度上做到这一点,并且仍然可以构建可用的 AST。(是的,解析树具有精确的文件/行/列号信息)。

有许多限制,允许它处理大多数代码。在它无法处理的少数情况下,通常对源文件进行小而简单的更改,提供等效代码即可解决问题。

这是一组粗略的规则和限制:

  • #includes 和 #defines 可以出现在可以出现声明或语句的任何地方,但不能出现在语句的中间。这些很少引起问题。
  • 宏调用可以出现在函数调用出现在表达式中的地方,也可以不使用分号代替语句出现。跨越非格式良好的块的宏调用处理得不好(有人感到惊讶吗?)。后者偶尔发生但并非很少发生,需要手动修改。OP 的“j(v,oid)*”示例是有问题的,但这在代码中确实很少见。
  • #if ... #endif 必须围绕主要语言概念(非终结符)(常量、表达式、语句、声明、函数)或此类实体的序列,或围绕某些格式不正确但经常出现的习语,例如if (exp) { . 条件的每个分支必须包含与其他分支相同类型的句法结构。#if 包裹在用作不良评论的随机文本周围是有问题的,但通过制作真正的评论很容易在源中修复。在不满足这些条件的地方,你需要修改原始源代码,通常通过移动#if #elsif #else #end 几个标记。

根据我们的经验,可以在几个小时内修改 50,000 行代码库来解决这些问题。虽然这看起来很烦人(确实如此),但替代方法是根本无法解析源代码,这比烦人要糟糕得多。

您还需要的不仅仅是解析器。请参阅解析后的生活,以了解成功获取解析树后会发生什么。我们在构建符号表方面做了一些额外的工作,其中声明与嵌入它们的预处理器上下文一起记录,从而使类型检查能够包括预处理器条件。

于 2012-05-28T15:56:27.440 回答
0

你可以看看这个ANTLR 语法。不过,您必须为预处理器令牌添加规则。

于 2012-05-28T13:01:17.917 回答
-1

您可以通过编写自己的解析来处理您的具体示例并忽略宏扩展。

因为FOO(1)它本身可以解释为函数调用。

然而,当考虑更多情况时,解析器会更加困难。您可以参考PDF 链接以查找更多信息。

于 2012-09-08T13:56:48.583 回答