Regex вытащить объявления прототипа функции C? - PullRequest
23 голосов
/ 24 января 2009

Я нахожусь где-то на кривой обучения, когда речь идет о регулярных выражениях, и мне нужно использовать их для автоматического изменения прототипов функций в группе заголовков C. Кто-нибудь знает приличное регулярное выражение, чтобы найти все и все прототипы функций в заголовке C, исключая при этом все остальное?

Редактировать: три вещи, которые не были ясны изначально:

  1. I не заботится о C ++, только прямое C. Это означает, что не нужно беспокоиться о шаблонах и т. Д.
  2. Решение должно работать с typedefs и структурами, не ограничиваясь только базовыми типами C.
  3. Это что-то одноразовое. Это не должно быть красиво. Мне все равно, сколько это будет, если оно работает, но мне не нужно сложное, трудно реализуемое решение.

Ответы [ 8 ]

17 голосов
/ 24 января 2009

Вы можете реализовать синтаксический анализатор, используя ANSI C yacc / lex грамматика .

10 голосов
/ 24 января 2009

Чтобы сделать это правильно, вам нужно проанализировать согласно грамматике языка Си. Но если это только для языка C и только для заголовочных файлов, возможно, вы можете воспользоваться некоторыми ярлыками и обойтись без полноценного BNF.

^
\s*
(unsigned|signed)?
\s+
(void|int|char|short|long|float|double)  # return type
\s+
(\w+)                                    # function name
\s*
\(
[^)]*                                    # args - total cop out
\)
\s*
;

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

Обратите внимание, что BNF можно преобразовать в регулярное выражение. Это будет большое, сложное регулярное выражение, но это выполнимо.

7 голосов
/ 24 января 2009

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

extern void (*function(int, void (*)(int)))(int);

(которая может быть стандартной функцией C signal()) жестко в регулярном выражении из-за вложенных скобок. Если у вас нет таких прототипов функций, время, потраченное на то, чтобы разобраться с ними, будет потрачено время. Подобные комментарии относятся к указателям на многомерные массивы. Скорее всего, у вас есть стилистические соглашения, чтобы упростить вашу жизнь. Вы не можете использовать комментарии C99 (C ++); Вам не нужно кодировать их. Вы, вероятно, не помещаете несколько объявлений в одну строку, либо с общим типом, либо без него, поэтому вам не придется иметь дело с этим.

extern int func1(int), func2(double); double func3(int);  // Nasty!
5 голосов
/ 10 сентября 2016

Предполагается, что ваш код отформатирован как

type name function_name(variables **here, variables &here)
{
    code
}

Вот одна строка для Powershell:

ls *.c, *.h | sls "^(\w+( )?){2,}\([^!@#$+%^]+?\)"

, который возвращает результаты как:

