Manejo de errores con Flex (lex) y Bison (yacc)
-
07-07-2019 - |
Pregunta
Del Manual de Bison:
En un analizador de comandos interactivo simple donde cada entrada es una línea, puede ser suficiente para permitir que devolver 1 por error y hacer que la persona que llama ignore el resto de la línea de entrada cuando eso sucede (y luego llama a yyparse de nuevo).
Esto es más o menos lo que quiero, pero tengo problemas para ir a trabajar. Básicamente, quiero detectar un error en flex, y si se detecta un error, haga que Bison descarte toda la línea. Lo que tengo ahora, no funciona del todo bien porque mis comandos aún se ejecutan:
kbsh: ls '/home
Error: Unterminated Single Quote
admin kbrandt tempuser
syntax error
kbsh:
En mi archivo Bison:
commands:
/*Empty*/ { prompt(); } |
command { prompt(); }
;
command:
error {return 1; } |
chdir_command |
pwd_command |
exit_command |
WORD arg_list {
execute_command($1, $2);
//printf("%s, %s\n", $1, $2);
} |
WORD { execute_command($1, NULL); }
;
Y en mi Flex:
' {BEGIN inQuote; }
<inQuote>\n {printf("Error: Unterminated Single Quote\n"); BEGIN(0); return(ERROR);}
Solución
No creo que encuentre una solución simple para manejar estos tipos de errores de análisis en el lexer.
Mantendría el lexer (flex / lex) lo más tonto posible, solo debería proporcionar una secuencia de tokens básicos (identificadores, palabras clave, etc.) y hacer que el analizador (yacc / bison) realice la detección de errores . De hecho, está configurado exactamente para lo que desea, con una pequeña reestructuración de su enfoque ...
En el lexer (parser.l), manténgalo simple (sin manejo de eol / newline), algo así como (no está lleno):
}%
/* I don't recall if the backslashify is required below */
SINGLE_QUOTE_STRING \'.*\'
DOUBLE_QUOTE_STRING \".*\"
%%
{SINGLE_QUOTE_STRING} {
yylval.charstr = copy_to_tmp_buffer(yytext); // implies a %union
return STRING;
}
{DOUBLE_QUOTE_STRING} {
yylval.charstr = copy_to_tmp_buffer(yytext); // implies a %union
return STRING;
}
\n return NEWLINE;
Luego, en su archivo parser.y, realice todo el manejo real (no es una tarea completa):
command:
error NEWLINE
{ yyclearin; yyerrorok; print_the_next_command_prompt(); }
| chdir_command STRING NEWLINE
{ do_the_chdir($<charstr>2); print_the_next_command_prompt(); }
| ... and so on ...
Hay dos cosas a tener en cuenta aquí:
- El cambio de cosas como NEWLINE al lado de yacc para que pueda determinar cuándo el usuario ha terminado con el comando y luego puede borrar las cosas y comenzar de nuevo (suponiendo que tenga "
int yywrap() {return 1;}
& "en alguna parte). Si intenta detectarlo demasiado pronto en flex, ¿cuándo sabe que se genera un error? - chdir no es un comando (a menos que se haya subrregulado y simplemente no lo haya mostrado), ahora tiene chdir_command STRING (el argumento para el chdir). Esto hace que el analizador pueda descubrir qué salió mal, luego puede yyerror si ese directorio no existe, etc ...
De esta manera deberías obtener algo como (adivinar cómo podría ser chdir):
cd 'algún_directorio
error de sintaxis
cd 'some_directory'
¡estás en el directorio de some_directory!
Y todo lo maneja el grabador de yacc, no el tokenizador.
He descubierto que mantener la flexibilidad lo más simple posible le brinda la mayor flexibilidad ***. :)