Автоматическое обнаружение и установка скобок в циклы C / C ++ в Python - PullRequest
1 голос
/ 10 февраля 2020

Отредактированный обзор и область действия

Эта проблема сводится к следующей проблеме; учитывая исходный файл, автоматически помещайте открывающие и закрывающие фигурные скобки для необязательных блоков управления в C / C ++. Это блоки if, else, do, while и for 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();

В принципе, без добавления новых строк кода, я хочу вставить функцию до , оператор начинается (легко) и после оператора (что может быть трудно). Например, следующий код на самом деле находится в хранилище кода:

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

Помимо ужасной читабельности, я бы хотел поместить фигурные скобки в этот тип l oop и вставить свои функции трассировки. Аргументы функции (для учета вложения) я пропустил.

Текущая реализация

Моя текущая реализация использует регулярное выражение в 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 - это пример, приведенный выше для демонстрационных целей. Я изо всех сил пытаюсь придумать алгоритм, который будет обрабатывать как встроенные циклы, циклы без соответствующих фигурных скобок, так и циклы, которые не содержат фигурных скобок, но имеют многострочные внутренние значения.

Любая помощь в разработке моего алгоритма будет очень полезной. оценили. Сообщите мне в комментариях, если есть что-то, к чему нужно обратиться подробнее.

РЕДАКТИРОВАТЬ :: Дополнительные примеры и ожидаемые результаты

Добавляемые символы окружены < и > жетоны для видимости.

Вложенный без скобок:

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();>

This Gross Multiliner:

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 Edgecase:

Возможно, моя реализация требует включения операторов 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];

Ответы [ 2 ]

1 голос
/ 11 февраля 2020

Вы спускаетесь по кроличьей норе. Чем больше дел вы столкнетесь, тем больше вы столкнетесь с тем, что вам придется написать настоящий синтаксический анализатор для C ++, который потребует изучения всего технологического инструментария.

Вместо этого я настоятельно рекомендую вам упростить свою жизнь, используя инструмент форматирования, такой как clang-format , который уже знает, как анализировать C ++, чтобы сначала переписать с помощью согласованного форматирования (так что фигурные скобки теперь всегда есть), а затем вам просто нужно беспокоиться о сбалансированных фигурных скобках.

(Если это часть процесса сборки, вы можете скопировать код, переформатировать его, а затем проанализировать переформатированный код.)

Обратите внимание: если код использует интересные шаблоны, этого может быть недостаточно. Но, надеюсь, это поможет вам пройти большую часть пути.

0 голосов
/ 11 февраля 2020

После обширных исследований, многочисленных приложений и многих реализаций я получил именно то, что мне нужно.

Существует существующее решение под названием 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 может в меньшей степени делать то, что может делать некрутист; однако требует прямой интеграции с базой данных вашего компилятора или полным списком опций компилятора, что может быть очень сложным.
  • indent аналогично clang-format
  • C++ Resharper не поддерживает форматирование скобок на 2019.3, хотя запланировано на 2020.1.
  • VS Code не поддерживает автоматическую / принудительную вставку скобок

Все эти претензии сделаны на сегодняшний день и надеюсь, скоро они устареют, поэтому есть множество инструментов, которые мы можем использовать и злоупотреблять: P

...