• Aucun résultat trouvé

Définition des macros unaires et binaires

9. Les macros

9.5. Définition des macros unaires et binaires

À plusieurs reprises, il a été signalé que la plupart des opérateurs et des commandes, qui ont été envisagés jusqu’à présent, sont en fait des macros prédéfinies. Elles englobent les opérateurs unaires tels que round et unitvector, les déclarations

telles quefilletdrawet les opérateurs binaires commedotprodet intersec-tionpoint. La différence majeure entre ces macros et celles déjà vues réside dans la façon de définir la syntaxe de leurs arguments.

Les macrosroundetunitvectorsont des exemples de ce que la figure 14 appelle des

<opérateurs unaires>. C’est-à-dire qu’elles sont suivies par une expression primaire.

Pour spécifier un argument d’une macro de ce type, la définition de la macro doit ressembler à

vardef round primary u =<texte de remplacement>enddef ; Le paramètreuest un paramètre expression et peut être utilisé exactement comme le paramètre expression défini en utilisant la syntaxe habituelle

(expr u)

Comme le suggère l’exempleround, une macro peut être définie pour prendre un pa-ramètre<secondaire>,<tertiaire>ou<expression>. Par exemple, la définition « pré-définie » de la macrofillest grossièrement

def fill expr c = addto currentpicture contour c enddef ; Il est même possible de définir une macro pour jouer le rôle de l’<opérateur of>dans la figure 14. Par exemple, la macrodirection ofpossède une définition de la forme vardef direction expr t of p = <texte de remplacement>enddef ; Des macros peuvent aussi être définies pour se comporter comme des opérateurs bi-naires. Pour l’instant, la définition de la macrodotprodest de la forme :

primarydef w dotprod z =<texte de remplacement>enddef ; Cela transforme dotprod en <opérateur binaire primaire>. De même, secon-darydef et tertiarydef introduisent la définition des <opérateurs binaires secondaires>et<opérateurs binaires tertiaires>. Toutes définissent des macros ordi-naires, et non des macrosvardef; par exemple, il n’y a aucune «primaryvardef».

Ainsi les définitions de macro peuvent être introduites pardef,vardef, primary-def,secondarydefoutertiarydef. Un<texte de remplacement>est n’importe quelle liste detokensorganisée en fonction du coupledef-enddefoù tous les cinq identificateurs de définition de macro sont traités commedefpour respecter la paire def-enddef.

Le reste de la syntaxe pour les définitions de macro est résumé figure 46. La syn-taxe comporte quelques surprises. Les paramètres de macro peuvent avoir un<partie délimitée>et une<partie illimitée>(ou partie non délimitée). Normalement, l’une d’entre elles est<vide>, mais il est possible d’avoir les deux parties non vides à la fois :

def foo(text a) expr b =<texte de remplacement>enddef ; Cela définit une macrofooqui requiert un paramètre texte entre parenthèses suivi d’une expression.

La syntaxe permet également par la<partie non délimitée>de spécifier un argument de typesuffixoutext. Un exemple de macro avec un paramètre suffixe non déli-mité est la macro prédéfinieincrqui est, en fait, définie comme ceci

vardef incr suffix $ = $ :=$+1 ; $ enddef ;

Ceci transforme incr en une fonction qui requiert une variable, l’incrémente et renvoie la nouvelle valeur. Des paramètres suffixes non délimités peuvent être mis entre parenthèses, ainsiincr aetincr (a)sont toutes les deux correctes siaest une variable numérique. Il existe une macro prédéfinie similairedecrqui soustrait 1.

Les paramètres textes non délimités fonctionnent jusqu’à la fin d’une instruction. Plus précisément, un paramètre texte non délimité est une liste d’identificateurs suivant l’appel de la macro jusqu’au premier « ; » ou «endgroup» ou «end» excepté qu’un argument contenant «begingroup» inclura toujours le «endgroup» correspondant.

Un exemple d’un paramètre texte illimité est donné avec la macro prédéfiniecutdraw dont la définition est plus ou moins celle-ci :

def cutdraw text t =

begingroup interim linecap :=butt ; draw t ; endgroup end-def ;

Cela rend la macrocutdrawsynonyme dedrawsauf en ce qui concerne la valeur de linecap(cette macro est fournie essentiellement pour assurer une compatibilité avec METAFONT).

<définition de macro>→<en-tête de macro>=<texte de remplacement>enddef

<en-tête de macro>→<tokensymbolique><partie délimitée><partie illimitée>

|vardef<variable générique><partie délimitée><partie illimitée>

|vardef<variable générique>@#<partie délimitée><partie illimitée>

|<def binaire>

<paramètre><tokensymbolique><paramètre><partie délimitée>→<vide>

|<partie délimitée>(<type paramètre><paramètrestokens>)

<type paramètre>→expr|suffix|text

<paramètrestokens>→<paramètre><paramètrestokens>,<paramètre>

<paramètre>→<tokenssymboliques>

<partie illimitée>→<vide>

|<type paramètre><paramètre>

|<niveau de priorité><paramètre>

|expr<paramètre>of<paramètre>

<niveau de priorité>→primary|secondary|tertiary

<def binaire>→primarydef|secondarydef|tertiarydef

FIG. 46 – Syntaxe de définitions de macro

10. Boucles

De nombreux exemples des chapitres précédents ont utilisé des bouclesforsimples de la forme

for<tokenssymboliques>=<expression>upto<expression> :

<texte de boucle>endfor

Il est également simple de construire une boucle qui compte en décroissant : il suffit de remplaceruptopardowntoen rendant la seconde<expression>inférieure à la première. Ce chapitre couvre des types plus compliqués de progressions, boucles dans lesquels le compteur de boucle se comporte comme un paramètre suffixe. Ce chapitre envisage également les sorties de boucle.

