Page précédente Page suivante Table des matières

2. Utilisation de Lex dans l'analyse lexicale

Le but de l'analyse lexicale est de transformer une suite de symboles en terminaux (un terminal peut être par exemple un nombre, un signe '+', un identificateur, etc...). Une fois cette transformation effectuée, la main est repassée à l'analyseur syntaxique (voir ci-dessous). Le but de l'analyseur lexical est donc de 'consommer' des symboles et de les fournir à l'analyseur syntaxique.

Un fichier de description pour Lex est formé de trois parties, selon le schéma suivant :

declarations
%%
productions
%%
code additionnel
dans lequel aucune partie n'est obligatoire. Cependant, le premier %% l'est, afin d'indiquer la séparation entre les déclarations et les productions.

2.1 La première partie d'un fichier Lex : les déclarations

Cette partie d'un fichier Lex peut contenir :

Exemple :

%{

#include "calc.h"

#include <stdio.h>
#include <stdlib.h>

%}

/* Expressions regulieres */
/* ---------------------- */

blancs    [\t\n ]+
lettre    [A-Za-z]
chiffre10 [0-9]     /* base 10 */
chiffre16 [0-9A-Fa-f]   /* base 16 */

identificateur  {lettre}(_|{lettre}|{chiffre10})*
entier10  {chiffre10}+
L'exemple en lui-même est clair, mais regardons d'un peu plus près comment sont formées les expressions régulières.

2.2 Les expressions régulières

    Symbole  |  Signification
-------------+-------------------
          x  |  Le caractere 'x'
          .  |  N'importe quel caractere sauf \n
      [xyz]  |  Soit x, soit y, soit z
      [^bz]  |  Tous les caracteres, SAUF b et z
      [a-z]  |  N'importe quel caractere entre a et z
     [^a-z]  |  Tous les caracteres, SAUF ceux compris entre a et z
         R*  |  Zero R ou plus, ou R est n'importe quelle expression reguliere
         R+  |  Un R ou plus
         R?  |  Zero ou un R (c'est-a-dire un R optionnel)
     R{2,5}  |  Entre deux et cinq R
      R{2,}  |  Deux R ou plus
       R{2}  |  Exactement deux R
"[xyz\"foo"  |  La chaine '[xyz"foo'
   {NOTION}  |  L'expansion de la notion NOTION definie plus haut
         \X  |  Si X est un 'a', 'b', 'f', 'n', 'r', 't', ou
             |  'v', represente l'interpretation ANSI-C de \X.
         \0  |  Caractere ASCII 0
       \123  |  Caractere ASCII dont le numero est 123 EN OCTAL
       \x2A  |  Caractere ASCII en hexadecimal
         RS  |  R suivi de S
        R|S  |  R ou S
        R/S  |  R, seulement s'il est suivi par S
         ^R  |  R, mais seulement en debut de ligne
         R$  |  R, mais seulement en fin de ligne
     <<EOF>> | Fin de fichier

Ainsi, la définition

identificateur  {lettre}(_|{lettre}|{chiffre10})*
reconnaitra comme identificateur les mots 'integer', 'une_variable', 'a1', mais pas '_ident' ni '1variable'. Facile, non ?

Enfin, comme dernier exemple, voici la définition d'un réel :

chiffre   [0-9]
entier    {chiffre}+
exposant  [eE][+-]?{entier}
reel    {entier}("."{entier})?{exposant}?

2.3 La deuxième partie d'un fichier Lex : les productions

Cette partie sert à indiquer à Lex ce qu'il devra faire lorsqu'il rencontrera telle ou telle notion. Celle-ci peut contenir :

Il faut de plus savoir que les commentaires tels que /* ... */ ne peuvent être présents dans la deuxième partie d'un fichier Lex que s'il sont placés dans les actions parenthésées. Dans le cas contraire, ceux-ci seraient considérés par Lex comme des expressions régulières ou des actions, ce qui donnerait lieu à des messages d'erreur, ou, au mieux, à un comportement inattendu.

Enfin, la variable yytext désigne dans les actions les caractères acceptés par expression_régulière. Il s'agit d'un tableau de caractère de longueur yyleng (donc défini comme char yytext[yyleng]).

Exemple :

%%
[ \t]+$   ;
[ \t]   printf(" ");

Ce programme supprime tous les espaces inutiles dans un fichier. Tu auras d'ailleurs noté que Lex permet de faire énormément de choses, et pas seulement des interpréteurs et des compilateurs (Il peut par exemple servir à la recherche/remplacement dans un texte, etc...).

2.4 Troisième partie d'un fichier Lex : Le code additionnel

Tu peux mettre dans cette partie facultative tous le code que tu veux. Si tu ne mets rien, Lex considère que c'est juste :

main() {
  yylex();
}

2.5 Conclusion sur Lex

Comme tu as pu le voir, Lex est très simple d'emploi (en tout cas dans les cas simples tels que ceux présentés). Pourtant, nous n'avons pas fait le tour de toutes les fonctionnalités de Lex, et je t'invite donc à consulter la page de manuel pour une information plus détaillée.


Page précédente Page suivante Table des matières