Post on 22-Jun-2020
TRADUCTION DIRIGÉE PAR LA
SYNTAXE
IFT580 – Compilation et interprétation des langages
1
Le traducteur
Analyseur
lexical
Analyseur
syntaxique
Génération
de code
Table des
symboles
Code
source Unité
lexicale
Arbre
syntaxe
Code
intermédiaire
2
Le traducteur
3
{ int i; int j; float[100] a; float v; float x; while ( true ) { do i = i+1; while ( a[i] < v ); do j = j-1; while (a[j] > v ); if ( i >= j ) break; x = a[i]; a[i] = a[j]; a[j] = x; } }
1: i = i + 1 2: t1 = a [ i ] 3: si t1 < v allerà 1 4: j = j – 1 5: t2 = a [ j ] 6: si t2 > v allerà 4 7: siFaux i >= j allerà 9 8: allerà 14; 9: x = a [ i ] 10: t3 = a [ j ] 11: a [ i ] = t3 12: a [ j ] = x 13: allerà 1 14:
Le traducteur • La partie frontale d’un compilateur identifie les unités
élémentaires d’un programme source et les transforme en
une représentation intermédiaire
• Cette représentation intermédiaire sera ensuite traduite
vers le langage cible équivalent, en appliquant des
optimisations
4
Le traducteur • La partie frontale, appelée « analyse » est organisée
autour de la syntaxe
• La syntaxe décrit ce à quoi le programme ressemble
• La sémantique décrit ce que le programme veut dire
5
Le traducteur • La syntaxe est décrite à l’aide d’une grammaire hors
contexte, en utilisant la forme de Backus-Naur (BNF en
anglais)
• La sémantique est plus complexe à représenter avec un
formalisme précis
• Utilisation de descriptions informelles
• Utilisation d’exemples
6
Le traducteur • Il y a plusieurs formalismes utilisés dans chacune des
étapes
• Expressions régulières pour l’analyse lexicale
• Grammaire hors contexte pour l’analyse syntaxique
• Grammaire attribuée pour l’analyse sémantique
• Ajout d’attributs aux symboles
• Ajout de règles sémantiques aux règles de production
7
Le traducteur
• La grammaire définit la syntaxe
• Elle peut aussi être utilisée pour diriger la traduction
• C’est faire une « traduction dirigée par la syntaxe »
8
Le traducteur
• Nous verrons
• L’analyse syntaxique
• L’analyse lexicale
• La production de code intermédiaire
• Arbre de syntaxe abstraite (arbre de syntaxe)
• Séquence d’instruction à 3 adresses
9
Grammaire hors contexte
• Une grammaire (G) est définie par
• Un ensemble fini de variables (V)
• Un ensemble fini de symboles terminaux (T)
• Un ensemble fini de règles de production (P)
• Un axiome de départ (S)
G = (V, T, P, S)
10
Grammaire hors contexte
expr → expr + terme
expr – terme
terme
terme → terme * facteur
terme / facteur
facteur
facteur → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
( expr )
11
Arbre d’analyse
12
expr → expr + terme
expr – terme
terme
terme → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
expr
expr + terme
expr - terme
terme
9
5
2
9-5+2
Traduction dirigée par la syntaxe
• Il faut attacher des règles sémantiques ou des fragments
de code aux règles de production d’une grammaire
• Les règles sémantiques sont définies par
• Des attributs
• Des schémas de traduction
13
Définition dirigée par la syntaxe (DDS)
• Une définition dirigée par la syntaxe associe
• Un ensemble d’attributs à chaque symbole grammatical
• Un ensemble de règles sémantiques à chaque règle de production
pour calculer les attributs des symboles de la règle
• Un arbre d’analyse qui contient des valeurs d’attributs sur
chaque nœud est dit « décoré »
14
Définition dirigée par la syntaxe • Chaque nœud d’un arbre d’analyse possède des attributs
• Un attribut est une quantité ou une information
quelconque
• Il y a deux types d’attributs
• Les attributs hérités
• Les attributs synthétisés
15
Définition dirigée par la syntaxe • Les attributs hérités
• C’est un attribut dont la valeur est déterminée par la valeur des
autres attributs du nœud, de ceux de son père et ceux de ses
frères
• Les attributs synthétisés
• C’est un attribut dont la valeur est déterminée par la valeur des
attributs de ses fils et de lui-même
16
Définition dirigée par la syntaxe
17
Règle de production Règle sémantique
expr → expr + terme expr.t = expr.t || terme.t || ‘+’
expr → expr – terme expr.t = expr.t || terme.t || ‘-’
expr → terme expr.t = terme.t
terme → 0 terme.t = ‘0’
terme → 1 terme.t = ‘1’
… …
terme → 9 terme.t = ‘9’
expr.t = 95-2+
expr.t = 95- + terme.t = 2
expr.t = 9 - terme.t = 5
terme.t = 9
9
5
2
9-5+2
Schéma de traduction
• Une autre façon de faire la traduction, sans utiliser
d’attributs, est d’exécuter des bouts de programme
lorsqu’on applique une règle
• Un schéma de traduction dirigé par la syntaxe associe
des fragments de programme aux règles de production de
la grammaire
18
Schéma de traduction
• Les fragments de programme sont appelés « actions
sémantiques »
• La position du fragment indique à quel moment l’action
doit être exécutée
• L’action est écrite entre accolades
19
Schéma de traduction
20
Règle de production
expr → expr + terme {print(‘+’)}
expr → expr – terme {print(‘-’)}
expr → terme
terme → 0 {print(‘0’)}
terme → 1 {print(‘1’)}
… … …
terme → 9 {print(‘9’)}
expr
expr + terme
expr - terme
terme
9
5
2
9-5+2
{print(‘9’)}
{print(‘5’)}
{print(‘-’)}
{print(‘+’)}
{print(‘2’)}
L’analyseur syntaxique • L’analyseur syntaxique a été vu en partie en IFT313
• Vous avez vu comment effectuer des dérivations et
obtenir l’arbre de dérivation pour une chaîne en entrée
• Vous n’avez pas vu comment utiliser cette information
pour effectuer une traduction et faire une analyse
sémantique du code
21
Analyse syntaxique • Il y a deux façons d’utiliser une grammaire hors contexte
pour analyser un texte
• Analyse ascendante
• Analyse descendante
22
Analyse syntaxique • Dans l’analyse descendante, on part de l’axiome, et on
applique les règles de production pour se rendre à la
phrase recherchée
• Dans l’analyse ascendante, on part de la phrase, et on
réduit en appliquant les règles de production pour
retrouver l’axiome original
23
Analyse descendante (Exemple) expr → expr + terme
expr – terme
terme
terme → terme * facteur
terme / facteur
facteur
facteur → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
( expr )
24
Analyse descendante • Pour faire une analyse syntaxique par descente récursive,
il faut habituellement modifier la grammaire sous-jacente
• Entre autres, il faut éliminer la récursivité à gauche dans
les règles de production
• Il faut éliminer toute ambiguïté
25
Analyse ascendante
• On définit des ensembles d’item pour la grammaire
• Nous verrons en détail au prochain chapitre comment
faire
26
L’analyse lexicale • L’analyse lexicale a été vue en IFT313
• Nous ne reverrons pas comment écrire un analyseur lexical
• Vous devrez relire vos notes si vous ne vous en rappelez plus
• Le chapitre 3 du livre du cours explique comment fonctionne un
analyseur lexical
• La dernière section du chapitre 3 aborde l’outil LEX. Vous devez le
maitriser. Il n’y aura aucun exercice ni exemple en classe!
27
L’analyse lexicale • Dans les exemples précédents, les unités lexicales
n’étaient que des symboles à un caractère, sans espace,
et pouvant être lu et interprété directement par l’analyseur
lexical
• Dans la réalité, ce n’est naturellement pas le cas
• Il faut aussi pouvoir éliminer les espaces blancs inutiles
28
L’analyse lexicale
• L’analyseur lexical produit des unités lexicales
• Une unité lexicale est sous la forme suivante
‹symbole, valeur d’attribut›
29
L’analyse lexicale • Habituellement, les grammaires vont gérer les
identificateurs ou les nombres comme des symboles
terminaux (id, nbr, etc.)
compteur = compteur + increment;
devient
‹id, ‘compteur’› ‹=› ‹id, ‘compteur’› ‹+› ‹id, ‘increment’› ‹;›
30
L’analyse lexicale
• Il y a un problème pour reconnaître un identificateur d’un
mot-clé du langage
• Habituellement, les mots-clés sont des identificateurs
réservés
31
L’analyse lexicale • Lors de la lecture, l’analyseur vérifie si le lexème est déjà
présent dans la liste des identificateurs
• Si c’est le cas, il retourne l’entrée de la liste des mots connus
• Sinon, il en crée une nouvelle avec le type ID
• Les mots-clés doivent être ajoutés au début de l’analyse
dans la table des identificateurs connus
32
Table des symboles
• La table de symboles est une structure de données qui
conserve de l’information sur les structures contenues
dans le code source
• Comme la plupart des langages supportent le concept de
portée, il y aura une table de symboles par portée
33
• Chaque table à une référence vers la table de la portée
englobante
Table des symboles
34
{ int i = 0; { int j = 0; { int i = 2; } } }
i int 2
…
j int 0
…
i int 0
…
Table des symboles • Lorsqu’on veut savoir si un lexème a déjà été défini, on
regarde dans la table de symboles courante
• S’il n’est pas présent, on regarde dans la précédente, et
ainsi de suite
• Ça permet d’avoir toujours la définition la plus proche en
cas de redéfinition
35
Table des symboles • Mais qui s’occupe d’ajouter les informations dans les
tables de symboles?
• L’analyseur lexical lorsqu’il voit un nouveau mot?
• L’analyseur syntaxique lorsqu’il applique une règle
particulière?
36
Production de code intermédiaire • Il y a deux représentations intermédiaires principales
• Les arbres (arbre d’analyse ou arbre de syntaxe abstraite)
• Les représentations linéaires (code à trois adresses)
• Nous avons abordé la représentation en arbre
• La représentation linéaire est nécessaire pour
l’optimisation
37
Production de code intermédiaire • La partie frontale du compilateur s’occupe de créer la
représentation intermédiaire
• Elle valide que le code source suit les règles syntaxiques
• Elle s’occupe aussi de faire la validation sémantique
• C’est la vérification statique On obtient les erreurs!
38
Production de code intermédiaire • Le compilateur peut émettre le code à trois adresses en
même temps qu’il construit l’arbre abstrait
• Mais pourquoi faire l’arbre au complet si le code a déjà été émis?
• Habituellement, l’analyseur syntaxique ne garde que la partie de l’arbre dont il a besoin pour écrire une partie du code à la fois
39
Vérification statique • Vérification syntaxique
• Unicité de la déclaration d’un identificateur dans une portée
• Identificateur déclaré avant utilisation
• Un énoncé break est seulement présent dans un switch ou une
boucle
• Etc.
• C’est plus que juste suivre les règles de la grammaire
40
Vérification statique • Contrôle de types
• Que faire lors de l’addition d’un entier et d’un réel?
• S’assurer que les paramètres d’une fonction sont du bon type
• Etc.
• Il faut garder une liste des types disponibles
• La conversion de type se nomme coercition
41
Vérification statique • Il y a une différence entre un identificateur à gauche ou à
droite du symbole d’affectation
• lvalue
• rvalue
i = 5;
i = i + 2;
42
Traduction des instructions • Utilisation du code à trois adresses
• Différentes formes • x = y op z
• x [ y ] = z
• x = y [ z ]
• siVrai x allerà E
• siFaux x allerà E
• allerà E
• x = y
43
Traduction des expressions
44
if
< =
a 4 b +
code calculant
expr et le
rangeant dans x
siFaux x allerà E
code calculant
instr
E: 1 b
t1 = a < 4
x = t1
t2 = b + 1
b = t2
Résumé • C’est seulement un survol du fonctionnement de la partie
frontale d’un compilateur
• Les prochaines semaines approfondiront ces techniques
• Dans le chapitre 2, vous avez des exemples d’implémentation pour un traducteur simple
• Seriez-vous capable d’étendre le langage fourni?
45