¿Por qué los comentarios de varias líneas en flex / bison son tan evasivos?

Estoy intentando analizar los comentarios de varias líneas de estilo C en mi archivo flex (.l):

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

No estoy devolviendo ningún token y mi gramática (.y) no responde a los comentarios de ninguna manera.

Cuando ejecuto mi ejecutable, recibo 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 de donde viene la primera mitad del mensaje de error redundante).

Puedo ver por qué el segundo ejemplo falla ya que la totalidad de la entrada es un comentario, y como la gramática ignora los comentarios, no hay declaraciones. Por lo tanto, la entrada no es un progtwig válido. Pero la primera parte produce un error de análisis antes de que incluso termine el comentario.

También confuso:

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

En este caso, el comentario se cierra antes de la entrada válida real (que, sin el comentario, analiza bien). La falla realmente ocurre después de analizar “a”, no después de intentar analizar la asignación “a = b;”. Si escribo “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?

EDITAR: Según la sugerencia de @Rudi, 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 encontré que /* foo */ = bar; de hecho analiza lo 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.

Creo que debe declarar su condición de inicio ML_COMMENT como una condición de inicio exclusiva para que solo las reglas de ML_COMMENT estén activas. %x ML_COMMENT lugar de %s ML_COMMENT

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

Analizar los comentarios de esta manera puede llevar a errores porque:

  • necesitas agregar condiciones a todas tus reglas de lex
  • se vuelve aún más complejo si también quieres manejar // comentarios
  • Todavía tiene el riesgo de que Yacc / Bison fusione dos comentarios, incluido todo lo que se encuentre entre ellos.

En mi analizador, manejo comentarios como este. Primero defina las reglas de lex para el inicio del comentario, así:

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

luego escriba las funciones SkipComment y SkipLine. Deben consumir toda la entrada hasta que se encuentre el final del comentario (este es un código bastante antiguo, así que perdóneme 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; } 

Además del problema con %x vs %s , también tiene el problema de que . en [.\n] coincide (solo) con un literal . y no ‘cualquier otro personaje que no sea nueva línea’ como un simple . hace. Quieres una regla como

 .|"\n" { /* do nothing */ } 

en lugar

Encontré esta descripción de la gramática del lenguaje C (en realidad solo el lexer) muy útil. Creo que es casi lo mismo que la respuesta de Patrick, pero un poco diferente.

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