Les unités lexicales (tokens) transmises par Lex à Yacc sont des identificateurs
définies dans le fichier-yacc où ils constituent les terminaux de
sa grammaire
(ces terminaux sont conventionnellement notés avec une minuscule ou un identifiant
commençant par une minuscule) ; Lex utilisera ces tokens en conséquence dans le
fichier-lex.
Dans le fichier-lex, le code associé à chaque expression se terminera par
returnnomtoken, la valeur nomtoken étant ainsi transmise à
Yacc quand le token correspondant est reconnu par Lex.
%token entier
%token plus
%token moins
%%
Operation : Operation plus entier
| Operation moins entier
| entier
;
La variable globale yylval commune à Lex et Yacc peut être
utilisée pour transmettre de Lex à Yacc la valeur associée
à un token. Cette valeur sera généralement calculée
à partir de yytext qui correspond à la suite de caractères
extraits par Lex du flux d'entrée. Par défaut, yylval est
définie comme un entier par Yacc, il doit alors être
déclaré comme variable externe dans Lex.
...
...
%%
Operation : Operation plus entier {$$=$1+$3;}
|
Operation moins entier {$$=$1−$3;}
|
entier
{$$=yylval;}
;
...
yylval peut être redéfini comme une union dans la deuxième partie du fichier-yac
avec la déclaration %union.
Cela permet de transmettre de Lex vers Yacc
des valeurs dont le type varie selon le motif reconnu. Il faudra alors
typer les terminaux et/ou non-terminaux de la grammaire
Yacc en conséquence en utilisant %type.
Exemple 3 : typage des valeurs transmises
fichier-lex
La définition %union sera traduite par Yacc en : typedef union{int nombre; char* variable;} YYSTYPE
et la variable yylval sera automatiquement déclarée de type YYSTYPE.
2. Compilation coordonnée de lex.yy. et yy.tab.c
Dans le programme exécutable issu de la compilation de lex.yy. et yy.tab.c,
la fonction main doit être la fonction attendue : incluse dans le fichier Yacc,
LA fonction main doit appeler la fonction d'analyse syntaxique yyparse
qui elle-même doit appeler la fonction d'analyse lexicale yylex.
De plus, la fonction main utilisée doit être prioritairement celle du fichier-yacc
ou sinon celle fournit par l'option −ly.
Les dépendances entre les codes sources produits par Lex et par
Yacc peuvent être résumées comme suit :
– yylex() doit être défini avant yyparse(),
– yyparse() doit être défini avant main(),
– la bibliothèque de Yacc (-ly) doit être incluse avant celle de Lex (-ll),
– les tokens définis dans le fichier-yacc doivent être utilisables dans le fichier-lex.
Pour interfacer Lex et Yacc, la méthode de référence préconise d'inclure y.tab.h dans la première partie du fichier-lex.
Méthode de référence pour interfacer Lex et Yacc.
– Construire le fichier-lex et le fichier-yacc en vérifiant leur bonne coordination (voir précédemment)
– Lancer Yacc avec l'option −d pour créer y.tab.h :
yacc −d fichier.yacc
– Ajouter l'inclusion de la définition des tokens dans la première
partie du fichier-lex :
#define y.tab.h
– Lancer Lex :
lex fichier.lex
– Vérifier la présence des trois fichiers lex.yy.c,
yy.tab.h, yy.tab.c
– Compiler dans le bon ordre :
gcc lex.yy.c y.tab.c -ly -ll
Ainsi la fonction main utilisée sera celle écrite par l'utilisateur
dans le fichier-yacc ou à défaut celle écrite par
l'utilisateur dans le fichier-lex ou sinon la fonction main() par défaut
de Yacc. Avec cette méthode, le fichier-lex ne contient pas de main().
Remarque : d'autres méthodes sont possibles mais elles ne sont pas recommandées.
Deuxième méthode : inclure lex.yy.c dans la
dernière partie du fichier-yacc.
– Construire les fichier-lex et fichier-yacc comme précédemment.
– Ajouter au début de la dernière partie du fichier-yacc la directive :
#include lex.yy.c
– Le cas échéant, rajouter en fin de fichier-yacc les fonctions
yyerror() et main().
– Lancer Yacc sur le fichier-yacc et Lex sur le fichier-lex.
– Compiler : gcc y.tab.c -ly -ll
Troisième méthode (la pire) : copier directement
y.tab.c dans la dernière partie du fichier-lex, avant de compiler comme précédemment.