La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

Syntaxe et sémantique des langages de programmation

Présentations similaires


Présentation au sujet: "Syntaxe et sémantique des langages de programmation"— Transcription de la présentation:

1 Syntaxe et sémantique des langages de programmation
IHDC 2105 Syntaxe et sémantique des langages de programmation Pierre-Yves Schobbens Bureau 409 081/ rue Grandgagnage 21 5000 Namur

2 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

3 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

4 Chapitre 0. Introduction

5 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,

6 Paradigmes de langages de programmation
1. impératifs : Algol, Pascal, Ada, Fortran, Cobol, Modula, C axés sur l’affectation 2. fonctionnels : Lisp, ML, Hope, Miranda, Haskell, FP basés sur l’évaluation des expressions 3. logiques : Prolog, Gödel basés sur la preuve de formules 4. orientés objet : Simula, Smalltalk, C++, Eiffel, CLOS, Java basés sur l’héritage N.B. : Les paradigmes ne sont pas mutuellement exclusifs.

7 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

8 Rôle d’un compilateur compilateur Code cible Code source compilateur
Code machine du compilateur compilateur d’implémentation Code d’implémentation du compilateur Générateur de compilateur (Flex, Bison) Spécification

9 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 reconnaître les « mots » avant 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 arrière

10 Ex. compilateurs GNU C ANSI 68000 Pentium Power Ada 95 C++ Pascal
Front-ends (1-4) partie avant Middle-end (5) Back-ends (6-9) partie arrière

11 Outils pour les langages
D17 Outils pour les langages Editeurs structurels : édition intelligente Analyseurs : p.ex. pour la rétro-ingénierie, Y2K Paragrapheurs : mettent en page

12 Chapitre 1 : Analyse Lexicale
Rôle : retrouver les mots qui composent le texte

13 Place de l’analyse lexicale
Analyseur lexical Texte source = caractères « mots » = lexèmes Automate fini générateur d’analyseur lexical Expressions régulières

14 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?

15 Définitions D112 : 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 e : phrase vide . : concaténation (parfois le . est omis)

16 Propriétés x . e = x = e . x neutre
D113 Propriétés x . e = x = e . x neutre x . (y. z) = (x . y) . z associativité Ces propriétés sont complètes

17 Ordres X est un préfixe de Y : Y commence par X
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 »

18 Opérations sur les langages
D114 Opérations sur les langages

19 D117 Propriétés

20 Expressions régulières
D115 Expressions régulières = les expressions écrites avec aussi noté ou + souvent omis Langage régulier = langage décrit par une expression régulière

21 Exemples d’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)*

22 } Abréviations L = L. L. ... L où n est un naturel Î L L* [a - z]
n fois + L L* [a - z] tous les caractères entre a et z dans le code ASCII = les lettres = a ç b ... z ? e [n- m] n+1 m

23 2. Automates finis D135 caractère courant b a e mot à reconnaître q0 b
état initial état courant c état final ou accepteur

24 D136 Définition Un automate fini (non-déterministe avec transitions vides) se compose de :

25 D137 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)

26 Exemple: constante naturelle décimale de C
[1-9] [0-9]

27 Exemple : multiples de 2 en notation binaire
1 Multiples de 2 en notation binaire : Ils se terminent toujours par 0. Ceci est facile à représenter par un automate non-déterministe

28 Exemple : identificateurs de Pascal
z 9 [a-z 0-9] [a-z] notation :

29 Exemple : Multiples de 7 r a s
Principe: r est le reste par 7 du nombre lu (n) m = n * 10 + a donc s = (r * 10 + a) mod 7

30 Exemple : Multiples de 7

31 Un langage est régulier c’est le langage d’un automate fini
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.

32 Expression  automate cas de base
début (a) Automate pour début e (b) Automate pour e. début a (c) Automate pour le caractère "a".

33 Expression  automate: union
Pour R1 Pour R2 début e (a) Construction de l’automate d’union pour deux expressions régulières

34 Expression  automate: concaténation
Pour R2 début e Pour R1 (b) Construction de l’automate de concaténation pour deux expressions régulières

35 Expression  automate: répétition
Pour R1 début e (c) Construction de l’automate de fermeture pour deux expressions régulières

36 Exemple : constante décimale de C
1 2 F 9 9 4 3 ... 5 1

37 Problèmes 1. L’automate contient des e-transitions
2. Il n’est pas déterministe: moins efficace 3. Il y a beaucoup trop d’états (80 dans l’exemple)

38 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-e É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

39 Idée de la déterminisation
début a1 a2 ... ak S1 S2 Sn ... début a1 a2 ... ak S1, S2 ,..., Sk Dans l’automate déterministe: Un seul chemin vers un état qui représente tous ceux où une copie peut être. Dans l’automate non déterministe: Plusieurs chemins étiquetés a

