1.1.
Avec un éditeur, construire un fichier
exc1.lex contenant le texte suivant :
%% .|\n ECHO;
%%
Taper : lex -t exc1.lex
pour voir à l'écran l'allure du fichier
créé par lex (l'option -t
affiche le code C produit au lieu de créer le
fichier lex.yy.c).
On voit ainsi la taille importante d'un analyseur qui
se contente de transmettre le flot d'entrée
inchangé au flot de sortie.
Les courageux pourront essayer de comprendre le
code C produit...
1.2.
Modifier ensuite le fichier
exc1.lex comme suit :
pour créer le code source
lex.yy.c de l'analyseur,
puis compiler ce code source et
créer l'exécutable exc1
Pour lancer ce premier analyseur syntaxique,
taper exc1.
Saisir une ligne, une autre ligne... que fait
l'analyseur ?
N.B. Le système doit reconnaitre le chemin menant
à cet exécutable, si ce n'est pas le cas,
écrire : ./exc1
Pour interrompre l'analyseur : Ctrl-C ou Ctrl-D.
Remarque :
si on ne veut pas utiliser la librairie Lex ou
Flex (option de compilation -ll ou -lfl selon les
systèmes) ou si elle est absente (parfois
le cas pour Flex), il faut alors définir les
fonctions yywrap et main dans le fichier Lex.
Pour l'exercice ci-dessus, le fichier-lex pourrait
s'écrire différemment.
Voici trois possibilités, de la plus simple
à la plus sophistiquée :
Notez qu'un des codes peut ne pas fonctionner dans une
des variantes de Lex (Unix-ATT, Unix-Berkeley, Linux-Lex,
Linux-Flex, Cygwin-Flex, ...).
Une bonne habitude : Effacer lex.yy.c (et
l'exécutable) avant chaque modification du
fichier lex et avant chaque nouvel exercice, on
peut vérifier ainsi, en cas d'erreur de
compilation, que les fichiers n'ont pas été
créés et éviter de lancer un code
ne correspondant pas au code Lex créé.
1.3.
Simplifions l'analyseur :
%%
[aeiou] {printf("!");}
%%
Pourquoi cet analyseur a-t-il le même comportement
que le précédent ?
1.4.
Pour que l'analyseur sache s'arrêter, rajouter
après la première ligne %% du fichier
exc1.lex :
^FIN$ {printf("bye \n") ; return 0 ;}
Tester son effet.
1.5.
Essayons maintenant de mettre deux lignes de commande
de fin :
^FIN$ {printf("premiere fin \n") ; return 0 ;}
^FIN$ {printf("seconde fin \n") ; return 0 ; }
Expliquer l'affichage final. Bien évidemment, on
n'écrira par la suite qu'une seule ligne de fin
de saisie !
1.6. Comment ferait-on pour que la commande
d'arrêt de l'analyseur soit une ligne vide ?
Exercice 2
2.1.
Compiler et exécuter le fichier exc2.lex suivant,
que fait cet analyseur ? Comment s'arrête-t-il ?
%{
int c ;
%}
%%
^\n { return 0 ;} . { c++ ; }
\n { printf("total ligne %d\n",c) ; }
%%
main() {
c=0 ;
yylex() ;
printf("total final : %d\n",c) ;
}
2.2.
Modifier ce fichier-lex pour qu'il affiche
correctement le nombre de caractères saisis
de chaque ligne en plus du nombre cumulé.
N.B.
Que se passe-t-il si , au lieu de
gcc lex.yy.c -ll,
on utilise pour compiler la commande:
gcc -ll lex.yy.c ? (le résultat
peut varier selon les systèmes)
2.3.
Modifier encore ce fichier pour compter en parallèle le
nombre de chiffres (0 à 9), de points (caractère '.'),
ainsi que le nombre d'opérateurs (+ - * /).
Exercice 3
3.1.
Ecrire un analyseur lexical qui supprime les espaces
et les tabulations inutiles (redoublés ou en
fin de ligne) du texte saisi en entrée et qui
compte le nombre de mots (séparés par
un (des) espacement ou une ponctuation).
3.2.
Ecrire un analyseur qui met en majuscule le premier
caractère de chaque ligne d'entrée.
3.3.
Ecrire un analyseur qui combine les opérations
des deux questions précédentes.
Comment aurait-on pu combiner les deux opérations
sans créer un troisième
opérateur ?
Exercice 4
Ecrire un analyseur qui reconnait si une phrase est
bien parenthésée.
(On rappelle qu'un mot d'un langage donné est
bien parenthésé quand : 1°) il
a autant de parenthèses ouvrantes que de
parenthèses fermantes et 2°) tout
préfixe du mot ne contient pas plus de
parenthèses fermantes que de parenthèses
ouvrantes.
Exercice 5
Le décodeur biologique
Un brin d'ADN peut être vu comme un mot sur l'alphabet {A,T,G,C}
et un brin d'ARN comme un mot sur l'alphabet {A,U,G,C}.
La réplication de l'ADN donne un brin anti-complémentaire en changeant
les lettres (A->T,T->A,C->G,G->C).
La transcription de l'ADN donne un brin d'ARN messager (A->U,T->A,C->G,G->C).
La translation de l'ARN messager donne une protéine (suite
d'amino-acides) selon le procédé décrit dans la question 5.3.
5.1.
Ecrire un analyseur qui reconnait les quatre bases de l'ADN,
représentées par leur initiale
(A pour Adénine, T pour Thymine, G pour
Guanine, C pour Cytosine), et retourne leur nom en toutes lettres.
Comment gérer les caractères non reconnus ?
Exemple : flot d'entrée : AaTCAGg... flot de sortie :
Adénine Adénine Thymine Cytosine Adénine Guanine
Guanine ...
5.2.
Ecrire un analyseur qui crée le brin d'ARN
messager à partir d'un brin
d'ADN (la lettre U représente l'Uracile).
5.3.
Chaque groupe de trois bases de l'ARN forme une
unité de codage (un codon), selon le code
dégénéré ci-dessous
(premier tableau) qui donne les amino-acides
correspondants (codes des lettres dans le
deuxième tableau).
La partie comprise entre un codon START et un
codon STOP de l'ARN donne une protéine.
Tableau 1 : Les codons
*U*
*C*
*A*
*G*
U**
UUU Phe
UUC Phe
UUA Leu
UUG Leu
UCU Ser
UCC Ser
UCA Ser
UCG Ser
UAU Tyr
UAC Tyr
UAA Stop
UAG Stop
UGU Cys
UGC Cys
UGA Stop
UGG Trp
C**
CUU Leu
CUC Leu
CUA Leu
CUG Leu
CCU Pro
CCC Pro
CCA Pro
CCG Pro
CAU His
CAC His
CAA Gln
CAG Gln
CGU Arg
CGC Arg
CGA Arg
CGG Arg
A**
AUU Ile
AUC Ile
AUA Ile
AUG Start
ACU Thr
ACC Thr
ACA Thr
ACG Thr
AAU Asn
AAC Asn
AAA Lys
AAG Lys
AGU Ser
AGC Ser
AGA Arg
AGG Arg
G**
GUU Val
GUC Val
GUA Val
GUG Val
GCU Ala
GCC Ala
GCA Ala
GCG Ala
GAU Asp
GAC Asp
GAA Glu
GAG Glu
GGU Gly
GGC Gly
GGA Gly
GGG Gly
Tableau 2 : Les vingt amino-acides
code L1
code L3
Nom
A
Ala
Alanine
C
Cys
Cystéine
D
Asp
Aspartate
E
Glu
Glutamate
F
Phe
Phénylalanine
G
Gly
Glycine
H
His
Histidine
I
Ile
Isoleucine
K
Lys
Lysine
L
Leu
Leucine
M
Met
Méthionine
N
Asn
Asparagine
P
Pro
Proline
Q
Gln
Glutamine
R
Arg
Arginine
S
Ser
Sérine
T
Thr
Thréonine
V
Val
Valine
W
Trp
Tryptophane
Y
Tyr
Tyrosine
Ecrire un analyseur qui décode, dans l'ARN,
les lignes qui commencent par
un codon START suivi d'un nombre indéfini de codons de
Phénylalanine, Leucine ou Sérine, avant de terminer
par un codon STOP.
L'analyseur serait-il très différent si le codon START
n'était pas forcément le premier codon et le
codon STOP pas forcément le dernier ?
* 5.4.
(Pour les plus rapides, qui ont le temps)
* 5.4.a.
Compléter le programme précédent
pour reconnaître tous les amino-acides.
Pour éviter un travail trop fastidieux, il faut bien
évidemment optimiser la reconnaissance des motifs en les factorisant...
et il y a des choix à faire pour gérer les erreurs ou incertitudes.
* 5.4.b. Utiliser les analyseurs 5.2 et 5.4.a pour "fabriquer"
la/les protéines
codées par un brin d'ADN.
* Exercice 6
(Pour aller plus loin, après le TP)
Le brin complémentaire d'un ADN se lit dans le sens inverse du brin
initial. Créer un programme de réplication de l'ADN qui tienne compte
de cette nouvelle contrainte.