【问题标题】:Automatically Detecting and Placing Braces On C/C++ Loops in Python在 Python 中自动检测并在 C/C++ 循环上放置大括号
【发布时间】:2020-05-26 04:55:28
【问题描述】:

编辑概述和范围

这个问题归结为以下问题;给定一个源文件,自动为 C/C++ 中的可选控制块放置左大括号和右大括号。这些块是ifelsedowhilefor afaik。

概述

我正在尝试跟踪和分析大量代码库中的各种循环、语句等,而这些代码库不是我自己编写的。我的最终目标是在给定的代码源中对所有循环执行时序统计(将来会扩展到其他事情,但超出了这个问题的范围)。这些跟踪函数做了各种各样的事情,但它们都遵循类似的问题;在感兴趣的块执行之前和之后放置。

本质上,我想改造代码:

for (i = 0; i < some_condition; i++) {
  some_code = goes(here);
}

for (i = 0; i < some_condition; i++)
{
  some_code = goes(here);
}

for (i = 0; i < some_condition; i++) { some_code = goes(here); }

for (i = 0; i < some_condition; i++)
  some_code = goes(here);

for (i = 0; i < some_condition; i++)
  for (i = 0; i < some_condition; i++)
    some_code = goes(here);

到以下:

S_TRACE(); for (i = 0; i < some_condition; i++) {
  some_code = goes(here);
} E_TRACE();

S_TRACE(); for (i = 0; i < some_condition; i++)
{
  some_code = goes(here);
} E_TRACE();

S_TRACE(); for (i = 0; i < some_condition; i++) { some_code = goes(here); } E_TRACE();

S_TRACE(); for (i = 0; i < some_condition; i++) {
  some_code = goes(here); } E_TRACE();

S_TRACE(); for (i = 0; i < some_condition; i++) {
  S_TRACE(); for (i = 0; i < some_condition; i++) {
    some_code = goes(here); } E_TRACE(); } E_TRACE();

基本上,如果没有添加新的 代码,我想在语句开始(简单)和 之后插入一个函数 before > 声明(这可能很难)。比如下面的代码其实就在代码的仓库里:

for( int i = 0; names[i]; i++ )
    if( !STRCMP( arg, names[i] ) )
    {
        *dst = names[i];
        return 0;
    }
return -1;

撇开糟糕的可读性不谈,我想在这种类型的循环上放置大括号,并插入我的跟踪函数。我省略了函数的参数(考虑嵌套)。

当前实施

我目前的实现在 Python 中使用正则表达式,因为我对这种语言相当熟悉和快速。相关实施环节如下:

import re
source = []
loops = [r"^\s*(for\s*\(.*\))\s*($|{\s*$|\s*)", r"^\s*(while\s*\(.*\))\s*($|{\s*$|\s*)", r"^\s*(do)\s*({?)$"]


def analyize_line(out_file):
    lnum, lstr = source.pop(0)

    for index, loop_type in enumerate(loops):
        match = re.findall(loop_type, lstr)
        if match:
            print(lnum + 1, ":", match[0][0])

            if '{' in match[0][1]:
                out_file.write(lstr.replace(match[0][0], "S_TRACE(); {}".format(match[0][0])))
                look_ahead_place()
                return
            else:
                last_chance = lstr + source[0][1]
                last_match = re.findall(loop_type, last_chance)
                if last_match and '{' in last_match[0][1]:
                    # same as above
                    out_file.write(lstr.replace(match[0][0], "S_TRACE(); {}".format(match[0][0])))
                    lcnum, lcstr = source.pop(0)
                    out_file.write(lcstr)
                    look_ahead_place()
                else:
                    # No matching bracket, make one
                    out_file.write(lstr.replace(match[0][0], "S_TRACE(); {} {{".format(match[0][0])))
                    look_ahead_and_place_bracket()
                return
    # if we did not match, just a normal line
    out_file.write(lstr)


def look_ahead_place():
    depth = 1
    for idx, nl in enumerate(source):
        substr = ""
        for c in nl[1]:
            substr += c
            if depth > 0:
                if c == '{':
                    depth += 1
                elif c == '}':
                    depth -= 1
                    if depth == 0:
                        substr += " E_TRACE(); "
        if depth == 0:
            source[idx][1] = substr
            return
    print("Error finding closing bracket here!")
    exit()


