首页 > 解决方案 > 如何编写与#ifdef...#else...#endif C 预处理器宏匹配的 awk 程序?

问题描述

我有大量具有以下代码块的 C 程序语料库。

100. #ifdef DEBUG1
    .
    .
    .
102. #else    
    .
    .
    .
105. #endif

或者,

200. #ifdef DEBUG2
    .
    .
    .
206. #endif

此外,单个文件可能包含多个#DEBUG宏。我想提取与宏对应的行号。假设代码片段中的数字是源文件中的行号,我希望输出采用以下格式:

FILE - MACRO_NAME - IFDEF - ELIF - ENDIF
----------------------------------------
prog.c - DEBUG1   -  100  -  102  -  105
prog.c - DEBUG2   -  200  -   X   -  206

我怎样才能编写一个awk程序来实现相同的目标?如果awk不是合适的工具,我可以使用什么工具?

标签: cawkc-preprocessorpreprocessor

解决方案


awk 实际上关联数组,所以我会采取的方法是:

  1. 对于每个#ifdef(或等价物,例如#if 1),增加一个变量,然后使用它来存储if行号,将elseendif行号设置为 -1。

  2. 对于#else一行,使用当前变量来设置else行号。

  3. 对于#endif, 输出您对行号的任何详细信息,然后递减变量。

  4. 对于 an #elif,您需要组合#else#if操作并确保相关#end关闭所有#if/#elif

例如,这是一个独立的bash脚本,展示了它是如何工作的:

#!/usr/bin/env bash

# Use this script as input file as well, luckily C preprocessor
# macros look like bash comments.

#ifdef XYZZY
    # Some text inside the first ifdef
    #if 0
        # This is the inner bit.
    #endif
    #if 1
        # blah blah blah
    #elif defined TWISTY
        # yada yada yada
    #elif defined PASSAGES
        # run out of phrases
    #else
        # still got nothing
    #endif
#else
    #ifdef PLUGH
        # This is the plugh stuff
    #else
        # This is the anti-plugh stuff
    #endif
#endif

awk <$0 '
    $1 == "#ifdef" || $1 == "#if" {
        level++
        line_mac[level] = $0
        gsub(/^[ \t]+/, "", line_mac[level])
        line_if[level] = NR
        line_else[level] = "X"
        line_end[level] = "X"
        typ_elif[level] = 0
        next
    }
    $1 == "#elif" {
        line_else[level] = NR
        level++
        line_mac[level] = $0
        gsub(/^[ \t]+/, "", line_mac[level])
        line_if[level] = NR
        line_else[level] = "X"
        line_end[level] = "X"
        typ_elif[level] = 1
        next
    }
    $1 == "#else" {
        line_else[level] = NR
        next
    }
    $1 == "#endif" {
        while (typ_elif[level] == 1) {
            printf "if-line %-4s, else-line %-4s, endif-line %-4s, macro '%s'\n", line_if[level], line_else[level], NR, line_mac[level]
            level--
        }
        printf "if-line %-4s, else-line %-4s, endif-line %-4s, macro '%s'\n", line_if[level], line_else[level], NR, line_mac[level]
        level--
    }
    '

的输出(带有用于检查的文件中的编号行)是:

 1: #!/usr/bin/env bash
 2: 
 3: # Use this script as input file as well, luckily C preprocessor
 4: # macros look like bash comments.
 5: 
 6: #ifdef XYZZY
 7:     # Some text inside the first ifdef
 8:     #if 0
 9:         # This is the inner bit.
10:     #endif
11:     #if 1
12:         # blah blah blah
13:     #elif defined TWISTY
14:         # yada yada yada
15:     #elif defined PASSAGES
16:         # run out of phrases
17:     #else
18:         # still got nothing
19:     #endif
20: #else
21:     #ifdef PLUGH
22:         # This is the plugh stuff
23:     #else
24:         # This is the anti-plugh stuff
25:     #endif
26: #endif

if-line 8   , else-line X   , endif-line 10  , macro #if 0
if-line 15  , else-line 17  , endif-line 19  , macro #elif defined PASSAGES
if-line 13  , else-line 15  , endif-line 19  , macro #elif defined TWISTY
if-line 11  , else-line 13  , endif-line 19  , macro #if 1
if-line 21  , else-line 23  , endif-line 25  , macro #ifdef PLUGH
if-line 6   , else-line 20  , endif-line 26  , macro #ifdef XYZZY

推荐阅读