40 Déterminisation D140 On construit un nouvel automate A’
- 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 d(S,a) = e-succs( {p | p  q, q S} ) 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

41 Exemple : constantes décimales de C Quelques ensembles fermés par e
1 2 F 9 9 4 3 ... 5 1 État initial

42 Constantes décimales de C déterminisé
1 9 9 9 9

43 Exemple Reconnaître une suite de 1, 2, 3 où le dernier chiffre est apparu précédemment. Expression régulière : S* (1 S* 1 | 2 S* 2 | 3 S* 3), où S=[1-3] 1,2,3 1 1 1 1,2,3 1,2,3 2 2 Automate : 2 4 1,2,3 3 3 3

44 Exemple : déterminisation
1 3 0,1,4 1/2 1 1/2 0,1 2,4 2 2 0,1 0,1,2 3 3 1 3 2 0,1 2,3 1/2/3 1/2/3 1 0,1,3 1 0,1 2,3,4 2 0,2 1/3 2 1/3 1 0,1 3,4 2 0,2,4 3 3 1 1 3 1 2 0,3 0,2,3 2/3 2/3 3 2 0,2 3,4 0,3,4 3 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.

45 Algorithme de minimisation
retirer les états non accessibles compléter l’automate Idée : On met les états dans des classes différentes quand on voit qu’ils ont un comportement différent.  : 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 répéter pour chaque K, K’ , K Î p p := ( p \ {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 p ne change pas Fusionner les états d ’une même classe Retirer l’état mort

46 Exemple de minimisation
Minimisation du nombre d’états « Constante décimale de C » Non finaux Finaux 1 9 9 9 9 P : partition des états

47 Exemple de minimisation
Fusion d’états « Constante décimale de C » 1 9 9 P : partition des états

48 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 e 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

49 automate fini  expression régulière (2)
2° étape : résolution des équations où S3 n’apparaît pas dans les expressions x, y. S 3 = x ç y . S ~> S = y* x

50 Exemple 0-9 1-9 S0 S1 S2 1° étape: l ’automate est équivalent aux équations S0 = 0 . S1 | (1 | 2 | … | 9) . S2 S1 = e S2 = e | (0 | 1 | | 9) . S2

51 Exemple (2) 2° étape : résolution par triangulation 1) éliminer S1 :
S0 = 0 | [1-9] . S2 S2 = e | [0-9] . S2 2) résoudre S2 : S2 = [0-9]* . e 3) éliminer S2 : S0 = 0 | [1- 9] . [0-9]*

52 Complémentation On calcule un automate qui accepte le complément de A.
Algorithme: Déterminiser A; Le compléter: Ajouter un état mort; Ajouter des transitions vers cet état (si car. manque). Propriété: chaque chaîne amène dans un seul état. Inverser les états finaux et non-finaux.

53 Exemple de complémentation
complété : il y a une flèche pour chaque caractère de  = [0-9] Automate déterministe [0-9] [1-9] S0 S1 S2 Négation : on intervertit les états finaux [0-9] [0-9] S0 S1 S3 [1-9] [0-9] S2

54 Langages non réguliers
D119 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. y z x si k est le nombre d’états une chaîne plus longue doit faire une boucle.

55 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.

56 Chapitre 2 : Analyse Syntaxique
Rôle : L’analyseur syntaxique retrouve la structure (arbre) grammaticale du programme.

57 Place de l’analyse syntaxique
Analyseur syntaxique Analyseur lexical Arbre mots Automate à pile générateur d’analyseur syntaxique Grammaire non contextuelle (BNF)

58 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 non terminal Ex. : < instruction > : : = if < expr > then < instruction > else < instruction > La règle pour un non-terminal A regroupe toutes les productions de A: | while < expr > do < instruction > | <variable> := <expr> | … terminal

59 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

60 BNF : Exemple : Pascal Exemple 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>.

61 D192 W254 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: a,b,g

62 Historique - Pourquoi « non contextuel » ? (context-free)
Chomsky (en 1956) a choisi ce nom par 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).

63 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.

64 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.

65 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é  * 

66 Exemple produit directement j : program tutu < déclarations >
< instr. composée >. y : program tutu < déclarations > begin < suite d’instructions > end. Comp Comp : < instr. composée > : : = begin < suite d’instructions > end produit program tutu begin end. < programme > *

67 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.

68 Algorithme: Productif
Algorithme qui donne les non-terminaux productifs. Au départ: aucun non-terminal n’est marqué productif tous les terminaux sont productifs répéter parcourir les productions A ::=  si tous les symboles du côté droit  sont marqués productifs alors 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.

69 Algorithme: Accessible
Algorithme qui donne les non-terminaux accessibles. Au départ: seul le symbole de départ S est marqué accessible ré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).

