Génération d'analyseurs avec Flex&Bison
Génération d'analyseurs avec Flex&Bison
Généralités
Analyse lexicale avec Flex
Analyse syntaxique avec Bison
Association de Flex et Bison
Fabrice Harrouet
École Nationale d’Ingénieurs de Brest
harrouet@[Link]
[Link]
' $
Flex&Bison
Origine des outils
. Lex&Yacc
Générateurs d’analyseurs lexicaux/syntaxiques en C
' années 1970, laboratoires Bell
Outils UNIX (Posix) standards
+ de nombreuses variantes (commerciales ou non)
. Flex&Bison
Version GNU de Lex&Yacc
Flex : “fast Lex” Bison : jeu de mot sur Yacc
Beaucoup de possibilités supplémentaires !
Disponible pour un très grand nombre de plateformes
. ∃ de nombreux outils différents ayant le même propos
AntLR, Spirit . . .
On retrouve des choses issues de Lex&Yacc
& enib, F.H . . . 2/44 %
' $
Flex&Bison
Principe des outils
' $
Flex&Bison
Principe des outils
Fic
Fic
hie
Flex
hie
rF
rC
lex
Fic
Ex
Fic
Compilation et
hie
éc
Bison
hie
uta
r
édition de liens
Bis
rC
b
le
on
Fic
hie
rs
C
%} Copie du pré−code C
' $
A. lexicale en Flex
Expressions rationnelles en Flex
. Comprend les ER POSIX
Voir le cours correspondant
. Quelques possibilités supplémentaires
Un atome peut être une chaı̂ne C littérale
◦ ex : "ab"{3} → ababab
Les caractères spéciaux du C sont reconnus
◦ ex : Hello\tWorld , Hello\123World , Hello\x2aWorld
La notation { } permet aussi de réutiliser une ER nommée
◦ ex : ({integer}|{real})? (integer et real définies avant)
L’atome <<EOF>> représente la fin de fichier
Les start-conditions (voir plus loin)
& enib, F.H . . . 6/44 %
' $
A. lexicale en Flex
Syntaxe Flex
' $
A. lexicale en Flex
L’analyseur généré
. La fonction int yylex(void)
Extrait des caractères du flux yyin (stdin par défaut)
Confronte les séquences aux ER des règles de production
◦ Test de haut en bas, la plus longue correspondance est retenue
Exécute les actions sémantiques (code C ) associées
◦ Variable yytext : chaı̂ne de caractères correspondant à l’ER
◦ Variable yyleng : longueur de yytext
Écrit les non-correspondances sur yyout (stdout par défaut)
Jusqu’à la fin de fichier ou un return dans les actions C
. La valeur de retour
0 en cas de fin de fichier
Un code numérique (#define, enum) indiquant l’ER reconnue
→ Analyse lexicale
& enib, F.H . . . 8/44 %
' $
A. lexicale en Flex
Compteur de lignes, mots et caractères
%{
int nbChar=0,nbWord=0,nbLine=0; $
%} $ flex -oprog.c prog.l
$ gcc -oprog prog.c
/* doesn’t need yywrap() */ $ ./prog < prog.l
%option noyywrap 24 39 347
$
endOfLine \n
character [^ \t\n]
%%
{endOfLine} { ++nbChar; ++nbLine; }
{character}+ { nbChar+=yyleng; ++nbWord; }
. { ++nbChar; }
%%
int
main(void)
{
yylex();
fprintf(stderr,"%d\t%d\t%d\n",nbLine,nbWord,nbChar);
return(0);
&
} %
enib, F.H . . . 9/44
' $
A. lexicale en Flex
Un analyseur trivial
%{ $ cat [Link]
%} var1=123*45.67;
%option noyywrap _attr+=var1;
integer [0-9]+ $ ./prog < [Link]
real [0-9]+\.[0-9]*|\.[0-9]+ IDENT [var1]
ident [a-zA-Z_][0-9a-zA-Z_]* UNKNOWN [=]
%% INTEGER [123]
{real} { fprintf(stderr,"REAL [%s]\n",yytext); } UNKNOWN [*]
{integer} { fprintf(stderr,"INTEGER [%s]\n",yytext); } REAL [45.67]
{ident} { fprintf(stderr,"IDENT [%s]\n",yytext); } UNKNOWN [;]
\n { fprintf(stderr,"NEW_LINE [%s]\n",yytext); } NEW_LINE [
. { fprintf(stderr,"UNKNOWN [%s]\n",yytext); } ]
%% IDENT [_attr]
UNKNOWN [+]
int UNKNOWN [=]
main(void) IDENT [var1]
{ UNKNOWN [;]
yylex(); NEW_LINE [
return(0); ]
} $
' $
A. lexicale en Flex
Un analyseur plus conventionnel — 2/2
int
main(void)
{
int token;
do
{
token=yylex();
switch(token)
{
case 0: fprintf(stderr,"END_OF_FILE\n"); break;
case INTEGER: fprintf(stderr,"INTEGER [%s]\n",globalValue); break;
case REAL: fprintf(stderr,"REAL [%s]\n",globalValue); break;
case IDENT: fprintf(stderr,"IDENT [%s]\n",globalValue); break;
case NEW_LINE: fprintf(stderr,"NEW_LINE [%s]\n",globalValue); break;
case UNKNOWN: fprintf(stderr,"UNKNOWN [%s]\n",globalValue); break;
}
} while(token);
return(0);
}
' $
A. lexicale en Flex
Une calculette à pile — 2/2
int main(int argc,char ** argv)
{
vector<double> s;
int token;
if(argc>1) yyin=fopen(argv[1],"r"); // check result !!!
do
{
double x1,x2;
token=yylex();
switch(token)
{
case VALUE: s.push_back(val); break;
case PLUS: x2=[Link](); s.pop_back(); x1=[Link](); s.pop_back();
s.push_back(x1+x2); break;
case MINUS: /* ... x1-x2 ... */ break;
case MULT: /* ... x1*x2 ... */ break;
case DIV: /* ... x1/x2 ... */ break;
}
cerr << "-->"; for(size_t i=0;i<[Link]();i++) { cerr << " " << s[i]; } cerr << endl;
} while(token);
return(0);
}
& enib, F.H . . . 14/44 %
' $
A. lexicale en Flex
Les start-conditions
. Conditions inclusives
%s name dans les options, <name>ER dans les règles de production
Les règles qui commencent par <name> ou sans <> sont valables quand
on est dans la condition name
. Conditions exclusives
%x name dans les options, <name>ER dans les règles de production
Seules les règles qui commencent par <name> sont valables quand on
est dans la condition name
. Changement de condition
BEGIN(name) dans le code C place l’analyseur dans la condition name
BEGIN(INITIAL) pour revenir dans l’état de départ (règles sans <>)
' $
A. lexicale en Flex
Reconnaı̂tre des chaı̂nes C — 1/3
%{ $
#include <iostream> $ cat [Link]
#include <string> 123
using namespace std; "ab\tcd\n456\"hello"
enum {STRING=1,INTEGER}; 789 "toto
string val; $ ./prog [Link]
int nbLines; INTEGER[123]
%} STRING[ab cd
456"hello]
%option noyywrap INTEGER[789]
multi-line strings not allowed
%x strEnv STRING[toto]
3 lines
integer [0-9]+ $
%%
' $
A. lexicale en Flex
Reconnaı̂tre des chaı̂nes C — 3/3
%%
int main(int argc,char ** argv)
{
int token;
if(argc>1) yyin=fopen(argv[1],"r"); // check result !!!
do
{
token=yylex();
switch(token)
{
case STRING: cerr << "STRING[" << val << "]" << endl; break;
case INTEGER: cerr << "INTEGER[" << val << "]" << endl; break;
}
} while(token);
cerr << nbLines << " lines" << endl;
return(0);
}
'
A. syntaxique en Bison $
%} Tables d’analyse
Syntaxe Bison
. Règles de production
non terminal : sequence de symboles { /* code C */ }
| autre sequence { /* code C */ }
| ...
;
Les symboles sont représentés par des identificateurs
◦ Terminaux : lexèmes provenant de l’analyse lexicale
◦ Non-terminaux : symboles internes à l’analyseur syntaxique
Le code C est optionnel et est exécuté lorsque la règle est réduite
program : START instList END { cerr << "program" << endl; }
;
instList : instList inst
| inst
;
inst : IDENT ASSIGN expr SEMICOLON { cerr << "inst" << endl; }
;
expr : INTEGER { cerr << "integer expr" << endl; }
| REAL { cerr << "real expr" << endl; }
| IDENT { cerr << "ident expr" << endl; }
;
& enib, F.H . . . 21/44 %
'
A. syntaxique en Bison $
L’analyseur généré
. La fonction int yyparse(void)
Consomme les lexèmes obtenus par des appels à yylex() (à fournir)
int yylex(void);
Vérifie (LALR(1)) si la séquence de lexèmes permet de réduire l’axiome
de la grammaire exprimée
(%start non terminal dans les définitions)
Exécute les actions sémantiques (code C ) associées aux règles réduites
Signale les erreurs à l’aide de la fonction yyerror() (à fournir)
void yyerror(const char * msg);
◦ Possibilité de récupération sur erreur pour poursuivre l’analyse
Jusqu’à ce que l’axiome de la grammaire soit reconnu ou erreur
. La valeur de retour
0 si ok, 1 si erreur
Résultat de l’analyse complète → une seule invocation
& enib, F.H . . . 22/44 %
'
A. syntaxique en Bison $
Association Flex/Bison
. Flex fourni les lexèmes à Bison
Bison invoque la fonction yylex() produite par Flex
yylex() doit renvoyer des constantes connues de Bison
→ %token IDENT INTEGER ... dans les définitions de Bison
Bison genère un .h definissant ces constantes (et d’autres choses)
Le pré-code C de Flex inclu ce .h
. Étapes de la construction :
$ bison -d -[Link] prog.y
→ Produit le code C [Link] depuis le fichier Bison prog.y
→ Option -d pour générer le .h [Link]
$ flex -[Link] prog.l
→ Produit le code C [Link] depuis le fichier Flex prog.l
→ Le pré-code C doit inclure [Link]
$ g++ -oprog [Link] [Link]
& enib, F.H . . . 23/44 %
'
A. syntaxique en Bison $
'
A. syntaxique en Bison $
'
A. syntaxique en Bison $
'
A. syntaxique en Bison $
'
A. syntaxique en Bison $
Associativité et précédence
'
A. syntaxique en Bison $
$ bison -v prog.y
prog.y contains 195 shift/reduce conflicts.
$
& enib, F.H . . . 40/44 %
'
A. syntaxique en Bison $
%start expr
%%
expr : expr PLUS prod // associativite a gauche
| expr MINUS prod // operations les moins prioritaires
| prod
;
prod : prod MULT factor // associativite a gauche
| prod DIV factor // operations un peu plus prioritaires
| prod MODULO factor
| factor
;
factor : VALUE // constructions les plus prioritaires
| LP expr RP
;
$ bison -v prog.y --> pas de conflits mais demarche tres fastidieuse !!!
$
& enib, F.H . . . 41/44 %
'
A. syntaxique en Bison $
$ bison -v prog.y --> conflits resolus facilement par les %left %right %nonassoc
$
& enib, F.H . . . 42/44 %
'
A. syntaxique en Bison $
'
A. syntaxique en Bison $