def look_ahead_and_place_bracket():
    for idx, nl in enumerate(source):
        # Is the next line a scopable? how to handle multiline? ???
        # TODO
        return


def trace_loops():
    global source
    src_filename = "./example.c"
    src_file = open(src_filename)
    out_file = open(src_filename + ".tr", 'w')
    source = [[number, line] for number, line in enumerate(src_file.readlines())]
    while len(source) > 0:
        analyize_line(out_file)

trace_loops()

example.c 是上面提供的示例,用于演示目的。我正在努力想出一种算法来处理内联循环、没有匹配大括号的循环以及不包含大括号但具有多行内部的循环。

对我的算法开发的任何帮助将不胜感激。如果有需要进一步解决的问题,请在 cmets 中告诉我。

编辑 :: 更多示例和预期结果

添加的字符用&lt;&gt; 标记包围以提高可见性。

嵌套无括号:

for( int i = 0; i < h->fdec->i_plane; i++ )
    for( int y = 0; y < h->param.i_height >> !!i; y++ )
        fwrite( &h->fdec->plane[i][y*h->fdec->i_stride[i]], 1, h->param.i_width >> !!i, f );

<S_TRACE(); >for( int i = 0; i < h->fdec->i_plane; i++ )< {>
    <S_TRACE(); >for( int y = 0; y < h->param.i_height >> !!i; y++ )< {>
        fwrite( &h->fdec->plane[i][y*h->fdec->i_stride[i]], 1, h->param.i_width >> !!i, f );< } E_TRACE();>< } E_TRACE();>

嵌套混合:

for( int i = 0; i < h->fdec->i_plane; i++ ) {
  for( int y = 0; y < h->param.i_height >> !!i; y++ )
    fwrite( &h->fdec->plane[i][y*h->fdec->i_stride[i]], 1, h->param.i_width >> !!i, ff );
}

<S_TRACE(); >for( int i = 0; i < h->fdec->i_plane; i++ ) {
  <S_TRACE(); >for( int y = 0; y < h->param.i_height >> !!i; y++ )< {>
    fwrite( &h->fdec->plane[i][y*h->fdec->i_stride[i]], 1, h->param.i_width >> !!i, ff );< } E_TRACE();>
}< E_TRACE();>

大型多行嵌套无括号:

for( int i = 0; i < h->sh.i_mmco_command_count; i++ )
    for( int j = 0; h->frames.reference[j]; j++ )
        if( h->frames.reference[j]->i_poc == h->sh.mmco[i].i_poc )
            x264_frame_push_unused(
                h, 
                x264_frame_shift( &h->frames.reference[j] ) 
            );

<S_TRACE(); >for( int i = 0; i < h->sh.i_mmco_command_count; i++ )< {>
    <S_TRACE(); >for( int j = 0; h->frames.reference[j]; j++ )< {>
        if( h->frames.reference[j]->i_poc == h->sh.mmco[i].i_poc )
            x264_frame_push_unused(
                h, 
                x264_frame_shift( &h->frames.reference[j] ) 
            );< } E_TRACE();>< } E_TRACE();>

这个总多线器:

for( int j = 0; 
  j < ((int) offsetof(x264_t,stat.frame.i_ssd) - (int) offsetof(x264_t,stat.frame.i_mv_bits)) / (int) sizeof(int); 
  j++ )
    ((int*)&h->stat.frame)[j] += ((int*)&t->stat.frame)[j];
for( int j = 0; j < 3; j++ )
    h->stat.frame.i_ssd[j] += t->stat.frame.i_ssd[j];
h->stat.frame.f_ssim += t->stat.frame.f_ssim;

<S_TRACE(); >for( int j = 0; 
  j < ((int) offsetof(x264_t,stat.frame.i_ssd) - (int) offsetof(x264_t,stat.frame.i_mv_bits)) / (int) sizeof(int); 
  j++ )< {>
    ((int*)&h->stat.frame)[j] += ((int*)&t->stat.frame)[j];< } E_TRACE();>
