Java CUP - PLANIART - Université de Sherbrooke

publicité
IFT313
Introduction aux langages formels
Froduald Kabanza
Département d’informatique
Université de Sherbrooke
Java CUP
Générateur d’analyseurs LALR
Sujets
• Java CUP
– Introduction
– Actions sémantiques
– Gérer des conflits
– Recouvrement d’erreur
IFT313
© Froduald Kabanza
2
Références
[2] Appel, A. and Palsberg. J. Modern Compiler Implementation in Java.
Second Edition. Cambridge, 2004.
– Section 3.4 à 3.5
[4] Aho, A., Lam, M., Sethi R., Ullman J. Compilers: Principles, Techniques, and
Tools, 2nd Edition. Addison Wesley, 2007.
– Section 4.8 à 4.9
IFT313
© Froduald Kabanza
3
Java CUP
– CUP signifie « Construction of Useful Parser »
– C’est un générateur d’analyseurs LALR(1). Il est écrit en Java. Il génère
des analyseurs en Java.
– Écrit originalement par Scott Hudson, Frank Flannery, C. Scott Ananian
(University of Princeton)
– Maintenant maintenu par l’University of Munich.
http://www2.cs.tum.edu/projects/cup/
– Nous voyions une veille version (2006), alignée sur la syntaxe de Yacc
(Yet Another Compiler-Compiler). Yacc est un générateur d’analyseurs
syntaxiques écrit en C pour les environnements UNIX.
IFT313
© Froduald Kabanza
4
Exemple (calcsyntax1/parser.cup)
/* Terminals (tokens returned by the scanner). */
terminal
PLUS, TIMES, LPAREN, RPAREN;
terminal Integer
NUMBER;
/* Non terminals */
non terminal
E, T, F;
/* The grammar */
E
T
F
::=
::=
::=
IFT313
E PLUS T | T ;
T TIMES F | F ;
LPAREN E RPAREN | NUMBER ;
© Froduald Kabanza
5
Commande
Commande : java –jar java-cup-11a.jar grammaire.cup
Options
donne toutes les options
-parser Parser spécifie le nom de classe du parser (déf: parser.java)
-symbols Sym spécifie le nom de classe types de symboles (def
sym.java)
Il y en a d’autres : voir le manuel.
IFT313
© Froduald Kabanza
6
Génération d’un analyseur
Spécification
Java CUP
(parser.cup)
Spécification
JFlex
(scanner.jflex)
Sym.java
Code source
Scanner.class
(types symboles)
Java CUP
JFlex
Sym.class
Parser.java
(analyseur
syntaxique)
Scanner.java
(analyseur lexical)
javac
IFT313
© Froduald Kabanza
Parser.class
AST en mémoire
Ou inerprétation
7
Attributs synthétiques dans Java CUP
–
Dans Java CUP les attributs sont optionnels.
– Chaque symbole dans la partie droite d’une production peut être optionnellement
étiqueté par un autre symbole (un attribut).
–
L’étiquette apparaît juste après le symbole, les deux étant séparés de « : »
–
Les étiquettes pour une production doivent être uniques, et peuvent être utilisés
dans l’action sémantique associée à la production pour référer aux valeurs des
symboles correspondants.
–
Le symbole dans la partie gauche est implicitement étiqueté par RESULT.
–
Les valeurs des attributs par les terminaux sont fournies par le scanner. Les autres
sont calculés par les actions sémantiques.
IFT313
© Froduald Kabanza
9
Exemple
E ::= E:x PLUS T:y
{: RESULT = new Integer(x.intValue() + y.intValue()); :}
– Le symbole E dans la partie droite est étiqueté par x et le symbole T par y.
– RESULT est la valeur pour le symbole E dans la partie gauche de la
production.
– Chaque symbole dans une règle de production est représenté par un objet
(classe Symbol par défaut) sur la pile. Les étiquètes réfèrent aux valeurs de
ces objets.
– x et y réfèrent à des objets de la classe Integer parce que les symboles
correspondants ont été déclarés comme étant de la classe Integer.
– Il en va de même pour RESULT.
IFT313
© Froduald Kabanza
10
Gérer des conflits
– Si une grammaire n’est pas LALR(1), normalement Java CUP va générer
des conflits (shift/reduce ou reduce/reduce)
– En particulier, ce sera le cas si la grammaire est ambiguë (vu qu’elle
ne peut pas être LR (k) quelque soit le cas; elle donne lieu à plusieurs
arbres d’analyse).
– Toutefois, on peut utiliser une grammaire non LALR (1), voire ambiguë,
à condition de spécifier des règles de désambiguïsation de sorte qu’il y
ait un seul arbre d’analyse :
– Ces règles indiquent comment gérer les conflits (shift/reduce,
reduce/reduce).
• Cela permet de travailler avec des grammaires plus simples et plus facile
à comprendre, ou plus expressives que les grammaires LALR(1).
IFT313
© Froduald Kabanza
11
Règles par défaut
Par défaut Java CUP résout les conflits dans la table d’analyse LALR(1) en
utilisant les règles suivantes:
1.
Un conflit reduce/reduce est résolu en choisissant l’élément correspondant
à la règle de production qui apparaît en premier lieu dans la spécification de
la grammaire.
2.
Un conflit shift/reduce est résolu en faveur du shift.
Ceci permet entre autres de résoudre correctement le conflit shift/reduce
généré par l’ambiguïté sous-jacente à l’instruction if-then-else.
Il revient au programmeur de s’assurer que les règles par défaut correspondent à ce
qu’il veut. Si ce n’est pas le cas, il a deux choix : (a) spécifier des règles de
désambiguïsation; (b) utiliser une grammaire équivalente non ambiguë.
IFT313
© Froduald Kabanza
12
Règles spécifiées explicitement
– On peut spécifier des règles de précédence et d’associativité
comme suit :
– precedence left
terminal [, terminal...];
– precedence right terminal [, terminal...];
– precedence nonassoc terminal [, terminal...];
IFT313
© Froduald Kabanza
13
Règles spécifiées explicitement
–
L’ordre de précédence (priorité), du plus élevé au moins élevé, va du bas vers le
haut. Ainsi, les déclarations suivantes indiquent que :
– l’addition et la soustraction ont la même priorité;
– la multiplication et la division ont la mêmes priorités;
– et ces derniers ont une priorité plus élevée que l’addition et la soustraction:
precedence left ADD, MINUS;
precedence left TIMES, DIVIDE;
–
La précédence résout des conflit shift/reduce.
–
Par exemple, avec les déclarations précédentes, étant donné l’entrée
3 + 4 * 8,
–
L’analyseur doit déterminer s’il faut réduire ‘3 + 4’ ou avancer (shift) '*' sur la pile.
Puisque '*' a une plus grande priorité que '+', il va être mis sur la pile, de sorte que
la multiplication sera effectuée avant l’addition.
IFT313
© Froduald Kabanza
14
Règles spécifiées explicitement
– Java CUP assigne une priorité à chaque terminal, en se basant sur l’ordre
de leurs déclarations dans les spécification ‘precedence’, dans l’ordre
inverse de leur apparition.
– Java CUP assigne aussi une priorité à chaque production : c’est la priorité
du dernier terminal dans la partie droite de la production.
– Par exemple, expr ::= expr TIMES expr a la même précédence que TIMES.
IFT313
© Froduald Kabanza
15
Règles spécifiées explicitement
–
Lorsqu’on a un conflit shift/reduce :
– Si le terminal (avant le point) a une plus grande priorité que la production correspondant
à l’élément reduce, on fait shift.
– S’ils ont la même priorité, l’associativité du terminal détermine ce qu’on fait:
– Si l’associativité du terminal avant le point est left, on fait reduce. Par exemple
avec les déclarations précédentes, avec une entrée du genre 3 + 4 + 5, l’analyseur
va toujours faire reduce de gauche à droite, en commençant par 3 + 4.
– Si l’associativité du terminal avant le point est right, on fait shift. Ainsi les
réductions se feront de droite à gauche. Par exemple si on avait déclaré PLUS
comme étant associatif à droite, dans l’exemple précédent 4 + 5 serait réduit avant
d’additionner avec 3.
– Sinon on fait reduce.
–
Lorsqu’on a un conflit reduce/reduce, on réduit avec la production ayant la plus grande
priorité.
–
Dans les situations où le terminal le plus à droite dans une production ne donne pas la
priorité souhaitée, on peut spécifier la bonne priorité en utilisant %prec <terminal>. Ainsi la
priorité de la règle sera comme celle du terminal (qui doit dans ce cas être défini dans la
section de déclaration des précédences).
IFT313
© Froduald Kabanza
16
Règles spécifiées explicitement
– Si des terminaux sont déclarés nonassoc, deux occurrences successifs de
terminaux de même priorité génèrent une erreur.
– Par exemple, si '= =' est declaré nonassoc une erreur serait générée avec
l’entrée 6 = = 7 = = 8 = = 9.
– Tous les terminaux non déclarés dans les spécifications
précédence/associativité ont une priorité inférieure aux autres.
– Les productions sans terminaux ont aussi un priorité moindre inférieure
aux autres
– Si un conflit shift/reduce ou reduce/reduce implique de tels terminaux il
est reporté.
IFT313
© Froduald Kabanza
17
Recouvrement d’erreur
– Java CUP utilise un token spécial error pour spécifier des recouvrements
d’erreurs.
– Par exemple, on pourrait avoir des productions du genre:
stmt ::= expr SEMI | while_stmt SEMI | if_stmt SEMI | ... | error
SEMI ;
– Ceci signifie que si aucune des production normales pour stmt ne
correspond à l’entrée, une erreur syntaxique devrait être signalée. Le
recouvrement se fera en sautant les tokens erronés (ceci revient à les
scanner et les réduire par error) jusqu’au point où l’analyse peut se
poursuivre correctement en lisant un point-virgule (SEMI).
IFT313
© Froduald Kabanza
18
Téléchargement