70 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

71 Exemple d’arbre p id R ; D C Arbre de dérivation : S   b I e 
S : : = 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) S: programme R: paramètres du programme D: déclarations C: instruction composée Grammaire 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. (1) (4) (2) (7) (8) (1) (7) (8) (2) (4) Arbre de dérivation : S p id R ; D C   b I e

72 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 : = else x : = 3 Autre exemple: la grammaire précédente est ambigüe

73 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).

74 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)

75 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 | T T ::= T*F | F F ::= id | (E)

76 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>

77 Automate à pile pile a caractère courant
fenêtre de lecture (avance d’un caractère) . sommet = état courant pile l’état définit le sommet de la pile

78 Automates à pile Dé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 états à trouver au sommet de la pile caractère à trouver dans la fenêtre états à pousser sur la pile

79 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).

80 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.

81 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 • … .

82 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 •

83 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.

84 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)

85 Analyse descendante LL(k)
Grammaire LL(k) : Le choix de la bonne expansion doit pouvoir se faire sur base des k prochains symboles à lire.

86 Cas simple Si pour chaque non-terminal chacune des productions commence par un symbole terminal différent alors 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 de faire le bon choix.

87 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

88 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 terminal PREMIER(X) = P[X] si X est non-terminal

89 Exemple : sommes Une somme est une expression Pascal du genre :
x+y+z+w en Pascal, les opérations se regroupent de gauche à droite : ((x+y)+z)+w La grammaire des sommes est donc: S ::= S+id | id | (S) où id est le terminal pour les identificateurs P[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).

90 Comment rendre une grammaire LL(1) ?
éliminer la récursivité gauche factoriser les parties communes

91 Éliminer la récursivité gauche
D201 É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+Aa Une grammaire récursive à gauche n’est jamais LL(k). A ::= Aa | b devient A::= bA’ , A’ ::=aA’|e

92 Algorithme pre : G sans cycle AA, 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 g remplacer Ai ::= Ai a | b par Ai ::= bAi’ , Ai’ ::=aAi’|e

93 Exemple Élimination de la récursivité gauche
Grammaire d’expressions: E ::= E + T | T T ::= T * F | F F ::= ( E ) | id Résultat i=1 Ai::=Ai_a | b E ::= T E’ E’ ::= +T E’ | e i=2 Ai::=Ai_a | b T ::= F T ’ T’ ::= * F T’ | e F ::= ( E ) | id Ordre: 1: E, 2: T, 3: F

94 Exemple 2 Élimination de la récursivité gauche
Grammaire : A ::= B b | C e B ::= A a | d C ::= A c | d calcul : Résultat A ::= B b | C e i=2 Ai::=Aj g Ai::=Ai_a | b B ::= B b a | C e a | d B::= CeaB’|dB’ B’::=baB’| e C ::= B b c | C e c | d C ::= CeaB’bc|dB’bc|Cec|d C ::= dB’bcC’|dC’ C’::=eaB ’bcC’|ecC’|e Ordre: 1: A, 2: B, 3: C

95 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::= e | else I

96 Exemple : PREMIER Pour la grammaire des expressions sans récursivité gauche: 0 : S ::= E 3 : E’ ::= + E 6 : T’ ::= * T 1 : 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.

97 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é à vide SUIVANT[S] := {$} /* le marqueur de fin */ tant que SUIVANT change pour chaque règle A ::= B ajouter PREMIER() . SUIVANT[A] à SUIVANT[B]

98 Exemple : SUIVANT Pour la grammaire des expressions sans récursivité gauche: 0 : S ::= E 3 : E’ ::= + E 6 : T’ ::= * T 1 : 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’:{+,),$}

99 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.

100 Exemple table LL(1) pour expressions
D215

