Upload
jehan-chauvet
View
116
Download
0
Tags:
Embed Size (px)
Citation preview
IHDC 2105IHDC 2105Syntaxe et sémantique desSyntaxe et sémantique des
langages de programmationlangages de programmation
Pierre-Yves Schobbens
Bureau 409081/72 49 90rue Grandgagnage 215000 Namur
Plan
0. Introduction
1. Analyse lexicale : Langages réguliers
2. Analyse syntaxique : Langages non contextuels
3. Analyse statique (p.ex. typage): Grammaires attribuées
4. Eléments de génération de code
5. Introduction à la sémantique
Références
• Grune, Bal, Jacobs, Langendoen « Compilateurs », Dunod, 2002.
• Aho, Sethi, Ullman : « Compilateurs : principes, techniques et outils », InterEditions, 1998, BUMP # 1 412/037
• R. Wilhem, D. Maurer : « Les compilateurs : théorie, construction, génération », Masson, 1994, BUMP # 1 412/030
Chapitre 0. Introduction
Types de langages
• Les techniques de ce cours s’appliquent à:– des langages de programmation : Pascal, Ada, …
– des langages de commande : JCL, Sh, Csh, …
– des langages de description de données: SQL (DDL), XML, ...
– des langages de spécification : VDM, Z, Albert, ...
– des langages de formulaires : EDIFACT, CGI (WWW), ...
– des formats de messages (réseaux)
– des langages de documents : EQN, HTML,
Paradigmes de langages de programmation
1. impératifs : Algol, Pascal, Ada, Fortran, Cobol, Modula, Caxés sur l’affectation
2. fonctionnels : Lisp, ML, Hope, Miranda, Haskell, FPbasés sur l’évaluation des expressions
3. logiques : Prolog, Gödelbasés sur la preuve de formules
4. orientés objet : Simula, Smalltalk, C++, Eiffel, CLOS, Javabasés sur l’héritage
N.B. : Les paradigmes ne sont pas mutuellement exclusifs.
Exécution des langages
2 extrêmes
1. Les interpréteurs - lisent le programme au fur et à mesure les programmes peuvent être créés dynamiquement
2. Les compilateurs - traduisent l’ensemble du programme soit - en langage d’une machine concrète 486, P6, Power ou - en langage d’une machine abstraite SECD, WAM, JVM,
P-MACHINE
Rôle d’un compilateur
compilateurCode source Code cible
Code machine du compilateur
compilateurd’implémentation
Générateur de compilateur(Flex, Bison)
Code d’implémentation du compilateur
Spécification
Structure d’un compilateur
1. analyse lexicale
2. crible
3. analyse syntaxique
4. analyse sémantique
5. optimisations
6. allocation mémoire
7. génération de code
8. optimisations propres à la cible
avan
tar
rièr
e
reconnaître les « mots »
reconnaître les « phrases »
vérifier les types, etc.
élimination de code inutile, etc.
choisir la place des variables
instructions machine
séquence plus efficaces
D25
Ex. compilateurs GNU
CANSI
68000 Pentium Power
Ada95
C++ PascalFront-ends (1-4)partie avant
Middle-end (5)
Back-ends (6-9)partie arrière
Outils pour les langages
• Editeurs structurels : édition intelligente
• Analyseurs : p.ex. pour la rétro-ingénierie, Y2K
• Paragrapheurs : mettent en page
D17
Chapitre 1 : Analyse Lexicale
Rôle : retrouver les mots qui composent le texte
Place de l’analyse lexicale
Analyseurlexical
Texte source= caractères
« mots » = lexèmes
générateurd’analyseur
lexical
Expressions régulières
Automate fini
D104-108
Plan
1. Langages et expressions régulières
2. automates finis : Kleene 2.1 non-déterministe 2.2 déterministe 2.3 minimal
3. Régulier ou pas?
Définitions
: alphabet, ensemble de caractères (donné, fini)ex. : ASCII, EBCDIC, ISO-Latin-1, UniCode, ...
texte, mot, phrase : suite de caractères
Langage : ensemble de phrases
Note : convient pour les langages séquentiels (parlé, écrit) mais pas pour des langages graphiques
: phrase vide . : concaténation (parfois le . est omis)
D112
Propriétés
• x . = x = . x neutre• x . (y. z) = (x . y) . z associativité• Ces propriétés sont complètes
D113
Ordres
• X est un préfixe de Y : Y commence par X– « 12 » est un préfixe de « 1234 »
• X est un suffixe de Y : Y finit par X– « 34 » est un suffixe de « 1234 »
• X est une sous-chaîne de Y : X s’obtient par suppression d’un suffixe et d’un préfixe– « 23 » est une sous-chaîne de « 1234 »
D113
Opérations sur les langagesNotation Définition Nom
L1 L2 = {p p L1 ou p L2} union
L1 . L2 = {x . y x L1 et y L2} concaténation
C(L) = {x x est une phrase sur et x L} complément
Ln = L . L . L . … . L exponentielle
L0 = {} ou puissance
L* = n
0 L n étoile de Kleene
a = {"a"} singleton
= { } ( !) langage vide
= {} mot vide
D114
Propriétés
D117
Expressions régulières= les expressions écrites avec
souvent omisaussi noté ou +
Langage régulier = langage décrit par une expression régulière
D115
Exemplesd’expressions régulières
1. < constante décimale > = 0 | (1| 2 | 3 | 4 | 6 | 7 | 8 | 9) . (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9)*
une constante décimale de C est, soit un 0 seul, soit un chiffre significatif suivi d’autant
0 1 n’est pas une constante décimale en C, mais octale.1 0 est une constante décimale.
2. < multiples-de-2-en-binaire > = (0 | 1)* . 0
3. < identificateur > = (a | b | ... | z). (a | b | ... | z | 0 | ... | 1)*
Abréviations
}Ln = L. L. ... L où n est un naturel n
n fois
L+ = L L*
[a - z] = tous les caractères entre a et z dans le code ASCII = les lettres = a b ... z
L? = L
L[n-m] = Ln Ln+1 ... Lm
2. Automates finis
b a e
caractère courant
mot à reconnaître
état finalou accepteur
q4q3q2q1q0
état initial état couranta
c
b a b
D135
Définition
Un automate fini (non-déterministe avec transitions vides) se compose de :
D136
Fonctionnement
• Un automate accepte un mot m si on peut trouver un chemin c:– Qui commence dans l’état initial
– Qui finit dans un état final
– Qui suit des transitions
– La concaténation des étiquettes des transitions donne m
• Les mots acceptés forment le langage de l’automate L(A)
D137
Exemple: constante naturelle décimale de C
[1-9]
0
[0-9]
Exemple : multiples de 2 en notation binaire
0
0
1
Multiples de 2 en notation binaire :Ils se terminent toujours par 0.Ceci est facile à représenter par unautomate non-déterministe
Exemple : identificateurs de Pascal
a
a
z
z
0
9
notation : [a-z]
[a-z 0-9]
Exemple : Multiples de 7
r sa
Principe: r est le reste par 7 du nombre lu (n)m = n * 10 + a donc s = (r * 10 + a) mod 7
Exemple : Multiples de 7
Théorème de Kleene
Un langage est régulier
ssi
c’est le langage d’un automate fini
Preuve : La preuve est donnée par 2 algorithmes
1. qui construit un automate pour chaque expression régulière
2. qui construit une expression régulière pour chaque automate fini
en gardant le même langage.
Expression automatecas de base
début
(a) Automate pour
(b) Automate pour .
début
(c) Automate pour le caractère "a".
début a
Expression automate:union
Pour R1
Pour R2
début
(a) Construction de l’automate d’union pour deux expressions régulières
Expression automate:concaténation
Pour R1 Pour R2début
(b) Construction de l’automate de concaténation pour deux expressions régulières
Expression automate:répétition
Pour R1début
(c) Construction de l’automate de fermeture pour deux expressions régulières
Exemple : constante décimale de C
0
1 2
F
3
4
5
1
9
...
0
9
0
Problèmes
• 1. L’automate contient des -transitions• 2. Il n’est pas déterministe: moins efficace• 3. Il y a beaucoup trop d’états (80 dans l’exemple)
Automate déterministe
• Un automate fini est déterministe, intuitivement s’il ne peut se comporter que d’une façon pour un mot donné :– Il n’a pas de transition-– Étant donné un état de départ et un caractère lu, il y a
au plus 1 état d’arrivée
• Il est complet si, étant donné un état de départ et un caractère lu, il y a au moins un état d’arrivée
D138
Idée de la déterminisation
début 1 2 ... k S1, S2 ,..., Sk
Dans l’automate déterministe:Un seul chemin vers un état qui représentetous ceux où une copie peut être.
début 1 2 ... k S1
1 2 ... k S2
1 2 ... k Sn
...
Dans l’automate non déterministe:Plusieurs chemins étiquetés
Déterminisation
On construit un nouvel automate A’
Note : on peut diminuer le nombre d’états en ne considérant que les états accessibles.Notation : puisque est fonctionnelle pour les automates déterministes, on emploie une notation fonctionnelle
- De même alphabet- nouvel état = ensemble d’anciens états- état initial = états accessibles de q0 par des transitions vides- état final = état contenant un ancien état final- transition = ensemble des états où une copie peut arriver
S,a) = succs( {p | p q, q S} )
D140
a
Exemple : constantes décimales de CQuelques ensembles fermés par
0
1 2 F
3
4
5
1
9
...
0
9
État initial
0
Constantes décimales de C déterminisé
0
1
9
0
9
0
9
0
0
9
ExempleReconnaître une suite de 1, 2, 3 où le dernier chiffre est apparu précédemment.Expression régulière : * (1 * 1 | 2 * 2 | 3 * 3), où
Automate : 0 2
3
2 2
1 1
33
1
4
1,2,3
1,2,3
1,2,31,2,3
Exemple : déterminisation
3
0 2
1
3
0,1
1/2
0,2
0,3
0,1,4
0,1,2
0,1,3
0,2,4
0,2,3
0,3,4
2
1
2
31
2
12
3
3
2 1/2
3
3
1
1
2/3
1
1/3
2
3
1
3
0,12,4
0,12,3
0,13,4
0,23,4
1/3
2/3
1/2/3 1/2/3
2
1
0,12,3,4
Intuition : Les états marqués 1 (ou 2, 3) signifient «on a rencontré un 1 (ou 2,3)». L’état 4 signifie : on a rencontré un 2° caractère identique, donc on peut terminer.
Algorithme de minimisation
retirer les états non accessiblescompléter l’automateIdée : On met les états dans des classes différentes quand on voit qu’ils ont un comportement différent.
répéterpour chaque K, K’
:= ( \ {K}) {{K i}}
où {Ki} est la partition de K la plus grosse
tous les états de Ki arrivent dans la même classe pour chaque caractère
jusqu’à ce que ne change pas
: partition courante les états sont groupés en classes := {F, Q \ F} au départ, on crée deux classes: les finaux et les non-finaux
Fusionner les états d ’une même classeRetirer l’état mort
Exemple de minimisation« Constante décimale de C »
0
1
9
0
9
0
9
0
0
9
Non finaux
Finaux
Minimisation du nombre d’états
: partition des états
9
0
Exemple de minimisation« Constante décimale de C »
0
1
9
Fusion d’états
: partition des états
Kleene (2) automate fini expression régulière (1)
1° étape : automate fini jeu d’équations
pour chaque état
on crée une équation : S3 = a . S4 b . S5
S3 représente l’ensemble des chaînes qu’on peut lire en
partant de S3.
Note: ces équations ont plusieurs solutions, c'est la plus petite qui nous intéresse.
S3 S4
S5
a
b
si S3 est final
automate fini expression régulière (2)
2° étape : résolution des équations
où S3 n’apparaît pas dans les expressions x, y.
S3 = x y . S 3 ~> S 3 = y* x
Exemple
0-9
1-9
0S0 S1
S2
1° étape: l ’automate est équivalent aux équations
S0 = 0 . S1 | (1 | 2 | … | 9) . S2
S1 =
S2 = | (0 | 1 | ... | 9) . S2
Exemple (2)
2° étape : résolution par triangulation
1) éliminer S1 :
S0 = 0 | [1-9] . S2
S2 = | [0-9] . S2
2) résoudre S2 :
S2 = [0-9]* .
3) éliminer S2 :
S0 = 0 | [1- 9] . [0-9]*
Complémentation
• On calcule un automate qui accepte le complément de A.
• Algorithme:1. Déterminiser A;
2. Le compléter:1. Ajouter un état mort;
2. Ajouter des transitions vers cet état (si car. manque).
3. Propriété: chaque chaîne amène dans un seul état.
3. Inverser les états finaux et non-finaux.
Exemple de complémentation
Automate déterministe
complété : il y a une flèche pour chaque caractère de = [0-9]
[0-9]
[1-9]
0S0
[0-9][0-9]
Négation : on intervertit les états finaux
[0-9]
[1-9]
0S0
[0-9][0-9]
S3S1
S2
S1
S2
Langages non réguliers• Un langage est régulier ssi un programme peut le lire
(de gauche à droite) en ne mémorisant qu’une information finie.
• Lemme de pompage: Une chaîne plus longue que le nombre d’états de l’automate contient une boucle qu’on peut répéter.
si k est le nombre d’étatsune chaîne plus longuedoit faire une boucle.
y
z
x
D119
Exemple
• Le langage {0n 1n } n’est pas régulier :– Il faut retenir le nombre de 0, qui peut être grand :
l’information n’est pas finie.– S’il était régulier, on aurait un automate; Soit k son nombre
d’états. 0k 1k doit passer par une boucle :• Si la boucle ne contient que des 0, on accepte des chaînes avec plus
de 0 que de 1.• Si la boucle ne contient que des 1, on accepte des chaînes avec plus
de 1 que de 0.• Si la boucle contient des 0 et 1, alors on accepte des chaînes où 0 suit
1.
Chapitre 2 : Analyse Syntaxique
Rôle : L’analyseur syntaxique retrouve la structure (arbre) grammaticale du programme.
Place de l’analyse syntaxique
Analyseursyntaxiquemots Arbre
générateurd’analyseursyntaxique
Grammaire non contextuelle (BNF)
Automate à pile
Analyseurlexical
2.1 Grammaire non contextuelle (BNF) règle, production
déf : une production non-contextuelle se compose de:
• un non-terminal
• une suite de symboles
Ex. : < instruction > : : =
if < expr > then < instruction > else < instruction >
La règle pour un non-terminal A regroupe toutes les productions de A:
Ex. : < instruction > : : =
if < expr > then < instruction > else < instruction > | while < expr > do < instruction > | <variable> := <expr> | …
non terminal
terminal
Grammaire non contextuelle
une grammaire a 4 éléments:
• l’ensemble des symboles terminaux VT
• l’ensemble des non-terminaux VN
• l’ensemble des productions P
• le symbole de départ S (non-terminal)
finis
BNF : Exemple : PascalExemple
Pour Pascal, on reprend :
VT = les symboles reconnus lors de l’analyse lexicale :
mots-clés. Ex. if, identificateurs. Ex. x, ...
VN = les catégories syntaxiques :
Ex. : < programme >, < instructions >, < expr >
P = les productions de la grammaire Ex. : < instruction > : : = if < expr >
then < instruction > else < instruction >
S = la catégorie des textes complets : < programme >
Pour le langage C la catégorie < programme > n’existe pas, le symbole de départ(ce qu’on peut compiler) est <déclarations>.
Notations
• Terminaux: les minuscules, chiffres, symboles d’opérateurs, symboles en gras
• Non-terminaux: majuscules: A,B,C,S ; entre <crochets>
• Symboles: X, Y, Z• Chaînes de terminaux: u,v,w• Chaînes de symboles:
D192 W254
Historique
- Pourquoi « non contextuel » ? (context-free)
Chomsky (en 1956) a choisi ce nompar contraste avec ses « grammaires contextuelles » (type 1)où on peut mettre des conditions pour l’application d’une règle.
- BNF signifie « Backus Naur form », du nom de ses inventeurs(utilisé pour la définition d ’Algol 60, vers 1958).
Emploi d’une grammaire
• Partir du symbole de départ (ou axiome) S;
• répéter :– employer une règle : remplacer le côté gauche par le
côté droit
• jusqu’à ce qu’il n’y ait plus que des symboles terminaux dans le texte.
Langage
• Chaque chaîne qu’on peut obtenir ainsi est une phrase de G.
• L’ensemble des phrases est le langage de G noté L(G).• Les étapes intermédiaires sont des proto-phrases de
G.
Dérivations
• Une dérivation est une suite de pas de l ’algorithme précédent:
S 1 2 3 w
• Si l ’algorithme peut passer de à en zéro, une ou plusieurs étapes, on dit que produit , noté *
Exemple
: program tutu
< déclarations >
< instr. composée >.
: program tutu
< déclarations >
begin < suite d’instructions >
end.
Comp : < instr. composée > : : = begin < suite d’instructions > end
produit directement
Comp
< programme > * produit
program tutu
begin
end.
Simplifications
• Un non-terminal est inaccessible s’il ne fait partie d’aucune proto-phrase.
• Il est improductif s’il ne produit aucune chaîne de terminaux.
• On peut retirer ces non-terminaux de la grammaire sans changer le langage : ils ne servent à rien.
Algorithme: Productif
Algorithme qui donne les non-terminaux productifs.
Au départ: aucun non-terminal n’est marqué productiftous les terminaux sont productifs
répéter parcourir les productions A ::=
si tous les symboles du côté droit sont marqués productifsalors marquer le côté gauche A comme productif
jusqu’à ce qu’un parcours ne marque rien de plus
On enlève les non-terminaux improductifs et les productions où ils apparaissent.
Algorithme: AccessibleAlgorithme qui donne les non-terminaux accessibles.
Au départ: seul le symbole de départ S est marqué accessiblerépéter
parcourir les productions A ::= si le côté gauche A est marqué accessible alors marquer tous les non-terminaux du côté droit comme accessibles
jusqu’à ce qu’un parcours ne marque rien de plus
On enlève les non-terminaux inaccessibles et leurs règles.
Exercice: Réécrivez ces deux algorithmes avec un temps d'exécution optimal (linéaire plutôt que quadratique).
Arbre de dérivation
• Idée : pour une phrase, il y a beaucoup de dérivations essentiellement identiques
• Déf. : Un arbre de dérivation pour u dans G est un arbre ordonné : – Dont les nœuds sont étiquetés par des non-terminaux
– Dont les feuilles sont étiquetées par des terminaux (ou )
– Dont la racine est étiquetées par le symbole de départ
– Chaque nœud correspond à une règle de production
Exemple d’arbreS : : = p id R ; D C. (1)
D : : = (2)
R : : = (F) | (3 | 4)
F ::= id | id, F (5 | 6)
C ::= b I e (7)
I ::= (8)
Dérivations• gauche: S p id R ; D C. p id ; D C. p id ; C. p id ; b I e. p id ; b e.
• droite: S p id R ; D C. p id R ; D b I e. p id R ; D b e. p id R ; b e. p id ; b e.
Arbre de dérivation : S
p id R ; D C b I e
Grammaire S: programmeR: paramètres du programmeD: déclarationsC: instruction composée
(1) (4) (2) (7) (8)
(1) (7) (8) (2) (4)
Ambiguité
Déf. : une grammaire est ambiguë s’il y a une phrase qui a plusieurs arbres de dérivation.
A éviter quand on veut générer un analyseur.
Peut être utile comme grammaire abstraite.
Exemple d’ambiguïté : les « else pendants » du Pascal
< instruction > : : = if < expr > then < instruction > else < instruction> | if < expr > then < instruction >
if b then if c then x : = 5 else x : = 3
Autre exemple: la grammaire précédente est ambigüe
Ambiguité : exemple : expressions
La grammaire suivante des expressions de Pascal avec +,* :
E ::= E+E | E*E | (E) | id
est ambiguë: par exemple, x+y+z a deux arbres d'analyse:
E E
E E E E
E E E E
x + y + z x + y + z
On notera ces arbres (x+y)+z et x+(y+z).
Résolution des ambigüités
Une ambigüité (x+y)+z vs. x+(y+z) est appellée une ambigüité d'associativité:
on la résoud en choisissant le coté récursif (coté gauche en Pascal)
E ::= E+F | F
F ::= id | (E)
Résolution des ambigüités
Une ambigüité (x+y)*z vs. x+(y*z) est appellée une ambigüité de précédence:
on la résoud en ajoutant un nouveau non-terminal par niveau de précédence.
Ici, terme T et facteur F:E ::= E+T | TT ::= T*F | F F ::= id | (E)
Résolution des ambigüités
L'ambigüité du if then else peut aussi se résoudre en introduisant des non-terminaux:
<instruction close>: qui a tous ses else<instruction non close>: sinon.
<instruction> ::= <instruction close> | <instruction non close><instruction close> ::= if <expr> then <instruction close> else <instruction
close><instruction non close> ::= if <expr> then <instruction> | if <expr> then <instruction close> else <instruction non close>
Automate à pile
a
caractère courant
fenêtre de lecture (avance d’un caractère)
.
.
.
.
sommet= état courant
pile
a
l’état définit le sommetde la pile
Automates à pileDéfinition
Un automate à pile se compose de :
• VT l’alphabet d’entrée (fini)
• Q les états (fini)
• q0 l’état initial
• F les états finaux ou accepteurs
•
fini x ( { }) x Q *+TQ V
caractère àtrouver dans
la fenêtre
états àtrouver ausommet de
la pile
états à pousser
sur la pile
Fonctionnement
• L’automate démarre avec une pile vide, dans l’état initial.
• Il suit les transitions :– lire un caractère.– empiler/dépiler les éléments indiqués de la pile.
• Il accepte le texte s’il arrive dans un état accepteur (final).
Déterminisme
Un automate à pile est déterministe si :– il n’a pas de transitions .– si deux transitions lisent le même caractère, elle ne
peuvent pas s’appliquer sur la même pile :la pile consultée de l’une ne peut pas être un préfixe de la
pile consultée de l’autre.
BNF Automates à piles
• Les états sont des items, càd des règles avec un « point de lecture • » inséré.
• On utilise 3 type de transitions :– Expansions (expand) : si l’état a le point devant un non-
terminal: A::= … • B … on empile une règle pour celui-ci: B ::= • … avec le point au début.
– Réductions (reduce): si l’état a le point en fin: B ::= …•, on le dépile et on avance le point: A::= … B• …
– Lectures ou décalages (shift): si l’état a le point devant un terminal: A::= … • a … , on le lit et on avance le point A::= …a • … .
BNF: état initial/final
• On ajoute un nouveau symbole de départ S’
• l’état de départ est S’ ::= • S
• l’état final est S’ ::= S •
BNF = Automates à pile
• Il y a aussi une transformation des automates à pile vers les BNF.
• Un langage est donc non-contextuel ssi c’est le langage d’un automate à pile non-déterministe.
Non-déterminisme
• L’automate obtenu est non-déterministe : plusieurs expansions sont possibles, laquelle choisir ?– Regarder les k symboles en entrée pour choisir la bonne production:
analyseur descendants gauche LL(k)
– Postposer la décision (comme pour les automates finis) analyseur ascendant droit (ex. yacc) LR(k)
Analyse descendante LL(k)
• Grammaire LL(k) : Le choix de la bonne expansion doit pouvoir se faire sur base des k prochains symboles à lire.
Cas simple
Si pour chaque non-terminal chacune des productions commence par un symbole terminal différentalors la grammaire est simplement LL(1)
Exemple
< instr > : : = if < expr > then < instr > else < instr > fi
while < expr > do < instr > od
begin < instr > end
le premier symbole lexical permet defaire le bon choix.
LL(k): définition
Pour chaque choix d'une production pour A, on peut faire ce choix sur base des k premiers caractères non
encore lus: G est LL(k) ssi
pour tout choix A ::= , A::= de G () ,
PREMIER( ) PREMIER( ) =
pour tout qui peut suivre A dans une dérivation gauche: S g* wA
Algorithme : PREMIER
PREMIER(A) donne les possibilités de début de A, tronqués à k caractères.
Pour chaque non-terminal A, le tableau P[A] contient un sous-ensemble de PREMIER(A), initialement vide.
tant que P change: pour chaque règle A ::= ajouter PREMIER() à P[A]
PREMIER(X1…Xn) est la concaténation tronquée à k de PREMIER(X1) … PREMIER(Xn)
PREMIER(X) = {X} si X est terminalPREMIER(X) = P[X] si X est non-terminal
Exemple : sommes
Une somme est une expression Pascal du genre :x+y+z+wen Pascal, les opérations se regroupent de gauche à droite : ((x+y)+z)+wLa grammaire des sommes est donc:S ::= S+id | id | (S) où id est le terminal pour les identificateursP[S] = après la 1ère production
= {id} après la 2ème production
= {id, ( } après la 3ème production, qui est la valeur finale.Donc cette grammaire n’est pas LL(1).
Comment rendre une grammaire LL(1) ?
• éliminer la récursivité gauche
• factoriser les parties communes
Éliminer la récursivité gauche
• Une grammaire est récursive à gauche si un non-terminal A se dérive strictement en une proto-phrase commençant par A: A+A
• Une grammaire récursive à gauche n’est jamais LL(k).
• A ::= Adevient A’ , A’ ::=A’|
D201
Algorithme
• pre : G sans cycle AA, sans vide A• post : G sans récursivité à gauche
• invariant : (Ak ::= Al), si k<i alors l>k :
les règles déjà traitées commencent par des grands terminaux
pour i := 1..n pour j := 1..i-1 remplacer Aj par sa définition dans Ai ::= Aj remplacer Ai ::= Ai par Ai ::=Ai’ , Ai’ ::=Ai’|
ExempleÉlimination de la récursivité gauche
Grammaire d’expressions: E ::= E + T | T
T ::= T * F | F
F ::= ( E ) | id
D202
Ordre: 1: E, 2: T, 3: F
i=1 Ai::=Ai | E ::= T E’E’ ::= +T E’ | i=2 Ai::=Ai | T ::= F T ’T’ ::= * F T’ | F ::= ( E ) | id
Résultat
Exemple 2Élimination de la récursivité gauche
Grammaire : A ::= B b | C e
B ::= A a | d
C ::= A c | d
D203
Ordre: 1: A, 2: B, 3: C
i=2 Ai::=Aj B ::= B b a | C e a | d B::= CeaB’|dB’
B’::=baB’|
Ai::=Ai |
C ::= B b c | C e c | dC ::= CeaB’bc|dB’bc|Cec|d
C ::= dB’bcC’|dC’C’::=eaB ’bcC’|ecC’|
calcul : Résultat
A ::= B b | C e
Factorisation
• Idée : postposer le choix en regroupant les parties communes (facteurs)
• Exemple: I ::= if E then I | if E then I else I devient I ::= if E then I C C::= | else I
Exemple : PREMIERPour la grammaire des expressions sans récursivité gauche:
0 : S ::= E 3 : E’ ::= + E 6 : T’ ::= * T1 : E ::= T E’ 4 : T ::= F T’ 7 : F ::= (E)2 : E’ ::= 5 : T’ ::= 8 : F ::= id
k vaut 1. Les itérations de PREMIER sont listées en colonnes. On visite les productions en commençant par le fin (de 8 à 1). Par exemple, on peut lire dans la deuxième entrée de la première colonne, qu’après la visite des productions 8 puis 7, l’ensemble P du non-terminal F contient les symboles id et (.
8 : P[F] = {id} 5 : P[T’] = {*, } 2 : P[E’] = {+, }7 : P[F] = {id, ( } 4 : P[T] = {id, ( } 1 : P[E] = {id, ( }6 : P[T’] = {*} 3 : P[E’] = {+} 0 : P[S] = {id, ( }
P ne donne pas le début des productions 5 et 2: il faut connaître le début de ce qui est derrière E’ et T’, calculé par SUIVANT.
Suivant
• Quand PREMIER() est trop court, on regarde derrière avec SUIVANT(A) : ce qui peut suivre A.
• Algorithme :Le tableau des suivants est initialisé à videSUIVANT[S] := {$} /* le marqueur de fin */tant que SUIVANT change pour chaque règle A ::= B ajouter PREMIER() . SUIVANT[A] à SUIVANT[B]
Exemple : SUIVANTPour la grammaire des expressions sans récursivité gauche:
0 : S ::= E 3 : E’ ::= + E 6 : T’ ::= * T1 : E ::= T E’ 4 : T ::= F T’ 7 : F ::= (E)2 : E’ ::= 5 : T’ ::= 8 : F ::= id
PREMIER ne suffit pas pour E’, T’On calcule SUIVANT: S: {$}0: E: {$}1: T: {+,$} E’:{$}4: F: {*,+,$} T’:{+,$}7: E: { ),$}1: T: {+,),$} E’:{ ),$}4: F: {*,+,),$} T’:{+,),$}
Analyseur descendant à pile
• Si la grammaire est LL(k) : à partir du non-terminal à développer (derrière le • en sommet de pile) et des k caractères d’entrée, on sait quelle règle appliquer. On le met dans une table.
• L’analyseur fait :– une lecture si le • est devant un terminal:
On avance la fenêtre et le point dans l'item.– une réduction si le • est en fin de d'item:
On dépile l'item.– une expansion si le • est devant un non-terminal:
On empile l'item d’après la table.
Exempletable LL(1) pour expressions
( ) + * id #
E (E TE') erreur erreur erreur (E TE') erreur
E' erreur (E' ) (E' + E) erreur erreur (E' )
T (T FT') erreur erreur erreur (T FT') erreur
T' erreur (T' ) (T' ) (T' * T') erreur (T' )
F (F (E)) erreur erreur erreur (F id) erreur
S (S E) erreur erreur erreur (S E) erreur
D215
Trace de l’analyse LL(1) d’une expressionContenu de la pile Chaîne d'entrée
$ [S •E] id * id $
$ [S •E] [E •TE'] id * id $
$ [S •E] [E •TE'] [T •FT'] id * id $
$ [S •E] [E •TE'] [T •FT'] [F •id] id * id $
$ [S •E] [E •TE'] [T •FT'] [F id•] * id $
$ [S •E] [E •TE'] [T F•T'] * id $
$ [S •E] [E •TE'] [T F•T'] [T' •*T] * id $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] id $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] [T •FT'] id $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] [T •FT'] [F •id] id $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] [T •FT'] [F id•] $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] [T F•T'] $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] [T F•T'] [T' •] $
$ [S •E] [E •TE'] [T F•T'] [T' *•T] [T FT'•] $
$ [S •E] [E •TE'] [T F•T'] [T' *T•] $
$ [S •E] [E •TE'] [T FT'•] $
$ [S •E] [E T•E'] $
$ [S •E] [E T•E'] [E' •] $
$ [S •E] [E TE'•] $
$ [S E•] $
$ $
D216
Gestion de la pile
On peut représenter les items en mettant les non-terminaux non encore lus en ordre inverse sur la pile.
L’analyseur fait :– une lecture si le sommet est un terminal:
On avance la fenêtre et on dépile.
– une expansion si le sommet est un non-terminal:
On empile en ordre inverse la partie droite de la règle trouvée d’après la table.
– les réductions deviennent implicites.
D215
Analyseur récursif prédictif
• On peut employer la pile implicite de récursion :– le non-terminal de gauche = procédure appelée– règles = corps de la procédure– prédiction = test du fichier d’entrée– point = adresse de retour
Exemple : expression
procedure expr;begin if input^ in [ ‘(‘ , ‘id’ ] then begin term; exprend; end else erreurend;
E ::= T E’
choix par table LL(1)
TE’
Exemple: suite
procedure exprend;begin if input^ = ‘+’ then begin get;
term; exprend; end else if eof or input^ = ‘)’ then else erreurend
E’ ::= +TE ’ | E’
+TE’
Actions
• On peut insérer du code dans les règles de syntaxe, appelé « action »
• Ce code est exécuté lorsque l'analyseur exécute cette partie de la règle
• Souvent ces actions construisent un arbre d’analyse abstrait
Arbre abstrait : exemple
E
T E’
F T’
id +
E
T
id
E’
F
T’
Arbre syntaxique concret de x + y, pour la grammaire des expressions sans récursion à gauche.
+
x y
Arbre abstrait pour la grammaire ambiguë de départ
x + y
2.3 Analyse syntaxique ascendante
• Idée : On choisit la règle à appliquer après avoir lu le texte correspondant– l’arbre d’analyse est construit de bas (feuilles) en haut (racine)
– la dérivation correspondante est droite : le non-terminal le plus à droite est actif.
– Le programme remonte dans la dérivation.
• Problème : comment garder trace des possibilités ?
D222
Exemple : Texte Ada
if x + 1 > y then x := 1 else x := 2 end if ;
<expr>
<expr> <instr> <instr>
>
+
x 1
y
:=
x 1
:=
x 2
*
if < expr > then < instr > else < var > := < expr > end ifmanche
if < expr > then < instr > else < instr > end if
< instr >
...
Automate fini caractéristique
• Décrit le comportement du sommet de pile
• Sa déterminisation donne des ensembles de règles possibles
• Ses états finaux sont les réductions
Automate fini caractéristique
Déf. : L’automate fini caractéristique est donné par
états = items (G)
alphabet =
état initial =
états finaux =
transitions =
T NV V
[ 'S .S]
[X .] les états où on réduit
lecture d’un symbole :
expansions :
pas de réductions : elles sont représentées par des étatsterminaux.
([X ::= . ], , [X ::= . ]) V VN T
([X ::= . ], , [ ]) VN
Automate fini caractéristique
Déf. : L’automate fini caractéristique est donné par
états = items(G)
alphabet = les symboles (terminaux ou non)
état initial = l’item de départ: [S’ ::= •S]
états finaux = les états où on réduit: [A ::= • ]
transitions = lecture : on déplace le point après le symbole lu
expansion: si le point est devant un non-terminal, on choisit une
production pour ce non terminal
Déterminisation de l’automate fini caractéristique
• En appliquant la déterminisation, on obtient un automate déterministe où l’état atteint en suivant un préfixe viable est l’ensemble de ses items valides.
• Cet état peut être impropre ou conflictuel, s'il contient:– Une lecture (shift), càd un item où le point est devant un terminal
– Et une réduction, càd un item où le point est en fin
ou
- deux réductions (conflit R/R)Dans ce cas, l’automate à pile associé est non déterministe, et la grammaire
n'est pas LR(0).
Exemple : expression .E] [S E.]
[E .E + T] [E E. + T] [E E + .T] [E E + T.]
[E .T] [E T.]
[T .T * F] [T T. * F] [T T * .F] [T T * F.]
[T .F] [T F.]
[F .(E)] [F (.E)] [F (E.)] [F (E).]
[F .id] [F id.]
E
E + T
T
T * F
F
( E )
id
[S
S0
S1
S2
S5
S3
Préfixe viable
• = la partie déjà analysée du texte
• = l’état de la pile d’un analyseur ascendant (avant le point)
• def: un préfixe d’une proto-phrase droite qui ne s’étend pas au-delà du manche le plus à droite
Item valide
• Intuitivement, l’item valide donne un état possible de l’analyseur quand il a lu un préfixe.
Def. : A::= • est valide pour le préfixe viable
ssi
il y a une dérivation droite S d* A w d
* w
Construction directe
• On peut spécialiser la déterminisation pour l’automate caractéristique de façon à ne pas le construire: on fait les deux étapes ensemble.
D252 W342
Fermeture
fonction Fermeture (s : ensemble d’items) : ensemble d’items ; (* fermeture par les successeurs, ici les expansions *)
var q : ensemble d’items ;début q := s ; tant que [X •Y] q et Y P et [Y •] q /* il y a une expansion qui n'est pas encore dans q */ ajouter [Y •] à q retourne (q)fin ;
s est appelé le noyau de q; il détermine q.
D252 W342
Successeurs
fonction Transition(s : ensemble d’items, Y : VN VN ) : ensemble d’items; (* donne le noyau de l’état atteint par la lecture de Y *) retourne ( {[X •Y] | [X Y•] s } )
Le noyau du successeur par une transition étiquetée Y contient les items qui avaient le point devant Y, où on a avancé le point.
D253 W343
Construction des états
Q := { Fermeture({[S' ::= • S]})} /* l'ensemble des états part de l'item initial */ := /* les transitions sont initialement vides */
pour chaque état q de Q, symbole X apparaissant derrière le point dans q faire q’ := Fermeture(Transition(q,X)) ;
si q' non vide alors
si q’ n'est pas dans Q alors on l'ajoute à Q on ajoute la transition de q vers q' lisant X dans
D253 W343
Exemple : expressions
S4 S11
S5
S3
S2
S0
S1
S8
S9
+ T
E
T
F
id
(
(
T
F
id
F
(
S6
E
+
id
)
S7 S10*
*id
F
(
L’automate LR(0) donne :
D254 W339
Les états S1, S2, S9 sont impropres :ils contiennent des conflits LR(0) :cette grammaire n’est pas LR(0).
Exemple : expressions : états LR(0)
S0 = { [S .E], S5 = { [F id.]} [E .E + T], [E .T], S6 = { [E E + .T], [T .T * F], [T .T * F], [T .F], [T .F], [F .(E)], [F .(E)], [F .id] } [F .id] } S1 = { [S E.], S7 = { [T T * .F], [E E. + T] [F .(E)], [F .id] } S2 = { [E T.], S8 = { [F (E.)], [T T. * F] } [E E. + T] } S3 = { [T F.] } S9 = { [E E + T.], [T T. * F] } S4 = { [F (.E)] S10 = { [T T * F.] } [E .E + T], [E .T], S11 = { [F (E).] } [T .T * F], [T .F], [F .(E)], [F .id] }
Les états S1, S2, S9 sont impropres :ils contiennent des conflits LR(0) :cette grammaire n’est pas LR(0).
Exemple
La grammaire:S ::= C | DC ::= a C | bD ::= a D | en’est pas LL(k) : on ne peut pas choisir entre C et D car il peut y avoir des a d’abord dans les deux cas.Elle est LR(0) car on peut choisir après lecture.Son automate déterministe caractéristique ne contient pas de conflits LR(0).q0 = {[S’ ::= •S], [S::= •C], [C::= •aC], [C::= •b], [S::= •D], [D::= •aC], [D::= •e]}q1 = {[C::= a•C], [C::= •aC], [C::= •b], [D::= a•C], [D::= •aC], [D::= •e]}q2 = {[C::= b•]}q3 = {[D::= e•]}q4 = {[S::= C•]}q5 = {[C::= aC•]}q6 = {[S::= D•]}q7 = {[D::= aD•]}q7 = {[S’::= S•]}
q0
q7
q6
q4
q5
q8
q3
q2
q1a
e
b
C
DS
Cb
e
D
SLR
• Pour résoudre les conflits LR(0), on regarde SUIVANT(A), où A est le côté gauche de la règle à réduire.
D256 W352
Exemple : expressions SLR(1)
Les états conflictuels LR(0)S1: le conflit entre réduire S, SUIVANT(S) = { $ } et lire +, est résolu.S2 : le conflit entre réduire E, SUIVANT(E) = { $, +, ) } et lire *, est résolu. S9 : idem.
Cette grammaire est donc SLR(1).
D237 W353
Exemple : Affectations en C
S : : = L = R | RL : : = *R | idR : : = L
L = partie gauche (donne l’adresse)R : expression ; une expression est une instruction en C* : déréférenciation (prendre le contenu de l’adresse)
D257
Exemple : Affectations en C : automate SLR(1)
[S’ • S][S • L = R][S • R][L • * R][L • id][R • L]
S3
S
R
[S’ S • ]
[S R • ]
S2L
[S L • = R][R L • ]
[S L = • R][R • L][L • * = R][L • id]
=S6
RS9
[S L = R • ]
[L id • ]
id
S5
S4*
*id
L
L RS7S8
*
[R L • ] [L *R • ]
[L * • R][R • L][L • *R][L • id]
S2 est conflictuel : SUIVANT(R) contient =.Cette grammaire n’est pas SLR(1).
id
D258
Items LR(k) canonique
• On ajoute aux items une chaîne de longueur k (la prévision de l’item), qui indique le texte qui peut suivre cet item: [A•, u]
• La prévision est propagée par les transitions.• Une nouvelle prévision est calculée avec
PREMIER pour les expansions.• On crée plus d’états qu’avec les autres
techniques (LR(0), SLR(1), LALR(1) ).
D260 W350
Fermeture
fonction Fermeture(I);répéter pour chaque item LR(k) [A•B, u] de I et chaque chaîne v de PREMIER(u) pour chaque règle B ajouter [B •, v] à Ijusqu’à stabilisation
D261 W350
Exemple : expressions : états LR(1)
S4S11
S5
S3
S2
S0
S1
S8
S9
+ T
E
T
F
id
(
id
F
(
S6
E
id
)
S7 S10*
*
id
F
(
S15 S19
S12
S18
S17
S13(
T
F
F
E
id
)
S16 S21*
*
id
F
(
S20
+ T
id
(
S14
T
F
+
L’automate LR(0) est doublé : prévision avec $ (fin) ou avec )
Exemple : expressions : états LR(1)
S0 = Fermeture(Début) S6 = Fermeture( S1 , +))= { [S • E ,{$}] { [E E + • T, {$, +} ],
[E • E+T ,{$, +}], [T • T * F, {$, +, *} ],[E • T, {$, +} ], [T • F, {$, +, *} ],[T • T * F, {$, +, *} ], [F • (E), {$, +, *} ],[T F, {$, +, *}], [F • id, {$, +, *} ] }[F • (E), {$, +, *}],[F • id, {$, +, *} ] } S9 = Fermeture( Succ( S6 , T))
{ [E E + T • , {$, +}],S1 = Fermeture( Succ(S 0 ,E)) [T T • * F, {$, +, *}] }
{ [S E • , {$}],[E E • + T, {$, +}] }
S2 = Fermeture( Succ(S 1 ,T)){ [E T • , {$, +}],
[T T • * F, {$, +, *}] }
=
=
S4 =[E • E+T ,{), +}],[E • T, {), +} ],[T • T * F, {), +, *}[T F, {), +, *}],[F • (E), {), +, *}],[F • id, {), +, *} ] }
{ [F ( • E), {$, +, *}],
Notation: l’ensemble des prévisions des items LR(1) qui ont la même production sont regroupés.
LALR
• on garde les états LR(0)• les prévisions sont la fusion des prévisions LR ayant le
même cœur (état LR(0) )• Pour calculer les prévisions de [B •] :
– trouver les origines de la règle : expansions [B •] – trouver les sources de l’expansion [A•B, u] – calculer ce qui se trouve derrière B: PREMIER( u) – faire l’union– en cas de boucle: répéter jusqu’à stabilisation
D270 W
Exemple : affectations en C
[S’ • S , {$} ][S • L = R][S • R , {$} ][L • * R][L • id][R • L , {$} ]
S3
S
R
[S’ S • ]
[S R • ]
S2L
[S L • = R][R L •, {$} ]
[S L = • R][R • L][L • * = R][L • id]
=S6
RS9
[S L = R • ]
[L id • ]
id
S5
S4*
*id
L
L RS7S8
*
[R L • ] [L *R • ]
[L * • R][R • L][L • *R][L • id]
id
S0
Le conflit en S2 est résoluCette grammaire est LALR(1).
Exemple : expressionsE
tats
impr
opre
s(c
onfl
it d
écal
er/r
édui
re)
(
(
S0 = { [S .E], {$} S5 = { [F id.]} [E .E + T], {$, +} [E .T], {$, +} S6 = { [E E + .T], {), +, $} [T .T * F], [T .T * F], [T .F], [T .F], [F .(E)], [F .(E)], [F .id] } [F .id] } S1 = { [S E.], {$} S7 = { [T T * .F], [E E. + T] } {$, +} [F .(E)], [F .id] } S2 = { [E T.], {$, +, ) } S8 = { [F (E.)], {), +} [T T. * F] } [E E. + T] } S3 = { [T F.] } S9 = { [E E + T.], {$, +, )} [T T. * F] } S4 = { [F (.E)] S10 = { [T T * F.] } [E .E + T], { ), +} [E .T], { ), +} S11 = { [F (E).] } [T .T * F], [T .F], [F .(E)], Tous les conflits sont résolus : [F .id] } la grammaire est LALR(1).
(
Derrière, en mauve: les ensembles de prévision LALR(1)
Grammaires ambiguës
• Une grammaire ambiguë peut être plus naturelle
• Elle provoque toujours des conflits
• Les conflits peuvent être résolus par des règles de précédence et d’associativité, sans augmenter le nombre d’états
D277 W368
Exemple : expressionsLa grammaire la plus naturelle est : E ::= E + E | E * E | ( E ) | idqui est ambiguë, mais peut servir de grammaire abstraite.
En donnant des règles d ’associativité:%left ‘ + ’%left ‘ * ’et de précédence données par l’ordre d’écriture, on résout les conflits Shift/Reduce:• Si le dernier terminal de la règle à réduire est plus fort que celui la fenêtre, on réduit; sinon on lit.• Si c’est le même, on emploie l’associativité.• Enfin, Yacc et Bison décalent de préférence.Pour les conflits réduire/réduire, ils préfèrent la règle écrite en premier.
Exemple : if then else
En Pascal, on a : <conditionnelle > ::= if E then I else I | if E then Icette grammaire est ambiguë, mais sera analysée correctementgrâce à la préférence au décalage.
Langages contextuels
• Certains langages ne peuvent pas être traités avec les grammaires non-contextuelles.
• Méthodes:– 1. un langage est non-contextuel ssi il peut être reconnu par
un programme dont la seule structure de données infinie est une pile.
Lemme de pompage
– 2. Si un langage est non-contextuel, alors il a une longueur k : toute chaîne plus longue que k se décompose en 5 parties u v w x y, et les parties v et x peuvent être répétées le même nombre de fois: u vn w xn y fait aussi partie du langage. Les parties v w x ensemble ont une longueur de k au plus.
• En pratique on l’emploie dans l’autre sens : on trouve une série de phrases (une pour chaque k) dans le langage et, quelle que soit la décomposition, le lemme nous obligerait à inclure aussi des phrases incorrectes : on sait alors que le langage ne peut être décrit par une grammaire BNF.
Exemple
{an bn cn} n’est pas non-contextuel:
on peut empiler pour chaque a, puis dépiler pour chaque b
mais alors on ne sait plus compter les c.
Exemple
{an bn cn} n’est pas non-contextuel: si l'était,
ak bk ck se décomposerait en u v w x y
v w x est de longueur k au plus
donc il ne peut contenir à la fois des a, des b, et des c
donc en répétant v et x, on n'a plus assez du caractère manquant:
impossible.
Sémantique statiqueSémantique statique
Rôle :
Vérifier les propriétés contextuelles statiques, principalement la cohérence des types
D Ch.5-6 W Ch. 9
Syntaxe abstraite
La sémantique statique travaille sur les arbres de syntaxe abstraite.
if2
grt
plus assign assign
id
(x)
iconst
(1)
id
(y)
id
(z)
iconst
(1)
id
(z)
iconst
(2)
Exemple : Arbre de syntaxe abstraite
Cond Stat
Ifstat
E
T
F
id
(x)
E
F
iconst
(1)
+if
E
compop
(>)
E
T
F
id
(y)
then
E
id
(z)
E
T
F
iconst
(1)
:=
Ass
else
Stat
E
id
(z)
E
T
F
iconst
(2)
:=
Ass
fi
Arbre de syntaxe concrète pour le même exemple
Sémantique statique
• 1. un exemple : le typage
• 2. une technique de spécification : les grammaires attribuées
1. Typage
• Portée
• Systèmes de types– équivalence– coercition– polymorphisme– surcharge
D Ch. 6 W388
Portée
• Les types se déduisent des déclarations• Les déclarations sont valides dans leur portée• Elles peuvent être cachées par d’autres déclarations• Chaque langage a ses règles de portée / visibilité.
Portée : exemple de Pascal
• En Pascal, la portée d’une déclaration est le bloc dans lequel elle se trouve
• Il ne peut y avoir qu’une déclaration de ce nom dans un bloc
• Une nouvelle déclaration du même nom dans un bloc imbriqué cache la déclaration extérieure
• Les paramètres sont traités comme des variables locales
Portée : exemple d’Ada
• La portée d’une déclaration commence à la déclaration et se termine à la fin du bloc
• Si le bloc est un package, elle comprend de plus les packages qui importent celui-ci
• Une nouvelle déclaration ne cache un nom que si le type est le même
Portée : implémentation
• Pour chaque bloc on crée une table de symboles• Chaque table a un lien vers le bloc englobant• Chaque déclaration ajoute une entrée dans le
bloc (souvent avec des infos: type, taille, déplacement dans le bloc d’activation, etc.)
• Chaque occurrence d’identificateur est recherchée dans la liste des blocs en appliquant les règles du langage.
Expressions de types
• expressions de type : typiquement :– des types de base :
• booléen, entier, réel, …
– des noms de types– des constructeurs de type :
• tableaux• records (structures, enregistrement)• fonctions• pointeurs
D381
Règles de typage
• Chaque langage a ses règles• Parfois les règles demandent une vérification
dynamique (à l’exécution)• Un système de typage est sain si toutes les
valeurs utilisées à l’exécution sont du type calculé statiquement
• Un langage est typé si le typage impose des conditions sur les programmes.
D383
Exemple : Pascal
• le typage de Pascal n’est pas sain :– on n’est pas obligé de vérifier que les indices de
tableaux restent dans les bornes prévues– les variants peuvent être modifiés sans contrôle– les valeurs initiales ne sont pas nécessairement du
bon type
Exemple de règles
• Si a est un tableau, alors dans indexation sur a, les expressions des indices doivent avoir le même type de base que le type d’indices apparaissant dans la déclaration de a.– exemple: var a : array[1..10] of integer;
i : -10.. 0 ;i := a[i] (* correct car le type de base est integer , mais pas recommandé *)
Equivalence des types
Quand 2 types sont ils égaux ?
2 grandes familles :
• équivalence par nom : ils doivent avoir la même déclaration (Pascal, Ada)
• équivalence structurelle : on calcule s’ils se ramènent à la même structure (C, Algol68, ML)
D389
Equivalence par nom
ex. : Pascal, Ada1) var x : record a, b : integer end
y : record a, b : integer end les types de x, y sont différents (dépend de l’implémentation en Pascal74)
2) type r = record a, b : integer endvar x : r ;
y : r ;les types de x, y sont équivalents
3) var x, y : record a, b : integer endles types de x, y sont équivalents en Pascal, mais différents en Ada
Equivalence structurelle
• deux types sont équivalents si leur structure est la même (intuitivement, s’ils admettent les mêmes valeurs)
• vérification en parcourant la structure des types
• si cycles possibles, attention à la terminaison
D390
Algorithme
structure globale : partition des nœuds du graphe,notée t1 == t2 (les types déjà prouvés équivalents)
equiv(t1, t2) si t1 == t2 alors renvoyer vrai sinon si constructeur(t1) <> constructeur(t2)
alors renvoyer fauxsinon si pour tout i, equiv
(fils(i,t1),fils(i,t2)))alors union(t1, t2); renvoyer vrai
Particularités de typage
• surcharge
• coercition
• polymorphisme
Surcharge (ou « polymorphisme ad hoc »)
Un identificateur est surchargé s’il peut avoir des significations différentes
suivant son type.
Exemple en Pascal, en ML, …
+ : real x real real
+ : int x int int
en Ada : l’utilisateur peut surcharger lui-même, par exemple :
function "*" (x, y : Matrix) return Matrix ;
Résolution de la surcharge
+
3 f
p
int
int x int intreal x real real
real realint intchar char
real int
A la fin de l’algorithme, chaque fonction doit avoir un seul typesinon : ambiguïté de type
Soit vis(k) l’ensemble des définitions de k visibles, avec leurs types
Ex. : vis(« + ») = { int x int int, real x real real }
L’algorithme élimine les types d’abord en montant dans l’arbre puis en descendant. La contrainte est que les arguments doivent avoir le type attendu par la fonction.
Ex. : vis(« p ») = {int, real} vis(« f ») = {real real, int int, char char}
3 + f(p)
D400 W390
CoercitionDef : Conversion implicite entre types (insérée par le compilateur)
Exemple : En Pascal, un entier peut être employé à la place d’un réel dans une expression mais PAS à gauche de := ex. : var r : real ; i : integer ; begin r := i i := r x interdit
Notation : typedest note une coercition vers typedest (notation Ada)
Exemple : Dans tous les langages à objets : une variable peut être « coercée » vers tous les types dont son type statique hérite.
D396
Les coercitions introduisent de nombreuses ambiguïtés, surtout combinées aux surcharges :
Exemple : coercition integer real notée real / : integer x integer integer ; / : real x real real ;
r := 3/2 peut se comprendre : r := real (3/2) valeur 1. ou r := real (3) / real (2) valeur 1.5
en Pascal : on utilise 2 noms pour la division: / et div.
Les langages non-O.O. modernes interdisent les coercitions.
Résolution de la coercition
• Pour résoudre les ambiguïtés de coercition, on se donne des règles de préférence, p.ex.:– préférer une coercition unique à une suite de coercitions
– préférer les coercitions le plus haut dans l’arbre
• S’il n’y a pas de surcharge: insérer des coercitions lorsque nécessaire
• Avec surcharge : on étend l’ensemble des typages avec un ordre partiel de préférence
• Pour Pascal, une passe ascendante suffit.
Exemple : Pascal
• si les 2 opérandes sont entiers : résultat entier
• si les 2 opérandes sont réels : résultat réel
• si un opérande est entier, le coercer en réel.
• exemple:
3+2+5*4.1 = real(3+2) +( real(5) * 4.1 )
Polymorphisme (paramétrique)
Une définition est polymorphique si elle peut s’appliquer de la mêmefaçon à tout type.
Exemple en MLhead : ‘a list ‘a renvoie le 1er élément d’une liste où ‘a est une variable qui représente n'importe quel type en Pascal :nil : ‘a
Différence par rapport à la surcharge• l’opérateur a une seule définition :
son comportement est uniforme• l’ensemble des types est potentiellement infini.
D402 W391
Résolution du polymorphisme
• Pour chaque occurrence d’une fonction polymorphe, on introduit une copie de son type avec de nouvelles variables de type
• Pour chaque variable (de type inconnu), on introduit une nouvelle variable de type. La portée des variables est locale à une ligne.
• Les variables de type peuvent recevoir des valeurs particulières dans une substitution
• On résout les contraintes par unification, en calculant la substitution qui permet de les satisfaire. Il y a un résultat le plus général, le type principal.
D409 W394
Exemple 1 (en ML)On peut écrire une fonction « longueur » qui marche sur des listes de n’importe
quel type (impossible en Pascal)fun longueur( [] )= 0 | longueur (x :: l) = longueur(l) +1où: [] est la liste vide, de type ‘a list : liste de n’importe quel type. : : est le constructeur de liste: (x :: l) est la liste obtenue en mettant x devant l.
Il est de type ‘b x ‘b list ‘b list, càd que x doit avoir le même type que les éléments de l.
Les types de longueur, x, l sont inconnus a priori. On introduit donc de nouvelles variables ‘to, ‘tx, ‘tl pour leur type.
Les contraintes sont: ‘to = (‘a list) int pour la 1ère ligne ‘to = (‘b list) int pour la 2ème ligne. On en déduit ‘a = ‘b par unification.
D412
Exemple 2 (ML)
fun append ( [ ] ) (l2) = l2
append (x :: r) (l2) = x :: (append r l2)
Contraintes :
‘ta = ‘a list (‘tl2 ‘e1)‘tl2 = ‘e1
:: : ‘b x ‘b list ‘b list‘tr = ‘b list ‘tx = ‘b‘ta = ‘b list (‘tl3 ‘e2 )‘tx = ‘c‘ta = ‘tr (‘tl3 ‘c list)‘e2 = ‘c list
‘ta = ‘a list (‘a list ‘a list)
‘ta ‘a list ‘tl2 ‘tl2
‘tx ‘tr ‘tl3 !
‘tx list
W395
2. Grammaires attribuées
• basées sur une grammaire BNF
• on ajoute à chaque nœud de l’arbre des attributs (p.ex. : type, table de symboles, code, valeur). Ce sont des champs supplémentaires
• les attributs sont calculés par des règles d’attribution associées aux règles de la grammaire
Attributs synthétisés et hérités
• Attribut synthétisé : remonte dans l’arbre (calculé d’après les attributs de ses fils)
• Attribut hérité : descend dans l’arbre (calculé d’après les attributs de son père et/ou de ses frères)
• Attributs lexicaux (synthétisés) : pour les terminaux, calculés par l’analyseur lexical (p.ex. la valeur d’une constante)
Exemple : calculatrice
Règle de grammaire Règle d’attributionS ::= E val : attribut synthétisé de E, T, F, nombre
E ::= E1 +T E.val = E1.val + T.val
E ::= T E.val = T.val
T ::= T1 *F T.val = T1.val * F.val
T ::= F T.val = F.val
F ::= ( E ) F.val = E.val
F ::= nombre F.val = nombre.val
Pour l ’exemple suivant, il nous faut un type abstrait « table de symboles » :
ajout (table, id, def) : table - crée une erreur si id est déjà dans table - dans cet exemple, def est le type de la variable ;
on peut mettre d ’autres infos dans la table (le déplacement, etc.)
t1 + t2 : table les défs de t2 écrasent les défs de t1
vide : table
rech(table, id) : def retrouve la déf courante de id.
La table sera synthétisée par les déclarations et héritées par les instructions.
Exemple : types dans les déclarations
typeh: attribut hérité de L
tab: attribut synthétisé de L: table de symboles (nom, type)
Règles de grammaire Règles d'attribution
D ::= L : Type L.typeh = Type
L ::= id, L1 L1.typeh = L.typeh
L.tab = ajout(L1.tab , id.entrée, L.typeh)
| id L.tab = ajout(vide, id.entrée, L.typeh)
D316
Graphe de dépendances
• Sur un arbre attribué, on ajoute une flèche si un attribut est employé pour calculer un autre
• Les flèches changent au max. d’1 niveau
• L’ordre du calcul doit suivre les flèches
• S’il y a un cycle, le calcul n’est pas possible
Exemple : Graphe de dépendances
x, y, z : bool;
D
typeh L tab : T
id , typeh L tab bool
id , typeh L tab
id
Exemple : typage Pascal
E0.type = if E1.type = int
and E2.type = int
then int
else real| (E1)
E0 . type = E1 . type
Calcul du type d’une expression numérique en Pascal(avec surcharge à la place de coercition) dans la grammaire abstraite
type : attribut synthétisé de E : {int, real}
Règles de grammaire Règles d’attributionE0 ::= E1 op E2 op peut être +, -, *
Exemple : arbre abstraitLes grammaires attribuées peuvent aussi servir à calculer
l’arbre abstrait comme attribut de l’arbre concret.cons(e,g,d) construit un nœud d’arbre à 2 fils, g et d.a est un attribut synthétisé représentant l'arbre abstraitRègles de grammaire Règles d’attribution
E ::= E1 +T E.a = cons(+, E1 .a , T.a)E ::= T E.a = T.aT ::= ( E ) T.a = E.aT ::= id T.a = idPour un DAG, cons renvoie le nœud (si) existant
Ordres d’évaluation des attributs
• ordre fixe : donné a priori (p.ex. suit la méthode d’analyse syntaxique : LL ou LR)
• ordre statique : d’après la grammaire attribuée, on précalcule un ordre
• ordre dynamique : le graphe de dépendance est construit avec l’arbre; les attributs sont évalués dans un ordre compatible avec le graphe
Ordre fixe : grammaire S-attribuées
• Une grammaire est S-attribuée si elle ne contient que des attributs synthétisés
• Implémenté dans les analyseurs ascendants (Yacc, Bison, …)
• Syntaxe: ex: E : E '+' E { $$ = $1 + $3 } ;– l’évaluation de l’attribut synthétisé $$ se fait dans
l’action de réduction– $i désigne l’attribut du ième symbole
D326
Grammaires S-attribuées : Implémentation
• L’analyseur LR conserve une pile des non-terminaux déjà réduits
• Il leur associe leur attribut synthétisé
• Lors de la réduction, les $i sont trouvés en sommet de pile, utilisés pour calculer $$, et remplacés par $$.
D328
Exemple : calculatrice
Règle de grammaire Action d’attribution(Yacc)
S : E ;
E : E ‘+’ E { $$ = $1+$3 }
| E ‘*’ E { $$ = $1*$3 }
| ‘ (‘ E ‘ ) ’ { $$ = $2} ;
F : nombre { $$ = $1} ;
Exemple : calculatrice - pile
• Dans cet exemple, la pile des attributs se comporte comme une pile d’évaluation postfixe.
D329
Ordre fixe : grammaires L-attribuées
• Une grammaire est L-attribuée si on peut évaluer les attributs lors d'un parcours en profondeur de gauche à droite de l’arbre d’analyse
• càd: un attribut hérité ne dépend que d’attribut hérités plus à gauche ou du père
• peut s’implémenter dans LL(1), LALR(1), etc.
D330 W434
Exemple : expression LL(1)
val : attribut synthétiséh : attribut hérité de E': valeur de l'expression à gauche
Règles BNF AttributionE ::= T E' E'.h = T.val
E.val = E'.val
E' ::= - T E'1 E'1.h = E'.h - T.val
E'.val = E'1.val | E'.val = E'.h T ::= nb T.val = nb.val
Ex d’évaluation d’une expression
arbre attribué pour l’expression 9 - 5 - 3
E.val=1
T.val = 9
nb.val = 9
E’.h = 9
T.val = 5
nb.val = 5
E’.h = 4
T.val = 3
nb.val = 3
E’.h = 1val =1
val =1
val =1
L-Attributs dans LL(1) récursif
• On ajoute les attributs hérités comme paramètres d'entrée de la procédure du non-terminal, les attributs synthétisés comme paramètres de sortie
• On ajoute des variables locales pour les attributs apparaissant dans une règle du non-terminal
D340
Exemple : expressions LL(1)
Règle LL(1) attributionE' ::=
E'.val = E'.h
| - T E'1 E'1.h = E'.h - T.valE'.val = E'1.val
procedure reste(h: integer; var val: integer); var tval, e1val : integer;begin if eof then val := h else if input^ = ‘ - ’ then begin get; (* lit ‘ - ‘ *) terme(h, tval); reste(h-tval, e1val); val := e1val end else error end
L-attributs dans LR
• On ajoute des actions à l’endroit où les attributs doivent être calculés
• les actions dans un règle (mais pas à la fin) seront traduites par des marqueurs : des non-terminaux à définition vide, avec une action en fin.
• On accède aux attributs des non-terminaux précédents en remontant la pile ($0, $-1, etc.)
D342
Exemple : marqueur
Une règle
S : {$$ = 5} B ;
est traduite par
S : M B;
M : {$$ = 5};
Dans B la valeur de M est accessible comme $0
(c’est l’élément précédent sur la pile).
Ex: table de symboles pour C
BNF Attribution insérée dans la règle BNF
D ::= T { L.typeh = T.type }L
T ::= int { T.type = entier } L ::= {L1.typeh = L.typeh }
L1, id {L.tab = Ajout(id.nom, L.typeh, L1.tab) } | id {L.tab = Ajout(id.nom, L.typeh, L1.tab) }
D : déclarationL : liste d’identificateursT : type (se place avant en C)
D344
Ex: Table de symboles (Bison)
D: T {$$=$1} L {$$ = $3 };
T : int {$$ = integer() };
L: L ',' id {$$ = add($3, $<ty>0, $1)}
| id {$$ = add($1, $<ty>0, empty() ) };
Dans cet exemple l’action {$$=$1}n’est pas nécessaire:On peut utiliser l’attribut synthétisé type pour l’attribut hérité typeh
Ex: mise en page de formule
• EQN est un programme de mise en page de formules mathématiques (dites "boîtes")
• les indices doivent être plus bas et dans une fonte plus petite.
• P.ex. le texte d’entrée E sub 1 .a est mis en page comme E1.a . Les boîtes servent à l'espacement.
• sub s'associe à droite: E sub i sub j = E sub (i sub j)
D23, D333, D347
Ex: mise en page de formule
• tc: attribut hérité: taille des caractères
• ht: attribut synthétisé: hauteur totale de la boîte
D334
S ::= B
B ::= B1 B2
B ::= B1 sub B2
B ::= texte
B.tc = 10 au départ, taille 10 pointsS.ht = B.htB1.tc = B.tcB2.tc = B.tcB.ht = max(B1.ht, B2.ht)B1.tc = B.tcB2.tc = Rétrécir(B.tc) plus petit pour l'indiceB.ht = Déplacer(B1.ht, B2.ht) p.ex B1.ht + B2.ht/2B.ht = texte.htn B.tc htn: hauteur des lettres
Même exemple en Bison
%left ' '%right sub%%S : {$$ = 10 } B {$$ = $2};B : B ' '{$$ = $0} B {$$ = max($1, $4)} | B sub {$$ = Rétrécir($0)} B {$$ = Déplacer($1,$4)} | texte {$$ = $1 * $0};Avant chaque B, une action met tc dans $$ pour qu'il soit
disponible dans B comme $0. On peut omettre {$$=$0} avant le premier B d'une règle pour B.
S.ht B.htB.tc
Utiliser les attributs synthétisésEx: types dans les déclarations (Pascal)• La règle D ::= L ‘ : ’ T fait que le type n’est pas
disponible dans L ::= id, L• Solution (peu naturelle): mettre le type dans la liste:
D ::= id R
R ::= , id R | : T
T ::= integer | ...
D350
D
id , id : integer
R
RT
Ex: types dans les déclarations (Pascal)
D ::= id L D.tab = Ajout(id.nom, L.type, L.tab)
L ::= , id L1 L.type = L1.type
L.tab = Ajout(id.nom, L1.type, L1.tab)
L::= : T L.type = T.type
L.tab = vide
Tous les attributs sont synthétisés.
Ordre statique
• Pour certaines grammaires attribuées (l-ordonnées, fortement non-circulaires) on peut calculer un ordre de visite de l’arbre à partir de la grammaire.
• Utilisé dans les systèmes FNC-2, GAG, etc.
Ordre dynamique
• Si les conditions précédentes ne sont pas vérifiées, on peut toujours construire l’arbre d’analyse, son graphe de dépendances, et le trier pour faire le parcours.
• En pratique, cette technique est coûteuse.
• Utilisé p.ex. dans le système Elegant
Génération de CodeGénération de Codepour langages impératifspour langages impératifs
D, Chap. 2 et 7 à 10W, Chap. 2 : P-Code Chap. 12 : code optimisé
Plan
1. Machines abstraites: ex. P-machine1. Expressions2. Instructions3. Données
2. Machines concrètes: ex. MIPS1. Expressions2. Instructions3. Allocation mémoire
1. Machines abstraites
• Ont des instructions proches d'un langages:– P-machine pour Pascal– JVM pour Java– WAM pour Prolog– CAM pour CAML
• Simplifient les détails des machines concrètes– Registres, alignement, …
• Code portable
Pile d’expressionsLes expressions peuvent se calculer avec une pile en notationpostfixe (appelée aussi Polonaise inversée)(p. ex. calculette Hewlett-Packard)
Exemple : ((3 * 5) + (7 * 4)) / (3 + 5)
Postfixe : 3 5 * 7 4 * + 3 5 + /
Parenthèses inutiles
nombres empileropération prendre les données sur la pile
les remplacer par le résultat
Ce principe est employé dans les langages à pile PostScript et FORTH,et dans les machines abstraites: P-machine (Pascal), JVM (Java)
53 *}
47 *15
} 53 *43
} 843 /
2815
} + } 5
W8
P-instructions pour expressions
STORE : tableau représentant la mémoireune partie (jusque SP) est utilisée pour la pile.
Les deux valeurs en sommet de pile doiventavoir le même type numérique (entier, réel, ou adresse)
P. inst. Signification Condition Résultatadd N STORE [SP-1] := STORE [SP-1] +N STORE [SP] ;
SP := SP-1(N,N) (N)
sub N STORE [SP-1] := STORE [SP-1] -N STORE [SP] ;SP := SP-1
(N,N) (N)
mul N STORE [SP-1] := STORE [SP-1] *N STORE [SP] ;SP := SP-1
(N,N) (N)
div N STORE [SP-1] := STORE [SP-1] /N STORE [SP] ;SP := SP-1
(N,N) (N)
neg N STORE [SP] := -N STORE [SP] (N) (N)and STORE [SP-1] := STORE [SP-1] and STORE [SP] ;
SP := SP-1(b,b) (b)
or STORE [SP-1] := STORE [SP-1] or STORE [SP] ; (b,b) (b)not STORE [SP] := not STORE [SP] ;
SP := SP-1(b,b) (b)
equ T STORE [SP-1] := STORE [SP-1] =T STORE [SP] ;SP := SP-1
(T,T) (b)
geq T STORE [SP-1] := STORE [SP-1] T STORE [SP] ;SP := SP-1
(T,T) (b)
leq T STORE [SP-1] := STORE [SP-1] T STORE [SP] ;SP := SP-1
(T,T) (b)
les T STORE [SP-1] := STORE [SP-1] <T STORE [SP] ;SP := SP-1
(T,T) (b)
grt T STORE [SP-1] := STORE [SP-1] >T STORE [SP] ;SP := SP-1
(T,T) (b)
neq T STORE [SP-1] := STORE [SP-1] T STORE [SP] ;SP := SP-1
(T,T) (b)
W8
Variables globales de type simple
Les variables ont deux emplois différents :
• à gauche de := on emploie l’adresse codeG
• à droite de := on emploie la valeur. codeD
codeG(var) = ldc a addr(var)
codeD ajoute la valeur en sommet de pile
codeD(var) = codeG(var) ; ind type(var)
codeD(e1 op e2 ) = codeD(e1) ; codeD(e2 ) ; P(op)
code(v := e) = codeG(v) ; codeD(e2 ) ; sto type(v)
P(op) est la P-instruction correspondante, p.ex. P(+) = add iLes variables globales ont une adresse fixe ; on les met en début de la pile. L'adresse est stockée dans la table de symboles.Les variables locales des procédures non récursives peuvent aussi recevoir une adresse fixe (cas du COBOL, du FORTRAN IV)
W9
P-instructions pour variablesP-instr Signification Condition Résultat
ldo T q SP := SP + 1 ;STORE [SP] := STORE [q]
q : adresse (T)
ldc T q SP := SP + 1 ;STORE [SP] := q
Type(q) = T (T)
ind T STORE [SP] := STORE [STORE [SP]] (a) (T)
sro T q STORE [q] := STORE [SP] ;SP := SP - 1
(T)q : adresse
sto T STORE [STORE [SP - 1]] := STORE[SP] ;SP := SP - 2
(a,T)
W9
1.2 Instructions Instructions de branchement
Ces instructions modifient le Program Counter (PC)
goto absolu ujp q : goto qgoto conditionnel fjp q : if top = false then goto qgoto calculé ixj q : goto top + q
W11
Conditionnelles
codeD(e)
fjp
code(st1)
ujp
code(st2)
if
e
then
st1
else
st2
codeD(e)
fjp
code(st)
if
e
then
st
(a) deux branches (b) une seule branche
W12
Boucles
codeD(e)
fjp
code(st)
ujp
code(st)
whilee
do
st
repeat
st
until
e
(a) boucle while (b) boucle repeat
codeD(e)
fjp
W12
Exemplesldc a 5
ind i
ldc a 6
ind i
grt i
fjp
ldc a 7
ldc a 5
ind i
sto i
ujp
ldc a 7
ldc a 6
ind i
sto i
else
cb
:=
if
a
b
>
then
ca
:=
while
a
b
>
doc
c1+:=
a
a
b
-
:=
W13
Case
case e of 0 : st0 ; . . . k : stk
end
codeD e
neg iixj q
codeD st0
ujp
codeD st1
ujp
codeD stk
ujp
.
.
.
ujpujp
ujp
.
.
.
Laisse la valeur du sélecteuren sommet de la pile
Branchement indexé vers la fin de latable des branchements
Branchement vers la fin de l’instruction
Table des branchements
q
Code du cas 0
W14
1.3 Données1.3.1 Tableaux
• En Pascal les types d'indice d’un tableau sont statiques et font partie de son type
• Les extensions de Pascal ont des tableaux de taille dynamique
• La plupart des compilateurs rangent les tableaux en mémoire par ligne: le dernier
indice varie d'abord
• Les compilateurs Fortran rangent les tableaux par colonne
• Appelons li la borne inférieure du ième indice
• ui sa borne supérieure
• di sa dimension = ui - li + 1 (si ui > li)
W16
Indexation de tableaux
L’adresse de a[i1, ..., ik] est :
addr(a) + [ik - lk
+ (ik-1 - lk-1) * dk . . .
+ (i1 - l1) * d2 * ... * dk] * sizeof (type)
Dans la P-machine, on précalcule les parties constantes.
Autre optimisation: schéma de Horner
W17
P-code pour l'indexation
codeG c[i1, ..., ik] = ldc a (c)
codeI [i1, ..., ik] c
codeI [i1, ..., ik] n = codeD i1 ; ixa n* d2 * ... * dk
codeD i2 ; ixa n* d3 * ... * dk...
codeD ik ; ixa n
dec a n * (lk + lk-1 * dk + ...)
ixa q : sp i ixa q s s+i*q
W20
1.3.2 Records
En Pascal les types ont une taille statique
pour chaque champ on a un déplacement relatif :
On le stocke dans la table des symbolesEx.record taille déplacement
a : array [1 .. 5, 1 .. 5] of boolean 25x1 octets 0x : integer 4 octets 25y : real 4 octets 29
end
Traduction
codeG (v.ci) = codeG (v);inc a dépl(ci)
W24
Pointeurs
Les cases créées par new ne peuvent pas être allouées sur la pile carelles vivent plus longtemps que la procédure qui les a créées.On réserve une zone de mémoire spéciale, le TAS.
La gestion du tas
- récupérer la place lors d’un dispose- risque de fragmenter la mémoire
p.ex. : organisation en pools où toutes les cases ont la même taille.
PILE TAS
SP
W25
Procédures imbriquéesDistinguer l’imbrication
• statique du texte des procédures
• dynamique des appels de procédure
Une occurrence de variable a une différence d’imbrication statique :
procédure p
var x : integer
procedure q
... x ...
end
1 niveau d’imbrication statique
L ’imbrication de procédures n ’existe pas en C, FORTRAN, Java, ...
W28
proc p(a)var b;var cproc q
var avar qproc r
var bbac
.
.
.
abq
proc svar a...aq
aq
012
0
0
1
10
Les flèches montrent pour chaque utilisation de variable, la définition associée
Les nombres indiquent la différence d’imbrication des occurrences de variables.
W31
Prédécesseurs statiques
• Le lien d’accès (ou prédécesseur statique) pointe vers l’activation la plus récente de la procédure englobante
program h ;var i : integer ;proc p
var aproc r
var i : reali := i/2 ; a := 5
if i > 0then i := i - 1 ; pelse r
fii := 2 ;p ;p
end.
h
p
p
p
r
p
r
Liens
d’ac
cès
Figure : Un programme et son arbre d ’activation (ou d’appels). Les crochets donnent l ’imbrication des procédures.
W29
Accès non local
• Chaque occurrence de variable est identifiée par sa différence d’imbrication d et son déplacement o
• Pour trouver son adresse, on suit d liens d’accès (donne le bloc d’activation de sa déclaration) et on ajoute o.
D461 W36
P-instructions pour variables
P-instruction Signification Commentaire
lod T p q SP := SP+1 ;STORE[SP] :=STORE[base(p,MP)+q]
p = profondeur relativeq = adresse relative
lda T p q SP := SP+1 ;STORE[SP] :=base(p,MP)+q
str T p q STORE[base(p,MP)+q] :=STORE[SP] ;SP := SP-1
lod (load) charge une variable sur la pile
lda (load address) charge l’adresse d ’une variable
str (store) remet un résultat dans une variable.
base(p,a) = if p = 0 then a else base(p - 1, STORE[a + 1]) fi
W37
Mise à jour
• Lors d’un appel, le nouveau lien d’accès est obtenu en suivant d liens d’accès de l’appelant, où d est la différence d'imbrication de l'appel.
Exemplesproc p
proc q
q
proc rproc p
qproc q
p
proc rproc q
proc pq
pq
*p
q
d = 0(a)
d = 1(b)
d = 2(c)
*p
q
MP
SP
r*
q
q
MP
SP
r
*
p*
Bloc d'activation
• Les appels de sous-programme sont gérés sur la pile
• Chaque appel a son bloc d'activation avec ses données:
PILE Val de fonction
Préd. statique
Préd. dynamique
Val de EP
Adresse de retour
RET
paramètres
Vars. locales
Pile locale
TAS
EPSPMPp
s
pp
W33
Protocole d'appel
1. Mettre le prédécesseur statique à la dernière instance de la procédure englobant q.
2. Mettre le prédecesseur dynamique à l'appelant p.3. Préserver EP, le registre bornant la pile4. Calculer les arguments dans l'appelant5. Mettre MP sur le bloc6. Sauver l'adresse de retour7. Sauter à q8. Mettre EP à jour
W42
P-Instructions pour l’appel d’un sous-programme
P-Instruction Signification Commentairemst d STORE[SP+2] := base(d, MP) ; d différence d'imbrication
STORE[SP+3] := MP ; prédécesseur dynamiqueSTORE[SP+4] := EP ; Sauver EPSP := SP+5 Les paramètres peuvent déjà
être évalués à partir deSTORE[SP+1].
code(args)cup p q MP := SP - (p+4) ; p donne la place requise en
mémoire pour les paramètresSTORE[MP+4] := PC ; Sauver l'adresse de retourPC := q Aller à l'adresse de début q de la
procéduressp s SP := MP + s - 1 s taille de la partie statique de la
zone des donnéessep pp EP := SP + pp ; pp profondeur max. de la pile locale.
if EP > NP Test de Collisionthen erreur ("store overflow") entre pile et tas
base(p,a) = if p = 0 then a else base(p - 1, STORE[a + 1]) fi
W43
Protocole de retour
Remettre les registres à jour:
• SP
• PC
• EP
• MP
W44
P-instructions de retour de sous-programme
P-Instr. Signification Commentaireretf SP := MP ; Le résultat de la fonction est
au sommet de la pile localePC := STORE[MP + 4] ; Branchement de retourEP := STORE[MP + 3] ; Restaurer EPif EP > NPthen error ("store overflow")fiMP := STORE[MP + 2] Pointeur PrD
retp SP := MP - 1 ; Vraie procédure sans résultatPC := STORE[MP + 4] ; RetourEP := STORE[MP + 3] ; Restaurer EPif EP > NPthen error ("store overflow")fiMP := STORE[MP + 2] Pointeur PrD
W44
2. Machines concrètes
• Les machines concrètes = processeurs
• ex. Pentium, MIPS, G4, ARM
• Contraintes:– de registres– d'alignement– de parallélisme (pipeline, unités parallèles)
1. Expressions
• sans partage : Pile– Réordonnancement– Allocation de registres
• avec partage : DAG– utilisation avec pipelines
Registres
Types de registres courants:•universels•spécialisés:
• réels flottants •données•adresses•base•index•PC: program counter (prochaine instruction)•CC: codes de conditions
Les processeurs RISC ont de nombreux registres universels
W553
Allocation de registres
D613 W565
Technique simplifiée pour les arbres : 1. réorganisation des calculs 2. allocation des registres par simulation de pile
Réordonnancement
Idée : on évalue d’abord l’expression qui emploie le plus de registres car 1 registre sert pour le résultat.
Exemple : +
t1 t24 reg 3 reg
R4R3R2 RES 2R1 RES 1 RES 1 RES 1 RES}+
calcul de t1 ; calcul de t2 ; addition
D613 W566
Calcul du meilleur ordre
calculer récursivement le nombre de registres optimal :
r ( t1 op t2) = max (r1, r2) si r1 r2
r1 + 1 si r1 = r2
où r1 = r(t1)
r2 = r(t2)
Le cas de base dépend du type d’instructions de la machine.Pour une architecture load/store, il faut 1 registre.
{
D614 W567
Exemple
3
2
2
2
a b c
d e
1 1 1
1 1
_
+
+
_
(a + b) - (c - (d + e)) requiert 3 registres sur une machine load/store
D614 W568
type abstrait Pile
• empiler(P, E) ajoute E sur P• dépiler(P) enlève le sommet de P et le renvoie• échanger(P) échange le sommet et celui en dessous• sommet(P) renvoie le sommet sans changer la pile• sommet2(P) renvoie l’élément en dessous du sommet
Algorithme d’allocation
• pilereg : pile statique de registres libres
• pilereg n’est jamais vide
• Le sommet de pilereg contiendra le résultat
• pilereg doit être remis dans l’état initial
• Une pile de temporaires est employée lorsqu ’il n ’y a plus assez de registres (> r)
D615
Algorithme : cas de base
la variable est chargée dans le registre :ProdCode(n) :
si n est une feuille (une variable d’adresse var):
émettre( lw sommet(pilereg) , 0(var) )
Note: diffère de D617 car prévu pour une machine load/store (p.ex. MIPS)
D617
Algorithme : cas 3
• s’il est plus avantageux de respecter l’ordre : r1r2, r>r2
ProdCode(n1);
R := dépiler(pilereg);
ProdCode(n2);
émettre(op, R, R, sommet(pilereg)) ;
empiler(pilereg, R);
Algorithme : cas 2
s’il est plus avantageux d’inverser l’ordre càd si r2r1, r>r1: ProdCode(n2); R := dépiler(pilereg); ProdCode(n1); émettre( op, R, sommet(pilereg), R); empiler(pilereg, R);
Algorithme : cas 4
si les deux sous-termes utilisent plus de registres qu’il n’y en a: r1, r2 r
ProdCode(n1);
T := dépiler(piletemp);
émettre(sw sommet(pilereg), T ); // stocke le résultat en T
ProdCode(n2);
émettre(lw sommet2(pilereg), T ) ; // charge T
émettre(op sommet(pilereg), sommet2(pilereg), sommet(pilereg) );
empiler(piletemp, T)
Exemple
pour (a + b) - (c - (d + e)) avec 2 registres :lw R1 , alw R2, badd R1, R1, R2sw R1,T // T := a+blw R1, dlw R2, eadd R1,R1,R2 // R1 = d+elw R2, csub R1, R2, R1 // R1 = c-(d+e)lw R2, Tsub R1, R2, R1 // R1 = (a+b)-(c-(d+e))
3
2
2
2
a b c
d e
1 1 1
1 1
_
+
+
_
Bloc de base
• le graphe de flot de contrôle (ou ordinogramme) donne les successions possibles d’instructions
• un bloc de base est une suite d’instructions sans branchements entrants ni sortants.
D579 W563
Bex: while B do S
S
Partage de calculs : DAG
• représente les calculs d’un bloc de base
• les sous-expressions communes sont partagées
• forme: graphe acyclique :– les feuilles sont des variables ou des constantes– les nœuds sont des opérateurs– les nœuds sont étiquetés par les variables affectées
– on distingue la valeur initiale (x0) et la valeur finale
• Hyp. simplificatrice : pas de synonymie
D598 W575
Exemple : DAG simple
c := a[i]+1;
b := 1-a[i];b c- +
1 [ ] 1
a i
Registres pour DAG
• En cas de partage, la technique en pile n’est pas toujours optimale
• On divise le DAG en arbres en coupant aux nœuds partagés
D619
b c- +
1 [ ] 1
a i
t b c [ ] - +
a i 1 t t 1
Pipelines
• Dans les processeurs à pipeline, le résultat n’est disponible qu’après un délai
• Il faut insérer des instructions indépendantes• Algorithme glouton:
– tri topologique du DAG– choix d’une instruction parmi les minimales– calcul des collisions
• Augmente l’emploi des registres
W580
Algorithme pour pipelines
Simplification : le délai est 1 cycle.
Candidats := minimaux(DAG); /* instructions minimales non exécutées */
Collisions := {instruction précédente }; /* instructions en cours d’exécution */
répéter
b := ChoixHeuristique(Candidats \ enCollision(Candidats, Collisions );
si b = erreur alors insérer NOP; Collisions := vide;
sinon enlever b de Candidats; Collisions := {b};
ajouter dans candidats les nœuds dont b était le seul prédécesseur
jusqu’à Candidats vide
2. Instructions
• 2.1 conditionnelles
• 2.2 boucles
• 2.3 case
Instructions conditionnelles
La plupart des machines ont un jeu d’instructions qui contient :
exemple : MIPS
un goto absolu j q : goto q
un goto conditionnel beq $rs $rt q : if $rs = $rt then goto PC+q
un goto calculé jr $r : goto $r
change le PC (program counter), le registre qui contient l ’adresse de la prochaine instruction.
Instructions conditionnelles
codeD(e)beq $r, $zero
code(st1)
j
code(st2)
ife
thenst1
elsest2
codeD(e)
beq $r, $zero
code(st)
if
e
thenst
(a) deux branches (b) une seule branche
Instructions traduisant les instructions conditionnelles
Boucles
codeD(e)
beq $r, $zero
code(st)
j
code(st)while
e
dost
repeat
stuntil
e
(a) boucle while (b) boucle repeat
codeD(e)
beq $r, $zero
Génération de code pour les boucles
Instruction case
case e of 0 : st0 ; 1 : st1 ; . . . k : stk
end
codeD e sub $t1, q, $t1
jr $t1
code st0 j exit
codeD st1 j exit
code stk j exit
.
.
.
j L1j L0
j Lk...
Laisse la valeur du sélecteurdans un registre $re
Branchement indexé vers la fin de laTable des branchements
Branchement vers la fin de l’instruction
Table des branchements
q :exit :
L0:
Lk :
3. Allocation de mémoire
• les variables ont deux emplois différents :– à gauche de « := », c’est une adresse
– à droite de « := », c’est la valeur stockée à cette adresse
• le langage source admet-il:– la récursivité ? si oui: nouvelles variables à créer
– l’imbrication de procédures ? si oui : retrouver les liens
Allocation
• La mémoire est habituellement divisée en:– code : lecture seule– constantes : lecture seule– variables globales : adresse statique– pile : pour les variables locales de procédures
récursives– tas : pour les autres variables dynamiques
Tableaux• En Pascal les dimensions d’un tableau sont statiques
• Les extensions de Pascal ont des tableaux Algol-68 taille dynamique Ada C
}var < nom > : array [l1 .. u1, ..., lk .. uk] of < type > ex: var a : array[1..2,1..3] of integer;
la ième dimension di = ui - li + 1 (si ui > li)Rangement
• par ligne:le dernier indice varie d’abord: ex: a[1,1] a[1,2] a[1,3] a[2,1] a[2,2] a[2,3]
• par colonne
le premier indice varie d’abord: ex: a[1,1] a[2,1] a[1,2] a[2,2] a[1,3] a[2,3]
Tableaux : indices
• Pour accéder à un élément a[e,f,g] on calcule l’adresse a+( (e-l1)* d2 *d3 + (f-l2)*d3 + (g-l3) )*t
• en Pascal seuls e,f,g sont dynamiques
• on peut précalculer la partie statique
• Le schéma de Horner limite les multiplications : e* d2 *d3 + f *d3 + g = (e* d2 + f) *d3 + g
RecordsEn Pascal tous les types ont une taille statique
pour chaque champ on a un déplacement relatif Ex. record
a : array [1 .. 5, 1 .. 5] of boolean 1 byte par bool.x : integer 4 byte par int.y : real 8 byte par real
end
déplacement0
252937
Les machines ont souvent des contraintes d’alignement :p.ex. un réel doit avoir une adresse multiple de 4
on doit laisser du remplissageou réordonner les champs
Traduction d ’un accès à un champ
codeG (v.ci) = codeG (v) ; # laisse l ’adresse de v dans un registre $t
add $t, (ci), $t #laisse l ’adresse du champ dans $t.
# (ci) est le déplacement de ci
Pointeurs
Les cases créées par new ne peuvent pas être allouées sur la pile carelles vivent plus longtemps que la procédure qui les a créées.On réserve une zone spéciale, le TAS.
La gestion du tas
- comment récupérer la place lors d’un dispose ?- Si le dispose est automatique => algorithme de ramasse-miettes- risque de fragmenter la mémoire si plusieurs tailles => algorithmes de
regroupement
PILE TAS
SP
D488 W25
Procédures récursives
• Chaque appel à une procédure récursive doit avoir de nouvelles variables locales
• L’allocation est LIFO, donc en pile
• Les instances de procédures sont donc stockées dans une pile.
• Souvent un registre SP pointe vers le sommet de la pile.
D433
Pile de contrôle
L’exécution des procédures (récursives) est imbriquée représentable par une pileLe contenu de la pile représente une branche de l’arbre d’activation.La pile contient des blocs d’activation.Le registre FP (frame pointer) point sur le bloc courant, sommet de la pile.
Chaque bloc contient:• la valeur renvoyée (pour une fonction)• l’état des registres à restaurer, en particulier:
• l’adresse de retour est la valeur du PC (program counter)• le lien d’accès ou prédécesseur statique (si imbrication)• le lien de contrôle ou prédécesseur dynamique (état du FP) pointe vers le bloc appelant• les paramètres (parfois dans des registres)• les variables locales• des temporaires (pour l ’évaluation d ’expressions).
Protocole d ’appel
Répartit les rôles de l ’appelant et de l ’appelé.
Par exemple:1. l ’appelant crée le bloc d’activation2. l ’appelant évalue les arguments et les met dans les registres ($a) ou dans le bloc3. l ’appelant calcule le lien d ’accès en suivant le sien.4. l ’appelant sauve le PC et saute au début de l ’appelé5. l ’appelé alloue et initialise les variables locales6. l ’appelé commence son exécution (qui calcule la valeur de retour)Au retour:1. l ’appelé restaure les registres, en dernier le PC (saut de retour)
D446-449
Accès aux noms non locaux
• Algol, Pascal, Ada, ont des procédures imbriquées• Ils emploient la portée statique : les variables sont
recherchées dans le texte englobant• la portée d’une variable est le sous-programme où elle
est déclarée• On emploie la déclaration la plus locale
D453
Passage de paramètres
• Diffère d’un langage à l’autre
• Principaux types :– par valeur – par référence (ou par variable)– par copie (in out)– par nom
D467
Passage par valeur
• Le paramètre est comme une variable locale
• L’appelant évalue la valeur de l’argument et le place comme valeur initiale du paramètre
Passage par référence
• L’adresse de l’argument est passée.
• En Pascal: noté par var
Exemple
procedure Troquer(var x, y : integer);
var t : integer;
begin
t := x;
x := y;
y := t
end;
N’aurait aucun effet en passage par valeur !
Passage par copie
• Utilisé p.ex. en Ada (mode in out)
• La valeur de l’argument est copiée dans le paramètre ; au retour la valeur du paramètre est copiée dans l’argument
• Même effet que le passage par référence sauf en cas de synonymie.
D470
Exemple
program ValeurResultat(output);
var a : integer;
procedure Hasardeuse(var x:integer);
begin x:=2 ; a := 0 end;
begin
a:=1; Hasardeuse(a) ; writeln(a)
end.a=0 si passage par référence, a=2 si passage par copie.
D471
Passage par nom
• Remplacement textuel du paramètre par l’argument (comme pour une macro)
• Utilisé seulement en Algol-60.
D471
Exemple
procedure Troquer(x,y:integer);
var t:integer;
begin
t := x;
x := y;
y := t
end;
Troquer(i, t[i] ) met i dans t[t[i]] !
Passage de fonctions en paramètre
• Pour les langages à portée statique (p.ex. Pascal): On doit passer l’adresse de début du code et le lien d’accès
Exemple : fonction en paramètre
Program HP
proc p(func h)
h
proc qfunc f
p(g)
p(f)func g
q
HP
q
p
f
HP
q
p
f
g
p
(a)Un programme avec
fonction en paramètre
(b)situation de la pileaprès appel de p(f)
et (en pointillé)après appel de h
(c)situation de la pileaprès appel de p(g)
et (en pointillé)après appel de h
Introduction à la Introduction à la SémantiqueSémantique
Qu’est-ce que c’est ?
• La sémantique donne le sens d’un langage
• Pour un langage de programmation, elle permet de prévoir le comportement du programme
• Pour un langage séquentiel, on suppose que le seul comportement observable est le résultat final.
A quoi ca sert ?
La sémantique permet de:– décider ce que doit faire un programme dans des cas
complexes– calculer si des programmes sont équivalents, ce qui
permet à un compilateur de remplacer l’un par l’autre.
– dériver la plupart des analyses de programmes– construire des compilateurs fiables
Types de sémantique
• Sémantique opérationnelle : décrit comment un programme peut s’exécuter
• Sémantique dénotationnelle : donne un objet (p.ex. une fonction des données vers les résultats) comme sens d’un programme
• Sémantique axiomatique : donne des règles pour raisonner sur les programmes
Liens entre types de sémantique
les 3 types de sémantique ont leur utilité:– la sémantique opérationnelle facilite la construction de
compilateurs– la sémantique dénotationnelle donne les concepts du
langage– la sémantique axiomatique est la plus utile pour les
programmeurs
On peut passer de l’une à l’autre en démontrant leur équivalence.
Sémantique opérationnelleSémantique opérationnelle = on donne le sens du programme en donnant un ensemble d’exécutions possibles.
+ Avantages :
+ intuition claire+ facilité de construire un interpréteur+ traitement du non-déterminisme
Inconvénients fréquents :
non compositionnel : on ne peut pas donner de sens à certaines parties
peu abstrait : difficile de prouver que 2 programmes sont équivalents.
difficile de construire un compilateur optimisant.
Sémantique Opérationnelle Structurée
• On évite les inconvients habituels en ajoutant:– Compositionalité : On donne des règles
d’inférences qui donnent les exécutions possibles en fonction de celles des composants.
– Abstraction : On ajoute des règles de similarités disant quelles exécutions sont équivalentes.
Système de déduction
Une règle d’inférence est de la forme :
1
0
F F
Fn ... prémisses
conclusion
« Si on a prouvé F1 ... Fn on peut déduire F0 ».
(Ici les Fi décriront des exécutions possibles)
S’il n’y a pas de prémisses, on l’appelle un axiome.
Une règle peut contenir des méta-variables càd des variables qui représentent n’importe quel texte d’une catégorie syntaxique donnée. On l’appelle alors un schéma de règle.
Une preuve est un arbre fini composé de règles d’inférences ; elle a une conclusion à la racine, appelée théorème.
La théorie d’un système de déduction est l’ensemble des théorèmes.
Sémantique Opérationnelle Structurée : Définition
Les formules qui nous intéressent:
e1 e2
« de l’état e1, par un pas d’exécution on peut arriver dans l’état e2 »
Etats
Les états doivent contenir l’information nécessaire à l’exécution :– le point d’exécution dans le programme (ici, on le
représente par la partie du programme restant à exécuter);
– la valeur des variables;– l’état de la pile de récursion;– etc.
Exemple : expressions Pascal
• on ne traite d’abord que des expressions sans variables et sans effet de bord, donc la mémoire n’est pas (encore) nécessaire.
Règles
• de congruence : on peut simplifier n ’importe quelle sous-expression en Pascal :
où f est un opérateur ou un appel de fonction
• de calcul : pour chaque opérateur Pascal, on donne des règles pour calculer la valeur du résultat à partir de la valeur des arguments– par exemple :
not true false
not false true
),..,',..,( ),..,,..,(
'
11 eeeeeeee
mimi
ii
ff
Exemple
• Pour C: les arguments sont évalués de gauche à droite:
• Si le côté gauche suffit, on n’évalue pas le côté droit:
• le premier pas est la règle de congruence pour le 2ème argument; sa prémisse est justifiée par la règle de calcul pour *.
• le second pas est le calcul.
Expressions avec mémoire
Un état a maintenant 2 composantes:– l’expression à évaluer;
– l’état de la mémoire : une fonction Identificateur Valeur
• de congruence : on peut simplifier une sous-expression
• de calcul :
(not true, m) (false, m)
(not false, m) (true, m)
• de recherche de valeur en mémoire: (id, m) (m(id) , m)
)'),,..,,..,(( )),,..,,..,((
)m' ,( ) ,(
e e
'
i11
'
i
mfmf
m
eeeeee
nni
i
Exemple : expression
m) y),*(5 + 5)*((4 ) ),*5( )*4((m) 5,*(4 ) ,*4(
m) (5, m) (x,
myxmx
congruence
recherchecongruence
calcul *
( * , )(( * ) (5* ), )
4 54 5
m (4 *20, m) m (20 + (5* y), m)
y
congruence
m) 2),*(5 + (20 ) ),*5( 20(m) 2,*(5 ) ,*5(
m) (2, m) (y,
mymy congruence
recherche
congruence
(5* , )( (5* ), )
220 2
m (10, m) m (20 + 10, m)
calcul *
congruence
( , )20 10 m (30, m) calcul +
Instructions• affectation :
– d’abord évaluer l’expression – ensuite modifier l’état pour la variable avec la constante ainsi calculée
• conditionnelle : – d’abord évaluer l’expression– ensuite évaluer la branche choisie (2 cas)
• boucle :– créer une copie de l’expression– l’évaluer (en gardant l ’expression du while pour les itérations)
• séquence :– évaluer la première instruction– lorsqu’elle se termine exécuter la 2ème instruction
InstructionsAffectation : 1. (e, m) (e’, m ’)
congruence(id := e, m) (id := e’, m ’)
calcul :=
2. (id := n, m) (skip, update (id, n, m)) instruction vide
Conditionnelle : 1. (e, m) (e’, m)(if e then S1 else S2m) (if e’ then S2 else S2m)
2.1 (if true then S1 else S2, m) (S1, m)
2.2 (if false then S1 else S2, m) (S2, m)abréviation
(if e then S1, m) (if e then S1 else skip, m)
Boucle : (while e do S, m) (if e then begin S ; while e do S end, m)
Séquence : (S1, m) (S’ 1, m’)
(S1 ; S2, m) (S’1 ; S2, m’)
(skip ; S, m) (S, m)
e : exprid : identificateurn : const
Système de transitions
• Les règles de la SOS définissent une machine à états infinis, qu’on appelle aussi un système de transitions.
• On peut aussi la voir comme un graphe.
Questions importantes
• 1. Il y a-t-il plusieurs suites de flèches partant d’un même état ? (déterminisme)
• 2. Certaines de ces suites sont-elles infinies ? (terminaison)
• 3. Aboutissent-elles toutes au même état ? (confluence)
• 4. Quelle forme ont les états finals ? Les état finals corrects ?
Déf. : C final = il n’y a pas de C’ : C C’ (forme normale)
• 5. Les flèches préservent-elles les types ?
Equivalence sémantique
• La SOS détaille les calculs, donc peu de programmes sont équivalents
• Deux programmes P,Q sont équivalents ssi:– ils mènent aux mêmes états finaux– ils bouclent dans les mêmes circonstances :
• p*q signifie qu’un chemin d’exécution mène de p à q.
• p signifie qu’un chemin d’exécution infini part de p.
Sémantique dénotationnelle
• Donne pour chaque partie de programme, un objet qui est son sens: p.ex. un programme déterministe séquentiel dénote une fonction
• Compositionnelle si le sens d’un programme dépend du sens de ses parties mais pas de leur forme.
• Abstraite si le sens ne conserve que l’information qu’on peut observer.
Compositionnalité
• Si S1, S2 sont deux instructions dont le sens de chacune est une fonction des états de mémoire (de départ) vers les états de mémoire d’arrivée : f1, f2, alors la sequence S1; S2 a comme sens la fonction composée de f1 et f2
Sémantique axiomatique
• Donne des règles pour construire des programmes
• Les règles peuvent être démontrées à partir d ’une autre sémantique (p.ex. opérationnelle)
• Notation : {pre} P {post} signifie que si P démarre dans un état de mémoire satisfaisant pre, alors il se termine dans un état de mémoire satisfaisant post.
pre post
Exemple de règles
• règle pour l’affectation:
{ post [e/x] } x := e {post }
la règle est valide si x ne peut être accédée que par ce nom (pas de synonymie)
• règle pour le if:
{ pre et b} t {post }
{ pre et non b} e {post }
{ pre } if b then t else e {post }