<S_TRACE(); >for( int j = 0; j < 3; j++ )< {>
    h->stat.frame.i_ssd[j] += t->stat.frame.i_ssd[j];< } E_TRACE();>
h->stat.frame.f_ssim += t->stat.frame.f_ssim;

If 语句边缘情况:

也许我的实现需要包含 if 语句来解决这个问题?

if( h->sh.i_type != SLICE_TYPE_I )
    for( int i_list = 0; i_list < 2; i_list++ )
        for( int i = 0; i < 32; i++ )
            h->stat.i_mb_count_ref[h->sh.i_type][i_list][i] += h->stat.frame.i_mb_count_ref[i_list][i];

【问题讨论】:

  • 我预计非空连续行的数量在某些情况下会很大。是的? 25+?我建议您在示例中包含一两个特别困难的块(连同所需的结果)。
  • 当然可以。我现在将浏览代码并选择一些具有预期结果的示例。
  • 包括了一些可能是最严重的违规者。如果你愿意,我会包括更多。由于这篇文章很长,我可能会将所有这些都制作一个文件并将其附加到帖子中。
  • 看来我的算法应该先执行大括号/括号传递,然后我的原始实现才能生效......
  • @CarySwoveland 更新了问题描述,使问题简洁明了。希望对您有所帮助。

标签: python regex python-3.x algorithm parsing


【解决方案1】:

你正在进入一个兔子洞。您遇到的案例越多,您遇到的案例就越多,直到您必须为 C++ 编写一个实际的解析器,这将需要学习整个技术工具链。

相反,我强烈建议您通过使用像 clang-format 这样的格式化工具来简化您的生活,该工具已经知道如何解析 C++ 以首先使用一致的格式进行重写(因此大括号现在总是在那里),然后您只需要担心平衡括号。

(如果这是构建过程的一部分,您可以复制代码,重新格式化,然后分析重新格式化的代码。)

注意,如果代码有趣地使用了模板,这可能还不够。但希望它能让你顺利到达那里。

【讨论】:

  • 我实际上看过clang-format,也看过clang-tidy,最后是缩进。所有选项似乎都只处理空格或更简单的情况。据我的研究知识,他们不能自动插入平衡括号,但如果你能证明我错了,我会接受这个作为答案。
  • Relevant bounty 如果你确实能做到这一点!
【解决方案2】:

经过广泛的研究、大量的应用程序和许多实施,我得到了我所需要的。

有一个名为Uncrustify 的现有解决方案。文档有点缺乏,但是今天经过一些探索,以下配置将按照我上面的要求进行。

$ cat .uncrustify

  # Uncrustify-0.70.1
  nl_if_brace                     = remove
  nl_brace_else                   = force
  nl_elseif_brace                 = remove
  nl_else_brace                   = remove
  nl_else_if                      = remove
  nl_before_if_closing_paren      = remove
  nl_for_brace                    = remove
  nl_while_brace                  = remove
  nl_do_brace                     = remove
  nl_brace_while                  = remove
  nl_multi_line_sparen_open       = remove
  nl_multi_line_sparen_close      = remove
  nl_after_vbrace_close           = true
  mod_full_brace_do               = force
  mod_full_brace_for              = force
  mod_full_brace_function         = force
  mod_full_brace_if               = force
  mod_full_brace_while            = force

您可以使用以下命令运行它:

$ uncrustify -c /path/to/.uncrustify --no-backup example.c

对于正在寻找类似问题的未来居民:

  • clang-format 本质上是一个只有空格的格式化程序。
  • clang-tidy 在较小程度上可以做 uncrustify 可以做的事情; 然而需要与您的编译器数据库或编译器选项的完整列表直接集成,这可能很繁琐。
  • indent 类似于 clang-format
  • C++ Resharper 自 2019.3 起不支持括号格式,但计划在 2020.1 中使用。
  • VS Code 不支持自动/强制插入括号

所有这些声明都是从今天开始提出的,希望很快就会过时,因此我们可以使用和滥用大量工具:P

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 2014-01-10
    • 1970-01-01
    • 2011-10-10
    • 2015-07-28
    • 2010-10-27
    相关资源
    最近更新 更多