101 Trace de l’analyse LL(1) d’une expression
Contenu de la pile Chaîne d'entrée $ [S ® •E] id * id $ $ [S ® •E] [E ® •TE'] $ [S ® •E] [E ® •TE'] [T ® •FT'] $ [S ® •E] [E ® •TE'] [T ® •FT'] [F ® •id] $ [S ® •E] [E ® •TE'] [T ® •FT'] [F ® id•] * id $ $ [S ® •E] [E ® •TE'] [T ® F•T'] $ [S ® •E] [E ® •TE'] [T ® F•T'] [T' ® •*T] $ [S ® •E] [E ® •TE'] [T ® F•T'] [T' ® *•T] id $ $ [S ® •E] [E ® •TE'] [T ® F•T'] [T' ® *•T] [T ® •FT'] $ [S ® •E] [E ® •TE'] [T ® F•T'] [T' ® *•T] [T ® •FT'] [F ® •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' ® e•] $ [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' ® e•] $ [S ® •E] [E ® TE'•] $ [S ® E•]

102 D215 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.

103 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

104 Exemple : expression procedure expr; begin E ::= T E’
if input^ in [ ‘(‘ , ‘id’ ] then begin term; exprend; end else erreur end;  E ::= T E’ choix par table LL(1) T E’

105 Exemple: suite E’ ::= +TE ’ | e E’ procedure exprend; begin
if input^ = ‘+’ then begin get; term; exprend; end else if eof or input^ = ‘)’ then else erreur

106 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

107 Arbre abstrait : exemple
F T’ E + T E’ x y F T’ id e + id e e x + y Arbre syntaxique concret de x + y, pour la grammaire des expressions sans récursion à gauche. Arbre abstrait pour la grammaire ambiguë de départ

108 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 ?

109 Exemple : Texte Ada if x + 1 > y then x := 1 else x := 2 end if ;
<expr> <expr> <instr> <instr> > + x y := x 1 := x 2 * if < expr > then < instr > else < var > := < expr > end if manche if < expr > then < instr > else < instr > end if ... < instr >

110 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

111 Automate fini caractéristique
Déf. : L’automate fini caractéristique est donné par états = items (G) alphabet = état initial = états finaux = transitions = les états où on réduit lecture d’un symbole : expansions : pas de réductions : elles sont représentées par des états terminaux.

112 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

113 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).

114 Exemple : expression e e e e e S1 e e e e e e S2 S3 e e e e S5 S0

115 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

116 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

117 D252 W342 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.

118 D252 W342 Fermeture fonction Fermeture (s : ensemble d’items) : ensemble d’items ; (* fermeture par les e 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.

119 D253 W343 Successeurs 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. 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 } )

120 Construction des états
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 

121 Exemple : expressions D254 W339 L’automate LR(0) donne :
+ T S9 S6 Les états S1, S2, S9 sont impropres : ils contiennent des conflits LR(0) : cette grammaire n’est pas LR(0). id F S5 E ( + id S3 * S0 F id id ( F E ) S11 S4 S8 T ( ( T S2 F S10 * S7

122 Exemple : expressions : états LR(0)
Les états S1, S2, S9 sont impropres : ils contiennent des conflits LR(0) : cette grammaire n’est pas LR(0).

123 Exemple q1 q5 a b D C b q2 q7 e q0 e q3 C q4 D S q6 q8
La grammaire: S ::= C | D C ::= a C | b D ::= a D | e n’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 e q3 C q4 D S q6 q8

124 D256 W352 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.

125 Exemple : expressions SLR(1)
D237 W353 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).

126 Exemple : Affectations en C
D257 Exemple : Affectations en C S : : = L = R | R L : : = *R | id R : : = 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)

127 Exemple : Affectations en C : automate SLR(1)
D258 Exemple : Affectations en C : automate SLR(1) S [S’ • S] [S’ S • ] [S • L = R] [S • R] S3 [L R • * R] [S R • ] [L • id] * [R • L] S4 id S2 L * S5 [L * • R] [S L • = R] id [R • L] [R [L id • ] L • ] [L • *R] [L • id] S6 = id * L R [S L = • R] S8 S7 [R • L] [R L • ] [L *R • ] [L • * = R] L [L • id] S2 est conflictuel : SUIVANT(R) contient =. Cette grammaire n’est pas SLR(1). S9 R [S L = R • ]

128 D260 W350 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) ).

129 Fermeture D261 W350 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 g ajouter [B  • g, v] à I jusqu’à stabilisation

130 Exemple : expressions : états LR(1)
+ T S14 S20 id + + T S1 S6 S9 id S12 id ( F E S5 id * S18 F id id F F F * ( S0 S3 E ) S15 S13 id S19 ( ( ( T E ) S11 S4 S8 * F T S17 S16 S21 T ( * F S2 S7 S10 L’automate LR(0) est doublé : prévision avec $ (fin) ou avec )

131 Exemple : expressions : états LR(1)
= Fermeture(Début) S = Fermeture( S , +) ) 6 1 = { [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, {$, +, *} ] } S = Fermeture( Succ( S , T)) 9 6 { [E E + T • , {$, +}], S = Fermeture( Succ(S ,E)) [T T • * F, {$, +, *}] } 1 = { [S E • , {$}], { [F ( • E), {$, +, *}], S = [E E • + T, {$, +}] } 4 [E • E+T ,{), +}], [E • T, {), +} ], S = Fermeture( Succ(S ,T)) 2 1 [T • T * F, {), +, *} = { [E T • , {$, +}], [T F, {), +, *}], [T T • * F, {$, +, *}] } [F • (E), {), +, *}], [F • id, {), +, *} ] } Notation: l’ensemble des prévisions des items LR(1) qui ont la même production sont regroupés.