La première généralisation est suggérée par le fait queuptoest une macro prédéfinie pour

step 1 until etdowntopourstep -1 until. Un début de boucle

for i=a step b until c

balaye une séquence de valeursi :a,a+b,a+2b,... s’arrêtant avant queidépassec; c’est-à-dire que la boucle balaye les valeurs deipour lesquellesi≤csib>0eti≥csi b<0.

Il est préférable d’utiliser cette caractéristique seulement lorsque le pas est un entier ou un nombre qui peut être représenté exactement, en arithmétique à virgule fixe, comme un multiple de655361 . Autrement, les erreurs vont s’accumuler et l’index de boucle peut ne pas atteindre la borne finale attendue. Par exemple,

for i=0 step .1 until 1 : show i ; endfor montre 10 valeurs deidont la dernière est 0.90005.

La manière classique d’éviter les problèmes associés à un pas non entier est d’itérer sur des valeurs entières et ensuite de multiplier par un facteur d’échelle comme cela a été fait pour les figures 2 et 40.

D’une autre manière, les valeurs sur lesquelles on doit itérer peuvent être données ex-plicitement. Toute séquence d’expressions (même vides) séparées par des virgules peut être utilisée à la place dea step b upto c. En fait, les expressions ne nécessitent pas d’être toutes du même type et ne nécessitent pas d’avoir des valeurs connues. Ainsi

for t=3.14, 2.78, (a,2a), "hello" : show a ; endfor montre les quatre valeurs énumérées.

On notera que le corps de la boucle dans l’exemple précédent est une instruction sui-vie par un point-virgule. Il est classique que le corps de boucle soit constitué d’une ou plusieurs instructions mais il ne s’agit pas d’une obligation. Une boucle est comme une définition de macro suivie d’un appel de macro. Le corps de la boucle peut être virtuel-lement n’importe quelle séquence detokensdu moment qu’ils aient un sens ensemble.

Ainsi, l’instruction (ridicule)

draw for p=(3,1),(6,2),(7,5),(4,6),(1,3) : p -- endfor cycle ; est strictement équivalente à

draw (3,1) -- (6,2) -- (7,5) -- (4,6) -- (1,3) -- cycle ; La figure 18 présente un exemple plus réaliste d’une telle boucle.

Si une boucle ressemble à une définition de macro, l’index de boucle ressemble à un paramètre expression. Il peut représenter n’importe quelle valeur, mais ce n’est pas une

variable et ne peut pas être changé par une déclaration d’assignation. Pour réaliser ceci, il faut recourir à une boucleforsuffixes. Une boucleforsuffixesest presque comme une boucleforsauf que l’index de boucle se comporte comme un paramètre suffixe. La syntaxe est :

forsuffixes<tokensymbolique>=<liste de suffixes> :

<texte de boucle>endfor

où une<liste de suffixes>est une liste de suffixes séparés par des virgules. Si certains de ces suffixes sont<vides>, le<texte de boucle>sera exécuté avec le paramètre d’index mis à un suffixe vide.

Un bon exemple d’une boucleforsuffixesest la définition de la macrodotlabels vardef dotlabels@#(text t)=

forsuffixes $=t : dotlabel@#(str$,z$) ; endfor enddef ; Ceci explique pourquoi le paramètre dedotlabels doit être une liste de suffixes séparés par des virgules. La plupart des macros, qui acceptent des listes à longueurs variables, dont le séparateur est la virgule, utilisent celles-ci dans des bouclesforou forsuffixesde cette façon comme valeurs sur lesquelles la boucle itère.

S’il n’y a pas de valeurs sur lesquelles itérer, on peut utiliser une boucleforever : forever :<texte de boucle>endfor

Pour mettre un terme à une telle boucle lorsqu’une condition booléenne est vérifiée, il faut utiliser une clause de sortie :

exitif<expression booléenne>;

Lorsque l’interpréteur METAPOST rencontre une clause de sortie, il évalue l’<expression booléenne> et sort de la boucle courante si l’expression est vraie.

S’il est plus commode de sortir de la boucle quand l’expression est fausse, il faut alors utiliser la macro prédéfinieexitunless.

Ainsi la versionMETAPOSTd’une bouclewhileest :

forever : exitunless<expression booléenne>;<texte de boucle>endfor La clause de sortie peut tout aussi bien venir juste avantendfor ou n’importe où dans le<texte de boucle>. En fait, toute bouclefor,foreverouforsuffixespeut contenir n’importe quel nombre de clauses de sortie.

Le résumé de la syntaxe des boucles (figure 47) ne fait pas mention explicitement des clauses de sortie parce qu’un<texte de boucle>peut être virtuellement n’importe quelle séquence detokens. La seule restriction est qu’un<texte de boucle>doit être équilibré du point de vue desforetendfor. Naturellement, ce processus d’équilibre traiteforsuffixesetforeverexactement commefor.

<boucle>→<en-tête de boucle> :<texte de boucle>endfor

<en-tête de boucle>→for<tokensymbolique>=<progression>

|for<tokensymbolique>=<liste for>

|forsuffixes<tokensymbolique>=<liste de suffixes>

|forever

<progression>→<expression numérique>upto<expression numérique>

|<expression numérique>downto<expression numérique>

|<expression numérique>step<expression numérique>until <expres-sion numérique>

<liste for>→<expression>|<liste for>,<expression>

<liste de suffixes>→<suffixes>|<liste de suffixes>,<suffixes>

FIG. 47 – Syntaxe des boucles