L3/TL — TP 2 Expressions arithmétiques et arbres syntaxiques

publicité
L3/TL — TP 2
Expressions arithmétiques et arbres syntaxiques
L’objectif de ce TP est de continuer à se familiariser avec l’outil d’analyse syntaxique JavaCC
et d’aborder l’utilisation de l’outil JJTree. Les fichiers que vous aurez à modifier ou compléter au
cours de ce TP sont disponibles à l’adresse suivante :
http://circe.univ-fcomte.fr/Jerome-Voinot/TL/TP2/
1
Expressions arithmétiques
Le fichier Arith.jj, fourni et reproduit dans la figure 1, contient des règles lexicales et
syntaxiques permettant de reconnaı̂tre des expressions arithmétiques constituées de constantes
(nombres), du symbole binaire + et du symbole n-aire ∗.
Exercice 1.1
1. Compiler le fichier Arith.jj afin de générer les sources de l’analyseur correspondant.
2. Définir une classe Java permettant de lancer l’analyseur d’expressions arithmétiques que
vous venez de compiler (Indication : s’inspirer du TP précédent).
3. Compiler l’ensemble des sources Java (analyseur et lanceur).
4. Exécuter l’analyseur sur des exemples tels que 4 ∗ 3 + 2 ∗ 5, 4 + 5 ∗ 3 ou encore 4 + 5 ∗ 3 + 2
et en déduire l’ensemble des expressions arithmétiques reconnues par cet analyseur.
Exercice 1.2
1. Copier le fichier Arith.jj dans un autre dossier, en le renommant Arith2.jj.
2. Ajouter et/ou modifier, dans le fichier Arith2.jj, les règles lexicales et syntaxiques pour
reconnaı̂tre le symbole unaire − de négation, en plus de ceux déjà reconnus.
3. Compiler le fichier Arith2.jj.
4. Définir une nouvelle classe Java permettant de lancer l’analyseur généré à partir du fichier
Arith2.jj et compiler l’ensemble des sources Java.
5. Exécuter l’analyseur afin de vérifier qu’il reconnaı̂t bien des expressions telles que 4 ∗ 3 + −2
mais pas les expressions telles que 4 − 3.
Exercice 1.3
1. Remplacer dans le fichier Arith2.jj la règle syntaxique unElement par celle donnée dans
la figure 2.
2. Ajouter aux autres règles syntaxiques les instructions Java nécessaires à l’affichage de chaque
opérateur au moment où celui-ci est reconnu.
3. Vérifier que l’on obtient bien le bon affichage pour différentes expressions arithmétiques
constituées de constantes (nombres), du symbole unaire −, du symbole binaire + et du
symbole n-aire ∗.
1
/** Fichier Arith.jj
*
* Copyright (c) 2004-06 Universite de Franche-Comte
* Auteurs : A. Giorgetti, J. Julliand, P.-A. Masson, J. Voinot
*/
PARSER_BEGIN(Arith)
public class Arith {
}
PARSER_END(Arith)
/* ----------------- Regles lexicales ------------------------ */
SKIP: {
" "
|
"\r"
|
"\n" | "\t"
}
TOKEN: {
< CONSTANT: ( <DIGIT> )+ >
| < DIGIT: ["0"-"9"] >
}
TOKEN: { /* OPERATEURS */
< PLUS: "+" >
| < MULT: "*" >
}
/* ---------------- Regles syntaxiques ---------------------- */
void axiome() : {} {
unTerme() <EOL>
}
void unTerme() : {} {
unProduit() [ <PLUS> unTerme() ]
}
void unProduit() : {} {
unElement()
( <MULT> unElement() )*
}
void unElement() : {} {
<CONSTANT>
}
Fig. 1 – Fichier Arith.jj
void unElement() : { int x = 0; }{
<CONSTANT> {
try {
x = Integer.parseInt(token.image);
} catch (NumberFormatException ee) {
System.err.println("Error: "
+ token.image + " is not a number.");
x = 0;
}
System.out.println(x);
}
}
Fig. 2 – Règle syntaxique unElement()
2
2
Arbres syntaxiques
Dans cette partie du TP, nous allons aborder à travers différents exercices l’utilisation de
l’outil JJTree. Cet outil prend en entrée des fichiers contenant la description d’une grammaire
pour JavaCC, des annotations de construction d’arbres et éventuellement d’autres actions. Dans le
cas du fichier Aritha.jjt, fourni et reproduit dans la figure 3, l’exécution du préprocesseur JJTree,
par la commande jjtree Aritha.jjt, génère un fichier .jj dont la traduction par JavaCC génère
un traducteur Java d’expressions arithmétiques en arbres syntaxiques.
Exercice 2.1
1. Dans le fichier Aritha.jjt, ajouter et/ou modifier les règles lexicales et syntaxiques pour
reconnaı̂tre aussi le symbole unaire − de négation.
2. Exécuter le préprocesseur JJTree sur le fichier Aritha.jjt ainsi modifié.
3. Exécuter JavaCC sur le fichier Aritha.jj produit par JJTree.
4. Remplacer le fichier ASTunElement.java par celui qui est fourni, reproduit dans la figure 5.
5. Ajouter le fichier Launcher.java, fourni et reproduit dans la figure 4. Il sert à lancer l’analyseur généré à partir du fichier Aritha.jjt et à afficher l’arbre produit.
6. Construire l’exécutable Launcher.class et l’exécuter sur quelques exemples afin de comprendre son fonctionnement.
Indications sur le fonctionnement de JJTree
– JJTree crée un nœud d’arbre par non-terminal en partie gauche d’une règle syntaxique, sauf
si le nom de ce non-terminal est suivi de l’annotation #void.
– JJTree crée un arbre avec n fils pour chaque non-terminal suivi d’une annotation #NonTermin (n),
où NonTermin est le nom de ce non-terminal. Par exemple, dans la règle unTerme, JJTree
crée un arbre de classe ASTAdd avec 2 fils, qui seront les 2 arbres construits par les 2 nonterminaux unProduit() et unTerme() en partie droite de règle.
– La variable jjtThis est une référence sur le nœud courant.
3
/** Fichier Aritha.jjt
* Copyright (c) 2004-06 Departement Informatique UFR ST
* Auteurs : A. Giorgetti, J. Julliand, P.-A. Masson, J. Voinot
*
* Ce fichier decrit un petit analyseur syntaxique qui produit
* une representation sous forme d’arbre de syntaxe abstraite (ASA)
* d’une expression arithmetique en entree.
*
* L’option MULTI permet de typer les arbres par ASTNomArbre.
* Par exemple, l’arbre annote’ avec #Add est de type ASTAdd.
* Cette classe est creee automatiquement par JJTree
* et est modifiable. Elle herite de la classe SimpleNode.
**/
options {
MULTI=true;
}
PARSER_BEGIN(Aritha)
public class Aritha {}
PARSER_END(Aritha)
/** ----------------- Regles lexicales ------------------------ */
SKIP : { " "
|
"\r"
| "\n" | "\t" }
TOKEN : {
< CONSTANT: ( <DIGIT> )+ >
| < DIGIT: ["0"-"9"] >
/* OPERATEURS */
| < PLUS: "+" >
| < MULT: "*" >
}
/** ---------------- Regles syntaxiques --------------------- */
void axiome() #Exp() : {
/* #Exp indique que l’analyse d’un axiome construit
un ASA de classe ASTExp qui aura autant de fils que
de non-terminaux en partie droite de regle. */
} {
/* [ X ] abrege (epsilon | X) */
[unTerme()] <EOF>
}
void unTerme() #void : {} {
unProduit() [ <PLUS> unTerme() #Add(2) ]
}
void unProduit() #Mul() : {} {
unElement()
( <MULT> unElement() )*
}
void unElement() :
/* La reconnaissance d’un non terminal unElement construit un ASA
prédéfini, de classe ASTunElement. On modifie la définition de la
classe prédéfinie ASTunElement en lui ajoutant une méthode qui
permet de stocker la valeur du nombre reconnu. Voir le fichier
ASTunElement.java fourni. */
{ int x = 0; } {
<CONSTANT>
{
try { x = Integer.parseInt(token.image);
} catch (NumberFormatException ee) {
System.err.println("Error: "
+ token.image + " is not a number.");
x = 0;
}
jjtThis.setValeur(x);
}
}
Fig. 3 – Fichier Aritha.jjt
4
/** Fichier Launcher.java
*
* Copyright (c) 2006 Universite de Franche-Comte
* Auteurs : A. Giorgetti
*/
public class Launcher {
public static void main(String args[]) throws Exception {
Aritha parser = new Aritha(System.in);
parser.Axiome();
System.out.println("Debut arbre");
Node racine = parser.jjtree.rootNode();
((SimpleNode) racine).dump(" > ");
System.out.println("Fin arbre");
}
}
Fig. 4 – Fichier Launcher.java
Exercice 2.2
1. Ajouter dans les fichiers AST*.java et SimpleNode.java une méthode public int eval()
qui évalue une expression.
2. Ajouter dans Launcher un appel à cette méthode et faire afficher son résultat. Par exemple,
l’expression 4 + 3 devra produire l’affichage :
Valeur de l’expression : 7
Indications pour l’exercice 2.2 Chaque nœud construit hérite de toutes les méthodes définies
dans l’interface Node et implantées dans la classe SimpleNode (voir le fichier SimpleNode.java ou
sa documentation). Parmi ces méthodes, utiliser :
– int jjtGetNumChildren() qui retourne le nombre de fils d’un nœud.
– Node jjtGetChild(i) qui retourne le (i + 1)e fils d’un nœud, les fils étant numérotés de 0
à jjtGetNumChildren() − 1.
Exercice 2.3
1. Ajouter dans les fichiers AST*.java, SimpleNode.java et Launcher.java une méthode
commut() de transformation des arbres en arbres commutés, selon les règles suivantes :
commut(Entier : n) = Entier : n
commut(Mul x1 ... xn) = Mul commut(xn) ... commut(x1)
commut(Add x y) = Add commut(y) commut(x)
commut(Sub x) = Sub commut(x)
Indications pour l’exercice 2.3
– La méthode void jjtAddChild(Node n, int i) de la classe SimpleNode ajoute le fils n en
position i, les fils étant numérotés à partir de 0.
– Les constructeurs d’arbres
ASTMul(ArithTreeConstants.JJTMUL) et
ASTAdd(ArithTreeConstants.JJTADD)
créent respectivement un arbre de classe ASTMul et ASTAdd.
– Les constantes statiques JJTMUL et JJTADD sont définies dans le fichier ArithTreeConstants.java.
5
/**
* Fichier ASTunElement
*
* Copyright (c) 2004-2006 Département Informatique UFR ST
* Auteurs : A. Giorgetti, J. Voinot
*
* Ce fichier a été obtenu en complétant le fichier engendré
* automatiquement par JJTree pour définir les arbres de classe
* ASTunElement, qui héritent des arbres de classe
* prédéfinie SimpleNode.
*
* On a ajouté :
* - un attribut valeur de type entier qui contient la valeur
*
de l’élément,
* - une méthode getValeur pour lire cet attribut,
* - une méthode setValeur pour affecter cet attribut et
* - une méthode toString pour donner une forme textuelle à ces
*
arbres.
*/
public class ASTunElement extends SimpleNode {
private int valeur;
public ASTunElement(int id){
super(id);
}
public int getValeur() {
return valeur;
}
public void setValeur(int n) {
valeur = n;
}
public String toString() {
return "Entier : " + valeur;
}
}
Fig. 5 – Fichier ASTunElement.java
6
Téléchargement