2010-11-10 45 views
14

J'essaie d'analyser des commentaires de fichier multi-ligne C-style dans mon flex (.L):Pourquoi les commentaires multi-lignes dans flex/bison sont-ils si évasifs?

%s ML_COMMENT 
%% 

... 

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

Je ne renvoie aucun jeton et ma grammaire (.y) ne traite pas de commentaires de quelque manière que.

Quand je lance mon exécutable, je reçois une erreur d'analyse:

$ ./a.out 
/* 
abc 
def 
Parse error: parse error 
$ echo "/* foo */" | ./a.out 
Parse error: parse error 

(Ma fonction yyerror fait un printf ("Parse error:% s \ n"), qui est l'endroit où la première moitié du message d'erreur redondant vient de).

Je peux voir pourquoi le deuxième exemple échoue puisque l'intégralité de l'entrée est un commentaire, et puisque les commentaires sont ignorés par la grammaire, il n'y a pas d'instructions. Ainsi, l'entrée n'est pas un programme valide. Mais la première partie jette une erreur d'analyse avant même que je termine le commentaire.

Aussi confus:

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

Dans ce cas, le commentaire est fermé avant l'entrée en cours de validité réelle (qui, sans commentaire, parse très bien). L'échec se produit réellement après l'analyse de "a", pas après avoir tenté d'analyser l'affectation "a = b;". Si j'applique "a" sur sa propre ligne, cela génère une erreur. Étant donné que le message d'erreur est une erreur d'analyseur et non une erreur de scanner, y a-t-il quelque chose de crucial qui me manque dans mon fichier .y? Ou est-ce que je fais quelque chose de mal dans mes règles de scanner qui se propagent du côté de l'analyseur?

EDIT: @ Per suggestion de Rudi, je me tournai le débogage et trouvé:

$ ./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 

Je me suis tourné le débogage et a constaté que /* foo */ = bar; parse fait la même chose que foo = bar;. J'utilise flex 2.5.4; il ne me donne aucun avertissement sur les règles d'état que je tente d'utiliser.

+1

I retagged flex à gnu-flex. Les règles de votre scanner semblent correctes. L'erreur d'analyse indique une entrée de jeton non valide dans l'analyseur. Vous voudrez peut-être poster quelques règles Bison correspondantes. De plus, il peut être judicieux de placer des instructions printf() dans vos règles de bison, de cette façon vous pouvez voir quelles règles l'analyseur essaie pendant l'analyse du jeton. – Kizaru

+2

Ce serait également une bonne idée de créer un faisceau de test séparé pour votre scanner. De cette façon, vous pouvez isoler les défauts du scanner des défauts de l'analyseur. Tout système de scanner-analyseur est suffisamment complexe pour que vous n'ayez pas besoin d'injecter de la complexité supplémentaire en effectuant des tests d'intégration lorsque vous voulez effectuer des tests unitaires ... – bstpierre

+1

Lorsque vous ajoutez le drapeau '--debug' à votre bison invocation et définissez 'yydebug = 1' avant l'appel' yyparse() ', puis l'analyseur émet des informations de débogage pour chaque jeton qu'il voit à partir de la lexer. – Rudi

Répondre

5

Je pense que vous devez déclarer votre ML_COMMENT condition de démarrage en exclusivité start condition donc seules les règles ML_COMMENT sont actives.%x ML_COMMENT au lieu de %s ML_COMMENT

Sinon, les règles sans conditions de démarrage sont également actives.

+0

Ah! Cela semble avoir fait l'affaire. Ma seule question est la suivante: pourquoi le contenu de mes commentaires sur plusieurs lignes fait-il écho? Quand je tape '/ * foo bar * /' dans STDIN, j'obtiens 'foo bar' imprimé sur STDOUT. – adelarge

+2

[. \ N] ne fait pas ce que vous pensez faire. le remplacer par 2 règles pour un. et un pour \ n. Flex par défaut echos entrée qui ne correspond à aucune règle. C'est pourquoi de nombreux ensembles de règles lex se terminent par "." donc chaque entrée correspond à quelque chose. – Craig

+0

C'est ce qu'il a fait. Merci! – adelarge

5

Parsing commentaires de cette façon peut conduire à des erreurs parce que:

  • vous devez ajouter des conditions à toutes vos règles de lex
  • il devient encore plus complexe si vous voulez aussi gérer // les commentaires
  • vous avez toujours le risque que yacc/bison fusionne deux commentaires, y compris tout entre

Dans mon analyseur, je gère des commentaires comme celui-ci. d'abord définir des règles LEX le début du commentaire, comme ceci:

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

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

puis écrire les fonctions SkipComment et sauts de trait. Ils ont besoin de consommer toute l'entrée jusqu'à la fin du commentaire se trouve (ce qui est assez vieux code pour me pardonner les constructions peu archaïques):

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; 
} 
+1

Est-ce que cela gère la séquence de caractères début/fin de commentaire si elle se trouve dans du texte entre guillemets? (par exemple 'foo =" ceci ne contient pas un/* commentaire */"') –

+0

Je ne l'ai pas explicitement mentionné, mais vous devez analyser les chaînes exactement de la même manière. Vous devez en particulier le faire si vous voulez supporter les backslashes d'échappement comme en C/C++. – Patrick

+1

Ceci est plus complexe, plus sujette aux erreurs, plus verbeux, et plus difficile à faire que d'utiliser correctement les états de départ flex. C'est fondamentalement juste une partie de votre lexer à la main - si vous n'aimez pas le flex, pourquoi ne pas simplement écrire à la main? –

1

Outre le problème avec %x vs %s, vous avez aussi le problème que les . en [.\n] matchs (seulement) un littéral . et non pas « un caractère autre que saut de ligne » comme un . nu ne. Vous voulez une règle comme

<ML_COMMENT>.|"\n"  { /* do nothing */ } 

à la place