Introduction à l'analyse lexicale
Introduction à l'analyse lexicale
1
Plan
Introduction
Analyse lexicale automatique
Analyse lexicale manuelle
Annexe
2
Introduction
Présentation de l’analyseur lexical
3
Introduction
Les terminaux
• caractères)
Les terminaux sont
produits
les symboles de base (ensemble de
par l’analyse lexicale. Dans nos
programmes, nous les retrouvons sous la forme :
4
Introduction
Les terminaux
• Marqueurs du langage : ( ) , ;
• Chaînes de caractères : "la valeur est"
• Identificateurs : printf %d 5
Introduction
Les terminaux
6
Introduction
Les lexèmes
Un programme peut être vu comme une “phrase” ; le rôle
principal de l’analyse lexicale est d’identifier les “mots” de la
phrase.
Le scanner décompose le programme en lexèmes en identifiant
les unités lexicales de chaque lexème.
7
Introduction
Les séparateurs
• Des espaces
9
Introduction
10
Analyse lexicale
automatique
11
Réalisation d’analyseurs lexicaux
MÉTHODES DE CONSTRUCTION
Un analyseur lexical peut être écrit :
12
Réalisation d’analyseurs lexicaux
Utilisation de flex
14
Réalisation d’analyseurs lexicaux
Description de l’outil Flex
Flex (Fast Lexer) est un compilateur pour la génération automatique
d’analyseurs lexicaux.
Un fichier Flex est un fichier texte qui contient la description d’un analyseur
lexical en termes d’expressions régulières et d’actions écrites en langage C.
Ce dernier doit être compilé à l’aide d’un compilateur C (gcc, par exemple)
pour obtenir le code exécutable de l’analyseur lexical.
17
Réalisation d’analyseurs lexicaux
Description de l’outil Flex
Le contenu d’un fichier Flex comprend 3 sections
séparées par une ligne contenant le symbole « %% » :
%{
/∗ Déclarations ∗/
%}
/∗ Définitions ∗/
%%
/∗ Règles ∗/
%%
/∗ Code utilisateur ∗/
18
Réalisation d’analyseurs lexicaux
/∗ Déclarations ∗/
Exemple :
%{
#include<stdio.h>
#define N 100
int x = 12;
float z = 1.5; // une variable globale
int fct(int, float); // une fonction globale
%} 19
Réalisation d’analyseurs lexicaux
/∗Définitions ∗/
CHIFFRE [0 − 9]
ENTIER (+|−)? {CHIFFRE} +
20
Réalisation d’analyseurs lexicaux
/∗Définitions ∗/
Définition d’une expression régulière :
Syntaxe :
Nom Expression_Régulière
Exemple :
L [a-zA-Z]
D [0-9]
U "_"
Ident ({L}|{U})({L}|{D}|{U})*
21
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
Une règle Flex est la donnée :
- d’une expression régulière e.
- et d’une action (celle qui sera exécutée lorsqu’une séquence d’entrée
est reconnue par e).
Les règles de traduction sont de la forme
p1 { action1 }
p2 { action2 }
...
pn { actionn } où chaque pi est une expression rationnelle et
chaque action est une suite d'instructions en C.
Exemples
”int” {printf (”Mot cle int”); }
A chaque fois que la chaîne de caractères ”int” sera reconnue, le
message ”Mot cle int” sera affiché sur la sortie standard. 22
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
”\[]b−?.∗+|()$/{}%<>
Pour utiliser ces opérateurs comme caractères ordinaires, il
faut les protéger en les plaçant dans une chaîne entourée de
double-quotes (”) ou en les plaçant après un caractère \.
Par exemple, \n, \t et \b correspondent comme en C au saut
de ligne, `a la tabulation et au retour en arrière. 23
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
Spécification d’expressions régulières avec Flex
- ” : la chaîne elle-même.
- ”abc” : spécifie la chaîne abc
- [ ] : un des ´éléments de l’ensemble.
- [abc] : a, b ou c
- [a1-a2] correspond à l'un des caractères compris entre a1 et a2, en
supposant que l'alphabet est ordonné.
- [a − z] : toutes les lettres minuscules
- . : tout caractère sauf \n.
- ∗ : zéro ou plusieurs fois (l'étoile).
- (x|y)∗ : 0 ou plusieurs caractères x ou y.
- + : un ou plusieurs fois (l’´étoile positive).
- [a − z]+ : 1 ou plusieurs lettres minuscules.
- | : l’alternance (l’union). 24
- (ab|bc) : la chaîne ab ou la chaîne bc.
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
Spécification d’expressions régulières avec Flex
- ? : opérateur d’occurrence 0 ou 1 fois.
ab?c : la chaîne abc ou a c.
- / : condition de reconnaissance.
ab/cd : la chaîne ab seulement si elle est suivie de la chaîne cd.
- $ : reconnaissance en fin de ligne.
ab$ : la chaîne ab seulement si elle est en fin de ligne.
- b : reconnaissance en début de ligne.
bab : la chaîne ab seulement si elle est en début de ligne.
- { } : répétition bornée.
a{1, 5} : les chaînes a, aa, aaa, aaaa ou aaaaa.
a{2, } : les chaînes aa, aaa, aaa, ...etc
25
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
Exemple (entier de Z) :
26
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
Un motif est une expression régulière qui peut référencer les
expressions régulières définies dans la section des définitions.
27
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
Une action est séparée de son motif par au moins un espace (ou une
tabulation).
Exemple:
Digit [0-9]
%%
{Digit}+ printf("Un nombre\n");
"Flex" printf("Flex\n");
%%
28
Réalisation d’analyseurs lexicaux
/∗Règles ∗/
29
Réalisation d’analyseurs lexicaux
/∗Code utilisateur ∗/
30
Réalisation d’analyseurs lexicaux
/∗Code utilisateur ∗/
Pour obtenir un analyseur lexical opérationnel, les
deux fonctions complémentaires yywrap() et main ()
doivent être fournies.
main() : est le programme principal de l’analyseur
synthétisé. Il contient l’appel de la fonction yylex().
yylex() : analyse le contenu du flux yyin jusqu'`a
détecter une unité lexicale.
yywrap() : contrôle l’ouverture des fichiers de texte
soumis à l’analyseur synthétisé par Lex, comme
indiqué plus haut.
31
Réalisation d’analyseurs lexicaux
/∗Code utilisateur ∗/
Les variables yyin et yyout :
– Ce sont des variables globales de Flex de type « pointeur vers un fichier ».
– La variable yyin pointe vers le fichier d’entrée (fichier source) à analyser.
– Par défaut c’est stdin (yyin = stdin), c’est-à-dire que l’analyseur va lire les
données à partir du clavier.
– On peut ouvrir un fichier source par l’instruction : « yyin = fopen(…); ».
– La variable yyout pointe vers le fichier de sortie (fichier cible).
– Par défaut c’est stdout (yyout = stdout), c’est-à-dire que l’analyseur va écrire
les résultats sur l’écran.
– On peut ouvrir un fichier cible par l’instruction : « yyout = fopen(…); ».
- Une fois un lexème est reconnu, l’analyseur lexical le stocke sous forme
d’une chaîne de caractères dans une variable globale appelée « yytext ».
- La longueur du lexème est également stockée sous forme d’un entier dans
une variable globale appelée « yyleng » (yyleng = strlen(yytext)).
- Il est possible de contrôler la longueur maximale d’un lexème reconnu.
32
Réalisation d’analyseurs lexicaux
/∗Code utilisateur ∗/
33
Réalisation d’analyseurs lexicaux
/∗Code utilisateur ∗/
Fonctions complémentaires de Flex
yymore() : indique que les prochains caractères
fournis à yytext lors du prochain appel à flex doivent
être concaténés.
Exemple: pour l’entrée « mega-octets », le code
suivant écrira ”mega-mega-octets”
34
Réalisation d’analyseurs lexicaux
/∗Code utilisateur ∗/
Fonctions complémentaires de Flex
yyless(i) : Renvoie tout sauf les « n » premiers
caractères du lexème courant dans le flot d’entrée, où ils
seront réexaminés quand l’analyseur recherchera la
correspondance suivante.
Par exemple, pour l’entrée ”bonjour”, le code suivant
écrira ”bonjourjour” :
36
Réalisation d’analyseurs lexicaux
Exemple
comptage de caractères et
de lignes
37
Exemple
38
Fonction fopen
Cette fonction permet d'ouvrir un flux de caractère basé sur fichier. Vous
pouvez choisir entre différents modes d'ouverture du fichier (ouverture en
lecture, en écriture, en ajout, ...) en fonction de vos besoins.
39
Fonction fopen
40
Arguments de la ligne de commande
int main(int argc, char *argv[]);
Les arguments d'une ligne de commande sont transmis au
programme sous la forme de chaînes de caractères.
Le séparateur des arguments est une suite non vide de caractères
d'espacement : espace ou tabulation.
La fonction main a deux paramètres : argc de
type int et argv de type char * [].
argc donne le nombre d'éléments de la ligne de commande,
et argv contient ces éléments sous la forme d'un tableau de
chaînes de caractères.
argv[0] contient le nom de la commande, argv[i],
pour i allant de 1 à argc-1, le ième argument.
41
Réalisation d’analyseurs lexicaux
Résolution des conflits
En cas de conflit, Flex choisit toujours la règle qui produit le
plus long lexème.
Exemples
”prog” action1
”program” action2
42
Réalisation d’analyseurs lexicaux
Résolution des conflits
Exemples
”prog” action1
[a − z]+ action2
43
Réalisation d’analyseurs lexicaux
Résolution des conflits
Si aucune règle ne correspond au flot d’entrée, Flex choisit
sa règle par défaut implicite :
.|\n {ECHO}
44
Réalisation d’analyseurs lexicaux
Table des symboles
Flex est un outil pour la génération de scanners lexicaux en langage C.
Il n'utilise pas directement de structure de données pour représenter la
table des symboles, mais il fournit un moyen de générer du code C pour
effectuer l'analyse lexicale, y compris la construction de la table des
symboles.
La structure de données utilisée pour la table des symboles dépendra
donc de la façon dont vous choisissez de la représenter dans votre code
C généré par Flex.
En général, la table des symboles est souvent implémentée sous forme
de tableau associatif (ou dictionnaire) qui associe chaque identificateur à
ses propriétés.
Cependant, d'autres structures de données, telles que les listes
chaînées ou les tables de hachage, peuvent également être utilisées.
Rq: Nous allons étudier tout ça dans un chapitre à part
45
Analyse lexicale
« à la main »
46
Analyse lexicale « à la main »
Principe:
47
Analyse lexicale « à la main »
Rappel:
48
Analyse lexicale « à la main »
Rappel:
49
Analyse lexicale « à la main »
50
Analyse lexicale « à la main »
51
Analyse lexicale « à la main »
52
Analyse lexicale « à la main »
Rappel: construction d’un automate d’une expression
régulière
53
Analyse lexicale « à la main »
Rappel: construction d’un automate d’une expression
régulière
54
Analyse lexicale « à la main »
Rappel: construction d’un automate d’une expression régulière
Composer deux AFN
s, t : deux regexp
N(s) : l'automate acceptant le langage L(s)
N(t) : l'automate acceptant le langage L(t)
57
Analyse lexicale « à la main »
Construire un AFN pour un token
58
Analyse lexicale « à la main »
Construire un AFN pour un token
59
Analyse lexicale « à la main »
Construire un AFN pour un token
60
Analyse lexicale « à la main »
Construire un AFN pour un token
61
Analyse lexicale « à la main »
Construire un AFN pour un token
62
Analyse lexicale « à la main »
Construire un AFN pour un token
63
Analyse lexicale « à la main »
Construire un AFN pour un token
64
Analyse lexicale « à la main »
Construire un AFN pour un token
65
1 i 2 f 3
a-z
a-z
ε 4 5
ε 0-9
0-9
6 0-9 7
ε
0-9 0-9
ε 0-9 .
0 8 9 10
. 0-9
11
0-9
12
Est-ce que l’automate
ε est déterministe?
- - \n
13 14 15 16
a-z
blanc, etc
17
ε blanc, etc
66
any but \n
18 19
Analyse lexicale « à la main »
Passage d’un AFN à un AFD
(fermeture arrière)
67
Analyse lexicale « à la main »
Passage d’un AFN à un AFD
(fermeture arrière)
68
Analyse lexicale « à la main »
Passage d’un AFN à un AFD
(fermeture arrière)
69
Analyse lexicale « à la main »
Passage d’un AFN à un AFD
(fermeture avant)
70
Analyse lexicale « à la main »
Passage d’un AFN à un AFD
(fermeture avant)
71
Analyse lexicale « à la main »
Passage d’un AFN à un AFD
(fermeture avant)
72
1 i 2 f 3
i a-z
a-z
ε 4 5
ε a-z
0-9
0-9 0-9
6 0-9 7
ε
0-9 0-9 0-9
ε 0-9 .
0 8 9 10
. 0-9
.
0-9
11 12
- ε
- - \n
13 14 15 16
a-z
blanc, etc
blanc, etc
17
ε blanc, etc
any but \n
73
any but \n
18 19
1 i 2 f 3
i a-z
a-z
4 5
a-z
0-9
0-9 0-9
6 0-9 7
0-9 0-9 0-9
0-9 .
0 8 9 10
. 0-9
.
11
0-9
12
Est-ce que l’automate
-
est déterministe?
- - \n
13 14 15 16
a-z
blanc, etc
blanc, etc
17
blanc, etc
any but \n
74
any but \n
18 19
Analyse lexicale « à la main »
rappel
75
1 i 2 f 3
i a-z
a-z
4 5
a-z
0-9
0-9 0-9
6 0-9 7
0-9 0-9 0-9
0-9 .
0 8 9 10
. 0-9
.
0-9
11 12
-
- - \n
13 14 15 16
a-z
blanc, etc
blanc, etc
17
blanc, etc
any but \n
76
any but \n
18 19
1 i 2 f 3
i a-z
a-z
4 5
a-z
0-9
0-9
0-9 0-9
6 0-9 7 7,9
0-9 0-9 0-9
.
0-9 .
0 8 9 10
. 0-9
.
11
0-9
12
Est-ce que l’automate
-
est déterministe?
- - \n
13 14 15 16
a-z
Oui
blanc, etc
blanc, etc
17
blanc, etc
any but \n
77
any but \n
18 19
1 i 2 f 3
i a-z
a-z
4 5
a-z
0-9
0-9
0-9 0-9
6 0-9 7 7,9
0-9 0-9 0-9
.
0-9 .
0 8 9 10
. 0-9
.
11
0-9
12 Est-ce qu’on peux
-
minimiser l’automate?
- - \n
13 14 15 16
a-z
blanc, etc
blanc, etc
17
blanc, etc
any but \n
78
any but \n
18 19
Analyse lexicale « à la main »
rappel
79
1 i 2 f 3
i a-z
a-z
4 5
a-z
0-9
0-9
0-9 0-9
6 0-9 7 7,9
0-9 0-9 0-9
.
0-9 .
0 8 9 10
. 0-9
.
11
0-9
12 Est-ce qu’on peux
-
minimiser l’automate?
- - \n
13 14 15 16
a-z
Oui
blanc, etc
blanc, etc
17
blanc, etc
any but \n
80
any but \n
18 19
2 f 3
i a-z
5
a-z
0-9
0-9
0-9 7,9
0-9
.
0 10
0-9
.
0-9
11 12
-
- \n
14 15 16
a-z
blanc, etc
17
blanc, etc
any but \n
81
19
Analyse lexicale « à la main »
Gestion des conflits
Un autre exemple
Les mots-clés : "let", "in". En général, les mots-clés ne peuvent pas être utilisés
comme noms de variables. Ce parti pris évite pas mal d’ambiguïtés lors de l’analyse
grammaticale ultérieure.
Les variables : ['A'-'Z' 'a'-'z'] ['A'-'Z' 'a'-'z' '0'-'9']*. Les noms des variables sont des
suites de lettres et de chiffres commençant obligatoirement par une lettre.
Les entiers : ['0'-'9']+
Un blanc : [' ' '\n' '\t']. Les caractères '\n' et '\t' sont respectivement le retour à la
ligne et la tabulation.
82
Analyse lexicale « à la main »
Gestion des conflits
Un autre exemple
83
Analyse lexicale « à la main »
Gestion des conflits
Mais il y a encore des cas douteux :
let pourrait être reconnu comme une variable.
m s u
86
Analyse lexicale « à la main »
Gestion des conflits
87
Analyse lexicale « à la main »
Implémentation de l’automate
88
Analyse lexicale « à la main »
Implémentation de l’automate
2. Définir les règles lexicales : Pour chaque type de jeton, il est nécessaire de
définir une ou plusieurs règles lexicales qui permettent de reconnaître le jeton
dans le programme source. Les règles lexicales sont généralement définies sous
forme d'expressions régulières qui décrivent les motifs à rechercher dans le
texte.
89
Analyse lexicale « à la main »
Implémentation de l’automate
90
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
91
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
• Noter que le caractère « > » et un autre caractère sont lus quand on progresse
dans la suite d'arcs depuis l'état initial jusqu'à l'état d'acceptation 8. Etant
donné que le caractère supplémentaire ne fait pas partie de l'opérateur de
relation « >= » on doit reculer le pointeur avant d'un caractère. On utilise une *
pour signaler les états dans lesquels ce recul dans l'entrée doit être fait.
92
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
• Besoin :
- Une fonction permettant de lire le cas suivant : suivant ().
- Une procédure permettant de reculer d’un nombre de caractères de
pointeur avant: reculer().
93
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
/* variables */
Unsigned char cas ; Unsigned Etat ;
/* on utilise switch */
/* initialisation */
Etat=0 ;
While (1)
{
Switch (etat)
Case 0 : cas= suivant () ; If (cas== ‘> ‘) Etat=6 ;
Break ;
Case 6 : cas= suivant () ; If (cas== ‘= ‘) Etat=7 ; else Etat=8 ;
Break ;
Case 7 : printf(‘operl >= \n’) ;
Break ;
Case 8 : printf(‘operl >\n’) ; Reculer() ;
Break ; }
94
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
95
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
Pour les opérateurs arithmétiques, nous pouvons définir des règles lexicales
pour chaque opérateur (+, -, *, /).
•Addition : "+" +|-|*|/
1 2
•Soustraction : "-"
•Multiplication : "*"
•Division : "/"
96
Analyse lexicale « à la main »
Implémentation de l’automate
Exemple:
3. Implémentation des transitions de l'automate :
- Une implémentation simple utilise la fonction getchar() pour lire les
caractères d'entrée de l'utilisateur, puis utilise une simple structure de
contrôle switch pour identifier les jetons de l'expression arithmétique
101
Annexe
102
Annexe
103
Merci pour votre
attention!
[email protected]
104