BAZIN Danil et PRIEZ Jean-Baptiste LEX & YACC : Calculatrice Évoluée
Table des matières 1 Introduction 3 2 Description 4 3 La grammaire utilisée 6 4 Lexèmes et FLEX 8 5 Analyse syntaxique et YACC 8 5.1 Approche vue TP et ses limites................... 9 5.2 Arbres abstraits et fonctions récursives............... 10 5.2.1 Arbres abstraits....................... 11 5.2.2 Évaluateur récursifs..................... 12 6 Améliorations envisagées 13 7 Conclusion 14 2
1 Introduction Ce rapport décrit notre projet de compilation d une calculatrice évoluée. Notre calculatrice effectue des tâches simples tel que les additions, soustractions, multiplications et divisions. exemple 1. Voici un exemple d entrée : 4 + 4 3 * 5 4-2 Et notre calculette interprète et effectue les opérations pour transmettre le résultat suivant : Resultat de l expression: 8.000000 Resultat de l expression: 15.000000 Resultat de l expression: 2.000000 Mais elle est aussi capable d utiliser des fonctions plus avancées, tel que l affectation de variables, les instructions conditionnelles si-alors-sinon ou encore faire des itérations tant-que-faire. exemple 2. Voici un exemple d entrée : x = 4 + 4 si (x >= 0) alors { x = 3 * 5 } sinon { x = 4-2 } a = 0 tant que (x > 0) faire { x = x - 1 a = a + 1 } Et notre calculette interprète et effectue les opérations pour transmettre le résultat suivant : $RESULTATS : x = 0.000000 a = 8.000000 3
Elle permet d effectuer d autres fonctionnalités que nous détaillerons au cours du rapport. Nous avons choisi de ne pas trop étoffer les fonctionnalités basiques tournant autour de l addition, soustraction... pour nous concentrer sur des fonctionnalités plus ardues telles que les instructions itératives nécessitant d effectuer la création d arbre abstrait lors de la traduction dirigée par la syntaxe pour exécuter les instructions après l analyse syntaxique. En effet, cet aspect sort du cadre du cours et nous a permis d approfondir nos connaissances en compilation. Nous aborderons donc tout d abord un rapide descriptif de notre application, puis le détail des lexèmes reconnu par flex, ainsi que deux différentes parties développant l analyse syntaxique effectuée par yacc : la première décrivant l utilisation classique que l on peut avoir lorsque les règles sémantiques sont celles d une définition S-attribuée ou L-attribuée, puis la seconde décrivant l utilisation d arbres abstraits et de fonctions récursives nécessaire pour l utilisation de règles ne pouvant s exécuter en parallèle de l analyse syntaxique. 2 Description La super-calcultrice est composée de deux modes : un premier classique qui affiche uniquement les résultats, exemple 3. RESULTATS : x = 22.000000 y = 44.000000 un second qui affiche le détail des résultats de chaque instruction(mode verbose). exemple 4. Resultat de l expression: 44.000000 Resultat de l expression: 10.000000 x = 44.000000 toto = 44.000000 x = 22.000000 RESULTATS : x = 22.000000 toto = 44.000000 4
N importe quelle chaîne de caractère peut être le nom d une variable exceptée certains mots réservés comme les noms de fonctions (sqrt, log, exp) ou les mots définissant les structures autres (tant que,... ). Voici un exemple d exécution de l application en mode interprété comme on peut l avoir sur mathlab ou autre en mode verbose : exemple 5. >elix@kerios:~$./calculatrice -v 12 + 32 44.000000 44-34 10.000000 x = 10 * 4 + 4 x = 44.000000 toto = x toto = 44.000000 x = x / 2 x = 22.000000 RESULTATS : x = 22.000000 toto = 44.000000 >elix@kerios:~$ On peut aussi effectuer les opérations décrites dans un fichier : exemple 6. le fichier test.cal 12 + 32 44-34 x = 10 * 4 + 4 toto = x x = x / 2 al = log (exp (x)) bl = sqrt (4) i = 0 j = 10 TANT QUE ( i > 10 ) FAIRE { i = i + 1 j = j - 1 } et voici l exécution : 5
>elix@kerios:~$./calculatrice < test.cal RESULTATS : x = 22.000000 toto = 44.000000 al = 22.000000 bl = 2.000000 i = 10.000000 j = 0.000000 >elix@kerios:~$ Pour effectuer ces différents calculs nous avons utilisé la grammaire suivante décrite dans la prochaine section. 3 La grammaire utilisée Cette section décrit la grammaire utilisée pour les différents calculs : l addition la soustraction la multiplication la division la fonction exponentiel la fonction logarithme le fonction racine carré En plus de ces aspects de calculs simples, on a ajouté des structures d opérations conditionnelles et itératives. 6
La grammaire est la suivante : LIGNE LIGNE INST EOL LIGNE EOL ERROR EPSILON LDI = Liste d instructions INST EXPR INST = Instruction si ( COND ) alors { COND = condition LIGNE } si ( COND ) alors { LIGNE } sinon { LIGNE } tant que ( COND ) faire INST ITER =Instruction itérative { LIGNE } pour variable de EXPR a EXPR faire { LIGNE } EXPR EXPR + EXPR EXPR - EXPR EXPR * EXPR EXPR / EXPR - EXPR ( EXPR ) variable = EXPR variable fonc ( EXPR ) fonc = représente une fonction ( COND )? EXPR : EXPR nombre COND EXPR > EXPR EXPR >= EXPR EXPR == EXPR EXPR!= EXPR EXPR <= EXPR EXPR < EXPR COND = Expression booléenne La grammaire maintenant définit nous allons maintenant présenter les différents outils (expressions régulières) utilisés pour l analyse lexicale puis ensuite ceux 7
utilisés pour l analyse syntaxique. 4 Lexèmes et FLEX Dans cette section, nous présentons les expressions régulières utilisées par FLEX pour signifier les lexèmes reconnus à YACC. Lex et Flex sont des analyseurs lexicaux spécialement conçus pour être utilisés par Yacc. Nous avons donc utiliser Flex pour reconnaitre l ensemble des lexèmes transmis pour l analyse syntaxique à Yacc. Notre calculatrice reconnait les réelles pouvant prendre les formes suivantes : 2-1.2.2 2e3 1.2E-3... Pour reconnaître ces différentes formes, on a utilisé l expression régulière suivante : (({CHIFFRE}+(\.{CHIFFRE}+)?) (\.{CHIFFRE}+))(eSIGNE?{CHIFFRE}+)? Nous reconnaissons aussi les variables de la forme suivante : [a za Z]+ On note ici que l on reconnait les mots protégés : si, alors, sinon : pour les expressions conditionnelles tant que, faire : pour les expressions itératives sqrt, log, exp,... : pour les différentes fonctions reconnues Une fois les lexèmes reconnus lors de l analyse lexicale, nous passons à l analyse syntaxique effectué par Yacc. 5 Analyse syntaxique et YACC Dans cette section, nous décrivons comment nous utilisons le constructeur d analyseurs syntaxiques Yacc (normalement utilisé pour la production de la partie frontale d un compilateur) pour traiter et effectuer les calculs de notre calculette. Dans un premier temps, nous allons voir les limites de l approche abordée en TP et ensuite déduire du cours une nouvelle approche permettant l exécution d instruction complexe ne pouvant être effectuée en parallèle de l analyse syntaxique grâce à des arbres abstraits. 8
YACC permet la construction d analyseur LALR, c est-à-dire la construction d un analyseur LR tel que vu dans le cours permettant d établir certaines règles de priorité sur les opérateurs comme l associativité à droite ou à gauche. Commençons par établir l ordre de priorité des opérateurs : On va avoir dans l ordre du plus prioritaire au moins prioritaire > / > + > >? >=. Associé au priorité, on a aussi attribué à nos opérateurs une valeur d associativité gauche-droite comme suit : les opérateurs?, = sont associatifs à droites et les autres à gauche. Avec une petite exception dans le cas EXPR qui donnera à l opérateur une associativité droite. 5.1 Approche vue TP et ses limites Prenons une instruction simple(extraite de notre grammaire) tel que x = y+4 où y vaut 4, on va avoir l analyse ascendante suivante : PILE ENTRÉE SORTIE x = y +4$ x = y +4$ x = y +4$ x = y +4$ EXPR variable x = EXPR+ 4$ x = EXPR+4 $ EXP nombre x = EXPR+EXPR $ EXPR EXPR + EXPR x = EXPR $ EXPR EXPR EXPR $ INST EXPR INST $ LIGNE INST LIGN E $ Une manière simple d effectuer la traduction dirigée par la syntaxe de ces instructions serait de faire de la manière suivante : PRODUCTION RÈGLE SÉMANTIQUE...... EXPR EXPR 1 + EXPR 2 EXPR.val EXPR 1.val + EXPR 2.val...... EXP R variable = EXP R creerv ariable(variable, EXP R.val)...... En effet, si l on traite des instructions de ce type, il n y a pas de problème. Mais si l on souhaite traiter des instructions tel que celle représenté par l arbre suivant : 9
... INST tant que COND faire { LIGNE } ( EXPR < EXPR ) INST a b EXPR a = EXPR EXPR a + EXPR b Les instructions sont effectuées au fur et à mesure de l analyse ascendante donc l instruction a = a+b n est effectuée qu une seule et unique fois. En effet, ce type d instruction sort du cadre des définitions S-attribuées et L-attribuées. Pour cela, nous avons recréé l arbre de l expression pour effectuer à postériori l exécution des instructions. En effet, on va avoir la table d analyse suivante : PILE ENTRÉE SORTIE tant que(a < b) faire a = a+b$ tant que(a < b) faire a = a+b$...... tant que(cond) faire $ INST tant que(cond) {LIGN E} f aire{lign E}... $... On voit clairement que l instruction d affectation est effectué sans possibilité de retour en arrière pour exécuter d y retourner tant que la condition n est pas vérifié. 5.2 Arbres abstraits et fonctions récursives Pour résoudre ce problème inhérent aux analyses ascendantes nous avons choisi de créer l arbre abstrait de nos instructions à partir d une définition S- attribuée nous permettant à la suite de l analyse syntaxique d effectuer une 10
analyse descendante de l arbre (abstrait) pour effectuer nos calculs (ce qui va représenter un parcours en profondeur de notre arbre). Pour ce faire, on va transformer nos règles sémantiques comme cela : PRODUCTION RÈGLE SÉMANTIQUE...... EXPR EXPR 1 + EXPR 2 EXPR.nœud creernœud( +,EXPR 1.nœud,EXPR 2.nœud)...... EXPR variable = EXPR EXPR.nœud creernœud( AFF,variable.nœud,EXPR.nœud)...... De cette manière on passe d un calcul découlant immédiatement de la traduction dirigée par la syntaxe, à la construction de l arbre (avec une définition S-attribuée) de notre phrase qui est suivi par le calcul lors de l analyse descendante de notre arbre. En reprenant l exemple précédent, au cours de notre parcours de l arbre on va tomber sur l arbre abstrait suivant : tant-que-faire expression-booléenne file-instructions Ayant maintenant une analyse descendante, il devient aisé d exécuter les instructions LIGNE tant que COND est vrai. Ainsi contrairement à ce que l on a étudié en TP, l exécution d instructions va s effectuer en deux temps : le premier temps va permettre de créer des espaces mémoires pour stocker des valeurs (temporaires) et de définir l emplacement des variables de l application, tout en créant un arbre abstrait de chaque instruction qui est stockée dans un arbre. le second temps est l exécution de chaque instruction de l arbre. Définissons formellement les arbres abstraits : 5.2.1 Arbres abstraits Définition 1. Un arbre abstrait est une forme condensée d arbre syntaxique, adaptée à la représentation des constructions d un langage. La production I si B alors I 1 sinon I 2 va alors apparaître dans un arbre abstrait sous la forme : 11
si-alors-sinon B I 1 I 2 Dans un arbre abstrait, les opérateurs et les mots clés n apparaissent pas comme des feuilles, mais sont plutôt associés au nœud intérieur qui serait le père de ces feuilles dans l arbre syntaxique. La construction d un arbre abstrait pour une expression est semblable à la traduction de l expression en forme postfixée. Chaque nœud d un arbre abstrait peut être implanté sous la forme d une structure à plusieurs champs. Dans le nœud correspondant à un opérateur, les nœuds correspondent aux opérandes. L opérateur est souvent dénommé l étiquette du nœud. On utilise les fonctions créernœud et créerfeuille pour des expressions avec des opérateurs binaires (chaque fonction retourne un pointeur vers le nouveau nœud créé) CréerNœud(op, gauche, droit) crée un nœud opérateur d étiquette op avec deux champs contenant des pointeurs vers gauche et droit. CréerFeuille(id, entrée) crée un nœud identificateur d étiquette id avec un champ contenant entrée, un pointeur vers l entrée de l identificateur dans la table des symboles. CréerFeuille(nb, val) crée un nœud nombre d étiquette nb avec un champ contenant val, la valeur du nombre. Pour construire les arbres abstraits, on a construit une définition S-attribuée dirigée par la syntaxe sur la grammaire définie précédemment tout comme présenté dans l exemple précédent. Unefoislesarbresabstraitscréésetmisdanslafiled exécution,ilfautpouvoir évaluer les expressions, pour cela on définit des fonctions récursives similaires aux évaluateurs récursifs utilisés dans les définitions S/L-attribuées lors de l analyse syntaxique. 5.2.2 Évaluateur récursifs Dans cette partie, on définit les fonctions récursives pour évaluer les arbres abstraits définis dans l arbre. 12
1 2 3 4 tant que estfilevide faire traite nœud(sommet()); dépiler(); fin Algorithme 1 : Algorithme d évaluation de la pile 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 si est feuille(droit) alors tmp 1 traite feuille(droit) sinon tmp 1 traite nœud(droit) fin si est feuille(gauche) alors tmp 2 traite feuille(gauche) sinon tmp 2 traite nœud(gauche) fin retourner op(tmp 1,tmp 2 ) Algorithme 2 : Algorithme de traitement d un nœud si identif icateur alors retourner id.val sinon retourner nb.vallex fin Algorithme 3 : Algorithme de traitement d une feuille 6 Améliorations envisagées Notre projet nous a amené à stocker l intégralité de l arbre créé lors de l analyse syntaxique. Une des améliorations qui serait à envisager pour améliorer celui-ci serait d activer un drapeau lorsque l on voit des lexèmes tel que si, tant que,... pour ne stocker que les sous-arbres jusqu à réduction dans l analyse ce qui exécuterait alors les instructions. En effet, l exécution d un gros fichier risque de prendre beaucoup de place en mémoire. D autre part, notre gestion des accès en mémoires est très brutes sans optimisations de temps ni d espaces. Pour remédier à cela, une implantation d un automate-arbre(trie) permettrait un accès rapide aux variablex stocké en mémoire. (inspiré des structures de recherche d Aho-Corasick). 13
7 Conclusion Notre super-calculette, nous a permis de créer des structures permettant de stocker des arbres et surtout nous a appris à utiliser les analyseurs lexical et syntaxique que sont FLEX et YACC. 14