L3/TL — TP 3 Analyse syntaxique et mod`ele de conception

publicité
L3/TL — TP 3
Analyse syntaxique et modèle de conception
Ce TP est la suite du TP 2 sur la compilation des expressions arithmétiques. Il montre comment
appliquer plusieurs fonctions différentes sur les mêmes arbres de syntaxe abstraite (ASA), en
utilisant un modèle de conception.
1
Solution du TP 2
Dans le TP 2, les expressions arithmétiques sont soit évaluées numériquement, par la méthode
eval(), soit transformées en leur forme commutée, par la méthode commut(). Dans les deux cas,
on suit les 4 étapes suivantes :
1. Implantation de la méthode, eval() ou commut(), dans chaque classe d’ASA.
2. Définition de cette méthode dans l’interface Node.java et implantation d’une version par
défaut dans SimpleNode.java
3. Invocation de cette méthode sur le nœud racine de l’ASA (après sa construction par analyse
syntaxique).
4. Visualisation de l’expression obtenue, par exemple avec la méthode dump(">").
1.1
Exemple de la méthode commut()
1. Implanter la méthode commut() dans tous les fichiers AST*.java
Par exemple, dans ASTAdd.java, on peut écrire :
public Node commut() {
Node filsgauche, filsdroit;
filsgauche = this.jjtGetChild(0).commut();
filsdroit = this.jjtGetChild(1).commut();
return(this);
}
2. Définir commut() dans SimpleNode.java
public Node commut() { return(null); }
Déclarer cette méthode dans l’interface Node.java
public Node commut();
3. Dans la classe exécutable (Launcher.java), appeler la méthode commut() à partir de la
racine de l’arbre, puis visualiser l’ASA résultat
n.commut();
((SimpleNode) n).dump(">");
Comparez votre solution avec cette correction. Si besoin est, suivez les étapes de cette correction, vérifiez son bon fonctionnement sur quelques exemples et reprenez ces étapes pour la
méthode eval().
1
2
Le modèle de conception Visitor
Dans le TP précédent, l’implantation des traitements eval() et commut() modifie le code
source de nombreux fichiers Java. Outre le fait qu’il faut ensuite tout recompiler, cela disperse
l’information dans un trop grand nombre de classes de nœuds.
Il existe une autre façon de faire, plus pratique, qui utilise le modèle de conception (”design
pattern”) Visitor. Selon ce modèle, on crée une classe Java NomDuTraitement Visitor par traitement. Cette ”classe de visite” doit implémenter (implements) l’interface Parser Visitor, où
Parser est le nom de votre analyseur syntaxique.
Dans cette classe de visite, on définit une méthode de visite
public Object visit(MyNode n, Object d)
par classe de nœuds (ici, MyNode) et on y programme le traitement prévu pour cette sorte de
nœuds.
Par ailleurs, toutes les classes de nœuds héritent, via Node et SimpleNode, d’une méthode
public Object jjtAccept(ParserVisitor v, Object d);
Son premier paramètre accepte une instance d’une classe de visite telle que NomDuTraitement Visitor.
Invoquer cette méthode sur un nœud de classe MyNode appelle, par liaison dynamique, la
méthode de visite
public Object visit(MyNode n, Object d)
définie dans NomDuTraitement Visitor et provoque ainsi l’application du traitement voulu.
En ajoutant l’option VISITOR=true; JJTree automatise la plupart de ces tâches : il ajoute
la méthode jjtAccept dans l’interface Node.java et dans toutes les classes de nœuds qui l’implantent. De plus, il crée le fichier Parser Visitor avec un contenu tel que
/* Generated By:JJTree: Do not edit this line. ParserVisitor.java */
public interface ParserVisitor {
public Object visit(SimpleNode node, Object data);
public Object visit(MyNode node, Object data);
...
}
avec une méthode de visite par classe de nœuds.
Le grand avantage de cette façon de faire est que le code des classes des nœuds reste ensuite
constant : Pour ajouter un nouveau traitement, on développe simplement une nouvelle classe de
visite, et on la fait accepter par la racine de l’arbre de syntaxe abstraite.
1. Appliquer cette technique à la fonction eval(), en développant une classe EvalVisitor.
2. Tester cette classe de visite.
3. Développer une classe CommutVisitor pour la fonction commut().
2
Téléchargement