132 LALR on garde les états LR(0)
D270 W 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  g •] : trouver les origines de la règle : expansions [B  • g] 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

133 Exemple : affectations en C
• S , {$} ] [S’ S • ] [S • L = R] [S • R , {$} ] S3 [L R • * R] [S R • ] [L • id] * [R • L , {$} ] S4 id S2 L * S5 [L * • R] [S L • = R] id [R • L] [R [L id • ] L •, {$} ] [L • *R] [L • id] S6 = id * L R [S L = • R] S8 S7 [R • L] [R L • ] [L *R • ] [L • * = R] L [L • id] Le conflit en S2 est résolu Cette grammaire est LALR(1). S9 R [S L = R • ]

134 (conflit décaler/réduire)
Exemple : expressions ( (conflit décaler/réduire) Etats impropres ( ( Derrière, en mauve: les ensembles de prévision LALR(1)

135 Grammaires ambiguës Une grammaire ambiguë peut être plus naturelle
D277 W368 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

136 Exemple : expressions La grammaire la plus naturelle est :
E ::= E + E | E * E | ( E ) | id qui 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.

137 Exemple : if then else En Pascal, on a :
<conditionnelle > ::= if E then I else I | if E then I cette grammaire est ambiguë, mais sera analysée correctement grâce à la préférence au décalage.

138 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.

139 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.

140 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.

141 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.

142 Sémantique statique Rôle :
D Ch.5-6 W Ch. 9 Sémantique statique Rôle : Vérifier les propriétés contextuelles statiques, principalement la cohérence des types

143 Exemple : Arbre de syntaxe abstraite
La sémantique statique travaille sur les arbres de syntaxe abstraite. if2 grt plus assign id (x) iconst (1) (y) (z) (2) Exemple : Arbre de syntaxe abstraite

144 Arbre de syntaxe concrète pour le même exemple
Cond Stat Ifstat E T F id (x) iconst (1) + if compop (>) (y) then (z) := Ass else (2) fi Arbre de syntaxe concrète pour le même exemple

145 Sémantique statique 1. un exemple : le typage
2. une technique de spécification : les grammaires attribuées

146 1. Typage Portée Systèmes de types équivalence coercition
D Ch. 6 W388 1. Typage Portée Systèmes de types équivalence coercition polymorphisme surcharge

147 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é.

148 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

149 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

150 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.

151 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

152 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.

153 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

154 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 : ; i := a[i] (* correct car le type de base est integer , mais pas recommandé *)

155 Quand 2 types sont ils égaux ? 2 grandes familles :
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)

156 Equivalence par nom ex. : Pascal, Ada
1) 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 end var x : r ; y : r ; les types de x, y sont équivalents 3) var x, y : record a, b : integer end les types de x, y sont équivalents en Pascal, mais différents en Ada

157 Equivalence structurelle
D390 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

158 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 faux sinon si pour tout i, equiv (fils(i,t1),fils(i,t2))) alors union(t1, t2); renvoyer vrai

159 Particularités de typage
surcharge coercition polymorphisme

160 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 ;

161 Résolution de la surcharge
D400 W390 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) Résolution de la surcharge + int x int int real x real real real real int int char  char int f p  real  int A la fin de l’algorithme, chaque fonction doit avoir un seul type sinon : ambiguïté de type

162 D396 Coercition Def : 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.

163 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.

164 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.

165 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 )

166 Polymorphisme (paramétrique)
D402 W391 Polymorphisme (paramétrique) Une définition est polymorphique si elle peut s’appliquer de la même façon à tout type. Exemple en ML head : ‘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.

167 Résolution du polymorphisme
D409 W394 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.

168 D412 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) +1 où: [] 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.

169 Exemple 2 (ML) fun append ( [ ] ) (l2) = l2
append (x :: r) (l2) = x :: (append r l2) Contraintes : ‘ta = ‘a list (‘tl ‘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) W395 Exemple 2 (ML) ‘ta ‘a list ‘tl2 ‘tl2 ‘tx ‘tr ‘tl3 ! ‘tx list

170 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

171 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)

172 Exemple : calculatrice
Règle de grammaire Règle d’attribution S ::= 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

173 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.

174 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)

175 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

176 Exemple : Graphe de dépendances
typeh L tab : T id , typeh L tab bool id , typeh L tab id x, y, z : bool;

177 Exemple : typage Pascal
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’attribution E0 ::= E1 op E op peut être +, -, * E0.type = if E1.type = int and E2.type = int then int else real | (E1) E0 . type = E1 . type

178 Exemple : arbre abstrait
Les 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 abstrait Règles de grammaire Règles d’attribution E ::= E1 +T E.a = cons(+, E1 .a , T.a) E ::= T E.a = T.a T ::= ( E ) T.a = E.a T ::= id T.a = id Pour un DAG, cons renvoie le nœud (si) existant

