Traitement des erreurs avec Flex (lex) et Bison (yacc)
-
07-07-2019 - |
Question
Extrait du manuel de Bison:
Dans un analyseur de commande interactif simple où chaque entrée est une ligne, il peut être suffisant pour permettre à yyparse renvoyer 1 en cas d'erreur et demander à l'appelant ignorer le reste de la ligne d'entrée lorsque cela se produit (puis appelle yyparse encore).
C’est à peu près ce que je veux, mais j’ai du mal à me rendre au travail. En gros, je veux détecter les erreurs et les erreurs dans Flex, et si une erreur est détectée, demander à Bison de se défaire de toute la ligne. Ce que j'ai en ce moment ne fonctionne pas très bien car mes commandes sont toujours exécutées:
kbsh: ls '/home
Error: Unterminated Single Quote
admin kbrandt tempuser
syntax error
kbsh:
Dans mon fichier 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); }
;
Et dans mon Flex:
' {BEGIN inQuote; }
<inQuote>\n {printf("Error: Unterminated Single Quote\n"); BEGIN(0); return(ERROR);}
La solution
Je ne pense pas que vous trouverez une solution simple pour traiter ces types d’erreurs d’analyse syntaxique dans le lexer.
Je voudrais garder le lexer (flex / lex) aussi bête que possible, il devrait juste fournir un flux de jetons de base (identifiants, mots-clés, etc.) et laisser l'analyseur (yacc / bison) faire la détection des erreurs . En fait, il est configuré pour exactement ce que vous voulez, avec une petite restructuration de votre approche ...
Dans le lexer (parser.l), restez simple (pas de traitement eol / newline), quelque chose comme (c'est pas complet):
}%
/* 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;
Ensuite, dans votre fichier parser.y, effectuez toutes les manipulations réelles (elles ne sont pas complètes):
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 ...
Il y a deux choses à noter ici:
- Le déplacement d'éléments tels que NEWLINE vers yacc afin que vous puissiez déterminer quand l'utilisateur en a terminé avec la commande, vous pouvez alors effacer les éléments et recommencer (en supposant que vous avez "
int yywrap() {return 1;}
& quelque part). Si vous essayez de le détecter trop tôt en flex, quand savez-vous générer une erreur? - chdir n'est pas une commande (sauf si elle a été soumise et que vous ne l'avez pas montrée), elle a maintenant chdir_command STRING (l'argument de chdir). Cela permet à l’analyseur de comprendre ce qui ne va pas, vous pouvez alors afficher l’erreur si ce répertoire n’existe pas, etc ...
De cette façon, vous devriez obtenir quelque chose comme (devinant à quoi pourrait ressembler chdir):
cd 'répertoire_vous
erreur de syntaxe
cd 'un_répertoire'
vous êtes dans le mec some_directory!
Et tout est géré par le programmeur yacc, pas par le tokenizer.
J’ai constaté que garder le flex aussi simple que possible vous confère un maximum de flexibilité. :)