2009-09-16 7 views
2

De Manuel Bison:Erreur Handing avec Flex (LEX) et Bison (yacc)

Dans un analyseur simple de commande interactive où chaque entrée est une ligne, il peut être suffisante pour permettre yyparse à retourner 1 sur erreur et avoir l'appelant ignorer le reste de la ligne d'entrée lorsque qui se passe (et ensuite appeler yyparse à nouveau).

C'est à peu près ce que je veux, mais j'ai du mal à me rendre au travail. Fondamentalement, je veux détecter et erreur dans flex, et si une erreur est détectée, Bison rejette toute la ligne. Ce que j'ai en ce moment, ne fonctionne pas tout à fait raison parce que mes commandes sont exécutées encore:

kbsh: ls '/home 
Error: Unterminated Single Quote 
admin kbrandt tempuser 
syntax error 
kbsh: 

Dans mon dossier 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);} 

Répondre

6

Je ne pense pas que vous trouverez une solution simple pour gérer ces types d'erreurs d'analyse 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 (identificateurs, mots-clés, etc ...) et avoir l'analyseur (yacc/bison) faire l'erreur détection. En fait, il est configuré pour exactement ce que vous voulez, avec une petite restructuration de votre approche ...

Dans la lexer (parser.l), restez simple (pas de manipulation eol/newline), quelque chose comme (isn ' t chose pleine):

}% 

/* 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 faire toutes les manipulations réelles (n'est pas chose complète):

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:

  1. Le décalage de choses comme NEWLINE vers le côté yacc afin que vous puissiez déterminer quand l'utilisateur en a fini avec la commande, vous pouvez effacer les choses et recommencer (en supposant que vous avez "int yywrap() {return 1;}" quelque part). Si vous essayez de le détecter trop tôt, quand savez-vous faire une erreur?
  2. chdir n'est pas une commande (à moins qu'elle ne soit sous-estimée et que vous ne l'ayez pas montrée), elle a maintenant la commande chdir_command STRING (l'argument du chdir). Cela permet à l'analyseur de comprendre ce qui s'est mal passé, vous pouvez alors yyerror si ce répertoire n'existe pas, etc ...

De cette façon, vous devriez obtenir quelque chose comme (deviner ce que chdir pourrait ressembler):

cd « some_directory
erreur de syntaxe
cd 'some_directory'
vous êtes le mec some_directory!

Et il est tout géré par le Grammer yacc, non par le tokenizer.

J'ai trouvé que le fait de garder le flex aussi simple que possible vous donne la plus grande flexibilité. :)

+0

Terminé le bricolage avec les modifications maintenant ... – tim

+1

Je me retrouve souvent avec une balle dans le pied parce que j'ai écrit une lexer trop complexe. –

+0

Merci beaucoup tim, va essayer de travailler dans mon projet quand j'ai l'occasion. Je commence tout juste à apprendre ce genre de choses, donc en supposant que vous conseilliez de garder flex simple, bon conseil, cette réponse est géniale! –

Questions connexes