Flex Bison
Flex Bison
Flex Bison
Techniques
de compilation
G
en
erer un analyseur avec Flex&Bison
Generalites
Analyse lexicale avec Flex
Analyse syntaxique avec Bison
Association de Flex et Bison
Fabrice Harrouet
Ecole
Nationale dIngenieurs de Brest
[email protected]
http://www.enib.fr/~harrouet/
&
'
Flex&Bison
'
Flex&Bison
&
'
Flex&Bison
rC
lex
rF
hie
Fic
hie
Fic
Flex
Ex
le
tab
cu
Compilation et
dition de liens
on
Bis
ier
h
Fic
ier
h
Fic
Bison
rs
hie
Fic
C
. G
en
eration danalyseurs statiques et non dynamiques
Le code C produit est specifique au langage a` reconnaitre efficacite
Compilation du code genere comme le reste de lapplication
Modif du langage regeneration du code des analyseurs
&
'
A. lexicale en Flex
Fichier C gnr
%{
Dclarations, macros
Prcode C
Tables danalyse
%}
Copie du prcode C
Dfinitions et options
%%
int yylex(void)
{
...
Copie du code C
...
}
Autres fonctions ...
Postcode C
Copie du postcode C
%%
Rgles de production
. Pr
e/post-code C : du C tout a` fait classique
. Options : %quelquechose pour parametrer le fonctionnement
. D
efinitions : Expressions rationnelles auxquelles on attribue un nom
. R`
egles de production : Associations ER code C a` executer
&
'
A. lexicale en Flex
'
A. lexicale en Flex
Syntaxe Flex
. D
efinition des ER
Un nom coll
e`
a
ex : integer
ex : indent
nomm
ees
gauche, une ou plusieurs espaces, une ER
[1-9][0-9]*|0
[a-zA-Z ][0-9a-zA-Z ]*
. R`
egles de production
Une ER coll
ee `
a gauche, une ou plusieurs espaces, du code C
Code sur une seule ligne ou bloc avec { sur la meme ligne que lER
ex : {integer}
cerr << "INTEGER" << endl;
ex : {indent}
{
cerr << "IDENT" << endl;
}
Les commentaires ne doivent pas etre colles a` gauche ( ER!)
&
'
A. lexicale en Flex
Lanalyseur g
en
er
e
. La fonction int yylex(void)
Extrait des caract`eres du flux yyin (stdin par defaut)
Confronte les sequences aux ER des r`egles de production
Test de haut en bas, la plus longue correspondance est retenue
Execute les actions semantiques (code C ) associees
Variable yytext : chane de caract`eres correspondant `a lER
Variable yyleng : longueur de yytext
Ecrit
les non-correspondances sur yyout (stdout par defaut)
Jusqu`a la fin de fichier ou un return dans les actions C
. La valeur de retour
0 en cas de fin de fichier
Un code numerique (#define, enum) indiquant lER reconnue
Analyse lexicale
&
'
A. lexicale en Flex
\n
[^ \t\n]
%%
{endOfLine}
{character}+
.
%%
{ ++nbChar; ++nbLine; }
{ nbChar+=yyleng; ++nbWord; }
{ ++nbChar; }
int
main(void)
{
yylex();
fprintf(stderr,"%d\t%d\t%d\n",nbLine,nbWord,nbChar);
return(0);
}
&
$
$ flex -oprog.c prog.l
$ gcc -oprog prog.c
$ ./prog < prog.l
24
39
347
$
'
A. lexicale en Flex
Un analyseur trivial
%{
%}
%option noyywrap
integer
[0-9]+
real
[0-9]+\.[0-9]*|\.[0-9]+
ident
[a-zA-Z_][0-9a-zA-Z_]*
%%
{real}
{ fprintf(stderr,"REAL [%s]\n",yytext); }
{integer} { fprintf(stderr,"INTEGER [%s]\n",yytext); }
{ident}
{ fprintf(stderr,"IDENT [%s]\n",yytext); }
\n
{ fprintf(stderr,"NEW_LINE [%s]\n",yytext); }
.
{ fprintf(stderr,"UNKNOWN [%s]\n",yytext); }
%%
int
main(void)
{
yylex();
return(0);
}
&
$ cat file.txt
var1=123*45.67;
_attr+=var1;
$ ./prog < file.txt
IDENT [var1]
UNKNOWN [=]
INTEGER [123]
UNKNOWN [*]
REAL [45.67]
UNKNOWN [;]
NEW_LINE [
]
IDENT [_attr]
UNKNOWN [+]
UNKNOWN [=]
IDENT [var1]
UNKNOWN [;]
NEW_LINE [
]
$
enib, F.H . . . 10/44
'
A. lexicale en Flex
[0-9]+
[0-9]+\.[0-9]*|\.[0-9]+
[a-zA-Z_][0-9a-zA-Z_]*
%%
{real}
{integer}
{ident}
\n
.
%%
&
{
{
{
{
{
strcpy(globalValue,yytext);
strcpy(globalValue,yytext);
strcpy(globalValue,yytext);
strcpy(globalValue,yytext);
strcpy(globalValue,yytext);
return(REAL); }
return(INTEGER); }
return(IDENT); }
return(NEW_LINE); }
return(UNKNOWN); }
$ cat file.txt
var1=123*45.67;
_attr+=var1;
$ ./prog < file.txt
IDENT [var1]
UNKNOWN [=]
INTEGER [123]
UNKNOWN [*]
REAL [45.67]
UNKNOWN [;]
NEW_LINE [
]
IDENT [_attr]
UNKNOWN [+]
UNKNOWN [=]
IDENT [var1]
UNKNOWN [;]
NEW_LINE [
]
END_OF_FILE
$
enib, F.H . . . 11/44
'
A. lexicale en Flex
fprintf(stderr,"END_OF_FILE\n");
fprintf(stderr,"INTEGER [%s]\n",globalValue);
fprintf(stderr,"REAL [%s]\n",globalValue);
fprintf(stderr,"IDENT [%s]\n",globalValue);
fprintf(stderr,"NEW_LINE [%s]\n",globalValue);
fprintf(stderr,"UNKNOWN [%s]\n",globalValue);
break;
break;
break;
break;
break;
break;
'
A. lexicale en Flex
Une calculette `
a pile 1/2
%{
#include <iostream>
#include <vector>
using namespace std;
enum {VALUE=1,PLUS,MINUS,MULT,DIV};
double val;
%}
%option noyywrap
integer
real
value
%%
{value}
"+"
"-"
"*"
"/"
[ \t\n]+
.
%%
&
[0-9]+
[0-9]+\.[0-9]*|\.[0-9]+
{integer}|{real}
{
{
{
{
{
{
{
sscanf(yytext,"%lf",&val); return(VALUE); }
return(PLUS); }
return(MINUS); }
return(MULT); }
return(DIV); }
/* nothing to be done */ }
cerr << "lexical error: " << yytext << endl; }
$ cat calc.txt
1 2 +
4 *
5 10 - @/
$ ./prog calc.txt
--> 1
--> 1 2
--> 3
--> 3 4
--> 12
--> 12 5
--> 12 5 10
--> 12 -5
lexical error: @
--> -2.4
--> -2.4
$
'
A. lexicale en Flex
Une calculette `
a 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=s.back(); s.pop_back(); x1=s.back(); 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<s.size();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`egles de production
Les r`egles 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`egles de production
Seules les r`egles qui commencent par <name> sont valables quand on
est dans la condition name
. Changement de condition
BEGIN(name) dans le code C place lanalyseur dans la condition name
BEGIN(INITIAL) pour revenir dans letat de depart (r`egles sans <>)
&
'
A. lexicale en Flex
%{
#include <iostream>
#include <string>
using namespace std;
enum {STRING=1,INTEGER};
string val;
int nbLines;
%}
%option noyywrap
%x strEnv
integer
[0-9]+
$
$ cat string.txt
123
"ab\tcd\n456\"hello"
789 "toto
$ ./prog string.txt
INTEGER[123]
STRING[ab
cd
456"hello]
INTEGER[789]
multi-line strings not allowed
STRING[toto]
3 lines
$
%%
&
'
A. lexicale en Flex
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
val=yytext; return(INTEGER); }
val.clear(); BEGIN(strEnv); }
BEGIN(INITIAL); return(STRING); }
val+=\a; }
val+=\b; }
val+=\f; }
val+=\n; }
val+=\r; }
val+=\t; }
val+=\v; }
val+=\\; }
val+=\"; }
cerr << "multi-line strings not allowed" << endl; ++nbLines; }
val+=\n; ++nbLines; } // line cut by \
val+=yytext[1]; }
BEGIN(INITIAL); return(STRING); }
val+=yytext[0]; }
/* nothing to be done */ }
++nbLines; }
cerr << "lexical error: " << yytext << endl; }
enib, F.H . . . 17/44
'
A. lexicale en Flex
%%
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. lexicale en Flex
Quelques remarques
. Utilisation de variables globales (internes et applicatives)
Chaque invocation analyse la suite du flux, letat est sauve
Plusieurs analyseurs differents PB a` ledition de liens
Non reentrant PB si plusieurs analyses simultanees
. Lordre des r`
egles est important
Commencer par le plus specifique, finir par le plus general
ex : {ident} doit etre pris en compte apr`
es les mot-clefs
La plus longue correspondance est tout de meme preferee
ex : form est reconnu comme un {ident} meme si le mot-clef
for est teste avant.
. Beaucoup dautres options, fonctions, macros, variables
Le minimum est vu ici, tr`es proche du Lex originel
Permettent des horreurs, ou de contourner certaines limitations
&
'
A. syntaxique en Bison
Fichier C gnr
%{
Dclarations, macros
Prcode C
Copie du prcode C
%}
Tables danalyse
Dfinitions et options
%%
int yyparse(void)
{
...
Copie du code C
...
}
Autres fonctions ...
Postcode C
Copie du postcode C
%%
Rgles de production
. Pr
e/post-code C : du C tout a` fait classique
. Options : %quelquechose pour parametrer le fonctionnement
. D
efinitions : %autrechose pour definir les lex`emes, les priorites . . .
. R`
egles de production : R`egles de grammaires et code C a` executer
&
'
A. syntaxique en Bison
Syntaxe Bison
. R`
egles de production
non terminal : sequence de symboles { /* code C */ }
| autre sequence { /* code C */ }
| ...
;
'
A. syntaxique en Bison
Lanalyseur g
en
er
e
. La fonction int yyparse(void)
Consomme les lex`emes obtenus par des appels a` yylex() (`a fournir)
int yylex(void);
Verifie (LALR(1)) si la sequence de lex`emes permet de reduire laxiome
de la grammaire exprimee
(%start non terminal dans les definitions)
Execute les actions semantiques (code C ) associees aux r`egles reduites
Signale les erreurs a` laide de la fonction yyerror() (`a fournir)
void yyerror(const char * msg);
Possibilite de recuperation sur erreur pour poursuivre lanalyse
Jusqu`a ce que laxiome de la grammaire soit reconnu ou erreur
. La valeur de retour
0 si ok, 1 si erreur
Resultat de lanalyse compl`ete une seule invocation
&
'
A. syntaxique en Bison
Association Flex/Bison
. Flex fourni les lex`
emes `
a Bison
Bison invoque la fonction yylex() produite par Flex
yylex() doit renvoyer des constantes connues de Bison
%token IDENT INTEGER ... dans les definitions de Bison
Bison gen`ere un .h definissant ces constantes (et dautres choses)
Le pre-code C de Flex inclu ce .h
. Etapes
de la construction :
$ bison -d -oprogY.cpp prog.y
Produit le code C progY.cpp depuis le fichier Bison prog.y
Option -d pour generer le .h progY.hpp
$ flex -oprogL.cpp prog.l
Produit le code C progL.cpp depuis le fichier Flex prog.l
Le pre-code C doit inclure progY.hpp
$ g++ -oprog progL.cpp progY.cpp
&
'
A. syntaxique en Bison
'
A. syntaxique en Bison
%start program
%%
&
'
A. syntaxique en Bison
'
A. syntaxique en Bison
$ cat file3.txt
start
a := 1 ;
b := 2.3 ;
c := a ;
end
and then ...
$ ./prog file3.txt
integer expr
inst
real expr
inst
ident expr
inst
program
line 6: parse error
$
enib, F.H . . . 27/44
'
A. syntaxique en Bison
Valeurs associ
ees aux symboles
. Chaque symbole peut contenir une valeur
Au-del`a de lanalyse syntaxique analyse s
emantique
%union{ ... } dans les definitions de Bison (union C )
. Valeur dun symbole un seul champ de lunion
%type<nom champ> symboles dans les definitions de Bison
. Acc`
es aux valeurs depuis Bison
Dans le code associe a` non terminal : symboles
$$ valeur associee au non-terminal de la partie gauche (ecriture)
$1 valeur associee au premier symbole de la partie droite (lecture)
$2 , $3 . . . pour les autres symboles de la partie droite
Si pas de code C equivalent de {$$=$1;} par defaut
Attention aux %type warnings
Inhiber avec du code vide {}
&
'
A. syntaxique en Bison
&
'
A. syntaxique en Bison
'
A. syntaxique en Bison
'
A. syntaxique en Bison
Une calculette `
a pile . . . sans pile ! 1/2
%{ /*-------- prog.l --------*/
$ bison -d -oprogY.cpp prog.y
/* idem Analyse syntaxique simple */
$ flex -oprogL.cpp prog.l
%}
$ g++ -oprog progL.cpp progY.cpp
%option noyywrap
$ echo "1 2 + 4 * 5 10 - /" | ./prog
integer
[0-9]+
value=1
real
[0-9]+\.[0-9]*|\.[0-9]+
value=2
value
{integer}|{real}
1+2=3
%%
value=4
{value}
{
3*4=12
sscanf(yytext,"%lf",&yylval.val);
value=5
return(VALUE);
value=10
}
5-10=-5
"+"
{ return(PLUS); }
12/-5=-2.4
"-"
{ return(MINUS); }
Result: -2.4
"*"
{ return(MULT); }
Success
"/"
{ return(DIV); }
$
"\n"
{ ++lineNumber; }
[ \t]+
{ /* nothing to be done */ }
.
{ char msg[0x20]; sprintf(msg,"lexical error <%s>",yytext);
yyerror(msg);
}
%%
&
enib, F.H . . . 32/44
'
A. syntaxique en Bison
Une calculette `
a pile . . . sans pile ! 2/2
%{ /*-------- prog.y --------*/
/* idem Analyse syntaxique simple */
%}
%token VALUE PLUS MINUS MULT DIV
%union
{
double val;
}
%type<val> VALUE expr
%start calc
%%
calc : expr { cerr << "Result: " << $1 << endl; }
;
expr : VALUE
{ $$=$1; cerr << "value=" << $1 << endl;}
| expr expr PLUS { $$=$1+$2; cerr << $1 << "+" << $2 << "="
| expr expr MINUS { $$=$1-$2; cerr << $1 << "-" << $2 << "="
| expr expr MULT { $$=$1*$2; cerr << $1 << "*" << $2 << "="
| expr expr DIV
{ $$=$1/$2; cerr << $1 << "/" << $2 << "="
;
%%
/* idem Analyse syntaxique simple */
&
<<
<<
<<
<<
$$
$$
$$
$$
<<
<<
<<
<<
endl;
endl;
endl;
endl;
}
}
}
}
'
A. syntaxique en Bison
'
A. syntaxique en Bison
%token A2 B2 C2 D2
%start g2
%%
g2 : e2 B2 C2
| f2 B2 D2
;
e2 : A2
;
f2 : A2
;
%token A3 B3 C3
%start g3
%%
g3 : e3 B3
| f3 C3
;
e3 : A3
;
f3 : A3
;
. Cas 1 : r
eduire A1 en e1 ou f1 ?
Apr`es A1 : e1 -> A1 . et f1 -> A1 .
Le lex`eme suivant (B1) ne permet pas de choisir !
. Cas 2 : r
eduire A2 en e2 ou f2 ?
Idem, il faudrait repousser le choix a` deux lex`emes (C2 ou D2)
. Cas 3 : pas de conflit !
Le lex`eme suivant A3 (B3 ou C3) permet de choisir !
&
'
A. syntaxique en Bison
%token A2 B2 C2 D2
%start g2
%%
g2 : e2 B2 C2
| A2 B2 D2
;
e2 : A2
;
%token A3 B3 C3
%start g3
%%
g3 : e3 B3
| A3 C3
;
e3 : A3
;
. Cas 1 : r
eduire A1 en e1 ou d
ecaler pour g1 -> A1 B1 ?
Apr`es A1 : g1 -> A1 . B1 et e1 -> A1 .
Le lex`eme suivant (B1) ne permet pas de choisir !
. Cas 2 : r
eduire A2 en e2 ou d
ecaler pour g2 -> A2 B2 D2 ?
Idem, il faudrait repousser le choix `a deux lex`emes (C2 ou D2)
. Cas 3 : pas de conflit !
Le lex`eme suivant A3 (B3 ou C3) permet de choisir !
&
'
A. syntaxique en Bison
A1 B1
list
list elem
/* empty */
A1
B1
/* empty */
. Z
ero ou plusieurs
el
ements
Deux fois le cas vide (reduire/reduire) retirer elem ->
. D
ecaler/r
eduire dans if/else
if(c1)if(c2)f();else g(); if(c1){if(c2)f();}else g(); ?
if(c1){if(c2)f();else g();} ?
Le decalage par defaut est correct ! on ne change rien !
&
'
A. syntaxique en Bison
Rep
erer les conflits
. G
en
erer une description du proc
ed
e danalyse
bison -v -oprogY.cpp progY.l produit progY.output (-v)
Debut du fichier : conflits avec numero de letat o`u ils se situent
Juste apr`es : recapitulatif des r`egles, terminaux et non terminaux
Toute la suite : les differents etats de lanalyseur
Etat
davancement dans la reconnaissance des r`egles
Actions possibles (decaler, reduire) selon les symboles reconnus
State 1 contains 1 shift/reduce conflict.
...
state 1
g1 -> A1 . B1
(rule 2)
e1 -> A1 .
(rule 3)
B1
B1
$default
...
&
'
A. syntaxique en Bison
Associativit
e et pr
ec
edence
. Permettent d
eliminer facilement les conflits
%left, %right ou %nonassoc dans les definitions de Bison
Indique lassociativite du ou des lex`emes indiques sur la ligne
Ces lignes sont triees selon les priorites croissantes
%prec pour substituer une priorite dans la grammaire
Les conflits resolus sont tout de meme reportes dans le .output
ex : expressions arithmetiques et logiques
%left OR
%left AND
%right NOT
%nonassoc LT GT LE GE EQ NEQ
%left PLUS MINUS
%left MULT DIV MODULO
%right UMINUS
&
'
A. syntaxique en Bison
Expressions arithm
etiques et logiques 1/3
%token
%token
%token
%token
%token
OR AND NOT
LT GT LE GE EQ NEQ
PLUS MINUS
MULT DIV MODULO
VALUE LP RP
%start expr
%%
expr :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;
expr OR expr
expr AND expr
NOT expr
expr LT expr
expr GT expr
expr LE expr
expr GE expr
expr EQ expr
expr NEQ expr
expr PLUS expr
expr MINUS expr // - binaire
expr MULT expr
expr DIV expr
expr MODULO expr
MINUS expr
// - unaire
VALUE
LP expr RP
$ bison -v prog.y
prog.y contains 195 shift/reduce conflicts.
$
&
'
A. syntaxique en Bison
Expressions arithm
etiques et logiques 2/3
%token PLUS MINUS
%token MULT DIV MODULO
%token VALUE LP RP
%start
%%
expr :
|
|
;
prod :
|
|
|
;
factor
expr
expr PLUS prod
expr MINUS prod
prod
// associativite a gauche
// operations les moins prioritaires
// associativite a gauche
// operations un peu plus prioritaires
: VALUE
| LP expr RP
$ bison -v prog.y
$
&
'
A. syntaxique en Bison
Expressions arithm
etiques et logiques 3/3
%token
%token
%token
%token
%token
OR AND NOT
LT GT LE GE EQ NEQ
PLUS MINUS
MULT DIV MODULO
VALUE LP RP
%left OR
%left AND
%right NOT
%nonassoc LT GT LE GE EQ NEQ
%left PLUS MINUS
%left MULT DIV MODULO
%right UMINUS
%start expr
%%
$ bison -v prog.y
$
&
expr :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;
expr OR expr
expr AND expr
NOT expr
expr LT expr
expr GT expr
expr LE expr
expr GE expr
expr EQ expr
expr NEQ expr
expr PLUS expr
expr MINUS expr // - binaire
expr MULT expr
expr DIV expr
expr MODULO expr
// yylex() ne renvoie
MINUS expr %prec UMINUS // jamais UMINUS !!!
VALUE
// --> subst. de prio.
LP expr RP
'
A. syntaxique en Bison
R
ecup
eration sur erreurs
. Entr
ee non-conforme `
a la grammaire
Appel automatique de yyerror("parse error")
yyparse() se termine en renvoyant 1
La suite du flux dentree nest pas analysee !
. Le symbole sp
ecial error
Permet denrichir la grammaire
Lorsquaucune r`egle ne permet de poursuivre lanalyse
reduire celle qui utilise error
Synchronisation sur le lex`eme qui suit le symbole error
consommation des lex`emes intermediaires
Utilisation de la macro yyerrok; dans le code C
evite la sortie de yyparse() apr`es le message
assign : IDENT ASSIGN expr SEMICOLON { /* ... */ }
| IDENT ASSIGN error SEMICOLON { yyerrok; }
;
&
'
A. syntaxique en Bison
D
emarche usuelle pour la mise en uvre
. Mise au point de lanalyse lexical
Fichier Flex complet : reconnatre tous les lex`emes
Fichier Bison avec une grammaire degeneree
Accepter tous les lex`emes dans un ordre quelconque
Actions semantiques : afficher le lex`eme recu
. Mise au point de lanalyse syntaxique
Grammaire compl`ete du langage a` reconnatre
Actions semantiques : afficher les constructions reconnues
. Mise en place des actions s
emantiques
Construire/calculer les donnees utiles pour lapplication
. Envisager la r
ecup
eration sur erreurs
` effectuer en dernier lieu
Rend la grammaire plus complexe ! A
&