Ответ 1
Вы можете реализовать парсер, используя ANSI C yacc/lex grammar.
Я где-то на кривой обучения, когда дело доходит до регулярных выражений, и мне нужно использовать их для автоматического изменения прототипов функций в кучке заголовков C. Кто-нибудь знает о приличном регулярном выражении для поиска любых и всех прототипов функций в заголовке C, исключая все остальное?
Изменить: три вещи, которые были непонятны изначально:
Вы можете реализовать парсер, используя ANSI C yacc/lex grammar.
Чтобы сделать это правильно, вам нужно проанализировать в соответствии с грамматикой языка С. Но если это только для языка 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 можно преобразовать в регулярное выражение. Это будет большое сложное регулярное выражение, но оно выполнимо.
Для одноразового упражнения вы, вероятно, сделаете все возможное, запустив простой и посмотрев на код, который вы должны сканировать. Выберите три худших заголовка, создайте регулярное выражение или ряд регулярных выражений, которые выполняют эту работу. Вы должны решить, будете ли и как вы будете обрабатывать комментарии, содержащие объявления функций (и, действительно, объявления функций, содержащие комментарии). Работа с:
extern void (*function(int, void (*)(int)))(int);
(которая может быть стандартной функцией C signal()
) жесткой в регулярном выражении из-за вложенных круглых скобок. Если у вас нет таких прототипов функций, время, затрачиваемое на то, как справляться с ними, - это потраченное время. Аналогичные комментарии относятся к указателям на многомерные массивы. Скорее всего, у вас есть стилистические соглашения, чтобы упростить вашу жизнь. Вы не можете использовать комментарии C99 (С++); вам не нужно кодировать их. Вероятно, вы не ставите несколько деклараций в одну строку, либо с общим типом, либо без него, поэтому вам не нужно иметь дело с этим.
extern int func1(int), func2(double); double func3(int); // Nasty!
Предполагая, что ваш код отформатирован примерно как
type name function_name(variables **here, variables &here)
{
code
}
Вот один лайнер для Powershell:
ls *.c, *.h | sls "^(\w+( )?){2,}\([^[email protected]#$+%^]+?\)"
Что возвращает результаты, например:
...
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,}\([^[email protected]#$+%^]+?\)" | 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,}\([^[email protected]#$+%^]+?\)
^ 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
[^[email protected]#$+%^]+? A group of 0 or more characters
that AREN'T in "[email protected]#$+%^"
Одно языковое regex звучит очень сложно. Я лично использую perl script для этого. Это легко. Основной подход → 1. Вызовите свой любимый препроцессор c, чтобы исключить комментарии и расширить макросы. (так проще) 2. Подсчитайте символы '{' '}'. Для функций в простой C они имеют предсказуемое поведение, которое позволит вам определять имена функций. 3. Посмотрите имена функций в исходный источник (перед предварительной обработкой, чтобы получить подпись, которая имеет typedefs) Это неэффективный подход, но для меня это работает очень хорошо. Шаг 1 не нужен, но это облегчит вам жизнь.
Здесь регулярное выражение, которое является хорошей отправной точкой для поиска имен функций 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 script для чтения файла и извлечения всех прототипов функций и имен функций.
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."
Скажем, у вас есть весь файл c, который читается в $buffer. * сначала создайте регулярное выражение, которое заменяет все комментарии одинаковым количеством пробелов и строк, чтобы позиции строк и столбцов не менялись * создать регулярное выражение, которое может обрабатывать строку в скобках * then regexp, как это находит функции: (Статический |)\s + (\ W +)\с * $parenthezized_regexp + * {
этот reg exp не обрабатывает функции, в определении функции которых используются директивы препроцессора.
если вы идете на lex/yacc, вам нужно комбинировать ansi c и препроцессорные грамматики для обработки этих директив препроцессора внутри определений функций
Как продолжение великого Дин TH ответ
Это найдет
^([\w\*]+( )*?){2,}\(([^[email protected]#$+%^;]+?)\)(?!\s*;)