• Aucun résultat trouvé

iavarcript. méanmoins, l’idée de capturer le ǥot de contrôle et de retourner au code détourné n’est pas remise en cause. hl faut juste trouver d’autres mécanismes pour la réaliser.

.

En programmation littéraire

cans la programmation littéraire, le mécanisme qui nous in- téresse est de pouvoir nommer des blocs de code pour les déǤnir à un autre moment. oar exemple, on peut écrire un interpréteur minimal en iavarcript ainsi :

nn utilise ici la syntaxe de noweb zmo- web], un système de programmation littéraire indépendant du langage de pro- grammation. cans ces exemples, un bloc est déǤni par<<nom>>=, et comprend tout le code jusqu’à la prochaine ligne vide. ka syntaxe<<nom>> fait référence au bloc : lorsque cette référence appa- raît dans le code, tout le code associé au blocnom sera inséré à cet endroit lors de l’entrelacement.

<<terms>>=

let num = (n) => ({type: 'num', n})

let plus = (6, b) => ({type: 'plus', 6, b}) <<interp>>= function interp(n) { switch (n.type) { c6se 'num': <<interp-num>> bre6k c6se 'plus': <<interp-plus>> bre6k } }

kes termes sont représentés par de simples objets indiquant le type du nœud de l’arbre de syntaxe. ka fonction d’évaluation

interp ne fait que déléguer l’évaluation aux cas spéciǤques des termesnumetplus. bes cas ne sont pas encore déǤnis : pour l’ins- tant ce sont des références à des blocs de code qui seront inclus lors de l’entrelacement. olus loin, on peut rajouter leurs déǤnitions :

<<interp-num>>= return n.n

<<interp-plus>>=

return interp(n.6) + interp(n.b)

hci on sait que ces bouts de code vont être injectés dans un

c6se, donc on retourne directement le résultat suivant le type du terme. oour tester le programme, on pourra déǤnir l’extrait suivant comme point d’entrée :

<<terms>> <<interp>>

let e = plus(num( ), num( )) interp(e )

électre Variations de détournement sur un interpréteur minimal

b’est le programme principal qui inclut les blocstermetinterp. motons que l’on n’inclut pas explicitement les blocsinterp-numet

interp-plus, car ils seront inclus récursivement parinterp. oour pouvoir exécuter ce programme, on engendre le Ǥchier iavarcript entrelacé à l’aide deTANGLE:

let num = (n) => ({type: 'num', n})

let plus = (6, b) => ({type: 'plus', 6, b}) function interp(n) { switch (n.type) { c6se 'num': return n.n bre6k c6se 'plus':

return interp(n.6) + interp(n.b) bre6k

} }

let e = plus(num( ), num( )) interp(e ) //:

nn obtient un programme qu’on aurait très bien pu écrire ma- nuellement. ka diǣérence est qu’on a pu organiser les diǣérentes parties dans l’ordre qui nous intéresse.

laintenant, on souhaite détourner cet interpréteur. oar exemple, on souhaite modiǤer la fonction d’évaluation denum. hntuitivement, on aurait envie de redéǤnir juste le bloc interp-num, peut-être ainsi :

<<terms>> <<interp>>

<<interp-plus>>= return n.n *

let e = plus(num( ), num( )) interp(e ) //:

lais malheureusement, en noweb, ceci ne redéǤnit pas le bloc

interp-plus, mais ajoute du code à ce bloc. ri bien qu’après l’en- trelacement, on obtient :

