首页 > 解决方案 > 使用 RegEx 从 .h 文件中提取结构定义以根据 [XXX] 标准检查代码一致性

问题描述

我在我的项目中在不同的 .h 中定义了 C 结构,我正在寻找一种方法来检查每个结构是否满足我的编码要求。

例如:我希望我的所有位域结构类型都是相同的基本类型,例如:

typedef union {
    uint8_t data;
    struct {
        uint8_t LSB:4;
        uint8_t MSB:4;
    } bit;
} MyType_t;

我原来的想法是使用正则表达式从 .h 头文件中提取所有结构/联合定义,并“播放”结果数据(即匹配)。然后提出其他 RegEx(或任何其他方式)来断言类型是一致的,并且我的所有要求都得到了满足。我不确定这是最好的方法,我可以手动检查它,但整个目的是拥有一个自主的代码检查器或类似的东西。我发现它也是 RegEx 和解析的一个很好的练习。(我认为)

为此,我只是尝试创建 RegEx 以匹配以下代码。所以我想出了以下几点:

reg_A = r'((typedef )(union|struct)\n)([\t ]*\{\n)((([\t ]*(void|\w+_t) \w+[\t ]*(:\d)?;).*)\n)+([\t ]*((union|struct)\n)([\t ]*\{\n)((([\t ]*(void|\w+_t) \w+[\t ]*(:\d)?;).*)\n)+([\t ]*\} \w+;)\n)?(\} \w+;)\n'
reg_B = r'([\t ]*((((typedef )?(struct|union))|\{)|(((volatile|static|const|FAR|NEAR|INTERRUPT) )*(void|\w+_t)\*?[\t ]*\w+([\t ]*:\d+)?;.*)|(\} \w+ ?;))(\n+|$))+'
reg_C = r'([\t ]*typedef (struct|union))\n[\t ]*\{(([\n\t ]*(struct[\n\t ]*\{)([\n\t ]*(((volatile|static|const|FAR|NEAR|INTERRUPT) )*(void|\w+_t)\*?[\t ]*\w+([\t ]*:\d+)?;.*))+[\n\t ]*\} \w+[\t ]*;)|[\n\t ]*(((volatile|static|const|FAR|NEAR|INTERRUPT) )*(void|\w+_t)\*?[\t ]*\w+([\t ]*:\d+)?;.*))+[\n\t ]*\} \w+[\t ]*;'
reg_D = r'([\t ]*typedef (struct|union))\n[\t ]*\{(([\n\t ]*(struct[\n\t ]*\{)([\n\t ]*(((volatile|static|const|FAR|NEAR|INTERRUPT) )*(void|\w+_t)\*?[\t ]*\w+([\t ]*:\d+)?;.*)|([\t\n ]*\/\/.*))+[\n\t ]*\} \w+[\t ]*;)|[\n\t ]*(((volatile|static|const|FAR|NEAR|INTERRUPT) )*(void|\w+_t)\*?[\t ]*\w+([\t ]*:\d+)?;.*)|([\t\n ]*\/\/.*))+[\n\t ]*\} \w+[\t ]*;'
reg_E = r'(\s*typedef (struct|union))\n\s*\{((\s*(struct\s*\{)(\s*(((volatile|static|const|FAR|NEAR|INTER{2}UPT) )*(void|\w+_t)\*?\s*\w+(\s*:\d+)?;.*)|(\s*\/\/.*))+\s*\} \w+\s*;)|\s*(((volatile|static|const|FAR|NEAR|INTER{2}UPT) )*(void|\w+_t)\*?\s*\w+(\s*:\d+)?;.*)|(\s*\/\/.*))+\s*\} \w+\s*;'

它们都遵循相同的一般理念,并且可能或多或少地针对任务和/或大文件进行了优化。

顺便说一句,我正在使用 python 和一个“简单”的函数:

out = open('path/to/output/file.txt', 'w')
for file in self.header_files:
    with open(file) as f:
        whole = f.read()
        print(file)
        for match in re.finditer(reg_X, whole):
            rslt.append(match.group())
            group = match.group()
            out.write(group) # all available structure definition from .h files

self.header_files是我查看的所有文件的列表。并且可以很容易地替换为特定文件的路径并删除for循环语句。

reg_X这意味着您可以使用上面定义的任何正则表达式

现在我如何构建正则表达式(regex_D):

