.. title: Bison et Flex: exemple simple d'analyse syntaxique :name: bison-et-flex-exemple-simple-danalyse-syntaxique `Retour à la page Systèmes <../systeme.html>`__ - `Motivation <#motivation>`__ - `Exemple: mini-calculette <#exemple>`__ - `Pour aller plus loin <#plus>`__ .. _motivation: Motivation ---------- Ce mini-tutorial s'adresse à ceux qui connaissent déjà les principes de l'analyse lexicale et syntaxique, et qui ont simplement besoin d'un résumé des commandes élémentaires pour les mettre en œuvre avec `Flex `__ et `Bison `__. .. _exemple: Exemple: mini-calculette ------------------------ Le travail se décompose typiquement comme suit: - décrire la grammaire et les actions de l'analyseur syntaxique dans un fichier ``calculette.y``. - décrire la grammaire et les actions de l'analyseur lexical dans un fichier ``calc_flex.l`` Ensuite tout est automatique: - Bison produit un fichier ``calculette.c`` (et un fichier ``calculette.h``) à partir de ``calculette.y``. - Flex produit un fichier ``calc_flex.c`` à partir de ``calc_flex.l``. - On peut compiler et lier les deux fichiers grâce à ``calculette.h``. Ensuite, place aux tests ... .. _calculette.y: Le fichier calculette.y pour Bison ---------------------------------- Les fichiers C produits par bison s'attendent par défaut à ce que l'on ait déclaré quelque-part: - Le prototype de ``yyparse()``: c'est l'analyseur syntaxique dont Bison va fournir l'implémentation dans quelques instants... - Le prototype de ``yylex()``: Bison s'adressera à cette fonction pour récupérer les symboles de la grammaire. On pourrait l'implémenter, mais on va laisser flex la fournir (voir ``calc_flex.l`` plus loin) - Le prototype de ``yyerror()``: fonction appelée par Bison en cas de mauvaise nouvelle... - Un type de données symbolisé par la constante YYSTYPE, pour stocker les attributs des symboles dans les variables $$, $1, $2, etc. associées aux éléments de chaque règle de production. .. code:: text %{ #include #define YYSTYPE int int yyparse(); int yylex(); int yyerror(char *s); %} //Symboles terminaux qui seront fournis par yylex() %token ENTIER %left PLUS %left MOINS %left FOIS %left DIVISE %token OUVRIR %token FERMER %% Total: Calcul { printf("Resultat: %d\n", $1); } Calcul: ENTIER | Add | Moins | Fois | Divise | Paren { $$ = $1; } Paren : OUVRIR Calcul FERMER { $$ = $2; } Add : Calcul PLUS Calcul { $$ = $1 + $3; } Moins : Calcul MOINS Calcul { $$ = $1 - $3; } Fois : Calcul FOIS Calcul { $$ = $1 * $3; } Divise : Calcul DIVISE Calcul { $$ = $1 / $3; } %% int yyerror(char *s) { printf("yyerror : %s\n",s); return 0; } int main(void) { yyparse(); return 0; } On remarquera dans la syntaxe du fichier: - Les trois sections déclarations / grammaire / code C séparées par ``%%``. - Dans la section du haut, la partie ``%{ ... %}`` est copiée telle quelle vers le fichier ``calculette.h``. - Les symboles terminaux ne sont pas copiés littéralement de la sorte. Ils sont exploités de deux façons: - Ils seront reportés dans ``calculette.h`` pour que l'analyseur lexical sache signaler les symboles terminaux. - Les mots-clefs ``%left``,\ ``%right`` ne concernent que l'analyseur syntaxique, pour résoudre les conflits de priorité shift/reduce. .. _calc_flex.l: Le fichier calc_flex.l pour Flex -------------------------------- Entre autres choses que le fichier C produit par Flex s'attend à trouver dans le fichier ``calculette.h`` produit par Bison: - Le type ``YYSTYPE`` pour la variable ``yylval`` qu'il remplit à chaque terminal trouvé. Selon les cas, le parseur produit par Bison se chargera de le traduire en ``$$ $1 $2 ...`` - Les valeurs numériques que Bison a attribuées aux symboles terminaux qu'on lui a indiqués, i.e. ``ENTIER PLUS MOINS ...`` .. code:: text %{ #include "calculette.h" %} %option noyywrap blanks [ \t\n]+ entier [0-9]+ plus \+ moins \- fois \* divise \/ ouvrir \( fermer \) %% {blanks} { /* ignore */ } {entier} { yylval = atoi(yytext); return(ENTIER); } {plus} { return(PLUS); } {moins} { return(MOINS); } {fois} { return(FOIS); } {divise} { return(DIVISE); } {ouvrir} { return(OUVRIR); } {fermer} { return(FERMER); } Quelques remarques: - L'option ``noyywrap`` fournie en début de fichier évite de s'embêter avec la fonction ``yywrap`` (seulement utile lorsque l'entrée est répartie sur plusieurs fichiers). - La variable ``yytext`` contient l'identifiant reconnu. - Si aucune expression n'est reconnue par l'analyseur lexical, ``yylex()`` renverra juste le code ASCII du prochain caractère lu. .. _ensemble: Tout mettre ensemble -------------------- Exemple en environnement Linux: .. code:: text $ bison calculette.y --defines=calculette.h -o calculette.c $ gcc -c -Wall calculette.c $ flex -o calc_flex.c calc_flex.l $ gcc -c -Wall calc_flex.c $ gcc -Wall calculette.o calc_flex.o -o calculette Un exemple d'expression à calculer dans un fichier ``valid.txt``: .. code:: text 3*4-(7-3) Place au test: .. code:: text $ ./calculette < valid.txt Resultat: 8 .. _plus: Pour aller plus loin -------------------- - `Exemple brut `__ pour m'inspirer - Pour plus de détails `exemple plus évolué `__ - Exemple `Petit interpréteur `__ (utilise Memphis pour après Lex et Yacc)