...
common.h:37:float max(float a, float b)
common.h:42:float fclamp(float val, float fmin, float fmax)
common.h:51:float lerp(float a, float b, float b_interp)
common.h:60:float scale(float val, float valmin, float valmax, float min,
float max)
complex.h:3:typedef struct complex {
complex.h:8:double complexabs(complex in)
complex.h:13:void complexmult(complex *out, complex a, complex b)
complex.h:20:void complexadd(complex *out, complex a, complex b)
complex.h:27:int mandlebrot(complex c, int i)
...

Чтобы увидеть просто строку без особенностей файла, добавьте format-table -property line (или сокращенно ft -p line):

ls *.c, *.h | sls "^(\w+( )?){2,}\([^!@#$+%^]+?\)" | format-table -p line

Что возвращает:

Line
----
void render(SDL_Surface *screen)
void saveframe(SDL_Surface *screen)
int handleevents(SDL_Surface *screen)
int WinMain(/*int argc, char* args[]*/)
void printscreen(SDL_Surface *screen, unsigned int exclude)
void testsection(char name[])
void sdltests(SDL_Surface *screen, SDL_Window *window, int width, int height)
int WinMain(/*int argc, char *argv[]*/)
int random(int min, int max) {
int main(int argc, char *argv[])

БОНУС: Объяснение регулярного выражения:

^(\w+(\s+)?){2,}\([^!@#$+%^]+?\)
^                                Start of a line
 (         ){2,}                 Create atom to appear to or more times
                                 (as many as possible)
  \w+(\s+)?                      A group of word characters followed by
                                 an optional space
                \(            \) Literal parenthesis containing
                  [^!@#$+%^]+?   A group of 0 or more characters
                                 that AREN'T in “!@#$+%^”
2 голосов
/ 24 августа 2017

Вот регулярное выражение, которое является хорошей отправной точкой для поиска имен функций C:

^\s*(?:(?:inline|static)\s+){0,2}(?!else|typedef|return)\w+\s+\*?\s*(\w+)\s*\([^0]+\)\s*;?

И вот несколько тестов для проверки выражения:

// good cases
static BCB_T   *UsbpBufCtrlRemoveBack   (BCB_Q_T *pBufCtrl);
inline static AT91_REG *UDP_EpIER               (UDP_ENDPOINT_T *pEndpnt);
int UsbpEnablePort (USBP_CTRL_T *pCtrl)
bool_t IsHostConnected(void)
inline AT91_REG *UDP_EpCSR (UDP_ENDPOINT_T *pEndpnt)

// shouldn't match
typedef void (*pfXferCB)(void *pEndpnt, uint16_t Status);
    else if (bIsNulCnt && bIsBusyCnt)
            return UsbpDump(Buffer, BufSize, Option);

Наконец, вот простой скрипт TCL для чтения файла и извлечения всех прототипов функций и имен функций.

set fh [open "usbp.c" r]
set contents [read $fh]
close $fh
set fileLines [split $contents \n]
set lineNum 0
set funcCount 0
set funcRegexp {^\s*(?:(?:inline|static)\s+){0,2}(?!else|typedef|return)\w+\s+\*?\s*(\w+)\s*\([^0]+\)\s*;?}
foreach line $fileLines {
    incr lineNum
    if {[regexp $funcRegexp $line -> funcName]} {
        puts "line:$lineNum, $funcName"
        incr funcCount
    }; #end if

}; #end foreach
puts "$funcCount functions found."
1 голос
/ 05 апреля 2013

Регулярное выражение с одним вкладышем звучит очень тяжело. Я лично использую Perl-скрипт для этого. Это вроде легко. Основной подход> 1. Вызовите ваш любимый препроцессор c, чтобы исключить комментарии и расширить макросы. (так проще) 2. Подсчитайте символы {{''} '. Для функций в простом C они имеют предсказуемое поведение, которое позволит вам определять имена функций. 3. Просмотрите имена функций в исходном источнике (перед предварительной обработкой, чтобы получить сигнатуру с typedefs) Это неэффективный подход, но он работает довольно хорошо для меня. Шаг 1 не является действительно необходимым, но он облегчит вашу жизнь

0 голосов
/ 12 января 2018

Как продолжить великий Ответ Дина

Это найдет

  • Только функции и не объявление тоже
  • И функция, которая возвращает указатели

^([\w\*]+( )*?){2,}\(([^!@#$+%^;]+?)\)(?!\s*;)

0 голосов
/ 19 февраля 2010

Допустим, вы прочитали весь файл c в $ buffer. * сначала создайте регулярное выражение, которое заменяет все комментарии одинаковым количеством пробелов и перевода строки, чтобы позиции строк и столбцов не менялись * создать регулярное выражение, которое может обрабатывать строку в скобках * затем регулярное выражение, как это находит функции: (Статический |) \ s + (\ W +) \ с * $ parenthezized_regexp + * {

этот reg exp не обрабатывает функции, определение функций которых использует директивы препроцессора.

если вы выбираете lex / yacc, вы должны объединить грамматику ANSI C и препроцессора для обработки этих директив препроцессора в определениях функций

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...