【问题标题】:Compute the source code length of C functions计算 C 函数的源代码长度
【发布时间】:2019-04-07 12:10:45
【问题描述】:

我想部分自动化大学课程的 C 代码 (ANSI C99) 评分。我想计算的一个属性是每个 C 函数的行数(可选地不包括空白行和注释行)。

我知道有几种工具可以过滤掉文件中的空白行和注释行,但这只能解决我的一半问题。我想分隔属于单个 C 函数的行。

有人告诉我正则表达式不起作用。有没有巧妙的方法使用 gcc 预处理器?

【问题讨论】:

  • 您可以计算第一个 { 和最后一个 } 之间的行数。第一个表示函数的开始,然后将每个下一个 { 堆叠起来,并与它的结束 } 匹配。
  • 感谢 Paul,但如果我在函数外有带大括号的字符串或 cmets,这将不起作用。
  • LOC 计数如何成为衡量任何事物的良好客观衡量标准?任何 C 程序都可以做成单行。
  • @PSkocik 对于入门级程序员来说,一个常见的问题是他们编写的函数太长。我想要一个自动化工具,可以帮助我检测我应该特别关注的提交。

标签: c function c-preprocessor


【解决方案1】:

Clang 有一个用于打印语法树的开关。

例如,如果我运行

clang -Xclang -ast-dump -fsyntax-only lc.c 

lc.c:

int main()
{


}

void f()
{
}

我明白了:

...
|-FunctionDecl 0x558d2c812890 <lc.c:1:1, line:5:1> line:1:5 main 'int ()'
| `-CompoundStmt 0x558d2c812970 <line:2:1, line:5:1>
 `-FunctionDecl 0x558d2c8129c8 <line:7:1, line:9:1> line:7:6 f 'void ()'
   `-CompoundStmt 0x558d2c812a68 <line:8:1, line:9:1>

如果您编写一个脚本,从这些 depth=1 CompoundStmt's 中提取行号,这些行号前面是 FunctionDecl's(FunctionDecl + CompoundStmt == 函数定义)并减去它们,您会得到行长你的函数减 1。

预处理器只不过是一个分词器。为此,您需要一个适当的解析器。

【讨论】:

    【解决方案2】:

    您可以分两步解决这个问题:

    • 编写一个可以删除 cmets 的 C 解析器
    • 检测此解析器以检测函数名称和函数体并计算有意义的代码行数。您应该考虑空行和包含大括号和标点符号的行没有意义({{,;...)。这将使您的计数减少对程序员使用的编码风格的依赖。

    这是第一步的帮助:剥离 cmets 的解析器:

    /* strip C comments by chqrlie */
    
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    
    /* read the next byte from the C source file, handing escaped newlines */
    int getcpp(FILE *fp, int *lineno_p) {
        int ch;
        while ((ch = getc(fp)) == '\\') {
            if ((ch = getc(fp)) != '\n') {
                ungetc(ch, fp);
                return '\\';
            }
            *lineno_p += 1;
        }
        if (ch == '\n')
            *lineno_p += 1;
        return ch;
    }
    
    int main(int argc, char *argv[]) {
        FILE *fp = stdin, *ft = stdout;
        const char *filename = "<stdin>";
        int ch, lineno;
    
        if (argc > 1) {
            if ((fp = fopen(filename = argv[1], "r")) == NULL) {
                fprintf(stderr, "Cannot open input file %s: %s\n",
                        filename, strerror(errno));
                return 1;
            }
        }
        if (argc > 2) {
            if ((ft = fopen(argv[2], "w")) == NULL) {
                fprintf(stderr, "Cannot open output file %s: %s\n",
                        argv[2], strerror(errno));
                return 1;
            }
        }
        lineno = 1;
        while ((ch = getcpp(fp, &lineno)) != EOF) {
            int startline = lineno;
            if (ch == '/') {
                if ((ch = getcpp(fp, &lineno)) == '/') {
                    /* single-line comment */
                    while ((ch = getcpp(fp, &lineno)) != EOF && ch != '\n')
                        continue;
                    if (ch == EOF) {
                        fprintf(stderr, "%s:%d: unterminated single line comment\n",
                                filename, startline);
                        break;
                    }
                    putc('\n', ft);  /* replace comment with newline */
                    continue;
                }
                if (ch == '*') {
                    /* multi-line comment */
                    int lastc = 0;
                    while ((ch = getcpp(fp, &lineno)) != EOF) {
                        if (ch == '/' && lastc == '*') {
                            break;
                        }
                        lastc = ch;
                    }
                    if (ch == EOF) {
                        fprintf(stderr, "%s:%d: unterminated comment\n",
                                filename, startline);
                        break;
                    }
                    putc(' ', ft);  /* replace comment with single space */
                    continue;
                }
                putc('/', ft);
                /* keep parsing to handle n/"a//"[i] */
            }
            if (ch == '\'' || ch == '"') {
                int sep = ch;
                const char *const_type = (ch == '"') ? "string" : "character";
    
                putc(sep, ft);
                while ((ch = getcpp(fp, &lineno)) != EOF) {
                    putc(ch, ft);
                    if (ch == sep)
                        break;;
                    if (ch == '\\') {
                        if ((ch = getcpp(fp, &lineno)) == EOF)
                            break;
                        putc(ch, ft);
                    }
                    if (ch == '\n') {
                        fprintf(stderr, "%s:%d: unescaped newline in %s constant\n",
                                filename, lineno - 1, const_type);
                        /* This is a syntax error but keep going as if constant was terminated */
                        break;
                    }
                }
                if (ch == EOF) {
                    fprintf(stderr, "%s:%d: unterminated %s constant\n",
                            filename, startline, const_type);
                    break;
                }
                continue;
            }
            putc(ch, ft);
        }
        if (fp != stdin)
            fclose(fp);
        if (ft != stdout)
            fclose(ft);
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多