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);}
Était-ce utile?

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:

  1. 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?
  2. 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é. :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top