使用Python创建预处理器

3
我需要用Python编写一个C预处理器。我已经搜索过了,因为我需要完全自定义我的代码并且需要对其进行深入的理解,所以最好自己编写。
这是我的进展:首先,我解析一些头文件(.h)以查找#define关键字,并建立一个带有所有找到的指令的字典(其中包括它们的值)。然后,根据我之前找到的指令,我需要解析源文件(.c)。
目前我使用的机制来检查代码是否需要处理是:我获取所有定义的名称及其值,并执行exec("define_name = define_value")(未指定时值为“1”)。然后,为了解决诸如#if defined DEFINE_1 || defined DEFINE_2 && (DEFINE_3 == 10) ....等条件,我删除C预处理器关键字,使它们成为Python风格,从而产生DEFINE_1 or defined DEFINE_2 and (DEFINE_3 == 10)
最后,我在该字符串上使用eval(...)来查找结果。
问题是,我想知道是否必须使用exec / eval,很多人都有些不愿意使用它们,是否有更好的解决方案?

根据我使用基于Python的预处理器的经验,Python在这方面实用性太低了,速度太慢。此外,例如MSVC的标准库很难进行预处理(尽管编译更糟)。您想要解决什么问题? - geometrian
1个回答

4
当然,不需要使用exec(),也不应该使用它。我甚至不确定你期望它做什么,因为它会调用一个shell来设置一个变量,这个变量只存在于子shell中。
另外,通常情况下,你应该避免使用eval()语句,因为它很少是正确的选择。
那么,你能做什么呢?
首先,因为程序可以编写成一个语句覆盖前面的语句,所以你不能预处理.h文件(甚至不能假设你正在查找的#define仅位于.h文件中),而使其正常工作。考虑以下内容:
#define foo 1
#if foo == 1
this line is true!
#endif
#define foo 0

如果你对所有内容进行预处理,你会把 "foo" 的值先设置为1,然后设置为0,然后再评估#if。这是不行的...

2) 更常见的做法是编写一个解析器逐行处理每一行的内容。这样你甚至可以编写一个递归函数来处理#include语句,因此你可以从只有.c文件开始,让它引入它正在使用的正确头文件,而不需要以其他方式指定它们。

最终,你应该得到类似以下代码(在名为“read_file”的函数中):

# ... file opening not shown ...

for line in file:
    includematch = re.match("#include\\s+\\"(.*)\\"", line)
    if match:
        # deal with an include statement by calling a function to process it
        read_file(includematch.group(1), definedict)

    definematch = re.match("#define\\s+(\\w+)\\s+(.*)")
    if definematch:
        # deal with define statements by saving it in a dict
        definedict[match.group(1)] = definedict[match.group(2)]

    #....

显然,如果我展示整个解决方案(上面的代码并不漂亮,但是为了显示方便而简洁),那么我就在为你解决问题(作业?)。但是,上述方法比你之前走的路更好地设计了整个事情的架构。


谢谢您的回答,这不是作业,而是工作。只是为了澄清一些事情,我所在的项目非常严格,人们不允许随意放置#define(我只有1或2个文件,里面都是#define,所以我只需要解析它),因此不会出现像您所说的重新定义。目前我有一个可用的代码,但是有很多eval/exec,主要问题是如何处理复杂的#if/#elif条件,而不使用eval和exec来处理已经解析的内容? - Jaay
好吧,最终你必须要做一个完整的解析器(比较困难但更加安全),或者使用 eval。使用 eval 可以省去实现括号匹配、逻辑等步骤,你只需要专注于值的替换和剩余部分的 eval。你可以先从 eval 开始,然后逐步替换为更好的方案。 - Wes Hardaker
请注意:尽管您现在拥有一种具有某些规则的结构化严格环境,但这并不意味着这些规则将来不会改变。您可能首先考虑以尽可能与 C++ 相似的方式进行操作,以帮助自己未来更好地适应变化。 - Wes Hardaker
是的,我尽可能地从当前项目中抽象出来,但事情必须完成...像往常一样,感谢您的答案和建议 :) - Jaay
1
还要注意的是,你可能会在实现所属的头文件中找到如答案中所示的定义和重新定义。因此,即使你的编码标准不允许这样做,你仍然需要考虑它。 - This isn't my real name

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接