2009-01-24 7 views
21

Estoy en algún punto de la curva de aprendizaje cuando se trata de expresiones regulares, y necesito usarlas para modificar automáticamente prototipos de funciones en un montón de encabezados C. ¿Alguien sabe de una expresión regular decente para encontrar todos y cada uno de los prototipos de función en un encabezado C, mientras se excluye todo lo demás?Regex para extraer las declaraciones del prototipo de la función C?

Editar: Tres cosas que no estaban claras: inicialmente

  1. I No atención sobre C++, sólo el recto C. Esto significa que no hay plantillas, etc. que preocuparse.
  2. La solución debe funcionar con typedefs y structs, sin limitar solo a los tipos de C básicos.
  3. Esto es una especie de una sola vez. No necesita ser bonito. No me importa la cantidad de kludge que sea, siempre y cuando funcione, pero no quiero una solución compleja y difícil de implementar.

Respuesta

15

Puede implementar un analizador usando ANSI C yacc/lex grammar.

+3

No puedo repetirlo lo suficiente. Las expresiones regulares no son un sustituto de un analizador. +1. – dmckee

8

Para hacer esto correctamente, tendrá que analizar de acuerdo con la gramática del lenguaje C. Pero si esto es solo para el lenguaje C y solo para los archivos de encabezado, quizás pueda tomar algunos accesos directos y pasar sin un BNF completo.

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

Esto de ninguna manera es correcto, y necesita trabajar. Pero podría representar un punto de partida, si está dispuesto a esforzarse y mejorarlo. Se puede romper con las definiciones de función que abarcan líneas, argumento de puntero a función, MACROS y probablemente muchas otras cosas.

Tenga en cuenta que BNF puede convertirse en una expresión regular. Será una expresión regular grande y compleja, pero es factible.

+0

Sospecho que las expresiones regulares regulares no son adecuadas para analizar definiciones anidadas de plantilla C++. – jfs

+3

Es cierto - por suerte, están lidiando con C y no con C++. –

+0

Las expresiones regulares clásicas no son recursivas, por lo que no pueden expresar todo en BNF. – Darron

7

Para un ejercicio de una sola vez, probablemente lo mejor será comenzar de manera simple y mirando el código que tiene que escanear. Elija los tres peores encabezados, genere una expresión regular o una serie de expresiones regulares que hagan el trabajo. Debes decidir si vas a tratar con comentarios que contienen declaraciones de funciones (y, de hecho, con declaraciones de funciones que contienen comentarios) y cómo vas a hacerlo. Tratar con:

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

(que podría ser la función estándar de C signal()) es dura en una expresión regular debido a los paréntesis anidados. Si no tiene ninguno de esos prototipos de funciones, el tiempo dedicado a resolver cómo lidiar con ellos es tiempo perdido. Comentarios similares se aplican a punteros a matrices multidimensionales. Lo más probable es que tenga convenciones estilísticas para simplificar su vida. No puede usar comentarios C99 (C++); no necesita codificarlos. Probablemente no coloque varias declaraciones en una sola línea, con o sin un tipo común, para que no tenga que lidiar con eso.

extern int func1(int), func2(double); double func3(int); // Nasty! 
0

Digamos que tiene todo el archivo c leído en $ buffer. * primero cree una expresión regular que reemplace todos los comentarios con igual cantidad de espacios y avances de línea para que las filas y columnas no cambien * create regexp que pueda manejar cadenas entre paréntesis * entonces expresiones regulares como esta encuentra funciones: (estática |) \ s + (\ w +) \ s * $ parenthezized_regexp + * {

este reg exp no maneja funciones cuya definición de función usa directivas de preprocesador.

si vas para lex/yacc hay que combinar ANSI C y gramáticas del preprocesador para manejar esas directivas del preprocesador dentro de la función Definiciones de

1

A una expresión regular revestimiento suena muy duro. Yo personalmente uso un script de Perl para eso. Es algo fácil. El enfoque básico es> 1. Llame a su preprocesador c favorito para eliminar los comentarios y ampliar las macros. (así que es más fácil) 2. Contar símbolos '{' '}'. Para funciones en C simple, tienen un comportamiento predecible que te permitirá detectar nombres de funciones. 3. Busque los nombres de las funciones en la fuente original (antes de preprocesar para obtener la firma que tiene typedefs) Es un enfoque ineficaz pero me funciona bastante bien. paso 1 no es realmente necesario, pero que le hará la vida más fácil

+2

La calidad de esta respuesta se mejoraría de ninguna a buena con un ejemplo en el código. – Lighthart

3

Asumiendo que su código está formateado algo así como

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

Aquí hay una sola línea de Powershell:

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

que devuelve resultados como:

... 
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) 
... 

Ver solo la línea sin los detalles específicos de archivos, añadir format-table -property line (o abreviado como ft -p line):

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

que devuelve:

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[]) 

PRIMA: Explicación de la expresión regular:

^(\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]#$+%^” 
1

Aquí hay una expresión regular que es un buen punto de partida para encontrar nombres de funciones C:

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

Y estos son algunos casos de prueba para validar la expresión:

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

Por último, aquí es un simple script TCL para leer un archivo y extraer todos los prototipos de las funciones y nombres de funciones.

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." 
0

Como siguen a la gran Dean TH answer

Esto encontrará

  • Sólo las funciones y no la declaración demasiado
  • y función que devuelve los punteros

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

Cuestiones relacionadas