2009-12-05 5 views
4

Je veux passer la chaîne d'un jeton. Si j'ai un jeton appelé ID, alors je veux que mon fichier yacc sache réellement quel ID est appelé. Je pense que je dois passer une chaîne en utilisant yylval dans le fichier yacc du fichier flex. Comment je fais ça?Comment utiliser yylval avec des chaînes dans yacc

+4

Veuillez au moins consulter la documentation avant de poser une question ici. –

Répondre

4

Voir the Flex manual section on Interfacing with YACC.

15 Interfaçage avec Yacc

L'une des principales utilisations de flex est un compagnon à l'analyseur-générateur yacc . Les analyseurs yacc attendent pour appeler une routine nommée yylex() à trouver le jeton d'entrée suivant. La routine est censée renvoyer le type du jeton suivant ainsi que toute valeur associée dans le yylval global. Pour utiliser flex avec yacc, on spécifie l'option `-d 'pour yacc pour lui indiquer pour générer le fichier y.tab.h contenant les définitions de tous les jetons apparaissant dans l'entrée yacc. Ce fichier est ensuite inclus dans le scanner flexible . Par exemple, si l'un des jetons est TOK_NUMBER, une partie du scanner pourrait ressembler à:

 %{ 
    #include "y.tab.h" 
    %} 

    %% 

    [0-9]+  yylval = atoi(yytext); return TOK_NUMBER; 
+7

Cela n'explique pas du tout comment retourner une chaîne car YYSTYPE (non mentionné ici) est un int par défaut. – JonnyJD

12

La clé de retourner une chaîne ou tout autre type complexe via yylval est le syndicat YYSTYPE créé par yacc dans le fichier y.tab.h Le YYSTYPE est une union avec un membre pour chaque type de jeton défini dans le fichier source yacc. Par exemple, pour retourner la chaîne associée à un jeton de symbole dans le fichier source yacc vous déclarez cette union YYSTYPE utilisant l'union % de dans le fichier source yacc:

/*** Yacc's YYSTYPE Union ***/ 

/* The yacc parser maintains a stack (array) of token values while 
    it is parsing. This union defines all the possible values tokens 
    may have. Yacc creates a typedef of YYSTYPE for this union. All 
    token types (see %type declarations below) are taken from 
    the field names of this union. The global variable yylval which lex 
    uses to return token values is declared as a YYSTYPE union. 
*/ 

    %union { 
     long int4;    /* Constant integer value */ 
     float fp;    /* Constant floating point value */ 
     char *str;    /* Ptr to constant string (strings are malloc'd) */ 
     exprT expr;    /* Expression - constant or address */ 
     operatorT *operatorP; /* Pointer to run-time expression operator */ 
    }; 

%type <str> SYMBOL 

Puis dans le fichier source LEX il y a un modèle qui correspond au jeton SYMBOL. Il est de la responsabilité du code associé à cette règle de retourner la chaîne réelle qui représente le SYMBOL. Vous ne pouvez pas simplement passer un pointeur sur le tampon yytext, car il s'agit d'un tampon statique réutilisé pour chaque jeton correspondant. Pour renvoyer le texte correspondant, le tampon yytext statique doit être répliqué sur le tas avec _strdup() et un pointeur sur cette chaîne doit être passé via yyval.str. C'est alors la règle yacc qui correspond à la responsabilité du jeton SYMBOL de libérer la chaîne allouée par tas quand elle est faite avec elle.

[A-Za-z_][A-Za-z0-9_]* {{ 
    int i; 

    /* 
    * condition letter followed by zero or more letters 
    * digits or underscores 
    *  Convert matched text to uppercase 
    *  Search keyword table 
    *  if found 
    *   return <keyword> 
    *  endif 
    * 
    *  set lexical value string to matched text 
    *  return <SYMBOL> 
    */ 

    /*** KEYWORDS and SYMBOLS ***/ 
    /* Here we match a keywords or SYMBOL as a letter 
    * followed by zero or more letters, digits or 
    * underscores. 
    */ 

    /* Convert the matched input text to uppercase */ 
    _strupr(yytext);   /* Convert to uppercase */ 

    /* First we search the keyword table */ 
    for (i = 0; i<NITEMS(keytable); i++) { 
     if (strcmp(keytable[i].name, yytext)==0) 
      return (keytable[i].token); 
    } 

    /* Return a SYMBOL since we did not match a keyword */ 
    yylval.str=_strdup(yytext); 
    return (SYMBOL); 
}} 
+0

Grande réponse mec, belle et claire explication. –

1

Configuration du contexte

Analyse syntaxique (pour vérifier si un texte d'entrée suit une grammaire spécifiée) consistent en deux phases:

  1. tokenizing, qui se fait par des outils tels que lex ou flex, avec l'interface yylex()) et
  2. d'analyser le flux de jeton généré à l'étape 1 (selon une grammaire spécifiée par l'utilisateur), ce qui est fait par des outils comme bison/yacc avec l'interface e yyparse()).

Tout en faisant la phase 1, étant donné un flux d'entrée, chaque appel à yylex() identifie un jeton (une chaîne de char) et des points yytext sur le premier caractère de cette chaîne.Par exemple: Avec un flux d'entrée de "int x = 10;" et avec les règles lex pour la tokenisation conforme au langage C, les 5 premiers appels à yylex() identifieront les 5 jetons suivants: "int", "x", "=", "10", ";" et chaque fois que le yytext pointe vers le premier caractère du jeton de retour.

Phase 2, L'analyseur (que vous avez mentionné que YACC) est un programme qui appelle cette fonction yylex chaque fois pour obtenir un jeton et utilise ces jetons pour voir si elle assortit les règles d'une grammaire. Ces appels à yylex retourneront des jetons comme des codes entiers. Par exemple dans l'exemple précédent, les 5 premiers appels à yylex() peuvent renvoyer les entiers suivants à l'analyseur: TYPE, ID, EQ_OPERATOR et INTEGER (dont les valeurs entières réelles sont définies dans un fichier d'en-tête).

Maintenant, tout analyseur peut voir est ces codes entiers, ce qui peut ne pas être utile à certains moments. Par exemple, dans l'exemple en cours, vous pouvez associer TYPE à int, ID à un pointeur de table de symboles et INTEGER à décimal 10. Pour faciliter cela, chaque jeton renvoyé par yylex avec une autre valeur VALUE dont le type par défaut est int, mais vous pouvez avoir des types personnalisés pour cela. Dans l'environnement lex, cette valeur est accessible en tant que yylval.

Par exemple, encore une fois comme dans l'exemple en cours d'exécution, yylex peut avoir la règle suivante pour identifier les 10

[0-9]+ { yylval.intval = atoi(yytext); return INTEGER; } 

et suivant pour identifier x

[a-zA-Z][a-zA-Z0-9]* {yylval.sym_tab_ptr = SYM_TABLE(yytext); return ID;} 

Notez que là, je l'ai défini de la VALEUR (ou yylval's) type comme une union contenant un int (intval) et un int * pointeur (sym_tab_ptr).

Mais dans le monde yacc, cette valeur est identifiée/accédée comme $ n. Par exemple, considérons la règle de yacc suivante pour identifier une instruction d'affectation spécifique

TYPE ID '=' VAL: { //In this action part of the yacc rule, use $2 to get the symbol table pointer associated with ID, use $4 to get decimal 10.} 

répondre à votre question

Si vous souhaitez accéder à la valeur yytext d'un certain jeton (qui est lié au monde LEX) dans le monde yacc, utilisez cette ancienne valeur ami comme vous avez choisi:

  1. augment type union de VALUE pour ajouter un autre champ char * dire lex_token_str
  2. dans la règle lex, faire yylval.lex_token_str = strdup (yytext)
  3. Puis dans le monde yacc y accéder en utilisant le $ n approprié. Si vous souhaitez accéder à plus d'une valeur unique d'un jeton (par exemple pour l'identificateur de jeton identifié par lex, l'analyseur peut vouloir accéder à la fois au nom et au pointeur de la table des symboles), augmentez le type d'union VALUE avec un membre de structure, contenant char * (pour le nom) et int * (pour le pointeur symtab).
Questions connexes