On d´ecrit maintenant une impl´ementation en ML sans GADT de l’automate. Cette impl´ementation est bien typ´ee mais elle effectue les tests superflus mentionn´es dans la section pr´ec´edente.
Pour commencer, on doit sp´ecifier les moyens d’interaction entre l’analyse lexicale et notre analy- seur syntaxique. On d´eclare le type des symboles terminaux :
type token =
KPlus | KStar | KLeft | KRight | KEnd | KInt of int
On suppose que l’analyseur lexicale fournit deux fonctions, l’une permettant de lire le symbole en cours d’analyse et l’autre de passer au terminal suivant :
val peek : unit → token val discard : unit → unit
9.4 Une impl´ementation en ML 103
On se plonge maintenant dans la conception de l’analyseur syntaxique. L’ensemble des ´etats est d´ecrit par une ´enum´eration, c’est `a dire un type alg´ebrique ordinaire dont tous les constructeurs ont une arit´e nulle :
type state = S0 | S1 | . . . | S11
Ensuite, on d´eclare un autre type alg´ebrique ordinaire pour impl´ementer la pile de l’automate. L’approche canonique consiste `a mimer la d´efinition des piles (σ ::= ² | σsv, voir la section9.3) :
(∗ D´efinition temporaire. ∗) type stack =
SEmpty | SCons of stack × state × semantic value and semantic value = ...
Cette d´eclaration affirme qu’une pile est une liste de paires form´ees d’un ´etat et d’une valeur s´emantique.
Il ne nous reste plus qu’`a d´efinir le type des valeurs s´emantiques. Ici, les symboles +, *, (, ) et $ ont des valeurs s´emantiques valant de type unit alors que int, E, T et F ont des valeurs s´emantiques de type int. Il est clair que cette h´et´erog´en´eit´e des types des valeurs s´emantiques force la d´efinition d’un type somme, c’est-`a-dire une nouvelle d´efinition de type alg´ebrique ordinaire.
Bien que naturelle et simple, cette solution introduit une certaine redondance puisqu’`a la fois les cellules et les valeurs s´emantiques contiennent des ´etiquettes. Pour ´eviter cette redondance, on utilise une m´ethode un peu moins directe qui fusionne les ´etiquettes des valeurs s´emantiques et des cellules en int´egrant les diff´erents types de valeurs s´emantiques directement `a l’int´erieur de la d´efinition des piles : type stack = | SEmpty | SP of stack × state | SS of stack × state | SL of stack × state | SR of stack × state | SI of stack × state × int | SE of stack × state × int | ST of stack × state × int | SF of stack × state × int
(Dans les noms choisis pour les constructeurs de donn´ees, P, S, L, R, et I sont des raccourcis pour
Plus, Star, Left, Right, et Int.)
En examinant l’´etiquette attach´ee `a une valeur de type stack, on peut r´epondre en mˆeme temps `a deux questions : est-ce que la pile est vide ? si elle n’est pas vide, quel type de valeurs s´emantiques contient-elle `a son sommet ?
Par ailleurs, `a des fins d’optimisation, on choisit de ne pas repr´esenter `a l’ex´ecution les valeurs de type unit. Par cons´equent, les cellules associ´ees aux symboles +, *, ( et ) contiennent seulement un ´etat et non une paire contenant un ´etat et la valeur unit.
Enfin, aucune cellule n’est construite avec le symbole $ parce que, par construction, l’automate n’effectue jamais une action shift lorsqu’il rencontre ce symbole.
Pour r´esumer, chaque valeur du type stack contient une ´etiquette qui doit ˆetre examin´ee pour que le contenu r´eel soit accessible. Si, grˆace `a un raisonnement externe, l’´etiquette est connue par avance alors ce test dynamique est redondant.
C’est cette approche o`u les valeurs s´emantiques et/ou les cellules de la pile sont ´etiquet´es qui est adopt´ee par ML-Yacc et happy.
La fonction centrale de l’analyseur syntaxique, run impl´emente l’´evaluation de l’automate `a pile. Elle est param´etr´e par l’´etat courant et la pile courante. Il peut s’arrˆeter soit en lan¸cant l’exception
104 9. Application : Analyseur LR bien typ´e
1 exception SyntaxError 2
3 let rec run : state → stack → int = 4 fun s stack →
5 match s, peek() with 6 | . . .
7 | S9, KStar →
8 (∗ D´ecale vers l ’ ´etat 7. ∗)
9 discard ();
10 run S7 (SS (stack, S9))
11 | S9, KPlus →
12 (∗ R´eduit E{x} + T {y} → E{x + y}. ∗) 13 (∗ D´epile trois cellules . ∗)
14 let ST (SP (SE (stack, s, x), ), , y) =
15 stack in
16 (∗ Empile l’ ´etat courant et la nouvelle valeur s´emantique sur la pile . ∗) 17 let stack = SE (stack, s, x + y) in
18 (∗ Choisit un successeur sur la pile en utilisant la colonne E de la table goto. ∗)
19 gotoE s stack
20 | . . .
21 | , →
22 raise SyntaxError
23
24 and gotoE : state → stack → int = 25 fun s → 26 match s with 27 | S0 → 28 run S1 29 | S4 → 30 run S8
Fig. 9.3: Une impl´ementation bien typ´ee en ML.
SyntaxError ce qui signifie que l’entr´ee n’est pas conforme `a la grammaire, ou bien en retournant une
valeur s´emantique enti`ere correspondant `a la valeur de l’expression arithm´etique E qui a ´et´e analys´ee. `
A l’exception des fonctions peek et discard qui font des effets de bord pour manipuler le flux d’entr´ee, la totalit´e de ce programme est purement fonctionnel.
La d´efinition de run apparaˆıt dans la figure 9.3. La fonction examine l’´etat courant s ainsi que l’entr´ee courante obtenue par la fonction peek et d´etermine quelle action effectuer. Il y a plusieurs cas, on en d´etaille deux.
Quand l’´etat courant vaut 9 et le symbole `a analyser est * (ligne7), la table action de la figure9.2
indique que l’automate doit effectuer une action shift 7 . On passe donc au symbole suivant, l’´etat courant 9 est plac´e au sommet de la pile et l’´etat courant est maintenant 7 (ligne 10). Dans ce cas particulier, aucune valeur s´emantique n’est pouss´ee sur la pile parce qu’aucune valeur s´emantique n’est associ´ee au symbole *. De mˆeme, le constructeur de donn´ees n’attend pas un troisi`eme argument. L’´etat courant et la pile sont chang´es en suivant l’idiome de programmation fonctionnelle pure qui consiste `a effectuer un appel r´ecursif `a run avec ces nouveaux param`etres.
Quand l’´etat courant est 9 et le symbole `a analyser est + (ligne 11), la table action indique que l’automate doit r´eduire la production 1, c’est-`a-dire E{x} + T {y} → E{x + y}. Comme on l’a expliqu´e dans la section pr´ec´edente, la structure des trois ´el´ements plac´es au sommet de la pile est connue en