179 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

180 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

181 Grammaires S-attribuées : Implémentation
D328 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 $$.

182 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} ;

183 Exemple : calculatrice - pile
D329 Exemple : calculatrice - pile Dans cet exemple, la pile des attributs se comporte comme une pile d’évaluation postfixe.

184 Ordre fixe : grammaires L-attribuées
D330 W434 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.

185 Exemple : expression LL(1)
val : attribut synthétisé h : attribut hérité de E': valeur de l'expression à gauche Règles BNF Attribution E ::= 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

186 Ex d’évaluation d’une expression
E.val=1 E’.h = 9 T.val = 9 val =1 val =1 nb.val = 9 E’.h = 4 T.val = 5 val =1 nb.val = 5 T.val = 3 E’.h = 1 nb.val = 3 arbre attribué pour l’expression

187 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

188 Exemple : expressions LL(1)
procedure reste(h: integer; var val: integer); var tval, e1val : integer; begin if eof then val := h else if input^ = ‘ - ’ then get; (* lit ‘ - ‘ *) terme(h, tval); reste(h-tval, e1val); val := e1val end else error Règle LL(1) attribution E' ::=  E'.val = E'.h | - T E'1 E'1.h = E'.h - T.val E'.val = E'1.val

189 D342 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.)

190 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).

191 Ex: table de symboles pour C
D : déclaration L : liste d’identificateurs T : type (se place avant en 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) }

192 Ex: Table de symboles (Bison)
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 D: T {$$=$1} L {$$ = $3 }; T : int {$$ = integer() }; L: L ',' id {$$ = add($3, $<ty>0, $1)} | id {$$ = add($1, $<ty>0, empty() ) };

193 Ex: mise en page de formule
D23, D333, D347 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)

194 Ex: mise en page de formule
tc: attribut hérité: taille des caractères ht: attribut synthétisé: hauteur totale de la boîte S ::= B B ::= B1 B2 B ::= B1 sub B2 B ::= texte B.tc = 10 au départ, taille 10 points S.ht = B.ht B1.tc = B.tc B2.tc = B.tc B.ht = max(B1.ht, B2.ht) B2.tc = Rétrécir(B.tc) plus petit pour l'indice B.ht = Déplacer(B1.ht, B2.ht) p.ex B1.ht + B2.ht/2 B.ht = texte.htn  B.tc htn: hauteur des lettres

195 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.ht B.tc

196 D350 Utiliser les attributs synthétisés Ex: 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 | ... D R R T id , id : integer

197 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.

198 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.

199 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

200 Génération de Code pour langages impératifs
D, Chap. 2 et 7 à 10 W, Chap. 2 : P-Code Chap. 12 : code optimisé

201 Plan Machines abstraites: ex. P-machine Machines concrètes: ex. MIPS
Expressions Instructions Données Machines concrètes: ex. MIPS Allocation mémoire

202 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

203 Pile d’expressions } } } } } W8
Les expressions peuvent se calculer avec une pile en notation postfixe (appelée aussi Polonaise inversée) (p. ex. calculette Hewlett-Packard) Exemple : ((3 * 5) + (7 * 4)) / (3 + 5) Postfixe : 3 5 * 7 4 * / Parenthèses inutiles nombres empiler opération prendre les données sur la pile les remplacer par le résultat 4 7 * 15 } 5 3 * 43 } 5 3 * 28 15 } } 8 43 / } + 5 Ce principe est employé dans les langages à pile PostScript et FORTH, et dans les machines abstraites: P-machine (Pascal), JVM (Java)

204 P-instructions pour expressions
W8 P-instructions pour expressions Les deux valeurs en sommet de pile doivent avoir le même type numérique (entier, réel, ou adresse) STORE : tableau représentant la mémoire une partie (jusque SP) est utilisée pour la pile.

205 Variables globales de type simple
W9 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 i Les 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)

206 P-instructions pour variables
W9 P-instructions pour variables P-instr Signification Condition Résultat ldo T q SP := SP + 1 ; STORE [SP] := STORE [q] q : adresse (T) ldc T q STORE [SP] := q Type(q) = T ind T STORE [SP] := STORE [STORE [SP]] (a) sro T q STORE [q] := STORE [SP] ; SP := SP - 1 sto T STORE [STORE [SP - 1]] := STORE[SP] ; SP := SP - 2 (a,T)

207 1.2 Instructions Instructions de branchement
W11 1.2 Instructions Instructions de branchement Ces instructions modifient le Program Counter (PC) goto absolu ujp q : goto q goto conditionnel fjp q : if top = false then goto q goto calculé ixj q : goto top + q

