Exemple de programme pour les programmes lex et yacc

Cette section contient des exemples de programmes pour les commandes lex et yacc .

Ensemble, ces exemples de programmes créent un programme simple de calculatrice de bureau qui effectue des opérations d'addition, de soustraction, de multiplication et de division. Ce programme de calcul vous permet également d'affecter des valeurs à des variables (chacune désignée par une seule lettre minuscule), puis d'utiliser les variables dans les calculs. Les fichiers qui contiennent les exemples de programmes lex et yacc sont les suivants:

Fichier Contenu
calc.lex Indique le fichier de spécification de commande lex qui définit les règles d'analyse lexicale.
calc.yacc Indique le fichier de grammaire de la commande yacc qui définit les règles d'analyse syntaxique et appelle la sous-routine yylex créée par la commande lex pour fournir une entrée.

Les descriptions suivantes supposent que les exemples de programmes calc.lex et calc.yacc se trouvent dans votre répertoire de travail.

Compilation de l'exemple de programme

Pour créer l'exemple de programme de calculatrice de bureau, procédez comme suit:

  1. Traitez le fichier de grammaire yacc à l'aide de l'indicateur facultatif -d (qui indique à la commande yacc de créer un fichier qui définit les jetons utilisés en plus du code source du langage C):
    yacc -d calc.yacc
  2. Utilisez la commande ls pour vérifier que les fichiers suivants ont été créés:
    y.tab.c
    Fichier source en langage C créé par la commande yacc pour l'analyseur syntaxique
    y.tab.h
    Fichier d'en-tête contenant des instructions de définition pour les jetons utilisés par l'analyseur syntaxique
  3. Traitez le fichier de spécifications lex :
    lex calc.lex
  4. Utilisez la commande ls pour vérifier que le fichier suivant a été créé:
    lex.yy.c
    Fichier source en langage C créé par la commande lex pour l'analyseur lexical
  5. Compilez et liez les deux fichiers source en langage C:
    cc y.tab.c lex.yy.c
  6. Utilisez la commande ls pour vérifier que les fichiers suivants ont été créés:
    y.tab.o
    Fichier objet du fichier source y.tab.c
    lex.yy.o
    Fichier objet du fichier source lex.yy.c
    a.out
    Fichier du programme exécutable
Pour exécuter le programme directement à partir du fichier a.out , entrez:
$ a.out

OR

Pour déplacer le programme vers un fichier avec un nom plus descriptif, comme dans l'exemple suivant, et l'exécuter, entrez:
$ mv a.out calculate
$ calculate
Dans les deux cas, après avoir démarré le programme, le curseur se déplace sur la ligne située sous le$(invite de commande). Ensuite, entrez les nombres et les opérateurs comme vous le feriez sur une calculatrice. Lorsque vous appuyez sur la touche Entrée, le programme affiche le résultat de l'opération. Après avoir affecté une valeur à une variable, comme suit, le curseur passe à la ligne suivante.
m=4 <enter>
_
Lorsque vous utiliserez la variable dans les calculs suivants, elle aura la valeur affectée:
m+5 <enter>
9
_

Code source de l'analyseur syntaxique

L'exemple suivant montre le contenu du fichier calc.yacc . Ce fichier contient des entrées dans les trois sections d'un fichier de grammaire yacc : déclarations, règles et programmes.

%{
#include<stdio.h>

int regs[26];
int base;

%}

%start list

%union { int a; }


%token DIGIT LETTER

%left '|'
%left '&'
%left '+' '-'
%left '*' '/' '%'
%left UMINUS  /*supplies precedence for unary minus */

%%                   /* beginning of rules section */

list:                       /*empty */
         |
        list stat '\n'
         |
        list error '\n'
         {
           yyerrok;
         }
         ;
stat:    expr
         {
           printf("%d\n",$1);
         }
         |
         LETTER '=' expr
         {
           regs[$1.a] = $3.a;
         }

         ;

