2010-11-10 72 views
14

Estoy tratando de analizar los comentarios de estilo C multilínea en mi flex (.l) Archivo:¿Por qué los comentarios multilínea en flex/bison son tan evasivos?

%s ML_COMMENT 
%% 

... 

<INITIAL>"/*"     BEGIN(ML_COMMENT); 
<ML_COMMENT>"*/"    BEGIN(INITIAL); 
<ML_COMMENT>[.\n]+    { } 

No estoy de devolver cualquier token y mi gramática (.y) no se refiere a los comentarios de cualquier manera.

Cuando ejecuto mi ejecutable, me sale un error de análisis:

$ ./a.out 
/* 
abc 
def 
Parse error: parse error 
$ echo "/* foo */" | ./a.out 
Parse error: parse error 

(Mi función yyerror hace un printf ("Error de análisis:% s \ n"), que es donde la primera mitad de la mensaje de error redundante proviene de).

Puedo ver por qué el segundo ejemplo falla, ya que la totalidad de la entrada es un comentario, y dado que la gramática hace caso omiso de los comentarios, no hay enunciados. Por lo tanto, la entrada no es un programa válido. Pero la primera parte arroja un error de análisis antes de que termine el comentario.

también confuso:

$ ./a.out 
/* foo */ 
a = b; 
Parse error: parse error 

En este caso, el comentario está cerrado antes de la entrada válida real (que, sin el comentario, analiza muy bien). El error ocurre realmente después de analizar "a", no después de intentar analizar la asignación "a = b;". Si ingreso "a" en su propia línea, aún arroja un error.

Dado que el mensaje de error es un error del analizador y no un error del escáner, ¿hay algo crucial que me falta en mi archivo .y? ¿O estoy haciendo algo mal en las reglas de mi escáner que se propaga al lado del analizador?

EDIT: por @ sugerencia de Rudi, que activó la depuración y encontró:

$ ./a.out 
Starting parse 
Entering state 0 
Reading a token: /* 
foo 
Next token is 44 (IDENTIFER) 
Shifting token 44 (IDENTIFER), Entering state 4 
Reducing via rule 5 (line 130), IDENTIFER -> identifier 
state stack now 0 
Entering state 5 

Apagué la depuración y se encontró que de hecho /* foo */ = bar; analiza el mismo que foo = bar;. Estoy usando flex 2.5.4; no me da ninguna advertencia sobre las reglas de estado que estoy tratando de usar.

+1

I reetiquetado flexión a GNU-flex. Las reglas de su escáner se ven bien. El error de análisis indica una entrada de token no válida para el analizador. Es posible que desee publicar algunas reglas de Bison correspondientes. Además, puede ser una buena idea colocar las instrucciones printf() dentro de las reglas de bisonte, de esta manera puede ver qué reglas está intentando el analizador durante el escaneo del token. – Kizaru

+2

También sería una buena idea crear un arnés de prueba separado para su escáner. De esta forma, puede aislar los defectos del escáner de los defectos del analizador. Cualquier sistema analizador-analizador es lo suficientemente complejo como para no necesitar inyectar complejidad adicional realizando pruebas de integración cuando lo que realmente desea es realizar pruebas de unidades ... – bstpierre

+1

Cuando agrega el indicador '--debug' a su bisonte invoca y establece 'yydebug = 1' antes de la llamada' yyparse() ', luego el analizador emite información de depuración para cada token que ve desde el lexer. – Rudi

Respuesta

5

Creo que se necesita para declarar su ML_COMMENT condición de arranque como una exclusiva condición de inicio por lo que solo las reglas ML_COMMENT están activas.%x ML_COMMENT en lugar de %s ML_COMMENT

De lo contrario, las reglas sin condiciones de inicio también están activas.

+0

¡Ah! Esto parece haber hecho el truco. Mi única pregunta ahora es: ¿por qué los contenidos de mis comentarios multilínea se repiten? Cuando escribo '/ * foo bar * /' en STDIN, obtengo 'foo bar' impreso en STDOUT. – adelarge

+2

[. \ N] no está haciendo lo que cree que está haciendo. reemplázalo con 2 reglas una para. y uno para \ n. Flex por defecto echos de entrada que no coincide con ninguna regla. Es por eso que muchos conjuntos de reglas Lex terminan con "." por lo que cada entrada coincide con algo. – Craig

+0

Eso lo hizo. ¡Gracias! – adelarge

5

Analizar los comentarios de esta manera puede conducir a errores debido a que:

  • necesita añadir condiciones a todas las reglas de la lex
  • se hace aún más compleja si también quiere manejar // comenta
  • aún tiene el riesgo de que yacc/bison fusione dos comentarios, incluido todo entre

En mi analizador, manejo comentarios como este. En primer lugar definir reglas lex para el inicio del comentario, así:

\/\*  { 
     if (!SkipComment()) 
      return(-1); 
     } 

\/\/  { 
     if (!SkipLine()) 
      return(-1); 
     } 

luego escribir las funciones SkipComment y Skipline. Ellos necesitan consumir toda la entrada hasta que se encuentra al final del comentario (esto es bastante viejo código así que perdónenme las construcciones un tanto arcaicas):

bool SkipComment (void) 
{ 
int Key; 

Key=!EOF; 
while (true) 
    { 
    if (Key==EOF) 
     { 
     /* yyerror("Unexpected EOF within comment."); */ 
     break; 
     } 
    switch ((char)Key) 
     { 
     case '*' : 
     Key=input(); 
     if (char)Key=='/') return true; 
     else    continue; 
     break; 
     case '\n' : 
     ++LineNr; 
     break; 
     } 
    Key=input(); 
    } 

return false; 
} 

bool SkipLine (void) 
{ 
int Key; 

Key=!EOF; 
while (true) 
    { 
    if (Key==EOF) 
     return true; 
    switch ((char)Key) 
     { 
     case '\n' : 
     unput('\n'); 
     return true; 
     break; 
     } 
    Key=input(); 
    } 

return false; 
} 
+1

¿Esto maneja la secuencia de caracteres de inicio/finalización de comentarios si se produce dentro del texto entre comillas? (por ejemplo, 'foo =" esto no contiene un/* comentario */"') –

+0

No lo mencioné explícitamente, pero debe analizar las cadenas exactamente de la misma manera. En especial, tienes que hacer esto si quieres admitir el escape de barras diagonales inversas, como en C/C++. – Patrick

+1

Esto es más complejo, más propenso a errores, más detallado y más difícil de hacer que simplemente usar estados de inicio flexible correctamente. Básicamente es solo una parte de tu Lexer que escribe a mano: si no te gusta la flexión, ¿por qué no escribes a mano todo? –

1

Además del problema con %x vs %s, también tienen el problema de que los . en [.\n] partidos (sólo) un literal . y no 'cualquier carácter que no sea nueva línea' como un desnudo . hace. ¿Quieres una regla como

<ML_COMMENT>.|"\n"  { /* do nothing */ } 

lugar

1

me encontré con esta descripción de la gramática del lenguaje C (en realidad sólo el léxico) de gran utilidad. Creo que es más o menos lo mismo que la respuesta de Patrick, pero ligeramente diferente.

http://www.lysator.liu.se/c/ANSI-C-grammar-l.html

Cuestiones relacionadas