function interp(n) { switch (n.type) { c6se 'num':

return n.n

return n.n * // code mort

bre6k

. . En programmation littéraire élixir

hl n’y a pas de syntaxe en noweb pour exprimer cette redéǤni- tion. fénéralement, si on a un blocXqui fait référence à un bloc

Y:

<<X>>= ... <<Y>> ...

et que le blocYest déǤni ailleurs dans le document littéraire :

<<Y>>= ...

et si l’on dispose d’une déǤnition alternative deY,Y :

<<Y >>= ...

alors le problème est que l’on souhaite pouvoir dire, pendant l’entrelacement du bloc X, d’utiliser soit Y, soit Y . be n’est pas possible directement en noweb.

nn pourrait imaginer une extension. oar exemple, dire :

<<X with Y=Y >>

pour signiǤer que l’on souhaite inclure à cet endroit le contenu du blocX, mais utiliser le contenu du blocY pour chaque référence interne àXfaite au blocY. ce cette façon, on peut facilement modi- Ǥer le contenu du blocXlors de son inclusion, sans avoir à toucher sa déǤnition ; c’est-à-dire, le détourner.

dssentiellement, une telle extension de noweb revient à implé- menter la portée dynamique pour les noms de blocs. cans le bloc

X,Yest une variable libre qui, par défaut en noweb, fait référence au blocYs’il est déǤni. nn peut considérer que les noms de blocs résident tous dans un environnement global unique. dn ajoutant la constructionwith, la résolution de la référenceYdépend de l’ap- pelant : si l’environnement dynamique de l’appelant contient une redéǤnition deY, ce sera ce bloc qui sera utilisé.

bette extension de noweb permettrait de détourner des pro- grammes, quel que soit le langage de programmation utilisé. lais en l’absence de cette extension, on peut tout de même contourner le problème dans des langages spéciǤques. dn iavarcript, on peut déǤnir l’interpréteur ainsi :

<<interp>>= function interp(n) { switch (n.type) { c6se 'num': return interp_num(n) c6se 'plus': return interp_plus(n) } }

bette fois on n’utilise plus noweb pour faire référence au code qui va évaluer chaque terme, mais de simples fonctions :interp_num

etinterp_plus. nn pourrait déǤnir ces fonctions dans le même bloc, mais on va les déǤnir dans leur propre bloc :

émeril Variations de détournement sur un interpréteur minimal

<<interp-num>>=

function interp_num({n}) { return n } <<interp-plus>>=

function interp_plus({6,b}) { return interp(6) + interp(b) }

ke point d’entrée du programme est alors le suivant :

<<terms>> <<interp>> <<interp-num>> <<interp-plus>>

let e = plus(num( ), num( )) interp(e ) //:

ka diǣérence avec le précédent point d’entrée c’est que l’on dé- clare explicitement les blocs qui contiennent les déǤnitions des fonctions interp_num et interp_plus. ke programme entrelacé nous donne donc :

let num = (n) => ({type: 'num', n})

let plus = (6, b) => ({type: 'plus', 6, b}) function interp(n) { switch (n.type) { c6se 'num': return interp_num(n) c6se 'plus': return interp_plus(n) } }

function interp_num({n}) { return n } function interp_plus({6,b}) {

return interp(6) + interp(b) }

let e = plus(num( ), num( )) interp(e ) //:

motons que les déǤnitions deinterp_numetinterp_plusap- paraissent après leur utilisation dans interp. dn iavarcript, ce n’est pas un problème tant que les fonctions sont déǤnies avant queinterpne soit appelée.

ke résultat d’interp(e )est le même que précédemment, donc l’interpréteur fonctionne de la même façon. lais cette fois, on peut déǤnir une fonction d’évaluation alternative pour num, dans son propre bloc :

<<interp-num-double>>

. . En programmation littéraire épitoge

motons que l’on redé nitinterp_num. hl suǦt alors de modiǤer le point d’entrée :

<<terms>> <<interp>>

<<interp-num-double>> // num -> num-double

<<interp-plus>>

let e = plus(num( ), num( )) interp(e ) //:

et on parvient à redéǤnir l’évaluation dee , sans avoir changé le code de l’interpréteur, mais uniquement en modiǤant le code du point d’entrée. cans l’entrelacement, on a juste remplacé la déǤnition de la fonctioninterp_numpar une autre.

dn supposant qu’un interpréteur réaliste comporte plusieurs dizaines, voire centaines de fonctions d’interprétations, on voudra raccourcir le point d’entrée avec ce bloc qui les déclare toutes :

<<std-interp>>= <<terms>> <<interp>> <<interp-num>> <<interp-plus>> ...

Alors on pourra utiliser un interpréteur en incluant simplement ce bloc :

<<std-interp>> let e = ... interp(e ) //:

lais là encore, le détournement est encore possible, car il suǦt d’ajouter notre redéǤnition :

<<std-interp>>

<<interp-num-double>> let e = ...

interp(e ) //:

kors de l’entrelacement, la fonction interp_num sera déǤnie deux fois : une fois par le blocinterp-numinclus parstd-interp, et une fois par le bloc interp-num-double. lais en iavarcript, cela ne pose pas de problème : c’est la dernière déǤnition rencon- trée par l’interpréteur qui fait foi. nn peut donc empiler les déǤ- nitions de la sorte sans avoir à se préoccuper de retirer les précé- dentes.

ke détournement est rendu possible ici par le langage iavar- cript qui permet la redéǤnition d’une fonction. ka programmation

équille Variations de détournement sur un interpréteur minimal

littéraire nous donne la possibilité de générer plusieurs programmes à partir de diǣérents blocs, comme des pièces à assembler, mais elle n’est pas déterminante pour le détournement. hl suǦt de dé- Ǥnir une fonction pour chaque morceau de code qui pourra être détourné, et de juste redéǤnir cette fonction avant d’interpréter un programme.

function interp() { ... } function interp_num() { ... } function interp_plus() { ... }

function interp_num() { ... } // détournement

let e = ...

lais même si l’on ajoutait la résolution dynamique des noms de blocs à noweb, il y a au moins deux inconvénients à utiliser la programmation littéraire ainsi pour détourner un interpréteur. ke premier est que l’on doit tout de même découper l’interpréteur en blocs, et ajouter des références aux blocs de code qui sont suscep- tibles d’être détournés. hl faut donc modiǤer le code source, le réor- ganiser. ke second inconvénient est que ce détournement est uni- quement statique. dn plus de rajouter une étape de compilation, on doit choisir, dans le point d’entrée, quelle version de l’interpréteur on souhaite engendrer. nn peut imaginer avoir plusieurs points d’entrées, qui correspondent à des Ǥchiers diǣérents, où l’entrela- cement engendrera autant de programmes qu’il y a de points d’en- trée. lais on ne peut pas changer de la sorte le comportement de l’interprétation à l’exécution.