expr:    '(' expr ')'
         {
           $$ = $2;
         }
         |
         expr '*' expr
         {

           $$.a = $1.a * $3.a;
         }
         |
         expr '/' expr
         {
           $$.a = $1.a / $3.a;
         }
         |
         expr '%' expr
         {
           $$.a = $1.a % $3.a;
         }
         |
         expr '+' expr
         {
           $$.a = $1.a + $3.a;
         }
         |
         expr '-' expr
         {
           $$.a = $1.a - $3.a;
         }
         |
         expr '&' expr
         {
           $$.a = $1.a & $3.a;
         }
         |
         expr '|' expr
         {
           $$.a = $1.a | $3.a;
         }
         |

        '-' expr %prec UMINUS
         {
           $$.a = -$2.a;
         }
         |
         LETTER
         {
           $$.a = regs[$1.a];
         }

         |
         number
         ;

number:  DIGIT
         {
           $$ = $1;
           base = ($1.a==0) ? 8 : 10;
         }       |
         number DIGIT
         {
           $$.a = base * $1.a + $2.a;
         }
         ;

%%
main()
{
 return(yyparse());
}

yyerror(s)
char *s;
{
  fprintf(stderr, "%s\n",s);
}

yywrap()
{
  return(1);
}
Le fichier contient les sections suivantes:
  • Section Déclarations. Cette section contient des entrées qui:
    • Inclure le fichier d'en-tête d'E-S standard
    • Définir des variables globales
    • Définissez lelistrègle en tant qu'emplacement de début de traitement
    • Définir les jetons utilisés par l'analyseur syntaxique
    • Définir les opérateurs et leur priorité
  • Section Règles. La section des règles définit les règles qui analysent le flux d'entrée.
    • %start -Indique que la totalité de l'entrée doit correspondre à stat.
    • %union -Par défaut, les valeurs renvoyées par les actions et l'analyseur lexical sont des entiers. yacc peut également prendre en charge des valeurs d'autres types, notamment des structures. En outre, yacc conserve le suivi des types et insère les noms de membre d'union appropriés afin que l'analyseur syntaxique résultant soit strictement vérifié. La pile de valeurs yacc est déclarée comme étant une union des différents types de valeurs souhaités. L'utilisateur déclare l'union et associe les noms des membres de l'union à chaque jeton et symbole non terminal ayant une valeur. Lorsque la valeur est référencée via une construction $$ ou $n, yacc insère automatiquement le nom d'union approprié, de sorte qu'aucune conversion indésirable n'ait lieu.
    • %type -Permet d'utiliser les membres de la déclaration %union et fournit un type individuel pour les valeurs associées à chaque partie de la grammaire.
    • %toksn -Répertorie les jetons provenant de l'outil lex avec leur type.
  • Section Programmes. La section des programmes contient les sous-routines suivantes. Etant donné que ces sous-routines sont incluses dans ce fichier, vous n'avez pas besoin d'utiliser la bibliothèque yacc lors du traitement de ce fichier.
    Sous-routine Descriptif
    Principal Programme principal requis qui appelle la sous-routine yyparse pour démarrer le programme.
    yyerror (s) Cette sous-routine de traitement des erreurs n'imprime qu'un message d'erreur de syntaxe.
    aencapsulage Sous-routine de récapitulatif qui renvoie la valeur 1 lorsque la fin de l'entrée se produit.

Code source de l'analyseur lexical

Ce fichier contient des instructions d'inclusion pour les entrées et sorties standard, ainsi que pour le fichier y.tab.h . Si vous utilisez l'indicateur -d avec la commande yacc , le programme yacc génère ce fichier à partir des informations du fichier de grammaire yacc . Le fichier y.tab.h contient les définitions des jetons utilisés par le programme d'analyse syntaxique. En outre, le fichier calc.lex contient les règles permettant de générer ces jetons à partir du flux d'entrée. Vous trouverez ci-après le contenu du fichier calc.lex .
%{

#include <stdio.h>
#include "y.tab.h"
int c;
%}
%%
" "       ;
[a-z]     {
            c = yytext[0];
            yylval.a = c - 'a';
            return(LETTER);
          }
[0-9]     {
            c = yytext[0];
            yylval.a = c - '0';
            return(DIGIT);
          }
[^a-z0-9\b]    {
                 c = yytext[0];
                 return(c);
              }
%%