6. Stratégies de réécriture réflexives en Java 113
6.4. Rendre les stratégies utilisables
6.4.1. Comment définir des stratégies « utilisateur »
Le langage de stratégies permet de spécifier la manière d’appliquer des
transforma-tions dans un arbre. Ces transformatransforma-tions sont décrites par des stratégies spécifiées par
l’utilisateur du système, à la manière des règles nommées du langageElan, décrites en
section 6.1.2.
Dans l’expression de stratégie présentée en figure 6.7, les nœuds grisés correspondent
aux stratégies utilisateur qui décrivent les différentes optimisations effectuées par le
compilateur, tandis que les autres nœuds sont les combinateurs qui décrivent la procédure
d’optimisation.
Note A. La stratégie présentée en figure 6.7 est représentée avec toutes les stratégies
composées affichées sous la forme de leur graphe de stratégies élémentaires. Cette
straté-gie dont le graphe est complexe est programmée en combinant des stratéstraté-gies prédéfinies.
Ainsi, la première branche de la séquence principale est décrite dans le programme par :
InnermostId(ChoiceId(RepeatId((NopElimAndFlatten())),NormExpr()))
Dans laquelle NopElimAndFlatten() et NormExpr() construisent les stratégies «
utili-sateur » qui respectivement éliminent les instructions inutiles, et normalisent les
expres-sions booléennes. On voit ici que la règle NopElimAndFlatten() est réutilisée à trois
reprises, dans des contextes différents. En effet, dans certains cas elle est utilisée sous
une stratégie TopDown pour normaliser, mais elle est aussi utilisée sous une stratégie
OnceTopDownlorsque l’on sait dans le processus d’optimisation que la règle ne doit être
appliquée qu’une seule fois, après fusion des blocks et des tests qui peuvent l’être.
La définition de telles transformations se fait dans le cadre de la librairie JJTraveler
par l’écriture d’une classe Javaqui étend une classe spécifique à la structure de données
Sequence Mu Mu Sequence Sequence All SequenceId x ChoiceId x Mu NormExpr SequenceId Identity NopElimAndFlatten x Identity All SequenceId x ChoiceId x Sequence SequenceId Mu Mu InterBlock Mu SequenceId SequenceId IfSwapping x Identity SequenceId x ChoiceId Mu
BlockFusion IfFusion ChoiceId Identity Identity NopElimAndFlatten OneId
Identity x Identity ChoiceId Mu OneId SequenceId x NopElimAndFlatten x Identity
6.4. Rendre les stratégies utilisables
traversée. Nous appellerons par la suite cette classe dépendant de la structure de données
Fwd. Celle-ci prend en argument une stratégie, généralementIdentity() ouFail(), et
définit des méthodes de visite préservant les types de la structure de données, dont le
comportement par défaut est de déléguer l’appel à la stratégie argument. L’utilisateur
peut alors redéfinir une de ces méthodes spécifiques à un type dans la structure de
données pour obtenir un comportement particulier. On peut voir en figure 6.7 que les
stratégies utilisées pour la définition de l’optimiseur du compilateur Tom étendent le
comportement de la stratégie identité. Ainsi, lorsque la règle ne peut pas être appliquée,
la stratégie effectue l’identité, et ne change pas le terme courant, ni n’échoue.
Une construction de déclaration de stratégies
Pour pouvoir programmer une telle stratégie utilisateur, il est nécessaire de connaître
les détails de fonctionnement interne de la bibliothèque, comme illustré en figure 6.8. La
structure de données utilisée avec les stratégies est généralement une structure générée
automatiquement pas un outil commeGom. Ainsi, il n’est pas nécessaire que l’utilisateur
définisse lui-même la classeFwdconstituant la base des stratégies utilisateur. Cependant,
il reste nécessaire de connaître les patrons de conception dirigeant la librairie de stratégie
pour développer les stratégies.
Pour rendre le développement de stratégies plus facile, nous définissons une extension
du langageTompar une construction%strategypermettant de définir une stratégie de
manière abstraite. Cette construction, illustrée en figure 6.8 par l’encodage d’une règle de
réécrituref(x, x)→g(x), pour laquelle on supposef etgde sorteT, permet une écriture
concise et abstraite de la règle. La définition qui suitvisit Texplicite le comportement
de la stratégie lorsqu’elle visite un nœud de sorteT, en utilisant le filtrage sur les termes
comme dans l’instruction%match. On peut définir une constructionvisitpour chaque
sorte de l’algèbre de termes. Cette stratégie étend l’échec, ce qui signifie qu’elle échoue
lorsque la règle définie ne peut pas s’appliquer, et donc n’échoue pas uniquement sur les
radicaux qui filtrent f(x, x).
Cette construction %strategy permet aussi de décrire des stratégies paramétrées,
les paramètres étant passés comme arguments lors de la construction de la règle. La
stratégie une fois déclarée peut être utilisée à la manière des combinateurs élémentaires
dans l’écriture d’une expression de stratégie.
La stratégie définie à l’aide de la construction %strategy a le même statut que
les combinateurs pour la librairie de stratégie. Elle est donc elle-même visitable et
il est possible d’utiliser une construction de filtrage sur cette stratégie. La procédure
de µ-expansion traite ainsi les sous-termes d’une stratégie utilisateur qui sont de type
Strategy, ce qui permet d’écrire des versions spécialisées des briques de base, ainsi que
des stratégies dont les arguments représentent la suite du calcul pouvant être récursives.
Cela est utile pour décrire une stratégie réutilisable effectuant un appel récursif
expli-cite non pas sur la brique de base elle-même, mais sur la stratégie de parcours elle-même.
Stratégie f(x, x)→g(x)utilisant JJTraveler et Tom pour le filtrage :
public static class Rule extends TFwd {
public Rule() {
super(new Fail());
}
public T visit_T(T arg) throws jjtraveler.VisitFailure {
%match(T arg) {
f(x,x) -> { return ‘g(x); }
}
}
}
Stratégief(x, x)→g(x) utilisant%strategy :
%strategy Rule() extends Fail() {
visit T {
f(x,x) -> { return ‘g(x); }
}
}
Fig. 6.8: Exemple de stratégie codant une règle de réécriture
Dans le document
Réécriture et compilation de confiance
(Page 145-148)