(
    [\t ]*typedef (struct|union)                                <= 'OUTER' DEFINITION
)
\n[\t ]*\{                                                      <= SPACING & BRACKETS
(
    (
        [\n\t ]*(struct[\n\t ]*\{)                              <= 'INNER' DEFINITION
        (
            [\n\t ]*                                            <= SPACING
            (
                ((volatile|static|const|FAR|NEAR|INTERRUPT) )*  <= TYPE
                (void|\w+_t)\*?[\t ]*\w+                        <= 'FINAL' TYPE + NAME
                ([\t ]*:\d+)?                                   <= BITFIELD SPECIFICATION (optional)
                ;.*                                             <= EOL + whatever
            )
            |                                                   || OR
            (
                [\t\n ]*\/\/.*                                  <= LINE STARTING WITH A COMMENT
            )
        )+                                                      <= Variable definition + comment line could occurs multiple time
        [\n\t ]*\} \w+[\t ]*;                                   <= END OF 'INNER' definition1
    )
    |                                                           || OR
    [\n\t ]*                                                    <= SPACING
    (
        (
            (volatile|static|const|FAR|NEAR|INTERRUPT)          <= TYPE
        )*
        (void|\w+_t)                                            <= FINAL TYPE
        \*?[\t ]*\w+                                            <= VAR NAME
        ([\t ]*:\d+)?                                           <= Bitfield specification
        ;.*
    )
    |                                                           || OR
    (
        [\t\n ]*\/\/.*                                          <= Line starting with a comment
    )
)+
[\n\t ]*\} \w+[\t ]*;                                           <= End of outer definition

很多表情都是加倍的。我试图有一个“更好”的正则表达式(regex_B)

(
    [\t ]*
    (
        (
            (
                (typedef )?(struct|union)
            )
            |
            \{
        )
        |
        (
            ((volatile|static|const|FAR|NEAR|INTERRUPT) )*
            (void|\w+_t)
            \*?[\t ]*\w+
            ([\t ]*:\d+)?
            ;.*
        )
        |
        (
            \} \w+ ?;
        )
    )
    (
        \n+
        |
        $
    )
)+

它包含相同的“信息”机器人,但顺序不同,并且具有不同的“要求”,例如第二个在任何行上给出结果,例如: extern FAR varType_t var; 这只是一个简单的变量定义。

澄清:我选择了正则表达式,因为我对解析和实践知之甚少。我正在寻找完成任务的“最佳方式”。正如答案所指出的那样,编译器可能使用的代码解析器是(唯一的)最佳解决方案。

但这个问题有两个目标。第一个已经回答了。

这篇文章的第二个目标是更多地了解一般的正则表达式(和优化)。例如有一个匹配组与 the 相同regex_B但避免任何表达式重复。正如您所看到的,“外部”和“内部”的定义是相同的(只有一两件事),但可以匹配我想要的(仅此而已)。最后一个匹配相同的东西,但由于它更“灵活”,它匹配不应该的地方。

谢谢大家的时间:)

注意:如果您有任何资源可供我查看并从中获得一些知识/经验,请随时分享任何事情/想法。

[使用:https://regex101.com/ 测试正则表达式]

标签: pythoncregexstruct

解决方案


唯一正确的方法是改用解析器,例如用于 Python的解析器。其中有很多/examples...

将“正确处理语言本身的复杂性”的整个问题交给这个已经存在并经过测试的解析器,而不是尝试使用正则表达式来“自己创建一种解析器”。这是愚蠢的差事。

解析器以各种方式工作,但本质上它们通过扫描源代码以在内部创建抽象语法树AST 来工作。 然后,它们允许您探索该树或通过使用所谓的“访问者”对其进行迭代。

树本身不是“抽象的”。该术语来自这样一个事实,即其内容是源代码语法的抽象,代表源代码所说的内容,而不是它的实际文本。

然后,您可以创建一个“访问者”,只要在“行走”期间遇到感兴趣的 AST 节点,就会调用该访问者。这将是收集您需要的任何信息的“正确地点和时间”——但您不必担心您是如何到达那里的。这就是解析器的工作。

例如,您“访问”某个与typedef struct声明相对应的节点。该节点的子节点将代表您现在正在查看的结构的抽象本质,无论它是如何在源代码文本中编写的,这早已不复存在,现在完全无关紧要。这些子节点的组织和内容是已知的并且可以依赖。该节点的父节点代表“包含” this 的任何内容typedef。所有这些繁重的工作都是由解析器自动为您完成的。


脚注: “当然,这就是每种语言解释器或编译器本身在前端的实际工作方式。” 毫无疑问,这个包是使用C99 语言的官方所谓“语法”构建的,该语言的每个编译器也将使用它。该技术(包括构建此类解析器的方法)已经过验证、快速且高效。


推荐阅读