208 Conditionnelles W12 if if codeD(e) e codeD(e) e fjp then then st1
code(st1) fjp st ujp else code(st) st2 code(st2) (a) deux branches (b) une seule branche

209 Boucles W12 while repeat e codeD(e) code(st) st fjp until do codeD(e)
ujp fjp (a) boucle while (b) boucle repeat

210 Exemples W13 while a b > if a b do c > 1 + := a then c b a - :=
ldc a 5 ind i do c 1 + := a b - ldc a 6 ind i grt i then c a := fjp ldc a 7 ldc a 5 ind i sto i else c b := ujp ldc a 7 ldc a 6 ind i sto i

211 Case W14 case e of 0 : st0 ; . k : stk end
Laisse la valeur du sélecteur en sommet de la pile codeD e r neg i ixj q Branchement indexé vers la fin de la table des branchements Code du cas 0 codeD st0 r ujp codeD st1 r Branchement vers la fin de l’instruction ujp . case e of 0 : st0 ; . k : stk end codeD stk r ujp ujp . Table des branchements ujp q ujp

212 W16 1.3 Données 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)

213 Indexation de tableaux
W17 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

214 P-code pour l'indexation
W20 P-code pour l'indexation codeG c[i1, ..., ik] r = ldc a r(c) codeI [i1, ..., ik] c r codeI [i1, ..., ik] n r = codeD i1 r ; ixa n* d2 * ... * dk codeD i2 r ; ixa n* d3 * ... * dk . codeD ik r ; ixa n dec a n * (lk + lk-1 * dk + ...) ixa q : sp i ixa q s s+i*q

215 1.3.2 Records W24 En Pascal les types ont une taille statique
pour chaque champ on a un déplacement relatif : On le stocke dans la table des symboles Ex.record taille déplacement a : array [1 .. 5, ] of boolean 25x1 octets 0 x : integer 4 octets 25 y : real octets 29 end Traduction codeG (v.ci) = codeG (v); inc a dépl(ci)

216 W25 Pointeurs Les cases créées par new ne peuvent pas être allouées sur la pile car elles 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

217 Procédures imbriquées
W28 Distinguer 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, ...

218 W31 proc p(a) var b; var c proc q var a var q proc r var b b a c . q proc s 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. 1 2 1 1

219 Prédécesseurs statiques
W29 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 a proc r var i : real i := i/2 ; a := 5 if i > 0 then i := i - 1 ; p else r fi i := 2 ; p ; p end. h p r Liens d’accès Figure : Un programme et son arbre d ’activation (ou d’appels). Les crochets donnent l ’imbrication des procédures.

220 D461 W36 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.

221 P-instructions pour variables
W37 P-instructions pour variables 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

222 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.

223 Exemples proc p proc q q proc r proc p q proc q p proc r proc q proc p
* * p r r MP * * p q q SP MP * p q SP q d = 0 (a) d = 1 (b) d = 2 (c)

224 W33 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 s p EP SP MP pp

225 W42 Protocole d'appel Mettre le prédécesseur statique à la dernière instance de la procédure englobant q. Mettre le prédecesseur dynamique à l'appelant p. Préserver EP, le registre bornant la pile Calculer les arguments dans l'appelant Mettre MP sur le bloc Sauver l'adresse de retour Sauter à q Mettre EP à jour

226 P-Instructions pour l’appel d’un sous-programme
W43 P-Instructions pour l’appel d’un sous-programme base(p,a) = if p = 0 then a else base(p - 1, STORE[a + 1]) fi

227 W44 Protocole de retour Remettre les registres à jour: SP PC EP MP

228 P-instructions de retour de sous-programme
W44 P-instructions de retour de sous-programme

229 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)

230 1. Expressions sans partage : Pile avec partage : DAG Réordonnancement
Allocation de registres avec partage : DAG utilisation avec pipelines

231 Registres Types de registres courants: universels spécialisés:
W553 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

232 Allocation de registres
D613 W565 Allocation de registres Technique simplifiée pour les arbres : 1. réorganisation des calculs 2. allocation des registres par simulation de pile

233 D613 W566 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 : + t t2 4 reg reg } + calcul de t1 ; calcul de t2 ; addition

234 Calcul du meilleur ordre
D614 W567 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. {

235 D614 W568 Exemple _ 3 _ + 2 2 a b c + 2 1 1 1 d e 1 1 (a + b) - (c - (d + e)) requiert 3 registres sur une machine load/store

236 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

237 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)

238 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)

239 Algorithme : cas 3 s’il est plus avantageux de respecter l’ordre : r1r2, r>r2 ProdCode(n1); R := dépiler(pilereg); ProdCode(n2); émettre(op, R, R, sommet(pilereg)) ; empiler(pilereg, R);

