L'objectif de ces séances de travaux pratiques
de compilation est de se familiariser aux commandes
flex
et
bison,
les
deux outils de compilation par défaut sur les systèmes
unix
depuis plusieurs décennies ( déjà !). Le premier outil
flex
(version
gnu
de
la commande lex) construit un
analyseur lexical à
partir dun ensemble de règles/actions décrites
par des expressions régulières. Le second outil
bison
est
un compilateur de compilateur, version gnu de la célèbre
commande yacc
acronyme de « yet another compiler of compilers
». Il construit un compilateur d'un langage décrit par un
ensemble de règles et actions d'une grammaire LARL
sous une forme proche de la forme BNF de Backus-Naur.
[ manuel flex ] [ manuel bison] [ lex/yacc ] [ lex/flex ] [ yacc/bison ]
[ 1 ] Identifiez bien le rôle des différents lexèmes, les priorités, les ordres d'associations.
[ 2 ] Ecrire une fonction récursive int execute( char * prog) qui exécute la suite de calculs spécifiée par une chaine de caractère prog. Notez bien que pour cette approche, une analyse descendante est préférable. La fonction utilisera une autre fonction récursive int evaluation( char * exp) qui calcule la valeur d'une expression.
Nous allons faire la même chose avec bison. On commence
par quelque chose de trés élémentaire : le langage
des expressions (non parenthésées) construites
à partir de trois symboles terminaux NB,
PLUS,
FOIS,
un symbole non-terminal EXP
et
des règles
EXP := NB | EXP PLUS EXP | EXP FOIS EXP
Pour construire un analyseur syntaxique avec
la commande bison on édite un fichier de suffixe .y, disons
fichier mini.y incluant la définition d'un analyseur lexical,
obligatoirement identifié par yylex(),
la description des lexèmes ( tokens ) et des règles.
Les actions sémantiques écrites en langage C entre accolades
calculent l'attribut du père, représenté
par $$, en fonction des attributs des fils de gauche à droite
représentés par $1, $2, ...
%{int yylval ;%}
%token NB PLUS FOIS FIN
%start AXIOME
%%
AXIOME : EXP FIN { printf("%d", $1 ) }
EXP : NB { $$ = $1 }
int yylex( ) {
int car ;
car = getchar() ;
if ( car == EOF ) return 0 ;
if ( isdigit(car) ) {
yylval = car - '0';
return NB;
}
switch ( car ) {
case '+' : return PLUS;
case '*': return FOIS;
case '=': return FIN;
}
%left PLUS%left FOIS
bison mini.y
et préférablementgcc mini.tab.c -o lysa
gcc -Wall mini.tab.c -o lysa
[ B] Ajouter
des règles syntaxiques pour effectuer des calculs séparés
par des « ; »
On souhaite manipuler des suites d'instructions un peu plus complexes à base de 26 registres mémoires A, B, ..., Z. Pour écrire des programmes comme ci-dessous :
A := 3 ; B := 5*6 ; ( A- B) *(A+B) ;
[ C ] Ajouter
un lexème MEM dont les attributs possibles seront A, B, ... dans
l'analyseur lexical. Incorporer les règles syntaxiques pour gérer
les expressions contenant des variables.
[ E ] Ajouter
un lexème AFFECT
. Incorporer les règles syntaxiques pour reconnaître les affectations
MEM
:= EXP.
[ F ] Modifier les actions sémantiques pour afficher l'arbre syntaxique de l'expression.