240 Algorithme : cas 2 s’il est plus avantageux d’inverser l’ordre
càd si r2r1, r>r1: ProdCode(n2); R := dépiler(pilereg); ProdCode(n1); émettre( op, R, sommet(pilereg), R); empiler(pilereg, R);

241 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)

242 Exemple pour (a + b) - (c - (d + e)) avec 2 registres : lw R1 , a
lw R2, b add R1, R1, R2 sw R1,T // T := a+b lw R1, d lw R2, e add R1,R1,R2 // R1 = d+e lw R2, c sub R1, R2, R1 // R1 = c-(d+e) lw R2, T sub R1, R2, R1 // R1 = (a+b)-(c-(d+e)) _ 3 _ + 2 2 a b c + 2 1 1 1 d e 1 1

243 D579 W563 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. ex: while B do S B S

244 Partage de calculs : DAG
D598 W575 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

245 Exemple : DAG simple c := a[i]+1; b := 1-a[i]; b c [ ] a i

246 D619 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 b c [ ] a i t b c [ ] a i t t 1

247 W580 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

248 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

249 2. Instructions 2.1 conditionnelles 2.2 boucles 2.3 case

250 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.

251 Instructions conditionnelles
if if codeD(e) e codeD(e) e beq $r, $zero then then st1 code(st1) beq $r, $zero st j else code(st) st2 code(st2) (a) deux branches (b) une seule branche Instructions traduisant les instructions conditionnelles

252 Génération de code pour les boucles
while repeat e codeD(e) code(st) st beq $r, $zero until do codeD(e) e st code(st) j beq $r, $zero (a) boucle while (b) boucle repeat Génération de code pour les boucles

253 Instruction case case e of 0 : st0 ; 1 : st1 ; . k : stk end
Laisse la valeur du sélecteur dans un registre $re codeD e r sub $t1, q, $t1 jr $t1 Branchement indexé vers la fin de la Table des branchements case e of 0 : st0 ; 1 : st1 ; . k : stk end L0: code st0 r j exit Branchement vers la fin de l’instruction codeD st1 r j exit . Lk : code stk r j exit j Lk . Table des branchements j L1 q : exit : j L0

254 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

255 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

256 } 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]

257 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

258 Records En Pascal tous les types ont une taille statique
pour chaque champ on a un déplacement relatif Ex. record a : array [1 .. 5, ] of boolean 1 byte par bool. x : integer 4 byte par int. y : real 8 byte par real end déplacement 25 29 37 Les machines ont souvent des contraintes d’alignement : p.ex. un réel doit avoir une adresse multiple de 4 on doit laisser du remplissage ou 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, r(ci), $t #laisse l ’adresse du champ dans $t. # r(ci) est le déplacement de ci

259 D488 W25 Pointeurs Les cases créées par new ne peuvent pas être allouées sur la pile car elles 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

260 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.

261 D433 Pile de contrôle L’exécution des procédures (récursives) est imbriquée  représentable par une pile Le 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).

262 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’activation 2. l ’appelant évalue les arguments et les met dans les registres ($a) ou dans le bloc 3. 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 locales 6. 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)

263 Accès aux noms non locaux
D453 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

264 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

265 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

266 Passage par référence L’adresse de l’argument est passée.
En Pascal: noté par var

267 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 !

268 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.

269 Exemple program ValeurResultat(output); var a : integer;
D471 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.

270 D471 Passage par nom Remplacement textuel du paramètre par l’argument (comme pour une macro) Utilisé seulement en Algol-60.

271 Exemple procedure Troquer(x,y:integer); var t:integer; begin t := x;
y := t end; Troquer(i, t[i] ) met i dans t[t[i]] !

272 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

273 Exemple : fonction en paramètre
Program HP proc p(func h) h proc q func f p(g) p(f) func g q HP q p f HP q p f g (b) situation de la pile après appel de p(f) et (en pointillé) après appel de h (c) situation de la pile après appel de p(g) et (en pointillé) après appel de h (a) Un programme avec fonction en paramètre

274 Introduction à la Sémantique

275 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.

276 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

277 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

278 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.

279 Sémantique opérationnelle
Sé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.

280 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.

281 Système de déduction Une règle d’inférence est de la forme : 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.

282 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 »

283 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.

284 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.

285 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

286 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.

287 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)

288 Exemple : expression recherche congruence congruence calcul *

289 Instructions affectation : conditionnelle : boucle : séquence :
d’abord évaluer l’expression ensuite modifier l’état pour la variable avec la constante ainsi calculée conditionnelle : 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

290 Instructions Affectation : 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 : (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 : expr id : identificateur n : const

291 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.

292 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 ?

293 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.

294 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.

295 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

296 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. post pre

297 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 }


Télécharger ppt "Syntaxe et sémantique des langages de programmation"

Présentations similaires


Annonces Google