diff options
18 files changed, 32014 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/README b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/README new file mode 100644 index 00000000000..ed792bf0460 --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/README @@ -0,0 +1,162 @@ +-------------------------------- Avant propos --------------------------------- + +Ceci est le fichier LISEZMOI du livre « Apprendre à programmer en TeX » + +Tous les fichiers fournis dans ce paquet sont sous licence « LPPL v1.2 » + +Résumé : le présent paquet contient tous les fichiers permettant de compiler le + livre « Apprendre à programmer en TeX ». Il n'est pas nécessaire + d'effectuer la compilation pour obtenir les fichiers de sortie puisque + ceux-ci sont également fournis : + - apprendre-a-programmer-en-tex.pdf + - progtexcode.txt + +---------------------------------- Foreword ----------------------------------- + +This is the README file of the book "Apprendre à programmer en TeX" + +All the files of this bundle are under "LPPL v1.2" licence + +Abstract: this package contains all the files needed to compile the book + "Apprendre à programmer en TeX". It is not necessary to perform a + compilation to obtain the output files since they are also provided: + - apprendre-a-programmer-en-tex.pdf + - progtexcode.txt + +The text below is available in French only. + +------------------------------------------------------------------------------- + +I) Prendre le large + +Dès le début, c'était prévu après 4 ans grand maximum : libérer le code source +du livre et par la même occasion, en faire don à la communauté LaTeX. J'ai bien +conscience que les 4 ans sont passé depuis un bon moment, et c'est donc avec un +léger retard que je me décide à agir. Tout le « Matériel » de ce livre (code +source, fichiers pdf, fichiers générés par la compilation, fichiers nécessaires +à la compilation) va enfin prendre l'air et naviguer de par le monde via +internet, ce qui sera bien plus profitable que de dormir au fin fond d'un +répertoire de mon disque dur. + + + +II) Conséquences + +1) La première conséquence est, je l'espère en secret, que davantage +d'utilisateurs de LaTeX oseront jeter un œil à ce livre et s'essaieront à la +programmation ! + +2) La deuxième conséquence est que la licence change. Tout le Matériel se +trouve désormais sous la « LaTeX project public license » (LPPL) version 1.2, +mais ce changement implique la chose suivante : il n'est plus possible +d'utiliser dans le Matériel la police à chasse fixe « Luximono » qui était +employée dans le livre, car elle n'est pas sous licence libre. + +Faire un choix de police de remplacement n'a pas été facile mais finalement, +LinuxLibertineMono a été adoptée. Cela donne une certaine cohérence à +l'ensemble puisque la police du texte est LinuxLibertine. Malheureusement, +telle qu'elle est conçue, son énorme défaut est de chasser beaucoup trop. Il a +donc fallu la modifier pour créer deux autres polices plus « condensées » : + + a) pour les caractères dans le corps du texte, j'ai créé une police + LinuxlibertineMonoTC où les glyphes de LinuxLibertineMonoT ont été, via + fontforge, réduits de 75% horizontalement et 92% verticalement. Ce sont, + après bien des essais, les coefficients qui lui permettent de s'insérer au + mieux (à mon goût) dans le texte, qui donnent une chasse proche de celle de + Luximono et qui rendent le gris typographique acceptable ; + + b) pour les codes figurant dans le texte du livre, la police + LinuxlibertineMonoTCC a été créée, toujours à partir de LinuxLibertineMonoT, + mais les glyphes ont été davantage réduit horizontalement : 67% + horizontalement et 92% verticalement. + +3) Enfin, la dernière conséquence est que le fichier pdf produit après +compilation du code source n'est pas exactement celui qui a servi à imprimer le +livre. Il n'y a que très peu de modifications : + + a) les polices à chasse fixe sont différentes, comme expliqué ci-dessus. + Outre les considérations esthétiques sur lesquelles je ne m'étendrai pas, + cela ne devrait pas entraîner de modification de la mise en page car la + chasse de LinuxlibertineMonoTC est extrêmement proche de celle de Luximono ; + + b) les mentions Luximono sont changées pour LinuxlibertineMonoTC ; + + c) la licence est modifiée (page réelle 6) ; + + d) quelques corrections ont été faites (coquilles, erreurs dans les codes, + etc.), mais une infime partie en regard de ce qui resterait à faire ! + + + +III) Compilation + +Selon la LPPL 1.2, quiconque peut compiler et distribuer le Matériel sous le +nom originel, sous réserve qu'aucun changement n'y ait été apporté. + +Pour que la compilation puisse être effectuée le plus simplement possible, les +7 fichiers nécessaires à la compilation se trouvent dans le dossier /source, et +on y trouve en plus le fichier manderlbrot.tex qui a servi à générer le pdf où +est dessiné l'ensemble de Mandelbrot à l'aide d'une version modifiée de TeX. + +Par ailleurs, toujours par souci de simplicité : + - tout le code source est dans un seul fichier « livre.tex », qui est le + code source à compiler avec l'exécutable pdflatex ; + - seules 3 fontes sont à installer à la main, à savoir LinuxlibertineMonoTC, + LinuxlibertineMonoTCC et les fontes de libertine-legacy. + +Pour installer ces fontes, il faut aller dans le répertoire /fonts où se +trouvent des fichiers zippés permettant ces installations. La procédure est +donnée pour GNU-linux avec TeXlive car je ne connais ni windows ni mikTeX. + +1) Installation des fontes "libertine-legacy" + + a) dézipper le fichier libertine-legacy.zip + + b) copier les répertoires /fonts et /tex dans le texmf personnel (permettre + d'écrire à l'intérieur des dossiers existants) + + c) aller dans /fonts/maps/dvips/libertine-legacy et lancer dans la console la + commande suivante + updmap -user --enable map=libertine-legacy.map + +2) Installation des fontes LinuxLibertineMono « condensées » : + + a) dézipper le fichier linlibmonoc.zip + + b) copier les répertoires /fonts et /tex dans le texmf personnel (permettre + d'écrire à l'intérieur des dossiers existants) + + c) aller dans /fonts/maps/dvips/linlibmonoc et lancer dans la console les + deux commandes suivantes : + updmap -user --enable map=LinuxLibertineMonoTC.map + updmap -user --enable map=LinuxLibertineMonoTCC.map + +On peut s'assurer que l'installation s'est correctement déroulée en examinant +la sortie générée par la commande + updmap -user --listmaps + +Quoi qu'il en soit, afin que tout ceux qui ne peuvent compiler ce code source +puissent y accéder, les 2 fichiers dignes d'intérêt qui sont générés à la +compilation se trouvent dans le répertoire /output, c'est-à -dire : + - apprendre-a-programmer-en-tex.pdf (le fichier pdf de sortie) + - progtexcode.txt (le fichier qui contient les 454 codes commentés exposés + dans les exemples de ce livre). + + + +IV) Conclusion + +Bonne lecture et surtout, prenez du plaisir avec (La)TeX ! + +N'hésitez pas à poser des questions si certaines choses ne sont pas claires, +que ce soit à moi directement ou dans un forum où les contributeurs se parlent +dans la langue de Molière ! + +Bien évidemment, toute contribution est encouragée, de même que tout retour +voire un petit remerciement qui vaudra largement le prix auquel est vendu ce +livre (car il est toujours possible de l'acheter sur lulu.com) ; pour ce faire, +passer par l'email ou le dépôt sur framagit : + unbonpetit@netc.fr + https://framagit.org/unbonpetit/apprendre-a-programmer-en-tex/ + +Christian Tellechea
\ No newline at end of file diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/cover/couverture.pdf b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/cover/couverture.pdf Binary files differnew file mode 100644 index 00000000000..e4478f99a16 --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/cover/couverture.pdf diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/fonts/libertine-legacy.zip b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/fonts/libertine-legacy.zip Binary files differnew file mode 100644 index 00000000000..14bf29ca6f5 --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/fonts/libertine-legacy.zip diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/fonts/linlibmonoc.zip b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/fonts/linlibmonoc.zip Binary files differnew file mode 100644 index 00000000000..bf116f5297f --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/fonts/linlibmonoc.zip diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/output/apprendre-a-programmer-en-tex.pdf b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/output/apprendre-a-programmer-en-tex.pdf Binary files differnew file mode 100644 index 00000000000..9e349e04e8a --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/output/apprendre-a-programmer-en-tex.pdf diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/output/progtexcode.txt b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/output/progtexcode.txt new file mode 100644 index 00000000000..fcd1a19ec7c --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/output/progtexcode.txt @@ -0,0 +1,9712 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% Codes donnés dans le livre %% +%% « Apprendre à programmer en TeX » %% +%% %% +%% Encodage ISO 8859-1 %% +%% _____ %% +%% %% +%% © 2014 Christian Tellechea %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Les codes et les macros commentés donnés dans ce fichier sont diffusés sous +% la licence LaTeX project public license (LPPL) 1.2 +% +% https://www.latex-project.org/lppl/lppl-1-2/ +% https://www.latex-project.org/lppl/lppl-1-2.txt +% +% Attention : ce fichier n'a pas vocation à être compilé +\endinput + + +****************** Code 1 ****************** +Voici le code en \TeX{} du premier exemple.% ceci est un commentaire + +On peut observer l'affichage qu'il produit juste au-dessous ! +****************** Fin code ****************** + + +****************** Code 2 ****************** +3 tailles : $% entre en mode mathématique (espaces ignorés) +1^{2^3}% 2 est en taille "\scriptstyle" et 3 en taille "\scriptscriptstyle" +$% fin du mode math + +normal $1$, petit $\scriptstyle 2$, très petit $\scriptscriptstyle 3$. +****************** Fin code ****************** + + +****************** Code 3 ****************** +%%% comportement normal %%% +a) comportement normal : +Ligne 1 +Ligne 2 + +Ligne 3 +\medbreak +b) Aucun caractère de fin de ligne : +%%% aucune insertion en fin de ligne %%% +\endlinechar=-1 +Ligne 1 +Ligne 2 + +Ligne 3\endlinechar13 + +\medbreak +c) "X" comme caractère de fin de ligne : +%%% "X" est inséré à chaque fin de ligne +\endlinechar=88 +Ligne 1 +Ligne 2 + +Ligne 3 +****************** Fin code ****************** + + +****************** Code 4 ****************** +a) \number"1A \qquad b) \number"AB3FE \qquad c) \number"78 +****************** Fin code ****************** + + +****************** Code 5 ****************** +a) \number'15 \qquad b) \number'674 \qquad c) \number'46 +****************** Fin code ****************** + + +****************** Code 6 ****************** +a) \number`\a \quad %code de caractère de "a" +b) \number`\\ \quad % code de caractère de "\" +c) \number`\$ \quad % code de caractère de "$" +d) \number`\ \quad % code de caractère de l'espace +e) \number`\5 \quad % code de caractère de "5" +f) \number`\{ \quad % code de caractère de l'accolade ouvrante +g) \number`\} % code de caractère de accolade fermante +****************** Fin code ****************** + + +****************** Code 7 ****************** +a) \number\catcode`\a \quad %code de catégorie de "a" +b) \number\catcode`\\ \quad % code de catégorie de "\" +c) \number\catcode`\$ \quad % code de catégorie de "$" +d) \number\catcode`\ \quad % code de catégorie de l'espace +e) \number\catcode`\5 \quad % code de catégorie de "5" +f) \number\catcode`\{ \quad % code de catégorie de l'accolade ouvrante +g) \number\catcode`\} % code de catégorie de accolade fermante +****************** Fin code ****************** + + +****************** Code 8 ****************** +Ici, W est une lettre...\par +\catcode`\W=3 % W devient le signe de bascule en mode math +Wx+y=3W\par +$a+b=cW\par +W2^3=8$\par +\catcode`\W=11 % W redevient une lettre +De nouveau, W est une lettre... +****************** Fin code ****************** + + +****************** Code 9 ****************** +$3+4=7$ \par % $ est la bascule math +\catcode`\$=12 % $ devient un caractère affichable +$ s'affiche sans problème : $\par +\catcode`\$=3 % $ redevient la bascule math +$4+3=7$ +****************** Fin code ****************** + + +****************** Code 10 ****************** +Voici les premiers mots de chaque ligne : + +\catcode`\ =5 % l'espace est déormais de catcode 5 +Cette première ligne sera tronquée... + +La deuxième aussi ! + +Et la dernière également. +\catcode`\ =10 % l'espace reprend son catcode + +Le comportement normal est restauré. +****************** Fin code ****************** + + +****************** Code 11 ****************** +\def\foo{Bonjour}% définit le texte de remplacement de \foo +a) \foo Alice.\qquad% espace ignoré +b) {\foo} Bob.\qquad% espace non ignoré +c) \foo{} Chris.\qquad% espace non ignoré +d) \foo\space Daniel.% \space est remplacé par un espace +****************** Fin code ****************** + + +****************** Code 12 ****************** +\def\startbold{\begingroup \bf} +\def\stopbold{\endgroup} +Voici \startbold du texte en gras\stopbold{} et la suite. +****************** Fin code ****************** + + +****************** Code 13 ****************** +\def\foo{foo} +\begingroup A\aftergroup\foo B\endgroup\par +{A\aftergroup X\aftergroup\foo B} +****************** Fin code ****************** + + +****************** Code 14 ****************** +1) (un {\it cheval})\par% sans correction d'italique +2) (un {\it\aftergroup\/cheval})\par% avec correction d'italique +% on peut définir une macro \itacorr qui effectue automatiquement la correction +\def\itacorr{\it\aftergroup\/} +3) (un {\itacorr cheval})% avec correction d'italique +****************** Fin code ****************** + + +****************** Code 15 ****************** +\def\bar{Je suis bar.} +\let\foo\bar % \foo devient égal à \bar +\def\bar{ABC}% \bar est redéfinie +\foo\par% affiche "Je suis bar" +\bar% affiche "ABC" +****************** Fin code ****************** + + +****************** Code 16 ****************** +Initialement, c'est \TeX.\par +\let\TeXsauve\TeX% sauvegarde +\def\TeX{tEx}% redéfinition +Ici, on a modifié \TeX.\par +\let\TeX\TeXsauve% retauration +De nouveau, c'est \TeX. +****************** Fin code ****************** + + +****************** Code 17 ****************** +\let\sptoken= %2 espaces avant le "%" + +La commande \sptoken compose le paragraphe +****************** Fin code ****************** + + +****************** Code 18 ****************** +{% début du groupe +\catcode`\W=13 \def W{wagons} +Les W constituent le train. +}% fin du groupe +****************** Fin code ****************** + + +****************** Code 19 ****************** +\begingroup \catcode`\W=13 +\gdef\actiw{% + \catcode`\W=13 + \def W{wagons}} +\endgroup +a) Les trains ne sont pas constitués de W !\par +b) \begingroup\actiw Ici, les W forment les trains.\endgroup\par +c) Ici, les W sont redevenus des lettres. +****************** Fin code ****************** + + +****************** Code 20 ****************** +\begingroup + \catcode`\ =13 % rend l'espace actif + \gdef\>{\begingroup + \catcode`\ =13 + \def {\hskip5mm\relax}} +\endgroup +\let\<=\endgroup +a) Du texte normal\par +b) \>Des mots très espacés\<\par +c) Du texte normal +****************** Fin code ****************** + + +****************** Code 21 ****************** +\begingroup + \catcode`\,=13 \def,{\unskip\string,\space\ignorespaces} + La rue assourdissante autour de moi hurlait. + + Longue , mince,en grand deuil , douleur majestueuse , + + Une femme passa ,d'une main fastueuse + + Soulevant,balançant le feston et l'ourlet ; + +\endgroup\medbreak\hfill Charles {\sc Baudelaire} +****************** Fin code ****************** + + +****************** Code 22 ****************** +\begingroup + \catcode`\,=13 \def,{\unskip\string, \ignorespaces} + \catcode`\^^M=13 \let ^^M\par % rend le retour charriot égal à \par + La rue assourdissante autour de moi hurlait. + Longue , mince,en grand deuil , douleur majestueuse , + Une femme passa , d'une main fastueuse + Soulevant,balançant le feston et l'ourlet ; +\endgroup\medbreak\hfill Charles {\sc Baudelaire} +****************** Fin code ****************** + + +****************** Code 23 ****************** +{% ouvre un groupe + \let\AA=a \let\EE=e \let\II=i \let\OO=o \let\UU=u \let\YY=y + % sauvegarder avant de modifier le catcode de a et e : + \let\lEt=\let \let\cAtcOdE=\catcode + \cAtcOdE`\a=13 \lEt a=\EE \cAtcOdE`\e=13 \lEt e=\II + \cAtcOdE`\i=13 \lEt i=\OO \cAtcOdE`\o=13 \lEt o=\UU + \cAtcOdE`\u=13 \lEt u=\YY \cAtcOdE`\y=13 \lEt y=\AA + Ce texte devenu \`a peine reconnaissable montre que le r\'esultat + contient des sonorit\'es catalanes, corses ou grecques assez + inattendues. +}% ferme le groupe +****************** Fin code ****************** + + +****************** Code 24 ****************** +{% + \let\AA=a \let\lEt=\let \let~=\catcode + ~`a=13 \lEt a=e ~`e=13 \lEt e=i ~`i=13 \lEt i=o + ~`o=13 \lEt o=u ~`u=13 \lEt u=y ~`y=13 \lEt y=\AA + Ce texte devenu \`a peine reconnaissable... +} +****************** Fin code ****************** + + +****************** Code 25 ****************** +a) \def\foo{Bonjour}\meaning\foo\par +b) \let\bar=\foo\meaning\bar\par% \foo est "copiée" vers \bar +c) \def\baz{\foo}\meaning\baz\par +d) \catcode`\W=13 \def W{Wagons}% W est un caractère actif + \meaning W\par +e) \meaning\def% \def est une primitive +****************** Fin code ****************** + + +****************** Code 26 ****************** +a) \def\foo{ + +}Signification de \string\foo{} : \meaning\foo + +b) Signification de deux retours charriots consécutifs : \meaning + +****************** Fin code ****************** + + +****************** Code 27 ****************** +a) \meaning\relax\par% primitive +b) \meaning {\par% catcode 1 +c) \meaning }\par% catcode 2 +d) \meaning $\par% catcode 3 +e) \meaning &\par% catcode 4 +g) \meaning #\par% catcode 6 +h) \meaning ^\par% catcode 7 +i) \meaning _\par% catcode 8 +j) \meaning a\par% catcode 11 (une lettre) +k) \meaning +\par% catcode 12 (caractère "autre") +l) \meaning ~\par% catcode 13 (caractère actif) +****************** Fin code ****************** + + +****************** Code 28 ****************** +\begingroup% dans un groupe + \let\*=\meaning% rendre \* égal à \meaning + \* %<- espace pris en compte +\endgroup% fermer le groupe +****************** Fin code ****************** + + +****************** Code 29 ****************** +\begingroup +\catcode`\*0 +\catcode`\\12 % \ n'est plus un caractère d'échappement +*def*foo{bar} +*LaTeX{} et la macro *string*foo : *foo. + +L'ancien caractère d'échappement "\" et \TeX. +*endgroup +****************** Fin code ****************** + + +****************** Code 30 ****************** +\def\foo{\bar} +\begingroup +a) \escapechar=-1 \meaning\foo\qquad% pas de caractère d'échappement +b) \escapechar=`\@ \meaning\foo\qquad% "@" est le caractère d'échappement +\endgroup +c) \meaning\foo% caractère d'échappement par défaut +****************** Fin code ****************** + + +****************** Code 31 ****************** +\begingroup + \catcode`\|=0 |catcode`|\=11 |catcode`|2=11 |catcode`|1=11 + |gdef|1\2\a{foo} +|endgroup +Voici la macro : \csname 1\string\2\string\a\endcsname +****************** Fin code ****************** + + +****************** Code 32 ****************** +\newtoks\foo% allocation d'un nouveau registre +\foo={Bonjour le monde}% assignation +Contenu du registre {\tt\string\foo} : \the\foo. + +\newtoks\bar% allocation d'un autre registre +\bar=\foo% assigne à \bar les tokens du registre \foo +Contenu du registre {\tt\string\bar} : \the\bar. +****************** Fin code ****************** + + +****************** Code 33 ****************** +\def\hello#1#2{Bonjour #1 et #2.\par}% définition +\hello ab% #1=a et #2=b +\hello a b% #1=a et #2=b +\hello{foo}{bar}% #1=foo et #2=bar +\hello foobar% #1=f et #2=o +% (le reste "obar" est lu après que la macro est terminée) +****************** Fin code ****************** + + +****************** Code 34 ****************** +\def\tenlist#1#2#3#4#5#6#7#8#9{(#1,#2,#3,#4,#5,#6,#7,#8,#9\endlist} +\def\endlist#1{,#1)} +Une liste \tenlist abcdefghij de lettres. +****************** Fin code ****************** + + +****************** Code 35 ****************** +\def\foo#1#2{Bonjour #1 et #2.\par} +\begingroup\tt% passer en fonte à chasse fixe +a) \foo{monsieur}{madame} +b) \foo{{\bf toi}}{moi} +c) \foo{}{{}} +d) \foo{ }{ } +e) \foo{$\pi$} {$\delta$} +f) \foo ab +g) \foo X Y +\endgroup% fin de la fonte à chasse fixe +****************** Fin code ****************** + + +****************** Code 36 ****************** +\def\foo{Programmer en \TeX {} est facile} +\meaning\foo\par +\def\foo{Program^^6der en \TeX{} est facile} +\meaning\foo +****************** Fin code ****************** + + +****************** Code 37 ****************** +a) \gobone XY - \secondoftwo XY\par +b) \gobone{ab}{xy} - \secondoftwo{ab}{xy}\par +c) \gobone{123}4 - \secondoftwo{123}4\par +d) \gobone A{BCD} - \secondoftwo A{BCD} +****************** Fin code ****************** + + +****************** Code 38 ****************** +\gobone {a}{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ? + +\secondoftwo{a}{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ? +****************** Fin code ****************** + + +****************** Code 39 ****************** +\def\visible{\let\choix=\firstoftwo} +\def\cache{\let\choix=\secondoftwo} +\def\?#1{\choix{#1}{...}}% +\def\interro{J'ai ét\?{é} invit\?{é} à gout\?{er} chez Max. Après s'être + bien amus\?{é}, nous avons rang\?{é} et il a fallu rentr\?{er}.} +Compléter avec << é >> ou << er >> :\par +\cache\interro\par\medskip +Correction :\par +\visible\interro +****************** Fin code ****************** + + +****************** Code 40 ****************** +\def\visible{\let\?=\identity} +\def\cache{\def\?##1{...}} +\def\interro{J'ai ét\?{é} invit\?{é} à goût\?{er} chez Max. On s'est + bien amus\?{é} et ensuite, il a fallu rentr\?{er}.} +Compléter avec << é >> ou << er >> :\par +\cache\interro\par\medskip +Correction :\par +\visible\interro +****************** Fin code ****************** + + +****************** Code 41 ****************** +\def\makemacro#1#2{\def#1##1{##1 #2}} +\makemacro\LL{Lamport} +\makemacro\juin{juin 2014} +Paris, le \juin{3}.\medbreak +\LL{Cher Monsieur},\smallbreak +Vous êtes convoqué à un examen sur \TeX{} le \juin{21} à +9h00 précise dans le bureau de D. Knuth.\smallbreak +Veuillez croire, \LL{Monsieur}, en mes sentiments \TeX iens. +****************** Fin code ****************** + + +****************** Code 42 ****************** +\def\showcodes#1{\string#1 : $\number`#1 \rightarrow \number\lccode`#1$} +\showcodes A\qquad \showcodes B\qquad +\showcodes a\qquad \showcodes b\qquad \showcodes ^ +****************** Fin code ****************** + + +****************** Code 43 ****************** +\lowercase{CACAO foobar}\par +\lowercase{\def\foo{Bonjour}}% définit une commande \foo +\foo % la commande définie précédemment +****************** Fin code ****************** + + +****************** Code 44 ****************** +\begingroup + \lccode`A=`* % change le code minuscule de A + \lowercase{CACAO ABRACADABRA}\par +\endgroup +\lowercase{CACAO ABRACADABRA} +****************** Fin code ****************** + + +****************** Code 45 ****************** +\def\defactive#1{% + \catcode`#1=13 + \begingroup + \lccode`~=`#1 + \lowercase{\endgroup\def~}% +} +\begingroup% les modifications restent locales + \defactive W{wagons}% définit le caractère actif "W" + Les W constituent les trains. +\endgroup %fermeture du groupe, "W" redevient une lettre +\par Les W sont des lettres +****************** Fin code ****************** + + +****************** Code 46 ****************** +\defactive W{wagon} +1) Les W constituent le train.\par +2) \catcode`W=11 Les W sont des lettres.\par +3) \catcode`\W=13 Les W constituent-ils le train ? +****************** Fin code ****************** + + +****************** Code 47 ****************** +\def\defactive#1#2{% + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs + \lowercase{\endgroup\def~}{#2}% + } +\defactive W{Wagon} +Un W. +****************** Fin code ****************** + + +****************** Code 48 ****************** +\def\defactive#1{% + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs + \lowercase{\endgroup\def~}% + } +\defactive W{Wagon} +Un W. +****************** Fin code ****************** + + +****************** Code 49 ****************** +\def\defactive#1#2{% + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \uccode`~=`#1 % dans \uppercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs + \uppercase{\endgroup\def~}{#2}% +} +\defactive W{Wagon} +Un W. +****************** Fin code ****************** + + +****************** Code 50 ****************** +\def\letactive#1{% + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \uccode`~=`#1 % dans \uppercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs + \uppercase{\endgroup\let~}% +} +\def\W{wagon}% +\letactive X\W +Un X. +****************** Fin code ****************** + + +****************** Code 51 ****************** +\def\litterate#1{% #1=lit le token frontière choisi + \begingroup% ouvre un groupe pour y faire les modifications + \def\do##1{\catcode`##1=12 }% \do change le catcode de son argument à 12 + \dospecials% rend inoffensifs tous les tokens spéciaux + \defactive\^^M{\leavevmode\par}% définit le retour charriot + \letactive#1\endgroup% définit #1 qui sera un \endgroup + \tt% passe en fonte à chasse fixe +} +essai 1 : \litterate*\foo # #34{ } &_\ * +\medbreak +essai 2 : \litterate/une première ligne ,, >> + +un saut de ligne et la seconde --/ +****************** Fin code ****************** + + +****************** Code 52 ****************** +\def\litterate#1{% #1=lit le token frontière choisi + \begingroup% ouvre un groupe pour y faire les modifications + \def\do##1{\catcode`##1=12 }% \do change le catcode de son argument à 12 + \dospecials% rend inoffensifs tous les tokens spéciaux + \defactive\^^M{\leavevmode\par}% définit le retour charriot + \defactive\ {\ }% l'espace est actif et devient "\ " + \defactive<{\string<{}}\defactive>{\string>{}}% empêche + \defactive-{\string-{}}\defactive`{\string`{}}% les + \defactive,{\string,{}}\defactive'{\string'{}}% ligatures + \letactive#1\endgroup% #1 sera un \endgroup + \tt% passe en fonte à chasse fixe +} +essai 1 : \litterate*\foo # #34{ %} &_\ * +\medbreak +essai 2 : \litterate/une première ligne ,, >> + +un saut de ligne et la seconde --/ +****************** Fin code ****************** + + +****************** Code 53 ****************** +\def\baz#1#2#3#{"#1", puis "#2" et enfin "#3".} +\baz{abc}def{gh}ij +****************** Fin code ****************** + + +****************** Code 54 ****************** +\catcode`\@11 +\long\def\firstto@nil#1#2\@nil{#1} +\long\def\remainto@nil#1#2\@nil{#2} +a) \firstto@nil foobar\@nil \qquad b) \remainto@nil foobar\@nil \par +c) \firstto@nil {foo}bar\@nil \qquad d) \remainto@nil {foo}bar\@nil +****************** Fin code ****************** + + +****************** Code 55 ****************** +\catcode`\@=11 % "@" devient une lettre +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\@nil% appelle la macro auxiliaire où #1 est la <chaine> +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{gram}--\par +--\rightof{Programmation}{on}-- +****************** Fin code ****************** + + +****************** Code 56 ****************** +\catcode`\@=11 % "@" devient une lettre +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\@nil% appelle la macro auxiliaire +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{Gram} +****************** Fin code ****************** + + +****************** Code 57 ****************** +\catcode`\@=11 % "@" devient une lettre +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1#2\@nil% appelle la macro auxiliaire avec le motif #2 +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{ti}--\par +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{Gram}-- +****************** Fin code ****************** + + +****************** Code 58 ****************** +\catcode`\@=11 % "@" devient une lettre +\def\gobto@@nil#1\@@nil{} +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil% appelle la macro auxiliaire +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{ti}--\par +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{Gram}-- +****************** Fin code ****************** + + +****************** Code 59 ****************** +\catcode`\@=11 % "@" devient une lettre + +\def\rightof#1#2{% + \ifin{#1}{#2}% + {% si #1 contient le #2 + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\@nil% appelle la macro auxiliaire + }% + {}% sinon, ne rien faire +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{o}-- +****************** Fin code ****************** + + +****************** Code 60 ****************** +\catcode`\@=11 +\def\leftof#1#2{% + \def\leftof@i##1#2##2\@nil{##1}% renvoie ce qui est à gauche de #2 + \leftof@i #1#2\@nil% ajoute #2 après #1 +} +\catcode`\@=12 +--\leftof{Programmation}{ram}--\par +--\leftof{Programmation}{zut}-- +****************** Fin code ****************** + + +****************** Code 61 ****************** +\catcode`\@=11 +\def\leftof#1#2{% + \def\leftof@i##1#2##2\@nil{\leftof@ii##1#1\@nil}% + \def\leftof@ii##1#1##2\@nil{##1}% + \leftof@i #1#2\@nil +} +\catcode`\@=12 +--\leftof{Programmation}{ram}--\par +--\leftof{Programmation}{zut}-- +****************** Fin code ****************** + + +****************** Code 62 ****************** +\catcode`\@=11 +\def\rightof#1#2{% + \def\rightof@i##1#2##2\@nil{\rightof@ii##2#2\@nil}% + \def\rightof@ii##1#2##2\@nil{##1}% + \rightof@i#1#2\@nil +} +\catcode`\@=12 +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{z}-- +****************** Fin code ****************** + + +****************** Code 63 ****************** +\catcode`\@=11 +\def\gobto@@nil#1\@@nil{} +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil% appelle la macro auxiliaire +} +\catcode`\@=12 +\rightof{abc{1}def}{c{1}d}% affiche ce qui est à droite de "c{1}d" +****************** Fin code ****************** + + +****************** Code 64 ****************** +\expandafter\def\csname ab1c\endcsname{Bonjour} +Texte de remplacement de la macro : \csname ab1c\endcsname +****************** Fin code ****************** + + +****************** Code 65 ****************** +\def\defname#1{\expandafter\def\csname#1\endcsname} +\defname{ab1c}{Bonjour} +Texte de remplacement de \litterate|\abc1| : \csname ab1c\endcsname\par +\def\letname#1{\expandafter\let\csname#1\endcsname} +\def\foo{abc}\letname{foo1}=\foo% rend la macro "\foo1" égale à \foo +Texte de remplacement de \litterate|\foo1| : \csname foo1\endcsname +****************** Fin code ****************** + + +****************** Code 66 ****************** + \def\foo{Bonjour}\catcode`@=11 +Résultat : \firstto@nil\foo\@nil +\catcode`@=12 +****************** Fin code ****************** + + +****************** Code 67 ****************** +\def\foo{Bonjour}\catcode`@=11 +Résultat : \expandafter\firstto@nil\foo\@nil +\catcode`@=12 +****************** Fin code ****************** + + +****************** Code 68 ****************** +\newtoks\testtoks \testtoks={Bonjour} +\catcode`@=11 +Résultat : \expandafter\firstto@nil\the\testtoks\@nil +\catcode`@=12 +****************** Fin code ****************** + + +****************** Code 69 ****************** +\def\foo{bonjour} +a) \>\csname foo\endcsname< \par +b) \>\foo< \par +c) \expandafter\>\foo< \par +d) \>\foo\foo< \par +e) \expandafter\>\foo\foo< +****************** Fin code ****************** + + +****************** Code 70 ****************** +\def\foo{Bonjour} \expandafter\>\expandafter\foo\foo< +****************** Fin code ****************** + + +****************** Code 71 ****************** +\def\foo{Bonjour} +\expandafter\def\expandafter\bar\expandafter{\expandafter\foo\foo} +\meaning\bar +****************** Fin code ****************** + + +****************** Code 72 ****************** +\def\foo{\foo Bonjour}% définition récursive +\foo% l'exécution provoque un dépassement de la capacité de la pile +****************** Fin code ****************** + + +****************** Code 73 ****************** +\long\def\addtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} +\def\foo{Bonjour} +\addtomacro\foo{ le monde} +\meaning\foo +****************** Fin code ****************** + + +****************** Code 74 ****************** +\long\def\eaddtomacro#1#2{% + \expandafter\addtomacro\expandafter#1\expandafter{#2}% +} +\def\foo{Bonjour} \def\world{ le monde} +\eaddtomacro\foo\world +\meaning\foo +****************** Fin code ****************** + + +****************** Code 75 ****************** +\long\def\addtotoks#1#2{#1= \expandafter{\the#1#2}} +\newtoks\bar +\bar={Bonjour}% met "Bonjour" dans le registre +\addtotoks\bar{ le monde}% ajoute " le monde" +\the\bar% affiche le registre +****************** Fin code ****************** + + +****************** Code 76 ****************** +\long\def\eaddtotoks#1#2{\expandafter\addtotoks\expandafter#1\expandafter{#2}} +\bar={Bonjour} +\def\macrofoo{ le monde} +\eaddtotoks\bar\macrofoo +\the\bar +****************** Fin code ****************** + + +****************** Code 77 ****************** +\def\X{Bonjour} \def\Y{\X} +\expandafter\expandafter\expandafter\>\Y< +****************** Fin code ****************** + + +****************** Code 78 ****************** +\long\def\swaparg#1#2{#2{#1}} +\long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} +\def\X{Bonjour} +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\>\expsecond{\X}{\X}< +****************** Fin code ****************** + + +****************** Code 79 ****************** +\expsecond{\def\foo}\secondoftwo{A}{B} +****************** Fin code ****************** + + +****************** Code 80 ****************** +\expsecond{\def\foo}{\secondoftwo{A}{B}} +\meaning\foo +****************** Fin code ****************** + + +****************** Code 81 ****************** +\def\exptwoargs#1#2{\expsecond{\expsecond{#1}{#2}}} +\def\foo{Bonjour}\def\bar{ le monde} +\def\displaytwoargs#1#2{\detokenize{#1#2}}% affiche tels quels les arguments +\exptwoargs\displaytwoargs\foo\bar +****************** Fin code ****************** + + +****************** Code 82 ****************** +\def\aa{Bonjour}\def\bb{\aa} +\def\cc{ le monde} +\edef\foo{\bb\cc} +\meaning\foo +****************** Fin code ****************** + + +****************** Code 83 ****************** +\def\aa{Bonjour } \def\bb{\aa} \def\cc{le } \def\dd{ monde} +\edef\ee#1{\bb\cc#1 !!!} +\meaning\ee\par +\expandafter\>\ee{\dd}< +****************** Fin code ****************** + + +****************** Code 84 ****************** +\def\foo{123xyz} +a) signification du 1-développement d'un \litterate-\noexpand- : + \expandafter\meaning\noexpand\foo + +b) exécution d'un \litterate-\noexpand- : \noexpand\foo% identique à \relax + +c) 1-développement de \litterate-\noexpand- dans le texte de remplacement de + \litterate-\bar- : + \expandafter\def\expandafter\bar\expandafter{\noexpand\foo} + \meaning\bar +****************** Fin code ****************** + + +****************** Code 85 ****************** +\def\aa{Bonjour}\def\bb{\aa}\def\cc{\bb} +a) \edef\foo{\aa\noexpand\bb\cc}\meaning\foo \qquad b) exécution : \foo\par +c) \edef\foo{\aa\noexpand{\aa\cc}}\meaning\foo\qquad d) exécution : \foo +****************** Fin code ****************** + + +****************** Code 86 ****************** +\def\aa{Bonjour}\def\bb{Au revoir} +a) \edef\truc{\aa\noexpand\bb\noexpand\aa\bb}\meaning\truc \par +b) \edef\truc{\aa\unexpanded{\bb\aa}\bb}\meaning\truc +****************** Fin code ****************** + + +****************** Code 87 ****************** +\def\aa{Bonjour}\def\bb{Au revoir} +\toks0={\aa\bb} +\edef\foo{\aa\the\toks0 \bb} +\meaning\foo +****************** Fin code ****************** + + +****************** Code 88 ****************** +\def\aa{Bonjour} +\protected\def\bb{ le}% \ bb est protégée ! +\def\cc{ monde} + +\edef\foo{\aa\bb\cc} +Signification de \string\foo~: \meaning\foo \par +Exécution de \string\foo~: \foo +****************** Fin code ****************** + + +****************** Code 89 ****************** +\def\aa{Bonjour} +\protected\def\bb{ le} +\def\cc{ monde} + +\edef\foo{\expandafter\aa\bb\cc}% le \expandafter 1-développe \bb +\meaning\foo +****************** Fin code ****************** + + +****************** Code 90 ****************** +\def\foo{Bonjour}% définition +\foo% exécution +****************** Fin code ****************** + + +****************** Code 91 ****************** +\def\foo{abc} +\edef\bar{\def\foo{Bonjour}\foo} +\meaning\bar +****************** Fin code ****************** + + +****************** Code 92 ****************** +\def\foo{abc} +\edef\bar{\def\noexpand\foo{Bonjour}\noexpand\foo} +Signification : \meaning\bar\par +Exécution : \bar +****************** Fin code ****************** + + +****************** Code 93 ****************** +\long\def\>#1<{\detokenize{#1}} +\expandafter\>% provoque le 1-développement de : +\csname foo\expandafter\endcsname +\csname bar\expandafter\endcsname +\csname wee\expandafter\endcsname +\csname fin\endcsname< +****************** Fin code ****************** + + +****************** Code 94 ****************** +\long\def\>#1<{\detokenize{#1}} +\def\fin{YY} \def\wee{\weeA} \def\weeA{XX} +\expandafter\>% +\csname foo\expandafter\endcsname +\csname bar\expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\endcsname +\csname wee\expandafter\expandafter\expandafter\endcsname +\csname fin\endcsname< +****************** Fin code ****************** + + +****************** Code 95 ****************** +\long\def\>#1<{\detokenize{#1}} +\def\fin{YYY} \def\wee{\weeA} \def\weeA{XXX} +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\>% +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\foo +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\bar +\expandafter\wee\fin< +****************** Fin code ****************** + + +****************** Code 96 ****************** +\chardef\foo65 +a) |\foo|\qquad % employée seule, \foo affiche un A +b) |\number\foo|\qquad % si TeX lit un nombre, \foo est le nombre 65 +****************** Fin code ****************** + + +****************** Code 97 ****************** +\mathchardef\foo4944 +a) |$\foo$|\qquad % en mode math, affiche le signe "somme" (uniquement en mode maths) +b) |\number\foo|\qquad % si TeX lit un nombre, \foo est 4944 +****************** Fin code ****************** + + +****************** Code 98 ****************** +a) "\romannumeral 27 "\quad +b) "\romannumeral 4687 "\quad +c) "\romannumeral 0 "\quad% 0 donc développement vide +d) "\romannumeral -2014 "\quad% négatif donc développement vide +e) \def\foo{7}\def\bar{\foo}% + "\romannumeral \foo\foo\bar"\quad% 777 +f) "\romannumeral 1\bar8\foo"\quad% 1787 +g) "\romannumeral 1\bar8 \foo"% 178 (stoppé par l'espace) puis 7 +****************** Fin code ****************** + + +****************** Code 99 ****************** +\long\def\>#1<{\detokenize{#1}} +\def\swaparg#1#2{#2{#1}} +\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} +\def\X{Bonjour} +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\>\expsecond\X\X< +****************** Fin code ****************** + + +****************** Code 100 ****************** +\long\def\>#1<{\detokenize{#1}} +\def\swaparg#1#2{0 #2{#1}}% le "0 " stoppe ici le développement maximal +\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} +\def\X{Bonjour} +\expandafter\>\romannumeral\expsecond\X\X< +****************** Fin code ****************** + + +****************** Code 101 ****************** +\long\def\>#1<{\detokenize{#1}} +\def\swaparg#1#2{#2{#1}} +\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} +\def\X{Bonjour} +\expandafter\>\romannumeral\expsecond{0 \X}\X< +****************** Fin code ****************** + + +****************** Code 102 ****************** +\long\def\>#1<{\detokenize{#1}} +\def\X{Bonjour} +\catcode`\@=11 +\expandafter\>\romannumeral\expsecond{\z@\X}{\X}< +\catcode`\@=12 +****************** Fin code ****************** + + +****************** Code 103 ****************** +\def\newunivar#1{\def#1[##1]{\csname\string#1[##1]\endcsname}} +\def\defunivar#1[#2]{\defname{\string#1[#2]}} +\newunivar\foobar +\defunivar\foobar[0]{abcd} +\defunivar\foobar[1]{1 23} +\defunivar\foobar[3]{XY Z} +Cellule 0 : \foobar[0]\par +Cellule 1 : \foobar[1]\par +Cellule 2 : \foobar[2]\par% cellule non définie : \foobar[2] donne \relax +Cellule 3 : \foobar[3]\bigbreak + +\newunivar\client +\defunivar\client[nom]{M. Raymond {\sc Tartempion}} +\defunivar\client[adr]{5 rue de la paix} +\defunivar\client[cod_post]{75000} +\defunivar\client[ville]{Paris} +% fin des définitions, affichage de l'adresse : +\client[nom]\par +\client[adr]\par +\client[cod_post] \client[ville] +****************** Fin code ****************** + + +****************** Code 104 ****************** +\newcount\foo \newcount\bar +\foo=42 \bar=\foo \string\bar\ vaut : \the\bar \par +\bar=\foo57 \string\bar\ vaut : \number\bar\par +\bar=2014 \string\bar\ vaut : \romannumeral\bar +****************** Fin code ****************** + + +****************** Code 105 ****************** +\newcount\foo +\def\fonction#1{% + \foo=#1 % assigne l'entier #1 au compteur puis + \multiply\foo3 % multiplie par 3 + \advance\foo-4 % soustrait 4 + \multiply\foo\foo% élève au carré (lmultiplie \foo par lui même) + \number\foo% enfin, afficher le résultat +} +a) \fonction5\qquad b) \fonction{-13} +****************** Fin code ****************** + + +****************** Code 106 ****************** +\newcount\foo +\def\fonction#1{% + \foo=#1 \multiply\foo3 \advance\foo-4 \multiply\foo\foo + \number\foo} +\edef\bar{\fonction{5}}% +a) Signification : \meaning\bar\par +b) Exécution : \bar +****************** Fin code ****************** + + +****************** Code 107 ****************** +a) \number\numexpr2+3*4\relax\qquad +b) \romannumeral\numexpr2*3\relax\qquad +c) \number\numexpr(6-2*4)*(1-(2-6))\relax\qquad +d) \number\numexpr7/4\relax\qquad %7/4 vaut 1,75 +e) \edef\foo{\number\numexpr2+3*4\relax}\meaning\foo +****************** Fin code ****************** + + +****************** Code 108 ****************** +\number\numexpr3*\numexpr1+2*5\relax+2\relax +****************** Fin code ****************** + + +****************** Code 109 ****************** +\def\fonction#1{\number\numexpr(3*#1-4)*(3*#1-4)\relax} +a) \fonction5\qquad +b) \fonction{-13}\qquad +c) \edef\bar{\fonction5}\meaning\bar +****************** Fin code ****************** + + +****************** Code 110 ****************** +\def\fonction#1{\exparg\carre{\number\numexpr3*#1-4\relax}} +\def\carre#1{\number\numexpr#1*#1\relax} +a) \fonction{5}\qquad +b) \fonction{-13}\qquad +c) \edef\bar{\fonction5}\meaning\bar +****************** Fin code ****************** + + +****************** Code 111 ****************** +\def\truncdiv#1#2{\numexpr(#1-(#2-1)/2)/#2\relax} +8/3 : +a) \number\truncdiv83\qquad % doit donner 2 +b) \number\truncdiv{-8}3\qquad % doit donner -2 +c) \number\truncdiv8{-3}\qquad % doit donner -2 +d) \number\truncdiv{-8}{-3}\par % doit donner 2 +4/3 : +e) \number\truncdiv43\qquad % doit donner 1 +f) \number\truncdiv{-4}3\qquad % doit donner -1 +g) \number\truncdiv4{-3}\qquad % doit donner -1 +h) \number\truncdiv{-4}{-3} % doit donner 1 +****************** Fin code ****************** + + +****************** Code 112 ****************** +a) \ifnum 15=14 vrai\else faux\fi\qquad% test faux +b) \ifnum4<200 vrai\else faux\fi\qquad% test vrai +c) \newcount\foo \foo=9 + \ifnum\foo>9 nombre\else chiffre\fi% test faux +****************** Fin code ****************** + + +****************** Code 113 ****************** +\long\def\>#1<{\detokenize{#1}} +\expandafter\>\ifnum5=5 vrai\else faux\fi<\par% test vrai +\expandafter\>\ifnum5=6 vrai\else faux\fi<% test faux +\fi\fi% rétablir l'équilibre \ifnum et \fi +****************** Fin code ****************** + + +****************** Code 114 ****************** +\def\numtest#1{% + \ifnum#1>-10 + \ifnum#1<10 + chiffre% + \else + nombre positif% + \fi + \else + nombre négatif% + \fi +} +a) \numtest{3}\qquad +b) \numtest{-67}\qquad +c) \numtest{-8}\qquad +d) \numtest{21}\qquad +e) \edef\foo{\numtest{2014}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 115 ****************** +\def\normalise#1{% + \ifnum#1>-1 % ne faire quelque chose que si #1 est positif ou nul + \ifnum#1<100 % si <100 + 0% afficher un 0 + \ifnum#1<10 % si <10 + 0%afficher un autre 0 + \fi + \fi + \number#1 %afficher le nombre + \fi}% affiche le nombre +a) \normalise{749}\qquad +b) \normalise{0017}\qquad +c) \normalise8\qquad +d) \normalise{1789}\qquad +e) \normalise{-18}\qquad +f) \normalise{0} +****************** Fin code ****************** + + +****************** Code 116 ****************** +\def\mois#1{% + \ifcase#1\relax + \char`\#\char`\#% afficher "##" si argument = 0 + \or janvier\or f\'evrier\or mars\or avril% + \or mai\or juin\or juillet\or aout% + \or septembre\or octobre\or novembre\or d\'ecembre% + \else + \char`\#\char`\#% afficher "##" sinon + \fi +} +a) \mois{-4}\qquad +b) \mois{3}\qquad +c) \mois{11}\qquad +d) \mois{20}\qquad +e) \edef\foo{\mois{\month}}\meaning\foo% mois en cours +****************** Fin code ****************** + + +****************** Code 117 ****************** +\def\ifletter#1#2#3{% + \ifnum`#1<`a% si le caractère est avant "a" + #3% exécuter "<code faux>" + \else% sinon + \ifnum`#1>`z% si le caractère est après "z" + #3% exécuter "<code faux>" + \else% dans tous les autres cas + #2% "#1" est une lettre : exécuter "<code vrai>" + \fi + \fi} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 118 ****************** +\def\ifletter#1{% + \ifnum`#1<`a + \expandafter\secondoftwo% \expandafter 1-développe "\else...\fi" + \else + \ifnum`#1>`z + \expandafter\expandafter\expandafter% 1-développe "\else...\fi" puis "\fi" + \secondoftwo + \else + \expandafter\expandafter\expandafter%1-développe "\fi" puis "\fi" + \firstoftwo + \fi + \fi} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 119 ****************** +\def\ifletter#1#2#3{% + \romannumeral % <- initie le développement maximal + \ifnum`#1<`a + \expandafter\secondoftwo + \else + \ifnum`#1>`z + \expandafter\expandafter\expandafter\secondoftwo + \else + \expandafter\expandafter\expandafter\firstoftwo + \fi + \fi{0 #2}{0 #3}% <- "0 " stoppe le développement maximal +} +\long\def\>#1<{\detokenize{#1}} +a) \expandafter\expandafter\expandafter\>\ifletter{0}{vrai}{faux}<\qquad +b) \expandafter\expandafter\expandafter\>\ifletter{x}{vrai}{faux}< +****************** Fin code ****************** + + +****************** Code 120 ****************** +\def\ifletter#1{% + \ifnum`#1<`a + \let\donext=\secondoftwo + \else + \ifnum`#1>`z + \let\donext=\secondoftwo + \else + \let\donext=\firstoftwo + \fi + \fi + \donext% exécuter l'action décidée ci-dessus +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 121 ****************** +\def\ifletter#1{% + \csname + \ifnum`#1<`a + second% <- commenter la fin de ligne pour éviter un + \else% espace parasite dans le nom de la macro créée + \ifnum`#1>`z + second% + \else + first% + \fi + \fi + oftwo% + \endcsname +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 122 ****************** +\def\ifletter#1{% + \lowercase{\csname + \ifnum`#1<`a second% + \else + \ifnum`#1>`z second\else first\fi + \fi + oftwo\endcsname + }% +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 123 ****************** +\edef\foo{\lowercase{AbCd}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 124 ****************** +\def\ifletter#1{% + \csname + \ifnum`#1<`A second% + \else + \ifnum`#1<`Z first% + \else + \ifnum`#1>`a + \ifnum`#1<`z + first% + \else + second% + \fi + \else + second% + \fi + \fi + \fi + oftwo% + \endcsname +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}\qquad +d) \edef\foo{\ifletter{x}{vrai}{faux}}\meaning\foo\qquad +e) \edef\foo{\ifletter{=}{vrai}{faux}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 125 ****************** +\def\ifinside#1[#2,#3]{% + \csname + \ifnum#1<#2 second% si n<a, <code faux> + \else + \ifnum#1>#3 second% si n>b, <code faux> + \else first% sinon, <code vrai> + \fi + \fi + oftwo% + \endcsname +} +a) \ifinside 3[1,8]{oui}{non}\qquad +b) \ifinside -7[-20,-11]{oui}{non}\qquad +c) \ifinside 9[0,6]{oui}{non}\qquad +d) \ifinside -1[-5,1]{oui}{non} +****************** Fin code ****************** + + +****************** Code 126 ****************** +\def\ifinside#1[#2,#3]{% + \ifnum\numexpr(#1-#2)*(#1-#3)\relax>0 + \expandafter\secondoftwo + \else + \expandafter\firstoftwo + \fi +} +a) \ifinside 3[1,8]{oui}{non}\qquad +b) \ifinside -7[-20,-11]{oui}{non}\qquad +c) \ifinside 9[0,6]{oui}{non}\qquad +d) \ifinside -1[-5,1]{oui}{non} +****************** Fin code ****************** + + +****************** Code 127 ****************** +\def\absval#1{% + \ifnum#1<0 \number-#1 + \else \number#1 + \fi} +a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0} +****************** Fin code ****************** + + +****************** Code 128 ****************** +\def\absval#1{\number\ifnum#1<0 -\fi#1 } +a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0} +****************** Fin code ****************** + + +****************** Code 129 ****************** +\def\absval#1{\number\ifnum\numexpr#1\relax<0 -\fi\numexpr#1\relax} +a) \absval{-87}\qquad +b) \absval{75}\qquad +c) \absval{0}\qquad +d) \absval{-20+13}\qquad% -7 affiché 7 +e) \absval{5-3*(-4)+10-50}% -23 affiché 23 +****************** Fin code ****************** + + +****************** Code 130 ****************** +\catcode`\@11 +\def\absval#1{\exparg\absval@i{\number\numexpr#1\relax}} +\def\absval@i#1{\number\ifnum#1<\z@-\fi#1 } +\catcode`\@12 +a) \absval{-87}\qquad +b) \absval{75}\qquad +c) \absval{0}\qquad +d) \absval{-20+13}\qquad% résultat -7 qui est affiché 7 +e) \absval{5-3*(-4)+10-50}% résultat -23 qui est affiché 23 +****************** Fin code ****************** + + +****************** Code 131 ****************** +\def\sgn#1{\ifnum#1<0 -\fi} +\def\truncdiv#1#2{% + \numexpr + \sgn{#1}\sgn{#2}1*% multiplie le quotient ci-dessous par +1 ou -1 + (2*\absval{#1}-\absval{#2}+1)/(2*\absval{#2}) + \relax +} +a) \number\truncdiv{-8}3\qquad +b) \number\truncdiv{-8}{-3}\qquad +c) \number\truncdiv8{-3}\qquad +d) \number\truncdiv{0}{-5} +****************** Fin code ****************** + + +****************** Code 132 ****************** +\catcode`\@11 +\def\sgn#1{\ifnum#1<\z@-\fi} +\def\truncdiv#1#2{% + \exptwoargs\truncdiv@i{\number\numexpr#1\relax}{\number\numexpr#2\relax}% +} +\def\truncdiv@i#1#2{% + \numexpr(2*\sgn{#1}#1-\sgn{#2}#2+1)/(\sgn{#1}2*#2)\relax +} +\catcode`\@12 +a) \number\truncdiv{-8}3\qquad +b) \number\truncdiv{-8}{-3}\qquad +c) \number\truncdiv8{-3}\qquad +d) \number\truncdiv{0}{-5}\qquad +e) \number\truncdiv{20+2*3}{4-5*2}% 26/(-6) doit donner -4 +****************** Fin code ****************** + + +****************** Code 133 ****************** +\def\siecle#1{% + \unless\ifnum#1=0 % ne faire quelque chose que si #1 différent de 0 + \uppercase\expandafter{\romannumeral\absval{#1}}% chiffres romains majuscules + \raise 1ex \hbox{e\ifnum\absval{#1}=1 r\fi} siècle% affiche l'exposant puis "siècle" + \ifnum#1<0 {} av. J.-C.\fi% affiche si besoin "av. J.-C." + \fi +} +a) \siecle{19}\qquad +b) \siecle{-3}\qquad +c) \siecle{1}\qquad +d) \siecle{0}\qquad +e) \siecle{-1} +****************** Fin code ****************** + + +****************** Code 134 ****************** +\catcode`\@11 +\edef\saved@catcode{\number\catcode`\:}% sauvegarde du catcode de ":" +\catcode`\:12 % ":" est désormais un caractère non actif normal +\def\hdif{% + \begingroup% ouvrir un groupe semi-simple + \catcode`\:12 % modifier le catcode de ":" + \hdif@i% aller lire les deux arguments +} + +\def\hdif@i#1#2{% lit les 2 arguments + \hdif@ii#1-#2\@nil% et les transmet à la macro à argument délimités +} + +\def\hdif@ii#1:#2:#3-#4:#5:#6\@nil{% + %%%%%% calcul des nombres à afficher %%%%%% + \edef\nb@hrs{\number\numexpr#1-#4\relax}% différence des heures + \edef\nb@min{\number\numexpr#2-#5\relax}% différence des minutes + \edef\nb@sec{\number\numexpr#3-#6\relax}% différence des secondes + \ifnum\nb@sec<\z@ % si la différence des secondes est <0 + \edef\nb@sec{\number\numexpr\nb@sec+60\relax}% ajouter 60 sec + \edef\nb@min{\number\numexpr\nb@min-1\relax}% enlever 1 aux minutes + \fi + \ifnum\nb@min<\z@ % si les minutes sont <0 + \edef\nb@min{\number\numexpr\nb@min+60\relax}% ajouter 60 min + \edef\nb@hrs{\number\numexpr\nb@hrs-1\relax}% enlever 1 aux heures + \fi + %%%%%% affichage du résultat %%%%%% + \let\inter@space\empty% pas d'espace avant un nombre pour l'instant + \ifnum\nb@hrs>\z@ % si les heures sont >0 + \nb@hrs h% afficher les heures et "h" + \let\inter@space\space% espace pour plus tard + \fi + \ifnum\nb@min>\z@ % si les minutes sont >0 + \inter@space% afficher une espace éventuelle + \nb@min min% afficher les minutes et "min" + \let\inter@space\space + \fi + \ifnum\nb@sec>\z@ % si les secondes sont >0 + \inter@space% afficher une espace éventuelle + \nb@sec s% afficher les secondes et "s" + \fi + \endgroup% fermer le groupe ouvert au début +} +\catcode`\:=\saved@catcode\relax% restaure le code de catégorie de ":" +\catcode`\@12 +a) \hdif{10:51:20}{9:20:10}\par +b) \hdif{14:20:0}{13:50:0}\par +c) \hdif{7:50:20}{5:50:20}\par +d) \hdif{7:50:10}{6:50:20}\par +e) \hdif{20:00:00}{19:59:15}\par +f) \hdif{17:13:15}{14:12:45} +****************** Fin code ****************** + + +****************** Code 135 ****************** +\newcount\ii +\catcode`\@=11 +\def\ncar#1#2{% + \ii=0 % initialise le compteur à 0 + \def\val@max{#1}% stocke la valeur maximale + \def\car@save{#2}% stocke le caractère à afficher + \ncar@i% appelle la macro récursive +} +\def\ncar@i{% + \ifnum\ii<\val@max% si la valeur maxi n'est pas atteinte + \car@save% afficher le caractère + \advance\ii 1 % incrémenter le compteur + \ncar@i% recommencer + \fi +} +\catcode`\@=12 +\ncar{7}{*}\par +\ncar{13}W\par +\ncar{10}{foobar } +****************** Fin code ****************** + + +****************** Code 136 ****************** +\newcount\ii +\catcode`\@=11 +\def\ncar#1#2{% + \ii=0 % initialise le compteur à 0 + \def\val@max{#1}% stocke la valeur maximale + \def\car@save{#2}% stocke le caractère à afficher + \ncar@i% appelle la macro récursive +} +\def\ncar@i{% + \ifnum\ii<\val@max% si la valeur maxi n'est pas atteinte + \car@save% afficher le caractère + \advance\ii 1 % incrémenter le compteur + \ncar@i% recommencer + \fi +} +\catcode`\@=12 +\ncar{2}{3a} +****************** Fin code ****************** + + +****************** Code 137 ****************** +a) \romannumeral 9600 \qquad b) \romannumeral 12000 +****************** Fin code ****************** + + +****************** Code 138 ****************** +\def\ncar#1#2{% + \begingroup + \lccode`\m=`#2 % dans \lowercase, les "m" deviennent des "#2" + \lowercase\expandafter{\expandafter\endgroup\romannumeral#1000 }% +} +a) \ncar{7}{*}\qquad +b) \ncar{15}{\%}\qquad +c) \ncar{10}{4} +****************** Fin code ****************** + + +****************** Code 139 ****************** +\def\ncar#1#2{% + \ifnum#1>0 % <- espace après le "0" + #2% affiche le caractère + \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif + \fi +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}{7}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 140 ****************** +\def\ncar#1#2{% + \ifnum#1>0 \expandafter\identity% si #1>0 exécuter... + \else \expandafter\gobone% ...sinon manger + \fi% ce qui est entre ces accolades : + {#2% afficher le caractère + \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif + }% +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo +****************** Fin code ****************** + + +****************** Code 141 ****************** +\long\def\antefi#1\fi{\fi#1} +\def\ncar#1#2{% + \ifnum#1>0 + \antefi% comme si le \fi était "déplacé" ici + #2% affiche le caractère + \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif + \fi +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo +****************** Fin code ****************** + + +****************** Code 142 ****************** +\long\def\afterfi#1#2\fi{#2\fi#1} +\def\ncar#1#2{% + \ifnum#1>0 + \afterfi{\exparg\ncar{\number\numexpr#1-1}{#2}}% <- argument renvoyé après le \fi + #2% <- ceci reste avant le \fi + \fi +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo +****************** Fin code ****************** + + +****************** Code 143 ****************** +\def\ncar#1#2{% + \ifnum#1>0 + #2% afficher le caractère + \expandafter\ncar\expandafter% le pont d'\expandafter lance \number et \numexpr + {\number\numexpr#1-1\expandafter}% le dernier \expandafter mange "\else...\fi" + \else + \expandafter\gobone% si le test est faux, manger {#2} ci-dessous + \fi{#2}% +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo +****************** Fin code ****************** + + +****************** Code 144 ****************** +\catcode`\@11 +\def\ncar#1#2{% + \def\ncar@i##1{% + \ifnum##1<#1 % si i < valeur maxi + #2% afficher le caractère + \exparg\ncar@i{\number\numexpr##1+1\expandafter}% puis recommencer avec i+1 + \fi% \fi mangé par le \expandafter en fin de zone de \numexpr + }% + \ncar@i{0}% commence avec la valeur "compteur" 0 +} +\catcode`\@12 +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \ncar{5}{X} +****************** Fin code ****************** + + +****************** Code 145 ****************** +\catcode`\@11 \newcount\compte@cnt +\def\compte#1{% + \def\val@max{#1}\compte@cnt=0 % effectue les initialisations + \compte@i% appelle la macro principale +} +\def\compte@i{% + \ifnum\compte@cnt<\val@max% si compteur < valeur maxi + \advance\compte@cnt1 % incrémenter le compteur + \number\compte@cnt, % afficher le nombre, la virgule et l'espace + \expandafter\compte@i %recommencer + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \compte{2} +****************** Fin code ****************** + + +****************** Code 146 ****************** +\catcode`\@11 \newcount\compte@cnt +\def\compte#1{\def\val@max{#1}\compte@cnt=0 \compte@i} +\def\compte@i{% + \ifnum\compte@cnt<\val@max\relax + \advance\compte@cnt1 + \number\compte@cnt + \ifnum\compte@cnt<\val@max , \fi% afficher ", " si le maxi n'est pas atteint + \expandafter\compte@i + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b)\compte{-4}\qquad +c) \compte{2} +****************** Fin code ****************** + + +****************** Code 147 ****************** +\catcode`\@11 \newcount\compte@cnt +\def\compte#1{% + \ifnum#1>0 % ne faire quelque chose que si l'argument est positif + \def\val@max{#1}\compte@cnt=1 % faire les initialisations + \expandafter\compte@i% appeller la macro récrusive + \fi +} +\def\compte@i{% + \number\compte@cnt\relax % afficher le nombre + \ifnum\compte@cnt<\val@max\relax % si valeur maxi n'est pas atteinte + , % afficher la virgule et l'espace + \advance\compte@cnt1 % incrémenter le compteur + \expandafter\compte@i % recommencer + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \compte{2} +****************** Fin code ****************** + + +****************** Code 148 ****************** +\catcode`\@11 +\def\compte#1{% + \ifnum#1>\z@% ne faire quelque chose que si #1 > 0 + \antefi\compte@i{1}{#1}% + \fi +} + +\def\compte@i#1#2{% + #1% afficher le nombre + \ifnum#1<#2 % si le maxi n'est pas atteint + \expandafter\identity% exécuter... + \else + \expandafter\gobone% sinon, manger... + \fi% le code entre accolades ci-dessous + {, % afficher la virgule et l'espace + \exparg\compte@i{\number\numexpr#1+1}{#2}% appel récursif + }% +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \edef\foo{\compte{2}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 149 ****************** +\catcode`\@11 +\def\compte#1{% + \ifnum#1>\z@% ne faire quelque chose que si #1 > 0 + \antefi\compte@i{1}{#1}% + \fi +} + +\def\compte@i#1#2{% + #1% afficher le nombre + \ifnum#1<#2 % si le maxi n'est pas atteint + \antefi% déplace le \fi ici + , % affiche la virgule et l'espace + \exparg\compte@i{\number\numexpr#1+1}{#2}% appel récursif + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \edef\foo{\compte{2}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 150 ****************** +\catcode`\@11 +\def\for#1=#2to#3\do#4{% #1=\<macro> #2=n1 #3=n2 #4=<code> + \def#1{#2}% initialise la \<macro> à l'entier n1 + \def\val@max{#3}% stocke n2 + \def\loop@code{#4}% stocke le <code> + \for@i#1% appelle la macro récursive et passe la \<macro> en argument +} +\def\for@i#1{% + \unless\ifnum#1>\val@max\relax% tant que la \<macro> est <= n2 + \loop@code% effectue le code précédemment sauvegardé + \edef#1{\number\numexpr#1+1}% incrémenter la \<macro> + \expandafter\for@i\expandafter#1% et recommencer après avoir mangé le \fi + \fi +} +\catcode`\@=12 +\for\ii=1to5\do{Maintenant \string\ii\ vaut : \ii.\endgraf}\medbreak +"\for\ii=1to0\do{A}"\medbreak% boucle parcourue 0 fois +"\for\ii=1to1\do{A}"\medbreak% boucle parcourue 1 fois +\for\ii = 0 to 10 \do {[\ii]}. +****************** Fin code ****************** + + +****************** Code 151 ****************** +\catcode`\@11 +\long\def\for#1=#2to#3\do#4{% + \def\for@i{% + \unless\ifnum#1>\val@max\relax% tant que la \<macro> <= n2 + #4% code à exécuter + \edef#1{\number\numexpr#1+1}% incrémenter \<macro> + \expandafter\for@i% et recommencer après avoir mangé le \fi + \fi + }% + \edef#1{\number\numexpr#2\relax}% initialise la variable à n1 + \edef\val@max{\number\numexpr#3\relax}% stocke n2 + \for@i% appelle la sous-macro récursive +} +\catcode`\@=12 +\for\ii = 1 to 5 \do {Maintenant \string\ii\ vaut : \ii.\par}\medbreak +"\for\ii=1to0\do{A}"\medbreak% boucle parcourue 0 fois +"\for\ii=1to1\do{A}"\medbreak% boucle parcourue 1 fois +\for\ii = 0 to 10 \do {[\ii]}. +****************** Fin code ****************** + + +****************** Code 152 ****************** +\catcode`\@11 +\def\for#1=#2to#3\do#4#{% + \edef\for@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel + \ifnum\for@increment=\z@% s'il est nul, + \edef\for@increment{% le redéfinir : + \ifnum\numexpr#3\relax<\numexpr#2\relax + -% ajoute un signe - si #3<#2 + \fi + 1% devant "1" + }% \for@increment vaut donc 1 ou -1 + \fi + \ifnum\numexpr\for@increment*(#3-#2)\relax<\z@% si l'argument est incompatible + \expandafter\firstoftwo% exécuter le 1er argument qui suit + \else + \expandafter\secondoftwo% sinon le second + \fi + {Incrément incompatible !\gobone %\gobone mangera le <code> qui à lire + }% + {\edef#1{\number\numexpr#2\relax}% initialise la \<macro> + \edef\cmp@sgn{\ifnum\for@increment<\z@<\else>\fi}% stocke "<" ou ">" pour plus tard + \expandafter\for@i\expandafter#1\expandafter% appelle la macro récursive + {\number\numexpr#3\relax}% et lui lui passe la \<macro> (#1) et n2 (#3) + }% +} + +% #1 = \<macro> #2 = n2 +\long\def\for@i#1#2#3{% l'argument #3 (le <code>) est lu à ce moment-là + \def\for@ii{% + \unless\ifnum#1\cmp@sgn#2\relax% tant que la \<macro> n'a pas dépassé n2 + #3% exécute le <code> + \edef#1{\number\numexpr#1+\for@increment}% incrémente la \<macro> + \expandafter\for@ii% recommence après avoir mangé le \fi + \fi + }% + \for@ii% appelle la sous-macro récursive +}% +\catcode`\@=12 +a) \for\ii = -4 to 7 \do{(\ii)}\par +b) \for\jj = 20 to -50\do-10 {(\jj)}\par +c) \for\xx = 8 to 185\do 20 {[\xx]}\par +d) \for\yy = 0 to 10\do -2{(\yy)}\par +e) "\for\ii = 1 to 0 \do1{XX}"\par +f) "\for\ii = 1 to 1 \do{A}"\par% boucle parcourue 1 fois +g) \for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}% imbrication de boucles +****************** Fin code ****************** + + +****************** Code 153 ****************** +\catcode`\@11 +\def\for#1=#2to#3\do#4#{% + \edef\for@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel + \ifnum\for@increment=\z@% s'il est nul, + \edef\for@increment{% le redéfinir à -1 (si #3<#2) et 1 sinon + \ifnum\numexpr#3-#2\relax<\z@ -1\else 1\fi + }% \for@increment vaut donc 1 ou -1 + \fi + \ifnum\numexpr\for@increment*(#3-#2)\relax<\z@ + \expandafter\gobone% si argument optionnel incompatible, manger le {<code>} + \else + \edef#1{\number\numexpr#2}% initialise la \<macro> + \edef\macro@args{% définit et développe les arguments à passer à \for@i + %#1=nom de la macro récursive : + \expandafter\noexpand\csname for@ii@\string#1\endcsname + \ifnum\for@increment<\z@ <\else >\fi% #2=signe de comparaison + {\for@increment}% #3=incrément + \noexpand#1% #4=\<macro> + {\number\numexpr#3\relax}% #5=entier n2 + }% + \antefi% externalise la ligne ci-dessous de la portée du test + \expandafter\for@i\macro@args% appelle \for@i avec les arguments définis ci-dessus + \fi +} + +% #1=nom de la macro récursive de type "\for@ii@\<macro>" +% #2=signe de comparaison % #3=incrément +% #4=\<macro> % #5=entier n2 % #6=<code> à exécuter +\long\def\for@i#1#2#3#4#5#6{% + \def#1{% définit la sous macro récursive + \unless\ifnum#4#2#5\relax% tant que la \<macro> variable n'a pas dépassé n2 + \afterfi{% rendre la récursivité terminale + #6% exécute le code + \edef#4{\number\numexpr#4+#3\relax}% incrémente la \<macro> + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive +}% +\catcode`\@=12 +\for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}\medbreak + +\for\carA= `\A to `\E \do{\for\carB= `w to `z \do{\char\carA\char\carB}\quad} +****************** Fin code ****************** + + +****************** Code 154 ****************** +\catcode`\@11 +\long\def\for@i#1#2#3#4#5#6{% + \def\exitfor{\def#1{}}% + \def#1{% définit la sous macro récursive + \unless\ifnum#4#2#5\relax% tant que la variable n'a pas dépassé l'entier max + \afterfi{% rendre la récursivité terminale + #6% exécute le code + \edef#4{\number\numexpr#4+#3\relax}% incrémente la variable #1 + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive +}% +\catcode`\@=12 +\for\ii= 1 to 9 \do{\string\ii{} vaut \ii.\ifnum\ii=5 \exitfor\fi\par} +****************** Fin code ****************** + + +****************** Code 155 ****************** +\def\exitfor#1{% #1=\<macro> correspondant à la boucle de laquelle on veut sortir + \defname{for@ii@\string#1}{} +} + +\def\ifexitfor#1{% envoie vrai si on est prématurément sorti de la boucle de \<macro> #1 + % si la macro récursive est égale à la macro "\empty" + \expandafter\ifx\csname for@ii@\string#1\endcsname\empty + \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai" + \else + \expandafter\secondoftwo% sinon, renvoyer "faux" + \fi +} + +% on ne sort QUE de la boucle intérieure quand \ii=3 donc +% les boucles sont normalement exécutées pour \ii=4 et \ii=5 +a. \for\ii=1 to 5\do1{% + \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\jj\fi}% + \qquad + } + +% on sort de la boucle extérieure lorsque \ii=3 donc la boucle +% intérieure est faite entièrement même lorsque \ii=3 +b. \for\ii=1 to 5\do1{% + \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\ii\fi}% + \qquad + } + +% on sort de la boucle intérieure lorsque \ii=3 +% et aussi de la boucle extérieure à l'aide de \ifexitfor +c. \for\ii=1 to 5\do1{% + \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\jj\fi}% + \ifexitfor\jj{\exitfor\ii}{}% si on est sorti de \jj, sortir aussi de \ii + \qquad + } +****************** Fin code ****************** + + +****************** Code 156 ****************** +\def\foo{a}\ifx a\foo vrai\else faux\fi +****************** Fin code ****************** + + +****************** Code 157 ****************** +1) \meaning9\par% un caractère de catcode 12 +2) \meaning a\par% une lettre +3) \def\foo{a}\meaning\foo\par% une macro +4) \long\def\foo{a}\meaning\foo\par% une macro déclarée \long +5) \meaning\sdlkfj\par% une macro indéfinie +6) \edef\foo{\string a}\meaning\foo\par%\foo contient un "a" de catcode 12 +7) \def\foo{a}\meaning\foo\par%\foo contient un "a" de catcode 11 +****************** Fin code ****************** + + +****************** Code 158 ****************** +a) \ifx abvrai\else faux\fi\quad% a est n'est pas égal à b +b) \ifx++vrai\else faux\fi\quad% le caractère "+" est égal à "+" +c) \ifx\relax\par vrai\else faux\fi\quad% \relax n'est pas la même primitive que \par +d) \ifx\par\par vrai\else faux\fi\quad% \par et \par sont les mêmes primitives +e) \ifx\sdfk\qlms vrai\else faux\fi\quad% 2 macros non définies sont égales +f) \def\foo{abcd}\def\bar{abc}% \foo et \bar ont des textes de remplacement différents + \ifx\foo\bar vrai\else faux\fi\quad +g) \def\foo{abcd}\def\bar{abcd }% \foo et \bar ont des textes de remplacement différents + \ifx\foo\bar vrai\else faux\fi\quad +h) \def\foo{abcd}\def\bar{abcd} + \ifx\foo\bar vrai\else faux\fi\quad% \foo et \bar ont les mêmes textes de remplacement +i) \long\def\foo{a}\def\bar{a} + \ifx\foo\bar vrai\else faux\fi\quad% \foo est \long, \bar ne l'est pas +j) \edef\foo{\string a}% \foo contient un "a" de catcode 12 + \def\bar{a}% \bar contient un "a" de catcode 11 + \ifx\foo\bar vrai\else faux\fi +****************** Fin code ****************** + + +****************** Code 159 ****************** +\def\cmpmacro#1#2{% + \begingroup + \edef\tempa{\detokenize\expandafter{#1}}\edef\tempb{\detokenize\expandafter{#2}}% + \ifx\tempa\tempb% si égalité + \endgroup\expandafter\firstoftwo% ferme le groupe et lit 1er argument + \else + \endgroup\expandafter\secondoftwo% sinon, ferme le groupe et lit le 2e argument + \fi +} +a) \edef\foo{\string a}\def\bar{a} + \cmpmacro\foo\bar{vrai}{faux}\qquad +b) \edef\foo{\detokenize{$i^2=-1$\relax}}\def\bar{$i^2=-1$\relax} + \cmpmacro\foo\bar{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 160 ****************** +a) \let\rien\relax \ifx\rien\relax vrai\else faux\fi\qquad +b) \let\AA=a \ifx a\AA vrai\else faux\fi\qquad +c) \let\foo=_\let\bar=_ \ifx\foo\bar vrai\else faux\fi +****************** Fin code ****************** + + +****************** Code 161 ****************** +\def\quark{\quark}% définit un quark +1) \def\test{\quark} \ifx\quark\test vrai\else faux\fi +\qquad +2) \let\test=\quark \ifx\quark\test vrai\else faux\fi +****************** Fin code ****************** + + +****************** Code 162 ****************** +\def\ifempty#1{% + \def\tempa{#1}% stocke l'argument #1 dans \tempa + \ifx\tempa\empty% si \tempa = \empty + \expandafter\firstoftwo% 1er argument + \else + \expandafter\secondoftwo% sinon, second + \fi +} +a) \ifempty{abc}{vide}{pas vide}\qquad +b) \ifempty{}{vide}{pas vide}\qquad +c) \ifempty{ }{vide}{pas vide}\qquad +d) \ifempty{\empty}{vide}{pas vide} +****************** Fin code ****************** + + +****************** Code 163 ****************** +\def\empty{} +\long\def\ifempty#1{% + \ifx_#1_\expandafter\firstoftwo + \else\expandafter\secondoftwo + \fi} +a) \ifempty{abc}{vide}{pas vide}\qquad +b) \ifempty{}{vide}{pas vide}\qquad +c) \ifempty{ }{vide}{pas vide}\qquad +d) \ifempty{\empty}{vide}{pas vide}\qquad +e) \edef\foo{\ifempty{abc}{vide}{pas vide}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 164 ****************** +\long\def\ifempty#1{% + \ifx W#1W\expandafter\firstoftwo + \else \expandafter\secondoftwo + \fi} +1) \ifempty{foobar}{vide}{pas vide}\qquad +2) \ifempty{}{vide}{pas vide}\qquad +2) \ifempty{\empty}{vide}{pas vide}\qquad +4) \ifempty{Wagons}{vide}{pas vide} +****************** Fin code ****************** + + +****************** Code 165 ****************** +\long\def\ifempty#1{% + \expandafter\ifx\expandafter\relax\detokenize{#1}\relax + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi +} +a) \ifempty{abc}{vide}{pas vide}\qquad +b) \ifempty{}{vide}{pas vide}\qquad +c) \ifempty{ }{vide}{pas vide}\qquad +d) \ifempty{\relax}{vide}{pas vide}\qquad +e) \edef\foo{\ifempty{abc}{vide}{pas vide}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 166 ****************** +\catcode`\@11 +\def\reverse#1{% + \ifempty{#1} + {}% s'il n'y a rien à inverse, ne rien faire + {\reverse@i#1\@nil\@nil}% initialisation des réservoirs et appeler \reverse@i +} +\def\reverse@i#1#2\@nil#3\@nil{% #1 est le 1er caractère du réservoir A + \ifempty{#2}% si ce qui reste après ce 1er caractère est vide + {#1#3}% renvoyer #1#3 qui est le résultat final + {\reverse@i#2\@nil#1#3\@nil}% sinon, recommencer en déplaçant #1 + % en 1re position du réservoir B +} +\catcode`\@12 +a) \reverse{foobar}\qquad +b) \edef\foo{\reverse{Bonjour}}\meaning\foo\qquad +c) \reverse{Bonjour le monde}\qquad +d) \reverse{Bonjour{ }le{ }monde} +****************** Fin code ****************** + + +****************** Code 167 ****************** +\catcode`\@11 +\def\ifin#1#2{% + \def\ifin@i##1#2##2\@nil{% définit la macro auxiliaire + \ifempty{##2}% si ce qu'il y a derrière le motif est vide + \secondoftwo% aller à "faux" + \firstoftwo% sinon à "vrai" + }% + \ifin@i#1#2\@nil% appel de la macro auxiliaire +} +\catcode`\@12 +a) \ifin{abc\relax1}{bc}{vrai}{faux}\qquad +b) \ifin{abc \relax1}{c\relax}{vrai}{faux}\qquad +c) \ifin{abc \relax1}{ }{vrai}{faux}\qquad +d) \ifin{abc \relax1}{}{vrai}{faux}\qquad +e) \ifin{}{a}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 168 ****************** +\catcode`\@11 +\def\ifstart#1#2{% + \def\ifstart@i##1#2##2\@nil{\ifempty{##1}}% + \ifstart@i#1#2\@nil +} +\catcode`\@12 +a) \ifstart{abc}{bc}{vrai}{faux}\qquad +b) \ifstart{abc}{ab}{vrai}{faux}\qquad +c) \ifstart{ abc}{ }{vrai}{faux}\qquad +d) \ifstart{abc}{}{vrai}{faux}\qquad +e) \ifstart{}{a}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 169 ****************** +\catcode`\@11 +\def\ifstart#1#2{% + \def\ifstart@i##1#2##2\@nil{\ifempty{##1}}% + \ifempty{#2}% si motif vide + \firstoftwo% exécuter code "vrai" + {\ifstart@i#1\relax#2\@nil}% sinon, aller à la macro auxiliaire +} +\catcode`\@12 +a) \ifstart{abc}{bc}{vrai}{faux}\qquad +b) \ifstart{abc}{ab}{vrai}{faux}\qquad +c) \ifstart{ abc}{ }{vrai}{faux}\qquad +d) \ifstart{abc}{}{vrai}{faux}\qquad +e) \ifstart{}{a}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 170 ****************** +\catcode`\@11 +\def\ifend#1#2{% + \def\ifend@i##1#2##2\@nil{% ##2 = ce qui reste après le motif + \ifempty{##2}% si ##2 est vide + \firstoftwo% exécuter l'argument "vrai" + {\ifin{##2}{#2}% sinon, si ##2 contient le <motif> + {\ifend@i##2\@nil}% appeler \ifend@i avec ce qui reste + \secondoftwo% sinon, exécuter l'argument "faux" + }% + }% + \ifend@i#2#1\@nil% appelle la macro récursive +} +\catcode`\@12 +1) \ifend{abc de}{de}{vrai}{faux}\qquad +2) \ifend{abd de }{de}{vrai}{faux}\qquad +3) \ifend{abc de }{ }{vrai}{faux}\qquad +4) \ifend{}{a}{vrai}{faux}\qquad +5) \ifend{abc de}{}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 171 ****************** +\catcode`\@11 +\def\ifend#1#2{% + \def\ifend@i##1#2##2\@nil{% ##2 = ce qui reste après le motif + \ifempty{##2}% si ##2 est vide + \firstoftwo% exécuter l'argument "vrai" + {\ifin{##2}{#2}% sinon, si ##2 contient le <motif> + {\ifend@i##2\@nil}% appeler \ifend@i avec ce qui reste + \secondoftwo% sinon, exécuter l'argument "faux" + }% + }% + \ifempty{#2}% si le motif est vide + \firstoftwo% exécuter "vrai" + {\ifend@i#2\relax#1\@nil}% sinon, appelle la macro récursive +} +\catcode`\@12 +1) \ifend{abc de}{de}{vrai}{faux}\qquad +2) \ifend{abd de }{de}{vrai}{faux}\qquad +3) \ifend{abc de }{ }{vrai}{faux}\qquad +4) \ifend{}{a}{vrai}{faux}\qquad +5) \ifend{abc de}{}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 172 ****************** +\catcode`\@11 +\def\substin#1#2#3{% + \def\substin@i##1{% + \ifempty{##1}% si le <code> est vide + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#2}% sinon, si le <code> contient <motif1> + {\substin@ii##1\@nil}% appeler la macro à argument délimités + {##1}% sinon, afficher le <code> + }% + }% + \def\substin@ii##1#2##2\@nil{% + ##1#3% afficher ##1 et #3 (qui est <motif2>) + \substin@i{##2}% et recommencer avec ce qui reste + }% + \substin@i{#1}% +} +\catcode`\@12 +a) \substin{abracadabra}{a}{A}\qquad +b) \substin{abracadabra}{x}{W}\qquad +c) \substin{abracadabra}{br}{}\qquad +d) \substin{1\relax3}\relax{222}\qquad +****************** Fin code ****************** + + +****************** Code 173 ****************** +\catcode`\@11 +\def\substtocs#1#2#3#4{% + \def\substtocs@i##1{% + \ifempty{##1}% si le <code> est vide + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#3}% sinon, si le <code> contient <motif1> + {\substtocs@ii##1\@nil}% appeler la macro à argument délimités + {\addtomacro#1{##1}}% sinon, ajouter le <code> + }% + }% + \def\substtocs@ii##1#3##2\@nil{% + \addtomacro#1{##1#4}% ajouter ##1#4 + \substtocs@i{##2}% et recommencer avec ce qui reste + }% + \let#1=\empty% initialise la macro à vide + \substtocs@i{#2}% +} +\catcode`\@12 +\substtocs\foo{abracadabra}{a}{A}\meaning\foo\par +\substtocs\foo{abracadabra}{x}{W}\meaning\foo\par +\substtocs\foo{abracadabra}{br}{}\meaning\foo\par +\substtocs\foo{1\relax3}\relax{222}\meaning\foo\par +\substtocs\foo{\ifnum1=2 test vrai\fi}{2}{1}\meaning\foo +****************** Fin code ****************** + + +****************** Code 174 ****************** +\def\majuscule#1{\uppercase{#1}} +\def\majmot#1{\substin{\majuscule#1}{ }{ \majuscule}} +\majmot{un petit texte}\par +\majmot{Un grand texte sans importance} +****************** Fin code ****************** + + +****************** Code 175 ****************** +\def\majuscule#1{\uppercase{#1}} +\def\majmot#1{% + \begingroup + \substtocs\temp{\majuscule#1}{ }{ \majuscule}% + \temp% exécute la macro temporaire + \endgroup +} +\majmot{un petit texte}\par +\majmot{Un grand texte Sans importance} +****************** Fin code ****************** + + +****************** Code 176 ****************** +\catcode`\@11 +\newcount\cnt@occ +\def\cnttimes#1#2{% + \def\cnttimes@i##1{% + \ifempty{##1}% si le <code> est vide + {\number\cnt@occ}% afficher le nombre d'occurrences + {\ifin{##1}{#2}% sinon, si le <code> contient <motif1> + {\cnttimes@ii##1\@nil}% appeler la macro à argument délimités + {\number\cnt@occ}% sinon, afficher le nombre d'occurrences + }% + }% + \def\cnttimes@ii##1#2##2\@nil{% + \advance\cnt@occ1 + \cnttimes@i{##2}% et recommencer avec ce qui reste + }% + \cnt@occ=0 % initialise le compteur + \cnttimes@i{#1}% +} +\catcode`\@12 +a) \cnttimes{abracadabra}{a}\qquad +b) \cnttimes{abracadabra}{ra}\qquad +c) \cnttimes{abracadabra}{w}\qquad +d) \cnttimes{abc def ghijk l }{ }\qquad +e) \cnttimes{\ifnum1=2 vrai\else faux\fi}{\ifnum} +****************** Fin code ****************** + + +****************** Code 177 ****************** +\catcode`\@11 +\long\def\cnttimestocs#1#2#3{% #3=macro recevant le résultat + \long\def\cnttimestocs@i##1\@nil##2#2##3\@nil{% + % ##1=nb d'occurrences rencontrées jusqu'alors + % ##2 et ##3=ce qui est avant/après le <motif> + \ifin{##3}{#2}% si le <motif> est dans ce qui reste + {\expandafter\cnttimestocs@i% appeler la macro récursive + \number\numexpr##1+1\relax\@nil% avec une occurrence de plus + ##3\@nil% et ce qui reste + }% + {\edef#3{\number\numexpr##1+1\relax}}% sinon, stocker 1 occurrence de plus dans #3 + }% + \ifin{#1}{#2}% si le <motif> est dans le <code> + {\cnttimestocs@i 0\@nil#1\@nil}% appeler la macro récursive avec 0 occurrence + {\def#3{0}}% sinon, mettre 0 dans #3 +} +\catcode`\@12 +a) \cnttimestocs{abracadabra}{a}\foo \meaning\foo\qquad +b) \cnttimestocs{abracadabra}{ra}\foo \meaning\foo\qquad +c) \cnttimestocs{abracadabra}{w}\foo \meaning\foo\qquad +d) \cnttimestocs{abc def ghijk l }{ }\foo \meaning\foo\qquad +e) \cnttimestocs{\ifnum1=2 vrai\else faux\fi}{\ifnum}\foo \meaning\foo +****************** Fin code ****************** + + +****************** Code 178 ****************** +\catcode`\@11 +\def\multisubst#1#2{% + \def\subst@code{#1}% stocke le <code> + \multisubst@i#2\@nil% appelle la macro récursive avec la liste de motifs +} + +\def\multisubst@i#1#2#3\@nil{% #1 et #2=paire de motifs #3=motifs restants + \expandafter\substtocs\expandafter\subst@code\expandafter% 1-développe + {\subst@code}{#1}{#2}% le <code> et effectue la substitution en cours + \ifempty{#3}% si la liste des motifs est vide + \subst@code% exécuter le <code> obtenu + {\multisubst@i#3\@nil}% recommencer avec les motifs qui restent +} +\catcode`\@12 +1) \multisubst{abracadabra}{aA rR}\par +2) \multisubst{Ce texte devenu \`a peine reconnaissable montre que le + r\'esultat contient des sonorit\'es catalanes, corses ou grecques + assez inattendues.}{a{AA} ya uy ou io ei {AA}e} +****************** Fin code ****************** + + +****************** Code 179 ****************** +\catcode`\@11 +\def\quark{\quark} + +\def\multisubst#1#2{% #1 = <code> #2 = <liste des paires> + \def\subst@code{#1}% stocke le <code> + \multisubst@i#2\quark\quark% appelle la macro récursive avec + % 2 arguments spéciaux en fin de liste +} + +\def\multisubst@i#1#2{% #1#2 = paire de motifs en cours + \def\arg@a{#1}% stocke le <motif1> dans une macro + \ifx\arg@a\quark% si le motif spécial est atteint + \expandafter\subst@code% exécuter le code obtenu + \else + \expandafter\substtocs\expandafter\subst@code\expandafter% 1-développe + {\subst@code}{#1}{#2}% le <code> et effectue la substitution en cours + \expandafter\multisubst@i% puis lis la paire de motifs suivants + \fi +} +\catcode`\@12 +\multisubst{abracadabra}{aA rR}\par +\multisubst{Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des + sonorit\'es catalanes, corses ou grecques assez inattendues.}{a{AA} ya uy ou io ei {AA}e} +****************** Fin code ****************** + + +****************** Code 180 ****************** +\iftrue foobar\else ceci n'est jamais lu par \TeX\fi\par +\iffalse ceci n'est jamais lu par \TeX\else foobar\fi +****************** Fin code ****************** + + +****************** Code 181 ****************** +\newif\ifhomme +\def\debutlettre{Ch\ifhomme er Monsieur\else ère Madame\fi}% + +\hommetrue +\debutlettre +\medbreak + +\hommefalse +\debutlettre +****************** Fin code ****************** + + +****************** Code 182 ****************** +\catcode`\*=13 \catcode`\+=13 % "*" et "+" sont actifs +1) \def*{xyz}\def+{abc}% définit les caractères actifs + \ifcat *+vrai\else faux\fi\qquad +2) \ifcat \noexpand *\noexpand +vrai\else faux\fi\qquad +3) \def\foo{foobar}% + \ifcat \noexpand\foo\relax vrai\else faux\fi\qquad% \foo et \relax sont vus égaux +4) \ifcat = vrai\else faux\fi\qquad% "=" et " " n'ont pas le même catcode +5) \ifcat _^vrai\else faux\fi\qquad% "_" et "^" n'ont pas le même catcode +6) \let\foo=& + \ifcat &\foo vrai\else faux\fi% "&" et \foo (let-égal à "&") sont vus égaux +****************** Fin code ****************** + + +****************** Code 183 ****************** +\catcode`\@11 +\def\ifcs#1{% + \ifcat\noexpand#1\relax\expandafter\firstoftwo + \else \expandafter\secondoftwo + \fi +} +\catcode`\@12 +1) \def\foo{bar}\ifcs\foo{vrai}{faux}\qquad +2) \ifcs\def{vrai}{faux}\qquad +3) \ifcs A{vrai}{faux}\qquad +\catcode`\*=13 +4) \let*=W \ifcs*{vrai}{faux}\qquad +5) \let*=\relax \ifcs*{vrai}{faux}\qquad +6) \let\foo=A \ifcs\foo{vrai}{faux}\qquad +7) \ifcs\bgroup{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 184 ****************** +1) \def\foo{xxy}\def\bar{aab} + \if\foo\bar vrai\else faux\fi\qquad +2) \if aA vrai\else faux\fi\qquad% "a" et "A" n'ont pas les mêmes charcode +3) \if\relax\noexpand\foo vrai\else faux\fi\qquad% \relax et \foo ont un charcode=256 +4) \let\foo=&\if\foo &vrai\else faux\fi\qquad% \foo est vue comme "&" +5) \if\string~\noexpand~vrai\else faux\fi +****************** Fin code ****************** + + +****************** Code 185 ****************** +\catcode`\@11 +\def\ifcs#1{% + \begingroup + \escapechar=`\@ % prend "@" comme caractère d'échappement + \if% les premiers caractères de + \expandafter\firstto@nil\string#1a\@nil% "#1a" + \expandafter\firstto@nil\string\relax\@nil% et "\relax" sont-ils égaux ? + \endgroup\expandafter\firstoftwo% si oui, fermer le groupe et renvoyer "vrai" + \else% sinon, fermer le groupe et renvoyer "faux" + \endgroup\expandafter\secondoftwo + \fi +} +\catcode`\@12 +1) \def\foo{bar}\ifcs\foo{vrai}{faux}\qquad +2) \ifcs\def{vrai}{faux}\qquad +3) \ifcs A{vrai}{faux}\qquad +\catcode`\*=13 +4) \let*=W \ifcs*{vrai}{faux}\qquad +5) \let*=\relax \ifcs*{vrai}{faux}\qquad +6) \let\foo=A \ifcs\foo{vrai}{faux}\qquad +7) \ifcs\bgroup{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 186 ****************** +\edef\specialrelax{\ifnum1=1\fi} +\meaning\specialrelax +****************** Fin code ****************** + + +****************** Code 187 ****************** +\edef\specialrelax{\ifnum1=1\fi}% texte de remplacement = \relax spécial +\edef\normalrelax{\relax}% texte de remplacement = \relax normal +\meaning\specialrelax\par +\meaning\normalrelax\par +Les 2 \string\relax{} sont \ifx\specialrelax\normalrelax identiques\else différents\fi. +****************** Fin code ****************** + + +****************** Code 188 ****************** +\edef\specialrelax{\ifnum1=1\fi} +\expandafter\def\specialrelax{foobar}% redéfinition un \relax spécial +****************** Fin code ****************** + + +****************** Code 189 ****************** +\catcode`\@11 +\def\ifnumcase#1{% + \begingroup + \def\elseif{\elseif}\def\endif{\endif}% définir deux "quarks" locaux + \def\nombre@{#1}% stocker le nombre à comparer dans \nombre@ + \ifnumcase@i% appeller la macro récursive +} + +\def\ifnumcase@i#1{% + \def\nxt@arg{#1}% stocker l'argument suivant + \ifx\nxt@arg\elseif% si c'est \elseif + \def\next@todo{\endgroup\idto@endif}% fermer le groupe et aller à \idto@endif + \else + \ifx\nxt@arg\endif% si c'est \endif + \let\next@todo\endgroup% fermer le groupe + \else + \ifnum\nombre@=\nxt@arg% s'il y a égalité + \def\next@todo{\endgroup\firstto@endif}% fermer le groupe puis \firstto@endif + \else% sinon + \let\next@todo=\ifnumcase@ii% aller à \ifnumcase@ii + \fi + \fi + \fi + \next@todo% exécuter l'action décidée ci-dessus +} + +\def\ifnumcase@ii#1{\ifnumcase@i} +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\catcode`\@12 +\def\swaptwo#1#2{#2#1} +\def\testenombre#1{% + \ifnumcase{#1} + {1}{C'est "un" et je prends le premier argument: \firstoftwo} + {3}{J'ai vu un "trois" et je mange les deux arguments : \gobtwo} + {15}{L'argument est "15" et je prends le second argument : \secondoftwo} + \elseif + ni 1, ni 3, ni 5 et j'inverse les deux arguments : \swaptwo + \endif +} +\testenombre{3}{foo}{bar}\par +\testenombre{16}{foo}{bar}\par +\testenombre{15}{foo}{bar}\par +\testenombre{1}{foo}{bar} +****************** Fin code ****************** + + +****************** Code 190 ****************** +\catcode`\@11 +\def\endif{\endif}\def\elseif{\elseif}% définit les quarks + +\def\ifnumcase#1#2{% #1=<nombre> #2=prochain argument + \ifx\elseif#2% si #2 est \elseif + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + \idto@endif% aller à \idto@endif, sinon : + {\ifx\endif#2% si #2 est \endif + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% ne rien faire. Sinon #2 est une <valeur i>: + {\ifnum#1=#2 % s'il y a égalité entre <nombre> et <valeur i> + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + \firstto@endif% aller à \firstto@endif + {\ifnumcase@i{#1}}% sinon aller à \ifnumcase@i + }% + }% +} + +\def\ifnumcase@i#1#2{% #1=<nombre> #2=<code i> à suivre qui est mangé + \ifnumcase{#1}% retourner à \ifnumcase en transmettant #1 +} + +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\catcode`\@12 +\def\swaptwo#1#2{#2#1} +\def\testenombre#1{% + \ifnumcase{#1} + {1}{C'est "un" et je prends le premier argument: \firstoftwo} + {3}{J'ai vu un "trois" et je mange les deux arguments : \gobtwo} + {15}{L'argument est "15" et je prends le second argument : \secondoftwo} + \elseif + ni 1, ni 3, ni 5 et j'inverse les deux arguments : \swaptwo + \endif +} +\testenombre{3}{foo}{bar}\par +\testenombre{16}{foo}{bar}\par +\testenombre{15}{foo}{bar}\par +\edef\macro{\testenombre{1}{foo}{bar}}\meaning\macro +****************** Fin code ****************** + + +****************** Code 191 ****************** +\catcode`\@11 +\def\endif{\endif}\def\elseif{\elseif}% +\def\ifxcase#1#2{% #1=<token> #2=prochain argument + \ifx\elseif#2% si #2 est \elseif + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + \idto@endif% aller à \idto@endif, sinon : + {\ifx\endif#2% si #2 est \endif + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% ne rien faire. Sinon #2 est un <token i>: + {\ifx#1#2% s'il y a égalité entre <token> et <token i> + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + \firstto@endif% aller à \firstto@endif + {\ifxcase@i{#1}}% sinon aller à \ifnumcase@i + }% + }% +} +\def\ifxcase@i#1#2{% #1=<token> #2=<code i> à suivre (qui est mangé) + \ifxcase{#1}% retourner à \ifxcase en transmettant #1 +} +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\catcode`\@12 +\def\testtoken#1{% + \ifxcase{#1} + a{Le token est "a"} + +{J'ai vu un "+"} + \relax{L'argument est "\string\relax"} + \elseif + Tous les tests sont négatifs% + \endif +} +1) \testtoken a\par +2) \let\foo=a\testtoken\foo\par +3) \testtoken+\par +4) \testtoken\relax\par +5) \edef\foo{\testtoken\relax}\meaning\foo\par +6) \edef\foo{\testtoken W}\meaning\foo +****************** Fin code ****************** + + +****************** Code 192 ****************** +\newcount\xx % définit un compteur utilisé dans la boucle +\def\compte#1{% + \xx=0 % initialise le compteur + \loop% début de la boucle + \advance\xx1 % incrémente le compteur + \ifnum\xx<#1 % s'il est inférieur à la borne + \number\xx , % affiche le nombre et la virgule + \repeat% et recommence + \ifnum#1>0 \number\xx\relax\fi% si le nombre #1 est >0, afficher le dernier nombre + } +a) \compte{1}.\qquad b) \compte{-3}.\qquad c) \compte{7}. +****************** Fin code ****************** + + +****************** Code 193 ****************** +\catcode`\@11 +\newcount\cnt@repeat % définit le compteur de \xrepeat + +\def\xloop{% + \def\xiterate{}% initialiser \xiterate à vide + \cnt@repeat\z@% initialiser le compteur de \xrepeat + \xloop@i% aller à la macro récursive +} + +\long\def\xloop@i#1\xrepeat{% + \addtomacro\xiterate{#1}% ajoute ce qui est avant le premier \xrepeat + \exparg\cnttimestocs{\xiterate}\xloop\cnt@loop + % combien de \xloop dans \xiterate + \ifnum\cnt@loop=\cnt@repeat\relax + \expandafter\firstoftwo\else\expandafter\secondoftwo + \fi + {% autant que de \xrepeat -> \detokenize pour afficher + "\detokenize\expandafter{\xiterate}"% + } + {\addtomacro\xiterate\xrepeat% sinon, ajouter ce \xrepeat + \advance\cnt@repeat by 1% incrémenter le compteutr de \xrepeat + \xloop@i% et chercher le prochain \xrepeat + }% +} +\let\xrepeat\fi +\catcode`\@12 +\xloop a\xloop b\xloop 12\xrepeat c\xrepeat \xloop X\xrepeat\xrepeat +****************** Fin code ****************** + + +****************** Code 194 ****************** +\catcode`\@11 +\newcount\cnt@repeat +\newcount\cnt@nested +\cnt@nested=0 % compteur d'imbrications +\def\xloop{% + \global\advance\cnt@nested by 1 % augmente le compteur d'imbrications + \expandafter\def\csname xiterate@\number\cnt@nested\endcsname{}% + \cnt@repeat\z@% initialise le compteur de \xrepeat à 0 + \xloop@i% aller à la macro \xloop@i +} +\long\def\xloop@i#1\xrepeat{% + \expandafter\addtomacro\csname xiterate@\number\cnt@nested\endcsname{#1}% + \expandafter\expandafter\expandafter\cnttimestocs\expandafter\expandafter\expandafter + {\csname xiterate@\number\cnt@nested\endcsname}\xloop\cnt@loop + \ifnum\cnt@loop=\cnt@repeat\relax + \expandafter\firstoftwo\else\expandafter\secondoftwo + \fi + {\expandafter\eaddtomacro\csname xiterate@\number\cnt@nested\expandafter\endcsname + {\expandafter\expandafter\csname xiterate@\number\cnt@nested\endcsname\fi}% + %\expandafter\show\csname xiterate@\number\cnt@nested\endcsname + \csname xiterate@\number\cnt@nested\endcsname + \letname{xiterate@\number\cnt@nested}\relax + \global\advance\cnt@nested by -1 + } + {\expandafter\addtomacro\csname xiterate@\number\cnt@nested\endcsname\xrepeat + \advance\cnt@repeat by 1 + \xloop@i + }% +}% +\let\xrepeat\fi +\catcode`\@12 + +\newcount\cntxx \cntxx=1 % compteur des lignes +\newcount\cntyy +\xloop + \cntyy=1 % compteur des colonnes + \xloop + (\number\cntxx,\number\cntyy)% affiche "(ligne,colonne)" + \ifnum\cntyy<5 % tant que colonne<5 + \advance\cntyy1 % incrémente colonne + , % <- affiche ", " + \xrepeat% et recommence + \ifnum\cntxx<3 % tant que ligne<3 + \advance\cntxx1 % incrémente ligne + \par % va à la ligne +\xrepeat% et recommence +****************** Fin code ****************** + + +****************** Code 195 ****************** +\catcode`\@11 +\def\end@foreach{\end@foreach}% définit un quark +\long\def\doforeach#1\in#2#3{% + \def\loop@code{#3}% assigne le <code> à \loop@code + % appel à \doforeach@i : mettre la macro #1 en premier, puis la liste de valeurs #2 + \doforeach@i#1#2,\end@foreach,% ajouter à la fin ",\end@foreach," + % une fois les boucles finies, neutraliser les macros déjà définies + \let#1\relax \let\loop@code\relax +} + +\long\def\doforeach@i#1#2,{% #1=\<macro> #2=valeur courante + \def#1{#2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach#1% si \end@foreach n'est pas atteint + \antefi% amène le \fi ici + \loop@code% exécute le code + \doforeach@i#1% et recommencer + \fi +} +\catcode`@12 +\doforeach\x\in{a,bcd,{efg},hi}{\meaning\x.\par} +****************** Fin code ****************** + + +****************** Code 196 ****************** +\catcode`\@11 +\def\end@foreach{\end@foreach} +\def\defseplist#1{% + \long\def\doforeach##1\in##2##3{% + \def\loop@code{##3}% assigne le <code> à \loop@code + \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i + % après être sorti des boucles, neutraliser les macros déjà définies + \let\loop@code\relax \let##1\relax + }% + \long\def\doforeach@i##1##2#1{% + \def##1{##2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach##1% si la fin n'est pas atteinte + \antefi% amène le \fi ici + \loop@code% exécute le code + \doforeach@i##1% et recommencer + \fi + }% +} +\defseplist{,}% définit les macros avec le séparateur par défaut +\catcode`@12 + +\doforeach\x\in{a,bcd,efg}{Argument courant : "\x".\par}\medbreak + +\defseplist{--}% séparateur "--" +\doforeach\nn\in{4,19--0,5--8,575}{Nombre lu : "\nn"\par} +****************** Fin code ****************** + + +****************** Code 197 ****************** +\catcode`\@11 +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication +\def\end@foreach{\end@foreach} + +\def\defseplist#1{% + \long\def\doforeach##1\in##2##3{% + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication + \defname{loop@code@\number\cnt@nest}{##3}% assigne le <code> à \loop@code@<n> + \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i + % une fois fini : + \let##1\empty% dans ce cas, neutraliser les macros déjà définies + \letname{loop@code@\number\cnt@nest}\empty% + \global\advance\cnt@nest-1 %sortie de boucle : décrémenter le compteur d'imbrication + }% + \long\def\doforeach@i##1##2#1{% + \def##1{##2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach##1% tant que la fin n'est pas atteinte + \antefi% amène le \fi ici + \csname loop@code@\number\cnt@nest\endcsname% exécute le code + \doforeach@i##1% et recommencer + \fi + }% +} +\catcode`@12 +\defseplist{,} + +\doforeach\xxx\in{1,2,3}{% + Ligne \xxx{} : \doforeach\yyy\in{a,b,c,d,e}{(\xxx,\yyy)}.\par +} +****************** Fin code ****************** + + +****************** Code 198 ****************** +\catcode`\@11 +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication +\def\end@foreach{\end@foreach} +\long\def\save@macro#1{% sauvegarde la macro #1 + \ifdefined#1% si la macro #1 est déjà définie... + \letname{saved@var@\number\cnt@nest}#1% ...la sauvegarder + \fi +} +\long\def\restore@macro#1{% restaure la macro #1 +% le \csname donne \relax si la sauvegarde n'a pas été faite + \expandafter\let\expandafter#1\csname saved@var@\number\cnt@nest\endcsname +} +\def\defseplist#1{% + \long\def\doforeach##1\in##2##3{% + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication + \save@macro##1% sauvegarde la macro ##1 + \defname{loop@code@\number\cnt@nest}{##3}% assigne le <code> à \loop@code@<n> + \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i + % une fois fini, neutraliser la macro contenant le <code> + \letname{loop@code@\number\cnt@nest}\empty + \restore@macro##1% restaurer la \<macro> + \global\advance\cnt@nest-1 % et décrémenter le compteur d'imbrication + }% + \long\def\doforeach@i##1##2#1{% + \def##1{##2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach##1% si la fin n'est pas atteinte + \antefi% amène le \fi ici + \csname loop@code@\number\cnt@nest\endcsname% exécute le code + \doforeach@i##1% et recommencer + \fi% + }% +} +\catcode`@12 +\defseplist{,} +\doforeach\par\in{a,b,c,y,z}{$\par_i$} + +\meaning\par% \par doit être redevenu une primitive + +\doforeach\xxx\in{1,2,3}{% + Ligne \xxx{} : \doforeach\yyy\in{a,b,c,d,e}{(\xxx,\yyy)}.\par +} +****************** Fin code ****************** + + +****************** Code 199 ****************** +\let\foo=\djfhdsldv% \foo est rendue \let-égale à une macro indéfinie +a) \meaning\foo\par +% puis \djfhdsldv est rendue \let-égale à \foo (ou \foo est construit avec \csname) +\expandafter\let\expandafter\djfhdsldv\csname foo\endcsname +b) \meaning\djfhdsldv +****************** Fin code ****************** + + +****************** Code 200 ****************** +\catcode`\@11 +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication +\def\end@foreach{\end@foreach} +\long\def\save@macro#1#2{\letname{saved@var@\number\cnt@nest#1}#2} +\long\def\save@macro@i#1/#2{\save@macro{a}#1\save@macro{b}#2} +\long\def\restore@macro#1#2{% + \expandafter\let\expandafter#2\csname saved@var@\number\cnt@nest#1\endcsname +} +\long\def\restore@macro@i#1/#2{\restore@macro{a}#1\restore@macro{b}#2} +\def\defseplist#1{% + \long\def\doforeach##1\in##2##3{% + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication + \global\let\allow@recurse\identity% permet l'appel récursif plus bas + \ifin{##1}{/}% si ##1 contient "/" + {\save@macro@i##1% sauvegarde les macros + \doforeach@ii% appeler la macro récursive avec les arguments + {##3}% 1) code à exécuter + ##1% 2) variables sous la forme \<macro1>/\<macro2> + ##2#1\end@foreach/\end@foreach#1% puis la liste ##2 suivie de + % ",\end@foreach/\end@foreach," + \restore@macro@i##1% une fois sorti de toutes les boucles, restaurer les macros + }% si ##1 ne contient pas "/" + {\save@macro{}##1% sauvegarde la macro + \doforeach@i% appeler la macro récursive + {##3}% mettre en premier le <code> + ##1% puis la variable ##1 en 2e position + ##2#1\end@foreach#1% enfin la liste ##2 suivie de ",\end@foreach," + \restore@macro{}##1% une fois sorti de toutes les boucles, restaurer la macro + }% + \global\advance\cnt@nest-1 % décrémente le compteur d'imbrications + }% + % ##1 = code à exécuter, ##2= variable, ##3=valeur courante + \long\def\doforeach@i##1##2##3#1{% + \ifx\end@foreach##3% si la fin est atteinte + \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: + {\def##2{##3}% fait l'assignation à la variable + ##1% le code puis + \allow@recurse{\doforeach@i{##1}##2}% recommencer + }% + }% + % ##1 = code à exécuter, ##2/##3= variables, ##4/##5=valeurs courantes + \long\def\doforeach@ii##1##2/##3##4/##5#1{% + \ifx\end@foreach##4% si la fin est atteinte + \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: + {\def##2{##4}\def##3{##5}% fait l'assignation des deux variables + ##1% le code puis + \allow@recurse{\doforeach@ii{##1}##2/##3}% recommencer + }% + }% + % macro qui, si elle remplace \allow@recurse, annule l'appel récursif + \long\def\forbid@recurse##1\end@foreach#1{}% tout manger jusqu'à "\end@foreach," +} +\def\doforeachexit{\global\let\allow@recurse\forbid@recurse} +\catcode`\@12 +\defseplist{,} +\doforeach\par\in{a,b,c,y,z}{$\par_i$}\medskip + +\doforeach\sujet/\terminaison\in{Je/e,Tu/es,Il/e,Nous/ons,Vous/ez,Ils/ent} + {\sujet\ programm\terminaison{} en \TeX\par}\medskip + +Les voyelles lues sont : +\doforeach\ii\in{a,e,i,o,u,y}{\ii\if o\ii\doforeachexit\fi}.\medskip + +\doforeach\xx\in{a,b,c,d,e,f} + {\doforeach\ii\in{1,2,3,4}{\xx\ii{} \ifnum\ii=3 \doforeachexit\fi}\par} +****************** Fin code ****************** + + +****************** Code 201 ****************** +\newdimen\dimA \newdimen\dimB% alloue deux registres de dimension +a) \dimA=59.5pt + \the\dimA\qquad% doit afficher 59.5pt +b) \dimA=1.5cm + \the\dimA\qquad% convertit à l'affichage 1.5cm en pt +c) \dimB=7pt % assigne 7pt à \dimB + \dimA\dimB% rend \dimA égal à dimB + \the\dimA\qquad +d) \dimA=6\dimB% rend \dimA égal à 6 fois \dimB + \the\dimA +****************** Fin code ****************** + + +****************** Code 202 ****************** +\newdimen\foo +\foo=25pt % assigne 25pt au registre \foo +\divide\foo 4 % le multiplie par 4 +\advance\foo1.72pt % lui ajoute 1.72pt +\multiply\foo3 % le multiplie par 3 +\the\foo% affiche la dimension obtenue +****************** Fin code ****************** + + +****************** Code 203 ****************** +\begingroup% ouvrir un groupe + \edef\temp{\endgroup\def\noexpand\removept##1.##2\string p\string t}% +\temp{#1\ifnum#2>0 .#2\fi}% et le fermer avant de définir \removept +\newdimen\foo +a) \foo=15pt \expandafter\removept\the\foo\qquad +b) \foo=3.14pt \expandafter\removept\the\foo +****************** Fin code ****************** + + +****************** Code 204 ****************** +\def\dimtodec{\expandafter\removept\the} +\newdimen\foo +a) \foo=15pt \dimtodec\foo \qquad +b) \foo=3.14pt \dimtodec\foo +****************** Fin code ****************** + + +****************** Code 205 ****************** +a) \the\dimexpr 1cm + 0.5cm\relax \qquad +b) \the\dimexpr 1pt + 1pt\relax \qquad +c) \the\dimexpr 1pt + 2pt * 3\relax\qquad +d) \the\dimexpr (1pt + 2pt) * 3\relax\qquad +e) \the\dimexpr (1.2pt + 0.8pt) * 5\relax\qquad +f) \newdimen\foo \foo=15pt + \the\dimexpr\foo-(\foo + 1pt) / 4\relax +****************** Fin code ****************** + + +****************** Code 206 ****************** +\the\dimexpr0,7pt + 0.4pt\relax +****************** Fin code ****************** + + +****************** Code 207 ****************** +a) \newdimen\foodim \foodim=1cm \number\foodim\relax\qquad +b) \number\dimexpr 0.7pt + 0.4pt\relax\qquad +c) \number\dimexpr 1.1pt\relax +****************** Fin code ****************** + + +****************** Code 208 ****************** +\catcode`\@11 +\def\FOR#1=#2to#3\do#4#{% + \ifempty{#4} + {\let\FOR@increment\z@} + {\edef\FOR@increment{\the\dimexpr#4pt\relax}}% lit et normalise l'argument optionnel + \ifdim\FOR@increment=\z@% s'il est nul, + \edef\FOR@increment{% le redéfinir à -1pt (si #3<#2) et 1pt sinon + \ifdim\dimexpr#3pt-#2pt\relax<\z@ -1\else 1\fi pt + }% \FOR@increment vaut donc 1 ou -1 + \fi + \ifdim\dimtodec\dimexpr#3pt-#2pt\relax\dimexpr\FOR@increment\relax<\z@ + \expandafter\gobone% si argument optionnel incompatible, manger le {<code>} + \else + \edef#1{\dimtodec\dimexpr#2pt\relax}% initialise la \<macro> + \edef\macro@args{% définit et développe les arguments à passer à \FOR@i + %#1=nom de la macro récursive : + \expandafter\noexpand\csname FOR@ii@\string#1\endcsname + \ifdim\FOR@increment<\z@ <\else >\fi% #2=signe de comparaison + {\FOR@increment}% #3=incrément + \noexpand#1% #4=\<macro> + {\the\dimexpr#3pt\relax}% #5=dimension n2 + }% + \antefi% externalise la ligne ci-dessous de la portée du test + \expandafter\FOR@i\macro@args% appelle \FOR@i avec les arguments définis ci-dessus + \fi +} + +% #1=nom de la macro récursive de type "\FOR@ii@\<macro>" +% #2=signe de comparaison % #3=incrément +% #4=\<macro> % #5=dimension n2 % #6=<code> à exécuter +\long\def\FOR@i#1#2#3#4#5#6{% + \def#1{% définit la sous macro récursive + \unless\ifdim#4pt#2#5\relax% tant que la \<macro> variable n'a pas dépassé n2 + \afterfi{% rendre la récursivité terminale + #6% exécute le code + \edef#4{\dimtodec\dimexpr#4pt+#3\relax}% incrémente la \<macro> + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive +}% +\def\exitFOR#1{% #1=\<macro> correspondant à la boucle de laquelle on veut sortir + \defname{FOR@ii@\string#1}{}% +} + +\def\ifexitFOR#1{% envoie vrai si on est prématurément sorti de la boucle de \<macro> #1 + % si la macro récursive est \empty + \expandafter\ifx\csname FOR@ii@\string#1\endcsname\empty + \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai" + \else + \expandafter\secondoftwo% sinon, renvoyer "faux" + \fi +} +\catcode`\@=12 +a) \FOR\xx=1.5 to -5\do-1{"\xx" }\par + +b) \FOR\ii=1 to 2.742\do.25{"\ii" }\par + +c) \FOR\ii=0 to 1\do0.1{"\ii" \ifdim\ii pt>0.5pt \exitFOR\ii\fi} +****************** Fin code ****************** + + +****************** Code 209 ****************** +\newdimen\foo +\foo=15.2pt \foo=1.5\foo \the\foo% vaut 15.2*1.5 +****************** Fin code ****************** + + +****************** Code 210 ****************** +\catcode`\@11 +\newdimen\dim@a +\def\decmul#1#2{% + \dim@a=#2pt % range la dimension #2pt dans le registre de dimension + \dim@a=#1\dim@a% multiplier le registre par le décimal #1 + \dimtodec\dim@a% convertir la dimension en décimal +} +\catcode`\@12 +a) \decmul{15.2}{1.5}\qquad +b) \decmul{48.2}{.375} +****************** Fin code ****************** + + +****************** Code 211 ****************** +\def\decmul#1#2{\dimtodec\dimexpr#1\dimexpr#2pt\relax\relax} + +a) \decmul{15.2}{1.5}\qquad +b) \edef\foo{\decmul{48.2}{.375}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 212 ****************** +\def\decdiv#1#2{% divise le décimal #1 par le décimal #2 + \dimtodec + \dimexpr + \numexpr + \dimexpr #1pt \relax * 65536 / \dimexpr #2pt \relax + \relax + sp + \relax +} + +1) \decdiv{4.5}{0.075}\qquad% doit donner 60 +2) \decdiv{8}{0.1}\qquad% doit donner 80 +3) \decdiv{3.14}{1.6}\qquad% doit donner 1.9625 +4) \decdiv{687.59829}{5.29871}\qquad% doit donner 129.76706 +4) \edef\foo{\decdiv{0.37}{2.5}}\foo% doit donner 0.148 +****************** Fin code ****************** + + +****************** Code 213 ****************** +\def\convertunit#1#2{% + \dimtodec + \dimexpr + \numexpr + \dimexpr #1 \relax * 65536 / \dimexpr 1#2 \relax + \relax + sp + \relax +} + +a) \convertunit{15cm}{mm}\qquad +b) \convertunit{9,14in}{cm}\qquad +c) \convertunit{100000sp}{mm}\qquad +d) \convertunit{160.5pt}{cm}\qquad +e) \edef\foo{\convertunit{2,5cm}{cc}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 214 ****************** +\the\glueexpr 5pt plus 2pt minus 1.5pt + 7pt plus0.5pt minus 3pt\relax \par +\the\glueexpr 25pt plus2fil + 35pt plus 0.1fill\relax +****************** Fin code ****************** + + +****************** Code 215 ****************** +foo% les caractères font entrer TeX en mode horizontal +\kern0.5cm % espace insérée en mode horizontal +bar\par% \par fait passer en mode vertical +\kern0.5cm % espace insérée en mode vertical +boo +****************** Fin code ****************** + + +****************** Code 216 ****************** +Une première ligne\par +\nointerlineskip% n'insère pas de ressort d'interligne ici +Un second paragraphe constitué de plusieurs lignes. +Un second paragraphe constitué de plusieurs lignes. +Un second paragraphe constitué de plusieurs lignes. +\par% le ressort d'interligne sera inséré ici +Une dernière ligne +****************** Fin code ****************** + + +****************** Code 217 ****************** +\begingroup +\offinterlineskip +La macro \litterate-\offinterlineskip-, en modifiant de façon appropriée les trois +primitives \litterate-\baselineskip-, \litterate-\lineskip- et +\litterate-\lineskiplimit-, rend les boites consécutives jointives. + +On peut constater dans ces deux paragraphes où \litterate-\offinterlineskip- a été +appelée, que les lignes sont placées verticalement au plus près les unes des autres ce +qui rend la lecture très pénible et démontre que le ressort d'interligne est une +nécessité typographique !\par +\endgroup +Désactiver le ressort d'interligne ne se justifie que lorsque l'on doit composer +des boites contenant autre chose que du texte, sauf à vouloir des effets +typographiques spéciaux. +****************** Fin code ****************** + + +****************** Code 218 ****************** +\baselineskip=12pt +début \vtop{\hbox{ligne du haut ligne du haut ligne du haut} +\hbox{ligne du bas ligne du bas ligne du bas} +} +\par +Et la suite du texte suite du texte suite du texte +****************** Fin code ****************** + + +****************** Code 219 ****************** +\baselineskip=12pt +début \vtop{\hbox{ligne du haut ligne du haut ligne du haut} +\hbox{ligne du bas ligne du bas ligne du bas} +\xdef\sprevdepth{\the\prevdepth}% sauvegarde la valeur de \prevdepth +} +\par\prevdepth=\sprevdepth\relax% ment sur la boite précédente +Et la suite du texte suite du texte suite du texte +****************** Fin code ****************** + + +****************** Code 220 ****************** +\def\dummytext{Voici un texte qui ne revêt aucune importance + et dont le seul but est de meubler artificiellement un paragraphe. } +\begingroup% à l'intérieur d'un groupe, + \leftskip=0pt plus 0.5\hsize\relax + \rightskip=\leftskip\relax % modifier les ressort d'extrémités + \spaceskip=0.3em plus 0.05em\relax% dimension de l'espace intermot + \parfillskip=0pt \relax% annuler le ressort de fin de paragraphe + \dummytext\dummytext\dummytext% corps du paragraphe + \par% compose la paragraphe courant + Juste une phrase% + \par% compose la paragraphe courant +\endgroup% avant de sortir du groupe +****************** Fin code ****************** + + +****************** Code 221 ****************** +\def\narrowtext{% + \par + \begingroup + \advance\leftskip 0.1\hsize + \advance\rightskip 0.1\hsize +} +\def\endnarrowtext{\par\endgroup} +\def\dummytext{Ceci est un texte sans + importance destiné à meubler un paragraphe. } + +\dummytext\dummytext\dummytext +\narrowtext + \dummytext\dummytext\dummytext + \narrowtext + \dummytext\dummytext\dummytext + \endnarrowtext + \dummytext\dummytext\dummytext +\endnarrowtext +****************** Fin code ****************** + + +****************** Code 222 ****************** +a) \hfill Composition au fer à droite\par +b) Composition au fer à gauche\hfill\kern0pt\par +c) \hfill Centrage\hfill\kern0pt\par +d) Des \hfill mots\hfill régulièrement\hfill espacés +****************** Fin code ****************** + + +****************** Code 223 ****************** +a% le caractère "a" fait passer en mode horizontal +\hbox{b}c\hbox{d}% les \hbox sont ajoutées en mode horizontal +\medbreak +...à comparer avec... +\medbreak +a \par% \par fait passer en mode vertical +\hbox{b}% \hbox est ajoutée à la liste verticale +c% "c" fait passer en mode horizontal +\hbox{d}% la \hbox est ajoutée en mode horizontal +****************** Fin code ****************** + + +****************** Code 224 ****************** +Ligne de base : .........% +\vbox{\hbox{ligne du haut}\hbox{ligne du milieu}\hbox{ligne du bas}}% +.........% +\vtop{\hbox{ligne du haut}\hbox{ligne du milieu}\hbox{ligne du bas}}......... +****************** Fin code ****************** + + +****************** Code 225 ****************** +\def\dummytext{Bla, bla, bla, un texte sans importance. } +Ligne de base.........% +\vbox{\hsize=4cm \dummytext\dummytext}% +.........% +\vtop{\hsize=4cm \dummytext\dummytext}......... +****************** Fin code ****************** + + +****************** Code 226 ****************** +\newbox\foobox +a) \setbox\foobox=\hbox{foobar}% registre non vide + Le registre est \ifvoid\foobox vide\else non vide\fi\par +b) \setbox\foobox=\hbox{foobar} + "\box\foobox" et le registre est \ifvoid\foobox vide\else non vide\fi\par +c) \setbox\foobox=\hbox{foobar} + "\copy\foobox" et le registre est \ifvoid\foobox vide\else non vide\fi\par +d) \setbox\foobox=\hbox{} + Le registre est \ifvoid\foobox vide\else non vide\fi +****************** Fin code ****************** + + +****************** Code 227 ****************** +\newbox\foobox +\setbox\foobox=\hbox{Programmer est facile.} +<<\copy\foobox>> +mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. +\medbreak + +\setbox\foobox=\vbox{\hbox{Programmer}\hbox{est}\hbox{facile.}} +<<\copy\foobox>> +mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. +\medbreak + +\setbox\foobox=\vtop{\hbox{Programmer}\hbox{est}\hbox{facile.}} +<<\copy\foobox>> +mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. +****************** Fin code ****************** + + +****************** Code 228 ****************** +\def\vdim#1{\dimexpr\ht#1+\dp#1\relax} +a) \setbox\foobox=\vbox{\hbox{Programmer}\hbox{est}\hbox{facile.}} + Verticalité de la \litterate-\vbox- = \the\vdim\foobox\par +b) \setbox\foobox=\vtop{\hbox{Programmer}\hbox{est}\hbox{facile.}} + Verticalité de la \litterate-\vtop- = \the\vdim\foobox +****************** Fin code ****************** + + +****************** Code 229 ****************** +\def\countallchar#1{% + Il y a % + \setbox0=\hbox{\tt#1}% met #1 dans la boite + \edef\arglength{\number\wd0 }% stocke la largeur de la boite en sp + \setbox0=\hbox{\tt A}% met "A" dans la boite + \edef\charlength{\number\wd0 }% stocke la largeur d'un caractère + $\number\arglength/\charlength % affiche la division + =\number\numexpr\arglength/\charlength\relax$ % affiche le quotient + caractères% +} +\countallchar{abcd efgh}\par +\countallchar{A5 xW5 64 a1}\par +\countallchar{affligeant} +****************** Fin code ****************** + + +****************** Code 230 ****************** +\catcode`@11 +\def\countchar#1#2{% + \setbox\z@\hbox{\tt#2}% met #2 dans boite 0 + \edef\len@a{\number\wd\z@}% mesure la boite + \setbox\z@\hbox{\tt\substin{#2}{#1}{}}% recommencer sans "#1" + \edef\len@b{\number\wd\z@}% mesure la boite + \setbox\z@\hbox{\tt A}% met "A" dans la boite + \edef\charlength{\number\wd\z@}% stocke la largeur du caractère + \number\numexpr(\len@a-\len@b)/\charlength% et affiche le quotient +} +\catcode`@12 +a) \countchar{a}{abracadabra}\qquad +b) \countchar{b}{zigzag}\qquad +c) \countchar{ }{a bc de f ghi j k } +****************** Fin code ****************** + + +****************** Code 231 ****************** +Programmer \raise1ex\hbox{en} \lower1ex\hbox{\TeX} \lower2ex\hbox{est} facile. +****************** Fin code ****************** + + +****************** Code 232 ****************** +\def\cbox#1{% + \setbox0\vbox{#1}% met le contenu dans une \vbox + \lower\dimexpr(\ht0-\dp0)/2\relax\box0 % l'abaisse +} + +......\cbox{\hbox{$x^2$}}......\cbox{\hbox{foo}\hbox{et bar}}......% +\cbox{\hbox{Programmer}\hbox{en \TeX}\hbox{est facile}}....... +****************** Fin code ****************** + + +****************** Code 233 ****************** +......\cbox{\hbox{foo}\hbox{et bar}}......$\vcenter{\hbox{foo}\hbox{et bar}}$...... +****************** Fin code ****************** + + +****************** Code 234 ****************** +\def\htmath{\begingroup + \setbox0=\hbox{$\vcenter{}$}\the\ht0 +\endgroup +} +L'axe mathématique se trouve à \htmath{} de la ligne de base. +****************** Fin code ****************** + + +****************** Code 235 ****************** +1) \hbox{foobar}\par +2) \hbox spread 5pt{foo\hfil bar}\par +3) \hbox spread10pt{foo\hfil bar} +****************** Fin code ****************** + + +****************** Code 236 ****************** +foobar|\rlap{/////}123456\qquad foobar|\llap{/////}123456 +****************** Fin code ****************** + + +****************** Code 237 ****************** +\def\clap#1{\hbox to0pt{\hss#1\hss}} +a) avant la macro|\clap{SURIMPRESSION}après la macro\medbreak +b) avant la macro|\raise2.5ex\clap{Au-dessus}\lower2.5ex\clap{Au-dessous}% + après la macro\medbreak +c) avant la macro|\raise2.5ex\llap{Au-dessus avant}\lower2.5ex\rlap{Au-dessous après}% + après la macro +****************** Fin code ****************** + + +****************** Code 238 ****************** +\setbox0=\hbox{\tt//////////} +\wd0=0pt % fait croire à TeX que la larguer de la boite est nulle +Voici \copy0 du texte partiellement barré... +****************** Fin code ****************** + + +****************** Code 239 ****************** +\def\printdim{largeur=\the\wd0 \qquad hauteur=\the\ht0 \qquad profondeur = \the\dp0 } +\setbox0=\hbox{Programmer en \TeX{} est facile} +a) \printdim\par +b) \wd0=0pt \ht0=0pt \dp0=0pt% rend toutes le dimensions nulles + \printdim\par +c) \setbox0=\hbox{\unhbox0 }% reprend les dimensions d'origine + \printdim +****************** Fin code ****************** + + +****************** Code 240 ****************** +\setbox0=\hbox{}% le registre 0 contient une boite vide +Le registre \ifvoid0 est vide\else n'est pas vide\fi +****************** Fin code ****************** + + +****************** Code 241 ****************** +\catcode`\@11 +\def\ifzerodimbox#1{% #1=registre de boite +% revoie vrai si le registre est vide ou contient une boite de dimensions nulles + \csname% former la macro "\firstoftwo" ou "\secondoftwo" + \ifvoid#1first%% si le registre est vide "first" + \else% sinon + \ifdim\wd#1=\z@% si la largeur + \ifdim\ht#1=\z@% la hauteur + \ifdim\dp#1=\z@ first% et la profondeur=0pt, "first" + \else second% dans les autres cas "second" + \fi + \else second% + \fi + \else second% + \fi + \fi + oftwo% compléter avec "oftwo" + \endcsname +} +\catcode`\@12 +a) \setbox0=\hbox{}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +b) \box0 % affiche la boite vide, le registre est maintenant "void" + \ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +c) \setbox0=\hbox{x}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +d) \wd0=0pt \ht0=0pt \dp0=0pt % rend toutes les dimensions nulles + \ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +e) \setbox0=\vbox{}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi) +****************** Fin code ****************** + + +****************** Code 242 ****************** +\def\ifvoidorempty#1{% teste si le registre #1 est vide ou contient une boite vide + \ifvoid#1\relax + \expandafter\firstoftwo + \else + \begingroup% dans un groupe + \setbox0=% affecter à la boite 0 + \ifhbox#1\hbox\bgroup\unhcopy% un boite horizontale + \else \vbox\bgroup\unvcopy% ou verticale + \fi% dans laquelle on compose + #1\relax% #1 en dimensions naturelles + \expandafter\egroup% sauter la fin de la boite + \expandafter% et le \endgroup + \endgroup + \ifnum\lastnodetype=-1 % et tester si le dernier noeud est vide + \expandafter\expandafter\expandafter\firstoftwo + \else + \expandafter\expandafter\expandafter\secondoftwo + \fi + \fi +} +a) \setbox0=\hbox{}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +b) \box0 % affiche la boite vide, le registre est maintenant "void" + \ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +c) \setbox0=\hbox{x}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +d) \wd0=0pt \ht0=0pt \dp0=0pt % rend toutes les dimensions nulles + \ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +e) \setbox0=\vbox{}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi) +****************** Fin code ****************** + + +****************** Code 243 ****************** +\newdimen\hmaxsize +\def\cvtop#1{% + \hmaxsize=-\maxdimen% initialise à la plus petite longueur + \doforeach\htext\in{#1}% pour chaque élément : + {\setbox0=\hbox{\htext}% stocker l'élément "\htext" dans une \hbox + \ifdim\wd0 >\hmaxsize% si sa longueur est supérieure à \hmaxsize + \hmaxsize=\wd0 % mettre à jour \hmaxsize + \fi + }% + \vtop{% dans une \vtop... + \doforeach\htext\in{#1}% pour chaque élément : + {\hbox to\hmaxsize{\hss\htext\hss}% le centrer dans une \hbox de longueur \hmaxsize + }% + }% +} + +texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% +...texte après +****************** Fin code ****************** + + +****************** Code 244 ****************** +\newdimen\hmaxsize +\def\cvtop#1{% + \hmaxsize=-\maxdimen% initialise à la plus petite longueur + \doforeach\htext\in{#1}% pour chaque élément : + {\setbox0=\hbox{\htext}% stocker l'élément "\htext" dans une \hbox + \ifdim\wd0 >\hmaxsize% si sa longueur est supérieure à \hmaxsize + \hmaxsize=\wd0 % mettre à jour \hmaxsize + \fi + }% + \vtop{% dans une \vtop... + \hsize\hmaxsize % longueur de la \vtop = \maxhsize + \parindent=0pt % pas d'indentation + \leftskip=0pt plus1fil minus0pt \rightskip=\leftskip% ressorts de centrage + \doforeach\htext\in{#1}% pour chaque élément : + {\htext\par}% le composer et finir le paragraphe + }% +} + +texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% +...texte après +****************** Fin code ****************** + + +****************** Code 245 ****************** +\halign{X#**\ignorespaces&#&\hfil #\cr % préambule +foo &foo bar&123 456\cr % première ligne +deuxième ligne &1 &12\cr % deuxième ligne +a &\TeX &1 2 3 4 5 6\cr % troisième ligne +\crcr} +****************** Fin code ****************** + + +****************** Code 246 ****************** +\def\cvtop#1{% + \vtop{% \vtop assure que l'on est en mode vertical + \substtocs\temp{#1}{,}{\cr}% dans \temp, remplacer les "," par "\cr" + \halign{% + \hfil##\hfil\cr% préambule : centrer le contenu + \temp\crcr}% mettre \temp dans l'alignement + }% \temp est détruite en sortant de la boite +} + +texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% +...texte après +****************** Fin code ****************** + + +****************** Code 247 ****************** +\def\calcmaxdim#1#2{% + \setbox0=\vtop{% \vtop assure que l'on est en mode vertical (interne) + \substtocs\temp{#2}{,}{\cr}% dans \temp, remplacer les "," par "\cr" + \halign{##\hfil\cr% préambule : composer au fer à gauche + \temp\crcr}% mettre \temp dans l'alignement + }% \temp est détruite en sortant de la boite + \edef#1{\the\wd0 }% +} +a) La plus grande longueur vaut + \calcmaxdim\foo{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}\foo + +b) Vérification : \setbox0=\hbox{Très grande ligne du milieu}\the\wd0 +****************** Fin code ****************** + + +****************** Code 248 ****************** +a) .......\vbox{\hrule\hbox{foo}\hbox{ligne du bas}\hrule}.......\medbreak +b) .......\vrule\vtop{\hbox{foo}\hbox{ligne du bas}}\vrule....... +****************** Fin code ****************** + + +****************** Code 249 ****************** +Une réglure de 1.5cm :\hrule width1.5cm +foo\vrule width 2pt height .5cm depth .2cm bar +****************** Fin code ****************** + + +****************** Code 250 ****************** +\def\showdim#1{% + \vrule width 0.4pt height 1ex depth 0pt % trait vertical gauche + \vrule width #1 height0.4pt depth 0pt % réglure horizontale de longueur #1 + \vrule width 0.4pt height 1ex depth 0pt % trait vertical droit + \relax% stoppe la lecture de la précédente dimension +} + +a) une longueur de 1 cm : \showdim{1cm}\par +b) une longueur de 137,4 pt : \showdim{137,4pt}\par +c) une longueur de 2 mm : \showdim{2mm} +****************** Fin code ****************** + + +****************** Code 251 ****************** + +\frboxsep=5pt \frboxrule=1pt +\leavevmode +...% +\vbox{% + \hrule height\frboxrule% réglure supérieure + \kern\frboxsep% espace verticale haute + \hbox{\kern\frboxsep Programmation\kern\frboxsep}% contenu + espaces horizontales + \kern\frboxsep% espace verticale basse + \hrule height\frboxrule% réglure inférieure + }% +... +****************** Fin code ****************** + + +****************** Code 252 ****************** + +\frboxsep=5pt \frboxrule=0.6pt +\def\FRbox#1{% /!\ ne change pas le mode H ou V en cours + \hbox{% mettre à la suite horizontalement les 3 choses suivantes : + \vrule width\frboxrule% 1) réglure gauche + \vbox{% 2) un empilement vertical comprenant + \hrule height\frboxrule% a) réglure supérieure + \kern\frboxsep% b) espace verticale haute + \hbox{% c) contenu + espaces en mode H + \kern\frboxsep#1\kern\frboxsep + }% + \kern\frboxsep% d) espace verticale basse + \hrule height\frboxrule% e)réglure inférieure + }% + \vrule width\frboxrule% 3) réglure droite + }% +} +Ligne de base : ...\FRbox{Programmation}...% +\frboxrule=2pt \FRbox{Programmation}...% +\frboxsep=0pt \FRbox{Programmation}...% +\frboxrule0.4pt \FRbox{Programmation}... +****************** Fin code ****************** + + +****************** Code 253 ****************** +Persuadez-vous que : +\vtop{ + \vbox{ + \hbox{Programmer} + \hbox{en} + \hbox{\TeX} + \hbox{est}% <- ligne de base de la \vtop + } + \hbox{\it tout sauf} + \hbox{facile.} +} +****************** Fin code ****************** + + +****************** Code 254 ****************** +%\newdimen\frboxrule \newdimen\frboxsep +\frboxrule=0.4pt \frboxsep=2pt +\def\frbox#1{% ne pas changer le mode H ou V en cours + \hbox{% enferme dans une \hbox + \vrule width\frboxrule% réglure gauche + \vtop{% + \vbox{% 1er élément de la \vtop + \hrule height\frboxrule% réglure supérieure + \kern\frboxsep% espace haut + \hbox{% + \kern\frboxsep% espace gauche + #1% contenu + \kern\frboxsep% espace droite + }% + }% puis autres éléments de la \vtop, sous la ligne de base + \kern\frboxsep% espace bas + \hrule height\frboxrule% réglure inférieure + }% + \vrule width\frboxrule% réglure droite + }% +} +Ligne de base : ......\frbox{Programmation}......% +\frboxrule=2pt \frbox{Programmation}......% +\frboxsep=0pt \frbox{Programmation}......% +\frboxrule0.4pt \frbox{Programmation}...... +****************** Fin code ****************** + + +****************** Code 255 ****************** +\def\centretitre#1{% + \medbreak% passe en mode v puis saute une espace verticale + \noindent% pas d'indentation et passe en mode horizontal + \frbox{% encadre + \vbox{% une boite verticale + \hsize=\dimexpr\hsize-2\frboxrule-2\frboxsep\relax + \parindent=0pt % pas d'indentation + \leftskip=0pt plus1fil \rightskip=\leftskip% ressorts de centrage + \parfillskip=0pt % annule le ressort de fin de paragraphe + #1% insère le titre + \endgraf% et le compose + }% + }% + \medbreak% passe en mode v puis saute une espace verticale + \ignorespaces% mange les espaces situés après la macro \centretitre +} +\frboxrule=0.8pt \frboxsep=5pt +Voici un +\centretitre{Titre} +puis un +\centretitre{Texte très long pour composer un titre qui va prendre plusieurs +lignes et pour s'assurer que la composition s'effectue correctement} +****************** Fin code ****************** + + +****************** Code 256 ****************** +Une réglure en hauteur : \vrule width 1cm height 10.4pt depth -10pt +****************** Fin code ****************** + + +****************** Code 257 ****************** +Une réglure en dessous: \vrule width 1cm depth 2.4pt height -2pt +****************** Fin code ****************** + + +****************** Code 258 ****************** +\def\souligne#1{% + \setbox0=\hbox{#1}% stocke le contenu dans le registre no 0 + \setbox0=\hbox{% puis, dans une \hbox, construit une réglure + \vrule width\wd0 % de la longueur du contenu + depth\dimexpr\dp0 + 1.4pt\relax % dp = profondeur texte + 1.4pt + height\dimexpr-\dp0 - 1pt\relax % ht = -profondeux texte - 1pt + }% + \wd0=0pt \dp0=0pt \ht0=0pt % annule toutes les dimensions + \leavevmode \box0 % affiche la réglure + #1% puis le contenu +} +Voici \souligne{du texte normal}.\par +Voici \souligne{du texte profond}. +****************** Fin code ****************** + + +****************** Code 259 ****************** +\def\Souligne#1{% + \setbox0=\hbox{#1}% + \setbox0=\hbox{\vrule width\wd0 depth1.4pt height-1pt }% + \wd0=0pt \dp0=0pt \ht0=0pt + \leavevmode \box0 #1% +} +Voici \Souligne{du texte normal}.\par +Voici \Souligne{du texte profond}. +****************** Fin code ****************** + + +****************** Code 260 ****************** +\def\Souligne#1{% + \setbox0=\hbox{#1}% + \lower 1pt % abaisser à 1pt sous la ligne de base + \rlap{% une \hbox en surimpression vers la droite + \vrule width\wd0 height0pt depth0.4pt % contenant le soulignement + }% + #1% puis afficher le <texte> +} +Voici \Souligne{du texte normal}.\par +Voici \Souligne{du texte profond}. +****************** Fin code ****************** + + +****************** Code 261 ****************** +\newdimen\stackwd \stackwd=3em % dimension horizontale interne des cadres +\catcode`@11 +\def\stackbox#1{% + \par% termine le paragraphe en cours + \noindent + \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i + \par +} + +\def\stackbox@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin + \hfill % ressort infini de centrage (et fait passer en mode horizontal) + \noindent + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... + {\frbox{% ...encadrer + \hbox to\stackwd{% une \hbox de largeur \stackwd + \hss% ressort de centrage + \current@item% l'élément courant + \hss% ressort de centrage + } + }% fin de la \frbox + }% fin \doforeach + \hfill% ressort infini de centrage + \null% assure que le dernier ressort est pris en compte + \par% finir le paragraphe + \nobreak% interdire une coupure de page + \nointerlineskip% ne pas insérer le ressort d'interligne + \expandafter\stackbox@i% et recommencer + \fi +} +\catcode`@12 + +\frboxrule=0.5pt \frboxsep=3pt +\stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,} +****************** Fin code ****************** + + +****************** Code 262 ****************** +a\vrule width0.2pt height15pt depth0pt \quad +a\vrule width0.2pt height0pt depth5pt \quad +a\vrule width0.2pt height10pt depth10pt \quad +a\vrule width1cm height0.2pt depth0pt +****************** Fin code ****************** + + +****************** Code 263 ****************** +\frboxsep0pt %encadrement au plus proche +\leavevmode +\frbox{a\vrule width0pt height15pt depth0pt }\quad +\frbox{a\vrule width0pt height0pt depth5pt }\quad +\frbox{a\vrule width0pt height10pt depth10pt }\quad +\frbox{a\vrule width1cm height0pt depth0pt } +****************** Fin code ****************** + + +****************** Code 264 ****************** +\def\rectangle#1#2{% + \begingroup% dans un groupe + \frboxsep = 0pt % encadrer au plus proche + \frboxrule= 0.4pt % en traits assez fins + \frbox{% + \vrule width#1 height0pt depth0pt %strut horizontal + \vrule width0pt height#2 depth0pt %strut vertical + }% + \endgroup% fermer le groupe +} +Carré de 0.5 cm : \rectangle{0.5cm}{0.5cm}\smallskip + +Rectangle de 2.5cm par 3pt : \rectangle{2.5cm}{3pt} +****************** Fin code ****************** + + +****************** Code 265 ****************** +\newdimen\stackwd \stackwd=3em +\catcode`\@11 +\def\stackbox#1{% + \par% termine le paragraphe en cours + \begingroup% dans un groupe semi-simple + \parindent=0pt% pas d'indentation + \parskip=0pt% annuler le \parskip + \setbox0\hbox{Àgjp}% boite pour le strut + \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut + \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i + \unkern% annule la dernière compensation verticale + \par + \endgroup +} +\def\stackbox@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin + \hfill % ressort infini de centrage (passe en mode horizontal) + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... + {\frbox{% ...encadrer + \hbox to\stackwd{% une \hbox de largeur \stackwd contenant + \hss% 1) ressort de centrage + \stack@strut% 2) strut de dimension verticale + \current@item%3) l'élement courant + \hss}% 4)ressort de centrage + }% fin de la \fbox + \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales + }% fin de \doforeach + \unkern% annuler la dernière compensation horizontale + \hfill% ressort infini de centrage + \null% fait prendre en compte le dernier ressort + \par% termine le paragraphe + \nobreak% interdit une coupure de page + \nointerlineskip% sinon, ne pas ajouter le ressort d'interligne + \kern-\frboxrule% superposer les réglures horizontales + \expandafter\stackbox@i% et recommencer + \fi +} +\frboxrule=0.5pt +\frboxsep=3pt +\stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,} +****************** Fin code ****************** + + +****************** Code 266 ****************** +\catcode`\@11 +\def\lineboxed#1{% + \par% termine le paragraphe en cours + \begingroup% dans un groupe semi-simple + \parindent=0pt% pas d'indentation + \parskip=0pt% annuler le \parskip + \setbox0\hbox{Àgjp}% boite pour le strut + \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut + \lineboxed@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle la macro récursive + \unkern% annule la dernière compensation verticale + \par + \endgroup +} +\def\lineboxed@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin + \cnttimestocs{#1,}{,}\nb@args% reçoit le nombre d'arguments dans la ligne courante + \edef\dim@box{\the\dimexpr(\hsize-\frboxrule*(\nb@args+1)- + \frboxsep*2*\nb@args)/\nb@args}% + \hbox{% + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... + {\frbox{% ...encadrer + \hbox to\dim@box{% une \hbox de largeur \dim@box contenant + \hss% 1) ressort de centrage + \stack@strut% 2) strut de dimension verticale + \current@item%3) l'élement courant + \hss}% 4)ressort de centrage + }% fin de la \fbox + \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales + }% fin de \doforeach + \unkern% annuler la dernière compensation horizontale + }% + \par% termine le paragraphe + \nobreak% interdit une coupure de page + \nointerlineskip% sinon, ne pas ajouter le ressort d'interligne + \kern-\frboxrule% superposer les réglures horizontales + \expandafter\lineboxed@i% et recommencer + \fi +} +\catcode`\@12 +\lineboxed{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}\medbreak + +\frboxrule=1.5pt +\frboxsep=3pt +\lineboxed{,,,,,,,\\,,\\,,,,,,} +****************** Fin code ****************** + + +****************** Code 267 ****************** +\xunit=0.5cm \yunit=0.5cm \mainrule=0.8pt \subrule=0.2pt +\def\vlap#1{\vbox to0pt{\vss#1\vss}} +\catcode`@11 +\def\grid#1#2#3#4{% + \vbox{% empiler les éléments verticalement + \offinterlineskip% pas de ressort d'interligne + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale ds réglures + \for\ii = 1 to #3 \do1% pour chaque carreau vertical (\ii=variable muette) + {\vlap{\hrule width\total@wd height\mainrule}% tracer la réglure horizontale + \kern\yunit% insérer l'espace vertical + }% + \vlap{\hrule width\total@wd height\mainrule}% dernière réglure horizontale + }% +} +\catcode`@12 +\setbox0\hbox{\grid{4}{}{3}{}}% range la quadrillage dans le registre no 0 +Essai \copy0{} qui a pour +largeur=\convertunit{\wd0}{cm} cm et pour hauteur=\convertunit{\ht0}{cm} cm +****************** Fin code ****************** + + +****************** Code 268 ****************** +Début\vbox to0pt{\hbox{sous}\hbox{la ligne de base}\vss}suite +****************** Fin code ****************** + + +****************** Code 269 ****************** +\xunit=0.5cm \yunit=0.5cm \mainrule=0.8pt \subrule=0.2pt +\def\vlap#1{\vbox to0pt{\vss#1\vss}} +\catcode`@11 +\def\grid#1#2#3#4{% + \vbox{% empiler les éléments verticalement + \offinterlineskip% pas de ressort d'interligne + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale de la boite + \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% hauteur verticale de la subdivision + \for\ii = 1 to #3 \do% pour chaque unité verticale en partant du haut, tracer : + {\vlap{\hrule width\total@wd height\mainrule}% la réglure horizontale principale + % et dessous, les réglures horizontales secondaires : + \vbox to\z@{% dans une \vbox de hauteur nulle, + \for\jj = 2 to #4 \do 1% insérer #4-1 fois sous la position courante : + {\kern\sub@unit % l'espace verticale + \vlap{\hrule width\total@wd height\subrule}% et la réglure secondaire + }% + \vss% ressort qui se comprime pour satisfaire la hauteur nulle + }% + \kern\yunit% insérer l'espace vertical entre réglures principales + }% + \vlap{\hrule width\total@wd height\mainrule}% dernière réglure principale du bas + }% +} +\catcode`@12 +\setbox0=\hbox{\grid{4}{}{3}{5}} +Essai \copy0{} qui a pour +largeur=\convertunit{\wd0}{cm} cm et pour hauteur=\convertunit{\ht0}{cm} cm +****************** Fin code ****************** + + +****************** Code 270 ****************** +\mainrule=0.8pt \subrule=0.2pt +\def\vlap#1{\vbox to0pt{\vss#1\vss}} +\catcode`@11 +\def\grid#1#2#3#4{% + \vbox{% empiler les éléments verticalement + \offinterlineskip% pas de ressort d'interligne + % #################### Tracé des réglures verticales #################### + \vbox to\z@{% dans une \vbox de hauteur nulle + \edef\total@ht{\the\dimexpr\yunit*#3\relax}% hauteur totale + \edef\sub@unit{\the\dimexpr\xunit/#2\relax}% espace entre 2 subdivisions + \rlap{% mettre à droite de la position sans bouger + \for\ii = 1 to #1 \do 1% pour chaque unité horizontale + {\clap{\vrule width\dimexpr\mainrule height\total@ht}% réglure principale + \rlap{% mettre à droite de la position sans bouger + \for\jj = 2 to #2 \do 1% insérer #2-1 fois + {\kern\sub@unit % l'espace horizontal + \clap{\vrule width\subrule height\total@ht}% et la réglure verticale + }% + }% + \kern\xunit % insérer l'espace entre réglures horizontales + }% + \clap{\vrule width\mainrule height\total@ht}% dernière réglure principale + }% + \vss% compense la hauteur=0pt de la \vbox + }% + % #################### Tracé des réglures horizontales #################### + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale de la boite + \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% espace entre 2 subdivisions + \for\ii = 1 to #3 \do 1% pour chaque carreau vertical en partant du haut : + {\vlap{\hrule width\total@wd height\mainrule}% réglure horizontale principale + % et dessous, les réglures secondaires : + \vbox to\z@{% dans une \vbox de hauteur nulle, + \for\jj = 2 to #4 \do 1% insérer #4-1 fois sous la position courante : + {\kern\sub@unit % l'espace vertical + \vlap{\hrule width\total@wd height\subrule}% et la réglure secondaire + }% + \vss% ressort qui se comprime pour satisfaire la hauteur nulle + }% + \kern\yunit% insérer l'espace vertical entre réglures principales + }% + \vlap{\hrule width\total@wd height\mainrule}% dernière réglure horizontale + }% +} +\catcode`@12 +Grille 1 : \xunit=1cm \yunit=1cm \grid{5}{10}{2}{10}\smallskip + +Grille 2 : \xunit=0.5cm \yunit=0.5cm \grid{7}{1}{3}{1}\smallskip + +Grille 3 : \xunit=1.5cm \yunit=0.5cm \grid{4}{3}{3}{2} +****************** Fin code ****************** + + +****************** Code 271 ****************** +\vrule\hbox to10cm{\leaders\hbox to1.5cm{\hss A\hss}\hfill}\vrule +****************** Fin code ****************** + + +****************** Code 272 ****************** +\frboxsep=-\frboxrule \def~{\leavevmode\raise.75ex\hbox{\vrule height.2pt width1em}} +~\hbox to10cm{\leaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~ + +~\hbox to10cm{\cleaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~ + +~\hbox to10cm{\xleaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~ +****************** Fin code ****************** + + +****************** Code 273 ****************** +a) \leavevmode\hbox to10cm{\leaders\hrule\hfill}\smallskip + +b) \hbox to10cm{\leaders\hbox{\vrule height0.2pt width2.5mm \kern1.5mm}\hfill} + +c) \vrule width0.2pt height1ex depth0pt % première réglure verticale + \hbox to10cm{% puis répétition de "_|" + \leaders\hbox{\vrule width2em height0.2pt \vrule width0.2pt height1ex}\hfill} + +d) \hbox to10cm{\leaders\hbox{% + \vrule height.2pt width.5em% 1/2 palier bas + \vrule height5pt width0.2pt% montée au palier haut + \vrule height5pt depth-4.8pt width1em% palier haut + \vrule height5pt width0.2pt% descente au palier bas + \vrule height.2pt width.5em% 1/2 palier bas + }\hfill} +****************** Fin code ****************** + + +****************** Code 274 ****************** +\mainrule=0.8pt \subrule=0.2pt +\def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V + \vbox to\yunit{% dans un \vbox de hauteur \yunit + \offinterlineskip% pas de ressort d'interligne + \vlap{\hrule height\mainrule width\xunit}% réglure principale du haut + \leaders% répéter (ici ce sera donc #2-1 fois) + \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2 + {\vss% ressort qui va s'étirer à \yunit/#2 + \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt + }\vfill + \kern\dimexpr\yunit/#2\relax% dernière espace verticale + \vlap{\hrule height\mainrule width\xunit}% réglure principale du bas + }% +} +\yunit=1cm +\leavevmode \carreau{}{4} puis \carreau{}{10} +****************** Fin code ****************** + + +****************** Code 275 ****************** +\mainrule=0.8pt \subrule=0.2pt +\def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V + % ######## réglures horizontales ######## + \rlap{% mettre à droite de la position sans bouger + \vbox to\yunit{% dans un \vbox de hauteur \yunit + \offinterlineskip% pas de ressort d'interligne + \vlap{\hrule height\mainrule width\xunit}% réglure principale du haut + \leaders% répéter (ici ce sera #2-1 fois) + \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2 + {\vss% ressort qui va s'étirer à \yunit/#2 + \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt + }\vfill% ressort de \leaders + \kern\dimexpr\yunit/#2\relax% derniere espace verticale + \vlap{\hrule height\mainrule width\xunit}% réglure principale du bas + }% + }% + % ######## réglures verticales ######## + \hbox to\xunit{% dans une \hbox de longueur \xunit + \clap{\vrule height\yunit width\mainrule}% réglure principale de gauche + \leaders% répéter (ici ce sera #1-1 fois) + \hbox to\dimexpr\xunit/#1\relax + {\hss% ressort qui va s'étirer à \xunit/#1 + \clap{\vrule height\yunit width\subrule}% réglure H de dimension 0 + }\hfill% ressort de \leaders + \kern\dimexpr\xunit/#1\relax% dernière espace H + \clap{\vrule height\yunit width\mainrule}% réglure principale de droite + }% +} +\yunit=1cm \xunit=2cm +\leavevmode \carreau{3}{4} puis \carreau{8}{10} +****************** Fin code ****************** + + +****************** Code 276 ****************** +\mainrule=0.8pt \subrule=0.2pt +\def\grid#1#2#3#4{% + \vbox to#3\yunit{% dans une boite verticale de hauteur #3*\yunit + \leaders% répéter verticalement + \hbox to#1\xunit{% une boite de longueur #1*\xunit + \leaders% dans laquelle se répète horizontalement + \hbox{\carreau{#2}{#4}}% le carreau de largeur \xunit + \hfill}% + \vfill + }% +} +Grille 1 : \xunit=1cm \yunit=1cm \grid{5}{10}{2}{10}\smallskip + +Grille 2 : \xunit=0.5cm \yunit=0.5cm \grid{7}{1}{3}{1}\smallskip + +Grille 3 : \xunit=1.5cm \yunit=0.5cm \grid{4}{3}{3}{2} +****************** Fin code ****************** + + +****************** Code 277 ****************** +\outer\def\foo{Bonjour} +\def\bar{\foo} +****************** Fin code ****************** + + +****************** Code 278 ****************** +\outer\def\foo{Bonjour} +\expandafter\def\expandafter\bar\expandafter{\noexpand\foo} +\meaning\bar + +\edef\baz{\noexpand\foo} +\meaning\baz +****************** Fin code ****************** + + +****************** Code 279 ****************** +\catcode`\@11 +\long\def\filedef#1#2{% + \begingroup + \let\input\@@input% <- utilisateurs de latex uniquement + \everyeof{\eof@nil\noexpand}% insère "\eof@nil\noexpand" à la fin du fichier + \expandafter\filedef@i\expandafter#1% développe \input #2 + \expandafter\relax\input #2 +} +\long\def\filedef@i#1#2\eof@nil{% + \endgroup + \expandafter\def\expandafter#1\expandafter{\gobone#2}% mange le \relax +} +\catcode`\@12 +\filedef\foo{test.txt} +\meaning\foo +****************** Fin code ****************** + + +****************** Code 280 ****************** +% canal de lecture employé dans tout ce chapitre +\def\iffileexists#1#2{% #1=canal de lecture #2=nom du fichier + \openin#1=#2 + \ifeof#1% le fichier n'existe pas + \closein#1 + \expandafter\secondoftwo% renvoyer faux + \else + \closein#1 + \expandafter\firstoftwo% sinon renvoyer vrai + \fi +} +a) \iffileexists\rtest{test.txt}{vrai}{faux}\qquad +b) \iffileexists\rtest{foobar.txt}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 281 ****************** +\openin\rtest =filetest.txt +\read\rtest to \foo% lit la première ligne +1) Signification : \meaning\foo.\par% signification de ce qui a été lu +1) Exécution : \foo\par +\read\rtest to \foo% lit la deuxième ligne +2) Signification : \meaning\foo.\par +2) Exécution : \foo\par +\read\rtest to \foo% lit la dernière ligne +3) Signification : \meaning\foo.\par +3) Exécution : \foo +\closein\rtest +****************** Fin code ****************** + + +****************** Code 282 ****************** +\openin\rtest =filetest.txt +\readline\rtest to \foo% lit la première ligne +1) Signification : \meaning\foo.\par% signification de ce qui a été lu +1) Exécution : \foo\par +\readline\rtest to \foo% lit la deuxième ligne +2) Signification : \meaning\foo.\par +2) Exécution : \foo\par +\readline\rtest to \foo% lit la dernière ligne +3) Signification : \meaning\foo.\par +3) Exécution : \foo +\closein\rtest +****************** Fin code ****************** + + +****************** Code 283 ****************** +Valeur de \string\endlinechar = \number\endlinechar\par +Caractère correspondant : << \char\endlinechar{} >> +****************** Fin code ****************** + + +****************** Code 284 ****************** +\openin\rtest =filetest.txt +Ligne 1 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la première ligne +\meaning\foo.\par% donne ce qui a été lu +Ligne 2 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la deuxième ligne +\meaning\foo.\par +Ligne 3 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la dernière ligne +\meaning\foo. +\closein\rtest +****************** Fin code ****************** + + +****************** Code 285 ****************** +\def\xread#1to#2{% + \ifcs{#2}% si #2 est une séquence de contrôle + {\edef\restoreendlinechar{\endlinechar=\the\endlinechar}% + \endlinechar=-1 % supprime le caractère mis en fin de ligne + \read#1to#2% lit la ligne et l'assigne à la macro #2 + \restoreendlinechar\relax% restaure le \endlinechar + }% si #2 n'est pas une séquence de contrôle, + {\xread#1to}% ignorer #2, et recommencer +} + +\openin\rtest =filetest.txt +Ligne 1 : \xread\rtest to \foo% lit la première ligne +\meaning\foo.\par% donne ce qui a été lu +Ligne 2 : \xread\rtest to \foo% lit la deuxième ligne +\meaning\foo.\par +Ligne 3 : \xread\rtest to \foo% lit la dernière ligne +\meaning\foo. +\closein\rtest +****************** Fin code ****************** + + +****************** Code 286 ****************** +\def\xread{% doit être suivie de "<nombre> to \<macro>" + \edef\restoreendlinechar{\endlinechar=\the\endlinechar}% + \endlinechar=-1 % neutralise \endlinechar + \afterassignment\restoreendlinechar% après l'assignation, restaurer \endlinechar + \read% attend <nombre> to \<macro> pour effectuer l'assignation +} +\catcode`\@12 + +\openin\rtest =filetest.txt +Ligne 1 : \xread\rtest to \foo% lit la première ligne +\meaning\foo.\par% donne ce qui a été lu +Ligne 2 : \xread\rtest to \foo% lit la deuxième ligne +\meaning\foo.\par +Ligne 3 : \xread\rtest to \foo% lit la dernière ligne +\meaning\foo. +\closein\rtest +****************** Fin code ****************** + + +****************** Code 287 ****************** +\def\macroname{% se développe en le nom de la macro qui suit sans + % le caractère d'échappement + \ifnum\escapechar>-1 % si le caractère d'échappement est positif + \ifnum\escapechar<256 % et inférieur à 256, développer les 2 "\fi" + \expandafter\expandafter\expandafter\expandafter% et le "\string", puis + \expandafter\expandafter\expandafter\gobone% manger le "\" avec \gobone + \fi + \fi + \string% doit être suivi d'une macro +} +\catcode`\@11 +\newcount\field@cnt +\def\searchitem#1#2#3#4{% #1= canal #2=nom fichier #3=référence #4=macro à définir + \let#4\gobone% pour l'instant, #4=\gobone + \openin#1=#2\relax + \unless\ifeof#1% si le fichier existe + \lowercase{\def\sought@firstfield{#3}}% stocke le 1er champ à chercher + \edef\macro@name{\macroname#4}% nom de la macro sans "\" + \xread#1 to \current@line% lire la première ligne + \field@cnt=0 % initialiser le compteur de champs + % ################ sauvegarde du nom des champs ################ + \expsecond{\doforeach\current@field\in}\current@line% pour chaque champ + {\advance\field@cnt1 % incrémenter le compteur de champs + \lowercase\expandafter{% e texte de remplacement de \current@field en minuscule + \expandafter\def\expandafter\current@field\expandafter{\current@field}% + }% + % sauvegarder chaque champ de la 1re ligne (qui sont les intitulés) dans une macro + \letname{fieldname\number\field@cnt}=\current@field + }% + \edef\field@num{\number\field@cnt}% nombre de champs + % ################ lecture des lignes de données ################ + \loop% tant que... + \unless\ifeof#1\relax% ...la fin du fichier n'est pas atteinte + \xread#1 to \current@line% lire une ligne + \unless\ifx\current@line\empty% si elle n'est pas vide + % examniner les champs qu'elle contient (aller à \test@field) + \expsecond{\expandafter\test@field\current@line\@nil}\macro@name% + \fi + \repeat + \fi + \closein#1\relax +} + +\def\test@field#1,#2\@nil#3{% #1=champ no 1 #2=autres champs #3=nom de la macro sans "\" + \def\current@firstfield{#1}% stocke le premier champ de la ligne en cours + \ifx\current@firstfield\sought@firstfield% s'il est égal à celui cherché + \defname{#3.\csname fieldname1\endcsname}{#1}% définir la macros \<#3."champ 1"> + \field@cnt=1 % initialiser le compteur de champ + \doforeach\current@field\in{#2}% puis, pour i>2, définir les macros \<#3."champ i"> + {\advance\field@cnt1 % incrémenter le compteur de champ + \letname{#3.\csname fieldname\number\field@cnt\endcsname}=\current@field + }% + \defname{#3}##1{% et définir la macro \<#3> + \ifcsname#3.##1\endcsname% si la macro \<#3."argument"> existe déjà + \csname#3.##1\expandafter\endcsname% l'exécuter après avoir mangé le \fi + \fi + }% + \fi +} +\catcode`\@12 + +\searchitem\rtest{fournitures.txt}{4562u}\monarticle +réf = \monarticle{ref}, +dénomination = \monarticle{item}, +prix = \monarticle{prix}, +fournisseur = \monarticle{fournisseur}, +champ non existant = \monarticle{foobar}. + +\searchitem\rtest{fournitures.txt}{truc}\essai% référence "truc" n'existe pas +réf = \essai{ref}, +dénomination = \essai{item}, +prix = \essai{prix}, +fournisseur = \essai{fournisseur}. +****************** Fin code ****************** + + +****************** Code 288 ****************** +\def\macroname{% se développe en le nom de la macro qui suit sans + % le caractère d'échappement + \ifnum\escapechar>-1 % si le caractère d'échappement est positif + \ifnum\escapechar<256 % et inférieur à 256, développer les 2 "\fi" + \expandafter\expandafter\expandafter\expandafter% et le "\string", puis + \expandafter\expandafter\expandafter\gobone% manger le "\" avec \gobone + \fi + \fi + \string% doit être suivi d'une macro +} +\catcode`\@11 +\newcount\field@cnt +\newif\ifsearch@repeat +\def\assign@arg#1=#2\@nil{% + \def\sought@fieldnumber{#1}% no du champ à chercher + \def\sought@fielvalue{#2}% et sa valeur +} +\def\searchitem#1#2#3#4{% #1= canal #2=nom fichier #3=champ cherché #4=macro à définir + \let#4\gobone% pour l'instant, #4=\gobone + \openin#1=#2\relax% + \unless\ifeof#1% si le fichier existe + \edef\macro@name{\macroname#4}% nom de la macro sans "\" + \xread#1 to \current@line% lire et ignorer la première ligne + \ifin{#3}{=}% si #3 contient = + {\assign@arg#3\@nil}% trouver le no de champ et sa valeur + {\def\sought@fieldnumber{1}% sinon, no du champ = 1 + \def\sought@fielvalue{#3}% et sa valeur = #3 + }% + % ################ lecture des lignes de données ################ + \search@repeattrue% poursuite de la boucle loop : vraie + \loop% tant que... + \ifeof#1\relax% ...la fin du fichier n'est pas atteinte + \search@repeatfalse% sortir de la boucle loop + \else + \xread#1 to \current@line% lire une ligne + \unless\ifx\current@line\empty% si elle n'est pas vide + % examniner les champs qu'elle contient (aller à \test@field) + \expsecond{\expandafter\test@field\current@line\@nil}\macro@name% + \fi + \fi + \ifsearch@repeat% ne poursuivre que si le booléen en vrai + \repeat + \fi + \closein#1\relax +} + +\def\test@field#1\@nil#2{% #1=champs #2=nom de la macro sans "\" + \field@cnt=0 % initialiser le compteur de champ + \doforeach\current@field\in{#1}% parcourir les champs de la ligne en cours + {\advance\field@cnt1 % incrémenter le compteur de champ + \ifnum\field@cnt=\sought@fieldnumber\relax% si c'est le bon numéro de champ + \ifx\current@field\sought@fielvalue% et si le champ correspond à celui cherché + \search@repeatfalse% sortir de la boucle loop + \doforeachexit% sortir de la boucle \doforeach en cours + \fi + \fi + }% + \unless\ifsearch@repeat% si la ligne a été trouvée + \field@cnt=0 % initialiser le compteur de champ + \doforeach\current@field\in{#1}% parcourir à nouveau les champs de la ligne + {\advance\field@cnt1 % incrémenter le compteur de champ + \letname{#2.\number\field@cnt}=\current@field% faire l'assignation + }% + \defname{#2}##1{% et définir la macro \<#2> + \ifcsname#2.##1\endcsname% si la macro \<#2."argument"> existe déjà + \csname#2.##1\expandafter\endcsname% l'exécuter après avoir mangé le \fi + \fi + }% + \fi +} +\catcode`\@12 +a) \searchitem\rtest{basecourse.txt}{3=283}\foo + "\foo1", "\foo2", "\foo3", "\foo4", "\foo5", "\foo6", "\foo7" + +b) \searchitem\rtest{basecourse.txt}{Valet}\bar + "\bar1", "\bar2", "\bar3", "\bar4", "\bar5", "\bar6", "\bar{abcd}" +****************** Fin code ****************** + + +****************** Code 289 ****************** +\def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier + \begingroup + \tt% sélectionner la fonte à chasse fixe + \openin#1=#2\relax + \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et + Le fichier n'existe pas% afficher le message + \else% le fichier existe + \def\do##1{\catcode`##1=12 }% + \dospecials% neutraliser tous les tokens spéciaux + \obeyspaces% rendre l'espace actif + \loop + \xread#1 to \currline% lire une ligne + \unless\ifeof#1% si la fin du fichier n'est pas atteinte + \leavevmode\par% aller à la ligne + \currline% afficher la ligne lue + \repeat% recommencer + \fi + \closein#1\relax + \endgroup +} + +Contenu du fichier : "\showfilecontent\rtest{readtest.txt}", affiché tel quel +****************** Fin code ****************** + + +****************** Code 290 ****************** +\def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier + \begingroup + \tt% sélectionner la fonte à chasse fixe + \openin#1=#2\relax + \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et + Le fichier n'existe pas% afficher le message + \else% le fichier existe + \def\do##1{\catcode`##1=12 }% + \dospecials% neutraliser tous les tokens spéciaux + \obeyspaces% rendre l'espace actif + \def\magicpar{\let\magicpar=\par}% + \loop + \xread#1 to \currline% lire une ligne + \unless\ifeof#1% si la fin du fichier n'est pas atteinte + \leavevmode\magicpar% former le paragraphe (sauf à la 1er itération) + \currline% afficher la ligne + \repeat% recommencer + \fi + \closein#1\relax + \endgroup +} + +Contenu du fichier : "\showfilecontent\rtest{readtest.txt}", affiché tel quel +****************** Fin code ****************** + + +****************** Code 291 ****************** +% sera le canal d'écriture dans tout ce chapitre +\immediate\openout\wtest=writetest.txt % lie \wtest au fichier +\immediate\write\wtest{Programmer en \noexpand\TeX{} est facile.}% écrit une ligne +\immediate\write\wtest{Et utile...}% puis une autre +\immediate\closeout\wtest% défait la liaison +a) Contenu du fichier :\par +"\showfilecontent\rtest{writetest.txt}"% affiche le contenu du fichier +\medbreak +% 2e tentative : +b) Contenu du fichier :\par +\immediate\openout\wtest=writetest.txt % lie \wtest au fichier +\immediate\write\wtest{Essai d'écriture}% écrit une ligne +\immediate\closeout\wtest% défait la liaison +"\showfilecontent\rtest{writetest.txt}"% affiche le contenu du fichier +****************** Fin code ****************** + + +****************** Code 292 ****************** +\def\noexpwrite#1#2{% #1=numéro de canal #2=texte à écrire + \write#1{\unexpanded{#2}}% +} +\immediate\openout\wtest=writetest.txt +\immediate\noexpwrite\wtest{Programmer en \TeX{} est facile.}% +\immediate\noexpwrite\wtest{Et utile...}% +\immediate\closeout\wtest +Contenu du fichier :\par +\showfilecontent\rtest{writetest.txt}% affiche le contenu du fichier +****************** Fin code ****************** + + +****************** Code 293 ****************** +\catcode`\@11 +\def\exactwrite#1{% #1=numéro de canal + \begingroup + \def\canal@write{#1}% sauvegarde le numéro de canal + \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12 + \exactwrite@i% aller lire le <car> +} + +\def\exactwrite@i#1{% #1 est le <car> de catcode 12 + \def\exactwrite@ii##1#1{% ##1 est le <texte> à envoyer vers le fichier + \exactwrite@iii##1\@nil% envoyer <texte> à \exactwrite@iii + }% + \exactwrite@ii% va lire tout ce qui se trouve jusqu'au prochain <car> +} + +{\catcode`\^^M 12 \gdef\EOL@char{^^M}}% définit le caractère <EOL> de catcode 12 + +\def\exactwrite@iii#1\@nil{% #1 = <texte> à écrire dans le fichier + \expsecond{\ifin{#1}}\EOL@char% si #1 contient "retour charriot" + {\write@line#1\@nil% écrire la première ligne de #1 + } + {\immediate\write\canal@write{#1}% sinon : dernière ligne atteinte, l'écrire + \endgroup% puis sortir du groupe + }% +} + +% les \expandafter provoquent le 1-développement de \EOL@char : +\expandafter\def\expandafter\write@line\expandafter#\expandafter1\EOL@char#2\@nil{% + \immediate\write\canal@write{#1}% écrit la ligne (ce qui se trouve avant ^^M) + \exactwrite@iii#2\@nil% recommencer avec ce qui se trouve après "^^M" +} +\catcode`\@12 + +\immediate\openout\wtest=writetest.txt % lie le canal \wtest au fichier +\exactwrite\wtest|Programmer en \TeX{} est facile ! + +Et très utile.| +\immediate\closeout\wtest% et fermer le fichier + +Le contenu du fichier "writetest.txt" est :\par +"\showfilecontent\rtest{writetest.txt}" +****************** Fin code ****************** + + +****************** Code 294 ****************** +\catcode`\@11 +\newcount\exo@number% compteur pour le numéro d'exercice + +\def\exocalctotal#1\endtotal{\edef\total@points{\dimtodec\dimexpr#1}} + +\def\initexo#1{% + \def\exo@canal{#1}% sauvegarde le canal d'écriture + \exo@number=0 % initialiser le compteur d'exo + \iffileexists\exo@canal{\jobname.pts}% si le fichier .pts existe + {\input \jobname.pts }% le lire et exécuter son contenu + {\def\total@points{\char`\#\char`\#}}% sinon, définir un total alternatif + \immediate\openout\exo@canal=\jobname.pts % ouvrir le fichier .pts + \immediate\write\exo@canal{\noexpand\exocalctotal}% et commencer à y écrire dedans +} + +\def\exo#1{% définti la macro qui affiche l'exercice + \bigbreak% sauter une grande espace verticale + \immediate\write\exo@canal{+#1}% écrire "+#1" dans le fichier .pts + \advance\exo@number by 1 % incrémenter le numéro de l'exercice + \noindent\vrule height1ex width1ex depth0pt % trace le carré + \kern1ex% insérer une espace horizontale + {\bf Exercice \number\exo@number}% afficher "Exercice <nombre>" + \leaders\hbox to.5em{\hss.\hss}\hfill% afficher les pointillés + #1/\total@points% puis #1/<total> + \smallbreak% composer la ligne précédente et sauter une espace verticale +} + +\def\stopexo{% + \immediate\write\exo@canal{\relax\noexpand\endtotal}% + \immediate\closeout\exo@canal +} +\catcode`\@12 +\initexo\wtest + +\hfill{\bf Interrogation écrite. Sujet : \TeX{}}\hfill\null +\par +\leavevmode\hfill\vrule height.4pt width 2cm depth0pt\hfill\null +\exo{3pt} +Élaborer un test \litterate/\ifonebyte{<texte>}{<vrai>}{<faux>}/ qui teste, pour une +compilation avec un moteur 8 bits, si le \litterate/<texte>/ est codé sur un seul +octet. Ce test pourrait être utilisé pour déterminer si l'encodage d'un document +est à plusieurs octets (comme l'est UTF8) en prenant comme \litterate/<texte>/ +les caractères <<~é~>>, <<~à~>>, etc. + +\exo{5,5pt} +Si \verb-#1- est un nombre entier, quel est le test fait par ce code ? +\smallbreak +\hfill +\litterate/\if\string l\expandafter\firstto@nil\romannumeral#1\relax\@nil/ +\hfill\null + +\exo{4,5pt} +On a vu que pour provoquer un $n$-développement, les \litterate/\expandafter/se plaçaient +en nombre égal à $2^n-1$ devant chaque token précédant celui que l'on veut développer. +Or, ce nombre est {\it impair\/}. Trouver un exemple ou un cas particulier où il faut +placer un nombre {\it pair\/} d'\litterate/\expandafter/ devant un token (on pourra +envisager le cas de 2 \litterate/\expandafter/). +\stopexo +****************** Fin code ****************** + + +****************** Code 295 ****************** +\def\identite{Foo Bar}% Prénom Nom +\def\beforespace#1 #2\nil{#1} +\def\afterspace#1 #2\nil{#2} +\def\prenom{\beforespace\identite\nil} +\def\nom{\afterspace\identite\nil} +Mon prénom : \expandafter\expandafter\prenom + +Mon nom : \expandafter\expandafter\nom +****************** Fin code ****************** + + +****************** Code 296 ****************** +\newlinechar`\^^J +\immediate\openout\wtest=test1.txt +\immediate\write\wtest{Une première ligne^^JEt une seconde.} +\immediate\closeout\wtest +\showfilecontent\rtest{test1.txt} +****************** Fin code ****************** + + +****************** Code 297 ****************** +\newlinechar`\^^J +\immediate\openout\wtest=test2.txt +\immediate\write\wtest{Une première ligne^^JEt une seconde.} +\immediate\write\wtest{Et la dernière.} +\immediate\closeout\wtest + +{\endlinechar`\X % insère "X" à chaque fin de ligne +\openin\rtest=test2.txt % les fins de lignes sont commentées +\loop% pour éviter que \endlinechar + \read\rtest to \foo% ne soit inséré à chaque fin de + \unless\ifeof\rtest% ligne du code source + \meaning\foo\par% affiche le texte de remplacement de \foo +\repeat% +\closein\rtest}% +****************** Fin code ****************** + + +****************** Code 298 ****************** +\catcode`\@11 +\def\exactwrite#1{% #1=numéro de canal + \begingroup + \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12 + \newlinechar=`\^^M % caractère de fin de ligne = retour charriot + \exactwrite@i{#1}% donne le <canal> d'écriture comme premier argument +} + +\def\exactwrite@i#1#2{% #2 est le caractère délimiteur de catcode 12 + \def\exactwrite@ii##1#2{% ##1 est le <texte> à envoyer vers le fichier + \immediate\write#1{##1}% écrire le <texte> dans le fichier + \endgroup% puis, sortir du groupe + }% + \exactwrite@ii% traiter tout ce qui se trouve jusqu'au prochain #2 +} +\catcode`\@12 + +\immediate\openout\wtest=writetest.txt % lie le canal \wtest au fichier +\exactwrite\wtest|Programmer en \TeX{} est facile ! + +Et très utile.| +\immediate\closeout\wtest% et fermer le fichier + +Le contenu du fichier "writetest.txt" est :\par +"\showfilecontent\rtest{writetest.txt}" +****************** Fin code ****************** + + +****************** Code 299 ****************** +\newlinechar`\^^J +\message{^^JQuel est votre nom ? } +\xread-1 to \username +\message{Bonjour \username.^^J% +Depuis combien d'années pratiquez-vous TeX ? } +\read-1 to \yeartex +\message{% + \ifnum\yeartex<5 Cher \username, vous êtes encore un débutant ! + \else\ifnum\yeartex<10 Bravo \username, vous êtes un TeXpert ! + \else\ifnum\yeartex<15 Félicitations \username, vous êtes un TeXgourou. + \else Passez à autre chose, par exemple à Metafont et Metapost ! + \fi\fi\fi^^J} +****************** Fin code ****************** + + +****************** Code 300 ****************** +\catcode`\@11 +\def\answer@plus{+}\def\answer@minus{-}\def\answer@equal{=}% +\def\nb@found#1{% macro appelée lorsque le nombre (argument #1) est trouvé + \message{^^JVotre nombre est #1.^^JMerci d'avoir joué avec moi.^^J}% +} + +\def\nbguess#1#2{% + \message{Choisissez un nombre entre #1 et #2.^^J + Tapez entrée lorsque c'est fait.}% + \read-1 to \tex@guess% attend que l'on tape sur "entrée" + \nbguess@i{#1}{#2}% +} + +\def\nbguess@i#1#2{% + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si #1<#2 (donc le nombre n'est pas trouvé), + % mettre dans \tex@guess la troncature de la moyenne de #1 et #2 + {\edef\tex@guess{\number\truncdiv{\numexpr#1+#2\relax}2}% + \message{Je propose \tex@guess.^^J% afficher sur le terminal + Votre nombre est-il plus grand (+), plus petit (-) ou égal (=) : }% + \read -1 to \user@answer% lire la réponse de l'utilisateur + \edef\user@answer{% + \expandafter\firstto@nil\user@answer\relax\@nil% ne garder que le 1er caractère + }% + \ifxcase\user@answer% envisager les cas "+", "-" et "=" + \answer@plus{\exparg\nbguess@i{\number\numexpr\tex@guess+1\relax}{#2}}% + \answer@minus{\expsecond{\nbguess@i{#1}}{\number\numexpr\tex@guess-1\relax}}% + \answer@equal{\nb@found\tex@guess}% + \elseif% si la réponse ne commence pas par "+", "-" ou "=" + \message{Je n'ai pas compris votre réponse}% afficher message erreur + \nbguess@i{#1}{#2}% et recommencer avec les mêmes nombres + \endif + } + % si #1>=#2, le nombre est trouvé + {\nb@found{#1}}% +} +\catcode`\@12 + +\nbguess{1}{100} +****************** Fin code ****************** + + +****************** Code 301 ****************** +\def\syracuse#1{% + #1% affiche le nombre + \ifnum#1>1 % si le nombre est >1 + , % afficher une virgule+espace + \ifodd#1 % s'il est pair + \exparg\syracuse% appeler la macro \syracuse + {\number\numexpr3*#1+1% avec 3*n+1 + \expandafter\expandafter\expandafter}% après avoir rendu la macro terminale + \else % s'il est pair + \expandafter\syracuse\expandafter% appeler la macro \syracuse + {\number\numexpr#1/2% avec n/2 + \expandafter\expandafter\expandafter}% après avoir rendu la macro terminale + \fi + \else% si le nombre est 1 + .% afficher un point + \fi +} +a) \syracuse{20}\par +b) \syracuse{14}\par +c) \syracuse{99}\par +d) \edef\foo{\syracuse{15}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 302 ****************** +\def\syracuse#1{% + #1% afficher le nombre + \ifnum#1>1 % si le nombre est >1 + , % afficher une virgule+espace + \exparg\syracuse{% appeler la macro \syracuse + \number\numexpr% avec le nombre : + \ifodd#1 3*#1+1% 3n+1 si #1 est impair + \else #1/2% n/2 sinon + \fi% + \expandafter}% avant de rendre la récursivité terminale + \else + .% si #1=1, afficher un point + \fi +} +a) \syracuse{20}\par +b) \syracuse{14}\par +c) \syracuse{99}\par +d) \edef\foo{\syracuse{15}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 303 ****************** +\def\factorielle#1{% + \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {1}% "1" si #1=0} + {#1*\exparg\factorielle{\number\numexpr#1-1}}% +} +a) \factorielle{0}\qquad +b) \factorielle{3}\qquad +c) \edef\foo{\factorielle{8}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 304 ****************** +\catcode`\@11 +\def\factorielle#1{% + \number\numexpr\factorielle@i{#1}\relax% appelle \factorielle@i + % en lançant le développement maximal +} +\def\factorielle@i#1{% + \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {1}% "1" si #1= + {#1*\exparg\factorielle@i{\number\numexpr#1-1}}% +} +\catcode`\@12 +a) \factorielle{0}\qquad +b) \factorielle{3}\qquad +c) \edef\foo{\factorielle{8}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 305 ****************** +\catcode`\@11 +\def\PGCD#1#2{% + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\PGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument + {\PGCD@i{#1}{#2}}% +} + +\def\PGCD@i#1#2{% #1=a #2=b avec a>b + \exptwoargs\PGCD@ii% appeler la macro récursive avec + {\number\numexpr#1-#2*\truncdiv{#1}{#2}}% le reste de a/b + {#2}% et le divisieur b +} + +\def\PGCD@ii#1#2{% #1=reste r #2=diviseur b + \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {#2}% si le reste est nul, renvoyer b + {\PGCD@i{#2}{#1}}% sinon, recommencer avec b et r +} +\catcode`\@12 +a) \PGCD{120}{75}\qquad +b) \PGCD{64}{180}\qquad +c) \PGCD{145}{64}\qquad +d) \edef\foo{\PGCD{1612}{299}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 306 ****************** +\catcode`\@11 +\def\calcPGCD#1#2{% + \ifhmode\par\fi% si en mode horizontal, former le paragraphe + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\calcPGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument + {\calcPGCD@i{#1}{#2}}% +} + +\def\calcPGCD@i#1#2{% #1=a #2=b avec a>b + \edef\calcPGCD@quotient{\number\truncdiv{#1}{#2}}% stocke le quotient + $#1=\calcPGCD@quotient\times#2% en mode maths, afficher "a=q*b" (à suivre) + \exptwoargs\calcPGCD@ii% appeler la macro récursive avec + {\number\numexpr#1-#2*\calcPGCD@quotient}% le reste de a/b + {#2}% et le divisieur b +} + +\def\calcPGCD@ii#1#2{% #1=reste r #2=diviseur b + +#1$\par% (suite du mode math) afficher "+r", fermer le mode math et \par + \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% + {}% si le reste est nul, ne rien faire + {\calcPGCD@i{#2}{#1}}% sinon, recommencer avec b et r +} +\catcode`\@12 +\calcPGCD{39}{15}\medbreak +\calcPGCD{1612}{299} +****************** Fin code ****************** + + +****************** Code 307 ****************** +\frboxsep=0pt % encadrer au plus proche +\leavevmode\frbox{$=$} n'est pas identique à \frbox{${}={}$} +****************** Fin code ****************** + + +****************** Code 308 ****************** +\catcode`\@11 +\def\calcPGCD#1#2{% + \vtop{% mettre l'alignement dans une \vtop + \halign{% les "#" doivent être doublés puisqu'à l'intérieur d'une macro + $\hfil##$&${}=\hfil##$&${}\times##\hfil$&${}+##\hfil$% préambule + \cr% fin du préambule et début de la première cellule + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\calcPGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument + {\calcPGCD@i{#1}{#2}}% + \crcr% fin de l'alignement + }% + }% +} + +\def\calcPGCD@i#1#2{% #1=a #2=b avec a>b + \xdef\calcPGCD@quotient{\number\truncdiv{#1}{#2}}% stocke le quotient + #1 & \calcPGCD@quotient & #2 &% afficher "a=q*b" (à suivre) + \exptwoargs\calcPGCD@ii% appeler la macro récursive avec + {\number\numexpr#1-#2*\calcPGCD@quotient}% le reste de a/b + {#2}% et le divisieur b +} + +\def\calcPGCD@ii#1#2{% #1=reste r #2=diviseur b + #1% (suite de l'alignement) afficher "+r" + \cr% et terminer la ligne en cours + \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% + {}% si le reste est nul, ne rien faire + {\calcPGCD@i{#2}{#1}}% sinon, recommencer avec b et r +} +\catcode`\@12 +a) \calcPGCD{39}{15}\medbreak +b) \calcPGCD{1612}{299} +****************** Fin code ****************** + + +****************** Code 309 ****************** +\catcode`\@11 +\def\baseconv#1{% + \unless\ifnum#1=\z@ % si #1 est différent de 0 + \number\numexpr#1-2*\truncdiv{#1}2\relax% écrire le reste + \exparg\baseconv{\number\truncdiv{#1}2\expandafter}% recommencer avec #1/2 + \fi% après que le \fi ait été lu +} +\catcode`\@12 +a) \baseconv{43}\qquad +b) \baseconv{32}\qquad +c) \edef\foo{\baseconv{159}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 310 ****************** +\catcode`\@11 +\def\baseconv#1{% + \baseconv@i{}{#1}% +} + +\def\baseconv@i#1#2{% #1=restes #2=n + \ifnum#2=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% si n=0 + {#1}% si <n>=0, afficher tous les restes + {% sinon, recommencer en + \exptwoargs\baseconv@i% ajoutant le reste courant avant #1 + {\number\numexpr#2-2*\truncdiv{#2}2\relax #1} + {\number\truncdiv{#2}2}% et en prenant n:=n/2 + }% +} + +\catcode`\@12 +a) \baseconv{43}\qquad +b) \baseconv{32}\qquad +c) \edef\foo{\baseconv{159}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 311 ****************** +\catcode`\@11 +\def\z@@{\expandafter\z@\expandafter} +\def\basedigit#1{% + \ifcase#1 + \z@@ 0% + \or\z@@ 1\or\z@@ 2\or\z@@ 3\or\z@@ 4\or\z@@ 5\or\z@@ 6\or\z@@ 7% + \or\z@@ 8\or\z@@ 9\or\z@@ A\or\z@@ B\or\z@@ C\or\z@@ D\or\z@@ E% + \or\z@@ F\or\z@@ G\or\z@@ H\or\z@@ I\or\z@@ J\or\z@@ K\or\z@@ L% + \or\z@@ M\or\z@@ N\or\z@@ O\or\z@@ P\or\z@@ Q\or\z@@ R\or\z@@ S% + \or\z@@ T\or\z@@ U\or\z@@ V\or\z@@ W\or\z@@ X\or\z@@ Y\or\z@@ Z% + \fi +} +\long\def\>#1<{\detokenize{#1}} +a) \expandafter\>\romannumeral\basedigit{23}<\quad +b) \expandafter\>\romannumeral\basedigit{6}< +\catcode`@12 +****************** Fin code ****************** + + +****************** Code 312 ****************** +\catcode`\@11 +\def\baseconv#1#2{% #1=base #2=nombre à convertir + \ifnum#1<37 % base maxi = 36 (10 signes chiffres + 26 signes lettres) + \antefi + \baseconv@i{}{#2}{#1}% + \fi +} +\def\baseconv@i#1#2#3{% #1=restes #2=n #3=base + \ifnum#2=\z@ \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {#1}% si n=0, afficher tous les restes + {% si non, transmettre en dernier argument + \expsecond{\baseconv@ii{#1}{#2}{#3}}% + {\number\truncdiv{#2}{#3}}% le quotient + }% +} +\def\baseconv@ii#1#2#3#4{% #1=restes #2=n #3=base #4=q + \exparg\baseconv@i% recommencer, en ajoutant le <chiffre> avant les restes + {\romannumeral\basedigit{\number\numexpr#2-#4*#3\relax}#1}% + {#4}% et en remplaçant n par q + {#3}% +} +\def\z@@{\expandafter\z@\expandafter}% +\def\basedigit#1{% + \ifcase#1 + \z@@ 0% + \or\z@@ 1\or\z@@ 2\or\z@@ 3\or\z@@ 4\or\z@@ 5\or\z@@ 6\or\z@@ 7% + \or\z@@ 8\or\z@@ 9\or\z@@ A\or\z@@ B\or\z@@ C\or\z@@ D\or\z@@ E% + \or\z@@ F\or\z@@ G\or\z@@ H\or\z@@ I\or\z@@ J\or\z@@ K\or\z@@ L% + \or\z@@ M\or\z@@ N\or\z@@ O\or\z@@ P\or\z@@ Q\or\z@@ R\or\z@@ S% + \or\z@@ T\or\z@@ U\or\z@@ V\or\z@@ W\or\z@@ X\or\z@@ Y\or\z@@ Z% + \fi +} +\catcode`\@12 +a) "\baseconv{20}{21587}"\qquad +b) "\baseconv{16}{32}"\qquad +c) \edef\foo{\baseconv{16}{159}}% + "\meaning\foo"\qquad +d) "\baseconv{2}{43}" +****************** Fin code ****************** + + +****************** Code 313 ****************** +\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\finditem#1{% #1 = \<macrolist>, la position est lue plus tard par \finditem@i + \exparg\finditem@i{#1}% 1-développe la \<macrolist> +} +\def\finditem@i#1#2{% #1 = liste #2=position cherchée + \finditem@ii{1}{#2}#1,\quark@list,% appelle la macro récursive +} +\def\finditem@ii#1#2#3,{% #1=position courante #2=position cherchée #3=élément courant + \ifx\quark@list#3\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si la fin de liste est atteinte, ne rien renvoyer + {% sinon + \ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si la position est la bonne + \finditem@iii{#3}% renvoyer #3 et manger les éléments restants + } + {% si la position n'est pas la bonne, recommencer en incrémentant #1 + \exparg\finditem@ii{\number\numexpr#1+1}{#2}% + }% + }% +} +\def\finditem@iii#1#2\quark@list,{% renvoyer #1 et manger le reste de la liste + #1% +} +\catcode`\@12 +\def\liste{a,bcd,{ef},g,hij,kl} +a) \edef\foo{\finditem\liste5}\meaning\foo\qquad +b) \edef\bar{\finditem\liste3}\meaning\bar +****************** Fin code ****************** + + +****************** Code 314 ****************** +\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\finditem#1{% #1 = \<macro>, la position est lue plus tard par \finditem@i + \exparg\finditem@i{#1}% 1-développe la \<macro> +} +\def\finditem@i#1#2{% #1 = liste #2=position cherchée + \ifnum#2>\z@% ne faire quelque chose que si la position est >0 + \antefi + \finditem@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive + \fi +} +\def\finditem@ii#1#2#3,{% #1=position courante #2=position cherché #3=élément courant + \expandafter\ifx\expandafter\quark@list\gobone#3% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si la fin de liste est atteinte, ne rien renvoyer + {% sinon + \ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si la position est la bonne + \finditem@iii{#3}% renvoyer #3 et manger les éléments restants + } + {% si la position n'est pas la bonne, recommencer en incrémentant #1 + \exparg\finditem@ii{\number\numexpr#1+1}{#2}\relax + }% + }% +} +\def\finditem@iii#1#2\quark@list,{% renvoyer #1 et manger le reste de la liste + \gobone#1% +} +\def\finditemtocs#1#2#3{% #1 = \<macro> #2=position #3=macro à définir + \def\finditemtocs@iii##1##2\quark@list,{% renvoyer #1 et manger le reste de la liste + \expandafter\def\expandafter#3\expandafter{\gobone##1}% + }% + \let#3=\empty + \exparg\finditemtocs@i{#1}{#2}% 1-développe la \<macro> +} +\def\finditemtocs@i#1#2{% #1 = liste #2=position cherchée + \ifnum#2>\z@% ne faire quelque chose que si la position est >0 + \antefi\finditemtocs@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive + \fi +} +\def\finditemtocs@ii#1#2#3,{% +% #1=position courante #2=position cherché #3=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone#3% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si fin de liste ne rien faire. Sinon, si position bonne + {\ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\finditemtocs@iii{#3}% renvoyer #3 et manger les éléments restants + }% si la position n'est pas la bonne, recommencer en incrémentant #1 + {\exparg\finditemtocs@ii{\number\numexpr#1+1}{#2}\relax% + }% + }% +} +\catcode`\@12 +\def\liste{a,bcd,{ef},g,hij,kl} +a) "\finditem\liste5"\qquad +b) \edef\bar{\finditem\liste3}\meaning\bar\qquad +c) \finditemtocs\liste{3}\foo\meaning\foo +****************** Fin code ****************** + + +****************** Code 315 ****************** +\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\positem#1{% #1 = \<macrolist>, la position est lue plus tard par \positem@i + \def\positem@endprocess{\pos@item}% hook : afficher la position + \exparg\positem@i{#1}% 1-développe la \<macrolist> +} +\def\positem@i#1#2{% #1 = liste #2=élément cherché + \def\sought@item{#2}% définir l'élément cherché + \positem@ii{1}\relax#1,\quark@list,% appelle la macro récursive +} +\def\positem@ii#1#2,{% #1=position courante #2=\relax + élément courant + \expandafter\def\expandafter\current@item\expandafter{\gobone#2}% + \ifx\current@item\quark@list\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\def\pos@item{0}% si la fin de liste est atteinte, renvoyer 0 + \positem@endprocess% et aller au hook + }% sinon + {\ifx\current@item\sought@item\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\def\pos@item{#1}% si la position est la bonne, définir la position + \positem@goblist% et manger les éléments restants + }% si la position n'est pas la bonne, recommencer en incrémentant #1 + {\exparg\positem@ii{\number\numexpr#1+1}\relax + }% + }% +} +\def\positem@goblist#1\quark@list,{\positem@endprocess}% manger la liste et aller au hook + +\def\positemtocs#1#2#3{% #1=\<macrolist> #2=élément à chercher #3=macro à définir + \def\positem@endprocess{\let#3=\pos@item}% hook : mettre le résultat dans #3 + \exparg\positem@i{#1}{#2}% 1-développe la \<macrolist> +} + +\catcode`\@12 +\def\liste{a,bcd,{ef},g,hij,,kl} +a) \positem\liste{g}\qquad +b) \positem\liste{ef}\qquad +c) \positem\liste{{ef}}\qquad +d) \positem\liste{}\medbreak + +\def\liste{a,bcd,{ef},g,hij,,kl} +a) \positemtocs\liste{g}\foo\meaning\foo\qquad +b) \positemtocs\liste{ef}\foo\meaning\foo\qquad +c) \positemtocs\liste{{ef}}\foo\meaning\foo\qquad +d) \positemtocs\liste{}\foo\meaning\foo\qquad +****************** Fin code ****************** + + +****************** Code 316 ****************** +\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\insitem#1#2#3{% #1 = macro #2=position cherchée #3=élément à insérer + \let\item@list=\empty + \ifnum#2<1 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si position < 1 + {\addtomacro\item@list{#3,}% ajouter l'élement à insérer en premier + \eaddtomacro\item@list#1% puis la liste entière + } + % si la position > 1 + {% définir la macro récursive + \def\insitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone##2% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\addtomacro\item@list{#3}}% si fin de liste, ajouter l'élément en dernier + {% sinon, si la position cherchée est atteinte + \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\addtomacro\item@list{#3,}% ajouter l'élement + \add@remainlist##2,% et ##2 (en supprimant le \relax) et le reste + }% si la position n'est pas atteinte + {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément + \exparg\insitem@i{\number\numexpr##1+1}\relax% et recommencer + }% + }% + }% + % appel de la macro récursive + \expandafter\insitem@i\expandafter1\expandafter\relax#1,\quark@list,% + }% + \let#1=\item@list% rendre #1 égal au résultat +} + +\def\add@remainlist#1,\quark@list,{% + \eaddtomacro\item@list{\gobone#1}% ajouter #1 ainsi que les autres +} +\catcode`\@12 +\def\liste{a,bra,{cA},da,brA}\insitem\liste3{XX}\meaning\liste\par +\def\liste{a,bra,{cA},da,brA}\insitem\liste0{XX}\meaning\liste\par +\def\liste{a,bra,{cA},da,brA}\insitem\liste9{XX}\meaning\liste +****************** Fin code ****************** + + +****************** Code 317 ****************** +\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\delitem#1#2{% #1 = macro #2=position cherchée + \ifnum#2>0 % ne faire quelque chose que si la position est >0 + \def\delitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone##2% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si fin de liste, ne rien faire + {% sinon, si la position cherchée est atteinte + \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\add@reaminingitems% et ##2 (en supprimant le \relax) et le reste + }% si la position n'est pas la bonne + {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément + \exparg\delitem@i{\number\numexpr##1+1}\relax% et recommencer + }% + }% + }% + \let\item@list=\empty% initialiser la macro tempporaire + % appel de la macro récursive + \expandafter\delitem@i\expandafter1\expandafter\relax#1,\quark@list,% + \let#1=\item@list% rendre #1 égal au résultat + \fi +} + +\def\add@reaminingitems#1\quark@list,{% + \eaddtomacro\item@list{#1}% ajouter tout jusqu'au quark +} +\catcode`\@12 +\for\xx=0 to 8 \do{% +\def\liste{a,bcd,{ef},g,hij,kl} +position \xx{} : \expandafter\delitem\expandafter\liste\xx\meaning\liste.\par +} +****************** Fin code ****************** + + +****************** Code 318 ****************** +\catcode`\@11 +\def\moveitem#1#2#3{% #1 = liste, #2=position départ, #3=position arrivée + \ifnum#2>0 % ne faire quemque chose que si #2>0 + \finditemtocs#1{#2}\temp@item% sauvegarder l'élément + \delitem#1{#2}% supprimer l'élément + \expsecond{\insitem#1{#3}}\temp@item% insérer l'élément + \fi +} +\catcode`\@12 +% déplace "b" en 5e position +a) \def\liste{a,b,c,d,e,f} \moveitem\liste25 "\liste"\par +% déplace "d" en 1e position +b) \def\liste{a,b,c,d,e,f} \moveitem\liste41 "\liste"\par +% déplace "c" en 9e position +c) \def\liste{a,b,c,d,e,f} \moveitem\liste39 \liste"\par +% position départ=0 -> sans effet +d) \def\liste{a,b,c,d,e,f} \moveitem\liste02 "\liste" +****************** Fin code ****************** + + +****************** Code 319 ****************** +\catcode`\@11 +\def\runlist#1\with#2{% #1=liste #2=macro + \def\runlist@i##1,{% + \ifx\quark@list##1\relax\else% si la fin n'est pas atteinte + \addtomacro\collect@run{#2{##1}}% ajouter "\<macro>{<élément>}"" + \expandafter\runlist@i% et recommencer en lisant l'élément suivant + \fi + }% + \begingroup% fait la collecte dans un groupe + \let\collect@run=\empty% initialiser la macro + \expandafter\runlist@i#1,\quark@list,% appeler \runlist@i + \expandafter\endgroup% ferme le groupe et détruit \collect@run + \collect@run% après l'avoir développé ! +} +\catcode`\@12 +\newcount\foocnt +\foocnt=0 % compteur utilisé pour numéroter les éléments dans la macro \foo +\def\foo#1{% la macro qui va exécuter chaque élément de la liste. #1 = l'élément + \advance\foocnt1 % incrémente le compteur + L'argument \number\foocnt\ est : {\bf #1}\par% +} +\def\liste{a,bra,ca,da,BRA}% +\runlist\liste\with\foo% +****************** Fin code ****************** + + +****************** Code 320 ****************** +\catcode`\@11 +\def\ifspacefirst#1{% + \expandafter\ifspacefirst@i\detokenize{#1W} \@nil% "W" se prémunit d'un argument vide +} +\def\ifspacefirst@i#1 #2\@nil{\ifempty{#1}}% renvoyer vrai s'il n'y a rien avant " " +\catcode`\@12 +a) \ifspacefirst{a bc d}{vrai}{faux}\qquad +b) \ifspacefirst{ a bc d}{vrai}{faux}\qquad +c) \ifspacefirst{ }{vrai}{faux}\qquad +d) \ifspacefirst{}{vrai}{faux}\qquad +e) \ifspacefirst{{ } }{vrai}{faux}\qquad +f) \ifspacefirst{ {x} }{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 321 ****************** +\catcode`\@11 +\expandafter\def\expandafter\gobspace\space{} + +\def\removefirstspaces#1{% + \ifspacefirst{#1}% si #1 commence par un espace + {\exparg\removefirstspaces{\gobspace#1}}% recommencer sans le 1er espace + {#1}% sinon, renvoyer l'argument +} +\catcode`\@12 +a) "\removefirstspaces{12 {\bf3}4 567}"\qquad +b) "\removefirstspaces{ 12 {\bf3}4 567}"\qquad +c) \edef\foo{\space\space\space 12 34 567} + "\exparg\removefirstspaces\foo" +****************** Fin code ****************** + + +****************** Code 322 ****************** +\catcode`\@11 +\def\removefirstspaces{% + \romannumeral% lance le développement maximal + \removefirstspaces@i% et passe la main à la macro récursive +} + +\def\removefirstspaces@i#1{% + \ifspacefirst{#1}% si #1 commence par un espace + {\exparg\removefirstspaces@i{\gobspace#1}}% recommencer sans le 1er espace + {\z@#1}% sinon, renvoyer l'argument où \z@ stoppe l'action de \romannumeral +} +\catcode`\@12 +\long\def\>#1<{"\detokenize{#1}"} + +a) \expandafter\expandafter\expandafter\>\removefirstspaces{12 {\bf3}4 567}<\qquad +b) \expandafter\expandafter\expandafter\>\removefirstspaces{ 12 {\bf3}4 567}<\qquad +c) "\removefirstspaces{ 12 {\bf3}4 }" +****************** Fin code ****************** + + +****************** Code 323 ****************** +\catcode`\@11 +\edef\catcodezero@saved{\number\catcode0 }% stocke le catcode de ^^00 +\catcode0=12 % le modifie à 12 +\def\removelastspaces#1{% + \romannumeral% lance le développement maximal + \removelastspaces@i\relax#1^^00 ^^00\@nil% mettre un \relax au début + % et passer la main à \removelastspaces@i +} +\def\removelastspaces@i#1 ^^00{% prendre ce qui est avant " W" + \removelastspaces@ii#1^^00% ajouter "W" +} +\def\removelastspaces@ii#1^^00#2\@nil{% #1=ce qui est avant "W" #2=reliquat + \ifspacefirst{#2}% si le reliquat commence par un espace + {\removelastspaces@i#1^^00 ^^00\@nil}% recommencer sans passer par \removelastspaces + {\expandafter\z@\gobone#1}% sinon supprimer le \relax ajouté au début + % et stopper l'action de \romannumeral avec \z@ +} +\catcode0=\catcodezero@saved\relax% restaure le catcode de ^^00 +\catcode`\@12 +\long\def\>#1<{"\detokenize{#1}"} + +a) \expandafter\expandafter\expandafter\>\removelastspaces{ 12 {\bf3}4 }<\qquad +b) \expandafter\expandafter\expandafter\>\removelastspaces{12 {\bf3}4}<\qquad +c) "\removelastspaces{ 12 {\bf3}4 }" +****************** Fin code ****************** + + +****************** Code 324 ****************** +\catcode`\@11 +\def\removetrailspaces#1{% + \romannumeral% lance le développement maximal + \expandafter\expandafter\expandafter% le pont d'\expandafter + \removelastspaces + \expandafter\expandafter\expandafter% fait agir \removefirstspaces en premier + {% + \expandafter\expandafter\expandafter + \z@% stoppe le développement initié par \romannumeral + \removefirstspaces{#1}% + }% +} +\catcode`\@12 +\long\def\>#1<{"\detokenize{#1}"} + +a) \expandafter\expandafter\expandafter\>\removetrailspaces{ 12 {\bf3}4 }<\qquad +b) \expandafter\expandafter\expandafter\>\removetrailspaces{12 {\bf3}4}<\par +c) \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter + \foo\expandafter\expandafter\expandafter{\removetrailspaces{ 12 {\bf3}4 }}% + signification : \meaning\foo.\par +c) exécution : "\foo"\par +d) "\removetrailspaces{ 12 {\bf3}4 }" +****************** Fin code ****************** + + +****************** Code 325 ****************** +\catcode`\@11 +\def\sanitizelist#1{% #1 = liste + \let\item@list\empty% initialise le réceptacle de la liste assainie + \def\sanitizelist@endprocess{% définit le hook de fin + \expandafter\remove@lastcomma\item@list\@nil% supprimer dernière virgule + \let#1=\item@list% et assigner le résultat à #1 + }% + \expandafter\sanitizelist@i\expandafter\relax#1,\quark@list,% aller à la macro récursive +} + +\def\sanitizelist@i#1,{% #1="\relax" + élément courant + \expandafter\ifx\expandafter\quark@list\gobone#1% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\sanitizelist@endprocess% si fin de liste, hook de fin + }% si la fin de la liste n'est pas atteinte : + {\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter + \addtomacro% 1-développer \gobone pour retirer \relax + \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter + \item@list% puis 2-développer \removetrailspaces + \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter + {\expandafter\removetrailspaces\expandafter{\gobone#1},}% "#1" + \sanitizelist@i\relax% et continuer avec l'élément suivant + }% +} + +\catcode`\@12 +\frboxsep=0pt % encadrer au plus proche +\def\foo#1{\frbox{\tt\strut#1} }% boite avec un strut et en fonte "tt" puis espace +\def\liste{ Programmer, en \TeX{} ,est ,{\bf facile}, et utile } +a) \runlist\liste\with\foo% encadrer les items + +b) \sanitizelist\liste% supprimer les espaces inutiles des items + \runlist\liste\with\foo +****************** Fin code ****************** + + +****************** Code 326 ****************** +\catcode`\@11 +\def\boxsentence#1{% + \leavevmode% se mettre en mode horizontal + \boxsentence@i#1\quark% transmet "#1+\quark" à boxsentence@i +} +\def\boxsentence@i#1{% #1= argument lu + \def\current@arg{#1}% stocke l'argument dans une macro temporaire + \unless\ifx\quark\current@arg% si la fin n'est pas atteinte + \frbox{#1}% encadrer cet argument + \expandafter\boxsentence@i% lire l'argument suivant + \fi +} +\catcode`@12 +\frboxsep=1pt \frboxrule=0.2pt +\boxsentence{Programmer en \TeX\ est facile} +****************** Fin code ****************** + + +****************** Code 327 ****************** +\catcode`\@11 +\edef\star@macro{\string *}% stocke une étoile de catcode 12 +\def\expo#1{% + \def\temp@arg{#1}% stocke l'argument lu + \ifx\star@macro\temp@arg\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si l'argument est une étoile de catcode 12 + $^\dag$% affiche une dague + }% sinon + {$^\ddag$% affiche une double dague + {#1}% puis ré-écrit l'argument {#1} + }% +} +A\expo B\expo*C +****************** Fin code ****************** + + +****************** Code 328 ****************** +\def\boxsentence#1{% + \readtok#1\quark% met le token d'arrêt \quark à la fin de #1 +} +\def\readtok{\afterassignment\cmptok\let\nxttok= } +\def\cmptok{% + \unless\ifx\nxttok\quark% si la fin n'est pas atteinte + \frbox{\nxttok}% encadrer le token lu + \expandafter\readtok% puis aller lire le suivant + \fi +} +\frboxsep=1pt \frboxrule=0.2pt +\leavevmode\boxsentence{Programmer en \TeX\ est facile} +****************** Fin code ****************** + + +****************** Code 329 ****************** +\def\permutstart{\afterassignment\cmptok\let\nxttok= } +\def\permutend{\permutend}% +\def\cmptok{% + \unless\ifx\permutend\nxttok% tant que la fin n'est pas atteinte : + \ifxcase\nxttok + ae% si le token lu est "a", afficher un "e" + ei io ou uy ya% etc pour les autres lettres + \elseif + \nxttok% si ce n'est aucune voyelle, afficher le token + \endif + \expandafter\permutstart% aller lire le token suivant + \fi +} +\permutstart Un "a" puis "e puis "i" ensuite, un "o", un "u" et "y".\permutend +****************** Fin code ****************** + + +****************** Code 330 ****************** +\def\boxsentence#1{\readtok#1\boxsentence} +\def\readtok{\afterassignment\cmptok\let\nxttok= } +\def\cmptok{% + %\show\nxttok% à décommenter pour débogage + \unless\ifx\nxttok\boxsentence + \frbox{\nxttok}% + \expandafter\readtok + \fi +} +\frboxsep=1pt \frboxrule=0.2pt +\leavevmode\boxsentence{Pro{gra}mmer en \TeX\ est facile} +****************** Fin code ****************** + + +****************** Code 331 ****************** +\catcode`\@11 +\def\Litterate{% + \begingroup% ouvrir un groupe + \tt% et adopter une fonte à chasse fixe + \afterassignment\Litterate@i% après l'assignation, aller à \Litterate@i + \expandafter\let\expandafter\lim@tok\expandafter=\string% \lim@tok = token délimiteur +} +\def\Litterate@i{% + \afterassignment\Litterate@ii%après avoir lu le prochain token, aller à \Litterate@ii + \expandafter\let\expandafter\nxttok\expandafter=\string% lit le token suivant +} +\def\Litterate@ii{% + \ifx\nxttok\lim@tok% si le token suivant="token délimiteur" + \endgroup% fermer le groupe et finir + \else + \nxttok% sinon, afficher ce token + \expandafter\Litterate@i% et lire le token suivant + \fi +} +\catcode`\@12 +\Litterate|Programmer en \TeX {} est << facile >> !| +****************** Fin code ****************** + + +****************** Code 332 ****************** +\catcode`\@11 +\def\Litterate{% + \begingroup% ouvrir un groupe + \tt% et adopter une fonte à chasse fixe + \afterassignment\Litterate@i% après l'assignation, aller à \Litterate@i + \expandafter\let\expandafter\lim@tok\expandafter=\string% \lim@tok = token délimiteur +} +\def\Litterate@i{% + \afterassignment\Litterate@ii% après avoir lu un token, aller à \Litterate@ii + \expandafter\expandafter\expandafter% 1-développer \string + \let + \expandafter\expandafter\expandafter% puis 1-développer \space en " " + \nxttok + \expandafter\expandafter\expandafter + =\expandafter\space\string% et lire le token obtenu +} +\def\Litterate@ii{% + \ifx\nxttok\lim@tok% si le token suivant="token délimiteur" + \endgroup% fermer le groupe et finir + \else + \nxttok% sinon, afficher ce token + \expandafter\Litterate@i% et lire le token suivant + \fi +} +\catcode`\@12 +\Litterate|Programmer en \TeX {} est << facile >> : $~$^_#| +****************** Fin code ****************** + + +****************** Code 333 ****************** +\catcode`@11 +\newskip\ltr@spc +\def\letterspace#1#2{% + \ltr@spc=#1\relax % assigne le ressort + \letterspace@i#2\letterspace@i% appelle la macro \letterspace@i +} +\def\letterspace@i{% + \afterassignment\letterspace@ii + \let\nxttok= +} +\def\letterspace@ii{% + \ifx\nxttok\letterspace@i% si la fin est atteinte + \unskip% supprimer le dernier ressort qui est de trop + \else + \nxttok% afficher le token + \hskip\ltr@spc\relax% insère le ressort + \expandafter\letterspace@i% va lire le token suivant + \fi +} +\catcode`@12 +Voici "\letterspace{0.3em}{des lettres espacées}" +et voici "\letterspace{-0.075em}{de la compression}". +****************** Fin code ****************** + + +****************** Code 334 ****************** +\catcode`@11 +\newcount\test@cnt +\def\ifinteger#1{% + \ifstart{#1}{-}% si "-" est au début de #1 + {\exparg\ifinteger{\gobone#1}% l'enlever et recommencer + } + {\ifempty{#1}% sinon, si #1 est vide + \secondoftwo% lire l'argument <faux> + {\afterassignment\after@number% sinon, après l'assignation, aller à \after@number + \test@cnt=0#1\relax% faire l'assignation + %(\relax termine le nombre et n'est pas mangé) + }% + }% +} +\def\after@number#1\relax{% #1 est ce qui reste après l'assignation + \ifempty{#1}% teste si #1 est vide et lit l'argument <vrai> ou <faux> qui suit +} + +\catcode`@12 +1) \ifinteger{5644}{oui}{non}\qquad +2) \ifinteger{-987}{oui}{non}\qquad +3) \ifinteger{6a87}{oui}{non}\qquad +4) \ifinteger{abcd}{oui}{non}\qquad +5) \ifinteger{-a}{oui}{non}\qquad +6) \ifinteger{-}{oui}{non}\qquad +7) \ifinteger{3.14}{oui}{non} +****************** Fin code ****************** + + +****************** Code 335 ****************** +\catcode`\@11 +\def\teststar{% + \futurelet\nxttok\teststar@i% % pioche le token suivant puis aller à \teststar@i +} +\def\teststar@i{% + \ifx *\nxttok\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si le token suivant est une étoile + \afterassignment\teststar@bf% après avoir lu "*", aller à \teststar@bf + \let\nxttok= % lire l'étoile + }% si le token n'est pas une étoile + {\teststar@it% aller à \teststar@it + }% +} +\def\teststar@bf#1{{\bf#1}}% lit l'argument et le compose en gras dans un groupe +\def\teststar@it#1{{\it#1\/}}% lit l'argument et le compose en italique +\catcode`\@12 +Un essai \teststar{réussi} et un essai \teststar*{étoilé} réussi aussi. +****************** Fin code ****************** + + +****************** Code 336 ****************** +\catcode`@11 +\def\deftok#1#2{\let#1= #2\empty}% définit le token #1 (\empty au cas où #2 est vide) +\deftok\sptoken{ } +\def\ifnexttok#1#2#3{% lit les 3 arguments : #1=token #2=code vrai #3=code faux + \deftok\test@tok{#1}% stocke le token cherché + \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter + \ifnexttok@i% aller à la macro récursive +} +\def\ifnexttok@i{% + \futurelet\nxttok\ifnexttok@ii% piocher le token d'après et aller à \ifnexttok@ii +} +\def\ifnexttok@ii{% + \ifx\nxttok\sptoken% si le prochain token est un espace + \def\donext{% + \afterassignment\ifnexttok@i% recommencer après + \let\nxttok= % après avoir absorbé cet espace + }% + \else + \ifx\nxttok\test@tok% si le prochain token est celui cherché + \let\donext\true@code% exécuter le code vrai + \else + \let\donext\false@code% sinon code faux + \fi + \fi + \donext% faire l'action décidée ci-dessus +} +\catcode`@12 +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }a.\par +\ifnexttok *{je vais lire une étoile : }{je ne vais pas lire une étoile : } *.\par +\ifnexttok *{je vais lire une étoile : }{je ne vais pas lire une étoile : }a. +****************** Fin code ****************** + + +****************** Code 337 ****************** +\catcode`@11 +\newif\iftestspace \testspacefalse +\def\deftok#1#2{\let#1= #2}\deftok\sptoken{ } +\def\ifnexttok#1#2#3{% #1=token #2=code vrai #3=code faux + \let\test@tok= #1% stocke le token à tester + \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter + \iftestspace \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@ii}% + \else \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@iii}% + \fi% après avoir défini la macro récursive selon le booléen, + \ifnexttok@i% l'exécuter +} +\def\ifnexttok@ii{% macro "normale" qui ne teste pas les espaces + \ifx\nxttok\test@tok \expandafter\true@code% exécuter le code vrai + \else \expandafter\false@code% sinon code faux + \fi +} +\def\ifnexttok@iii{% macro qui ignore les espaces + \ifx\nxttok\sptoken% si le prochain token est un espace + \def\donext{% + \afterassignment\ifnexttok@i% lire le token d'après + \let\nxttok= % après avoir absorbé l'espace + }% + \else + \let\donext\ifnexttok@ii% sinon, faire le test "normal" + \fi + \donext% faire l'action décidée ci-dessus +} +\catcode`@12 +\testspacefalse +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : } W.\medbreak +\testspacetrue +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : } W. +****************** Fin code ****************** + + +****************** Code 338 ****************** +\def\ifstarred#1{\ifnexttok*{\firstoftwo{#1}}} +\catcode`\@11 +\def\teststar{\ifstarred{\teststar@bf}{\teststar@it}} +\def\teststar@bf#1{{\bf#1}} +\def\teststar@it#1{{\it#1\/}} +\catcode`\@12 + +Un essai \teststar{réussi} et un essai \teststar*{étoilé} réussi aussi. +****************** Fin code ****************** + + +****************** Code 339 ****************** +\catcode`\@11 +\def\parsestop{\parsestop}% définit la macro-quark se trouvant en fin de code +\newtoks\code@toks% registre contenant le code lu +\def\parseadd{\addtotoks\code@toks} +\def\parse{% + \code@toks={}% initialise \code@toks à vide + \parse@i% et passe la main à \parse@i +} +\def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token et va à \parse@ii +\def\parse@ii{% + \ifxcase\nxttok + \parsestop\parsestop@i% si la fin va être atteinte, aller à \parsestop@i + \sptoken\read@space% si un espace va être lu, aller à \read@space + \bgroup\read@bracearg% si une accolade ouvrante aller à \read@bracearg + \elseif + \testtoken% dans les autres cas, aller à \testtoken + \endif +} + +\def\parsestop@i\parsestop{% la fin est atteinte : manger \parsestop + \the\code@toks% afficher le registre de tokens +} +\expandafter\def\expandafter\read@space\space{% manger un espace dans le code + \testtoken{ }% et aller à \testtoken +} +\def\read@bracearg#1{% l'argument entre accolades est lu + \parseadd{{#1}}% puis ajouté (entre accolades) tel quel à \code@toks + \parse@i% ensuite, lire le prochain token +} +\catcode`@12 + +{\bf Exemple 1 :} +\catcode`@11 +\def\testtoken#1{% macro qui teste le token + \ifxcase{#1} + a{\parseadd{e}}% remplacer a par e + e{\parseadd{i}}% e par i + i{\parseadd{o}}% i par o + o{\parseadd{u}}% o par u + u{\parseadd{y}}% u par y + y{\parseadd{a}}% y par a + \elseif + \parseadd{#1}% sinon, ajouter le token tel quel + \endif + \parse@i% aller lire le token suivant +}% +\catcode`@12 +\parse +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, corses ou grecques} assez inattendues. +\parsestop\medbreak + +{\bf Exemple 2 :} +\leavevmode \frboxsep=1pt +\catcode`@11 +\def\testtoken#1{% + \ifxcase{#1}% si #1 est un espace + { }{\parseadd{\hskip 0.75em }}% ajouter un espace + \ {\parseadd{\hskip 0.75em }} + \elseif + \parseadd{\frbox{#1}}% sinon, l'encadrer + \endif + \parse@i}% +\catcode`@12 +\parse Programmer en \TeX\ est facile\parsestop\medbreak + +{\bf Exemple 3 :} +\catcode`@11 +\def\testtoken#1{% + \ifx a#1\parseadd{X}\else\parseadd{#1}\fi% remplace les "a" par des "X" + \parse@i +} +\catcode`@12 +\parse a\bgroup\bf braca\egroup dabra\parsestop +****************** Fin code ****************** + + +****************** Code 340 ****************** +\catcode`\@11 +\def\ifbracefirst#1{% + \ifnum\catcode\expandafter\expandafter\expandafter + `\expandafter\firstto@nil\detokenize{#1W}\@nil=1 % tester si son catcode est 1 + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi +} +\catcode`\@12 + +a) \ifbracefirst{123 456}{vrai}{faux}\qquad +b) \ifbracefirst{\bgroup12\egroup3 456}{vrai}{faux}\qquad +c) \ifbracefirst{{12}3 456}{vrai}{faux}\qquad +d) \ifbracefirst{1{2}3 456}{vrai}{faux}\qquad +\begingroup +\catcode`[=1 \catcode`]=2 % les crochets deviennent des accolades +e) \ifbracefirst[[]123 456][vrai][faux] +\endgroup +****************** Fin code ****************** + + +****************** Code 341 ****************** +\catcode`\@11 +\def\ifbracefirst#1{% teste si #1 commence par un token de catcode 1 + \ifspacefirst{#1}% si #1 commence par un espace + {\secondoftwo}% renvoyer faux + {\ifnum\catcode\expandafter\expandafter\expandafter + `\expandafter\firstto@nil\detokenize{#1W}\@nil=1 % tester si son catcode est 1 + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi + }% +} + +\catcode`\@12 +a) \ifbracefirst{}{vrai}{faux}\qquad b) \ifbracefirst{ }{vrai}{faux}\qquad +c) \ifbracefirst{ {}}{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 342 ****************** +\catcode`\@11 +\def\parsestop@i\parsestop{% la fin va être atteinte + \detokenize\expandafter{\the\code@toks}% afficher le contenu du registre +} +\def\read@bracearg{% + \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i +} +\def\read@bracearg@i#1\parsestop{% #1 = tout jusqu'à \parsestop + \exparg\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{" + {\expandafter\read@bracearg@ii\gobone#1\parsestop}% si oui, aller à \readbrace@ii + {\expandafter\testtoken\gobone#1\parsestop}% sinon, aller à \testtoken +} +\def\read@bracearg@ii#1{% lit l'argument entre accolades + \parseadd{{#1}}% ajoute cet argument entre accolades + \parse@i% aller lire le token suivant +} + +\def\testtoken#1{% + \parseadd{#1}% ajouter le token tel quel + \parse@i% aller lire le token suivant +} +\catcode`\@12 +\parse a\hbox\bgroup\bf braca\egroup {da}{}b{ra}\parsestop +****************** Fin code ****************** + + +****************** Code 343 ****************** +\catcode`\@11 +\def\parsestop{\parsestop}% définit le quark se trouvant en fin de code +\newtoks\code@toks% alloue le registre contenant le code lu +\def\parseadd#1{\code@toks\expandafter{\the\code@toks#1}} +\newif\ifparse@group +\def\parse{% + \code@toks{}% initialise le collecteur de tokens + \ifstarred% teste si la macro est étoilée + {\parse@grouptrue\parse@i}% mettre le booléen à vrai + {\parse@groupfalse\parse@i}% sinon à faux +} +\def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token + % et va à \parse@ii +\def\parse@ii{% + \ifxcase\nxttok + \parsestop\parsestop@i% si la fin va être atteinte, aller à \parsestop@i + \sptoken\read@space% si un espace va être lu, aller à \read@space + \bgroup\read@bracearg% si une accolade ouvrante aller à \read@bracearg + \elseif + \testtoken% dans les autres cas, aller à \testtoken + \endif +} +\def\parsestop@i\parsestop{% la fin est atteinte + \the\code@toks% afficher le registre de tokens +} +\expandafter\def\expandafter\read@space\space{% \read@space mange un espace dans le code + \testtoken{ }% et va à \testtoken +} +\def\read@bracearg{% + \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i +} +\def\read@bracearg@i#1\parsestop{% l'argument tout jusqu'à \parsestop + \expsecond\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{" + {\expandafter\read@bracearg@ii\gobone#1\parsestop}% lire l'argument entre accolades + {\expandafter\testtoken\gobone#1\parsestop}% sinon, tester le token +} +\def\read@bracearg@ii#1{% l'argument entre accolades est lu + \ifparse@group\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% si macro étoilée + {\begingroup% ouvre un groupe pour parser l'intérieur de l'accolade + \def\parsestop@i\parsestop{% redéfinir localement \parsestop@i pour + \expandafter\endgroup% ne fermer le groupe qu'après avoir + \expandafter\parseadd% 1-développé à l'extérieur du groupe + \expandafter{\expandafter{\the\code@toks}}% + \parse@i% puis va lire le token suivant + }% + \parse*#1\parsestop% <- le \parsestop@i fermera le groupe semi-simple + } + {\parseadd{{#1}}% macro non étoilée, on ajoute #1 tel quel entre accolades + \parse@i% puis va lire le token suivant + }% +} +\def\testtoken#1{% macro qui teste le token + \ifxcase{#1} + a{\parseadd{e}} + e{\parseadd{i}} + i{\parseadd{o}} + o{\parseadd{u}} + u{\parseadd{y}} + y{\parseadd{a}} + \elseif + \parseadd{#1}% + \endif + \parse@i% aller lire le token suivant +} +\catcode`@12 +\frboxsep=1pt +a) \parse +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues. +\parsestop\medbreak + +b) \parse* +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues. +\parsestop +****************** Fin code ****************** + + +****************** Code 344 ****************** +\catcode`\@11 +\def\parse{% + \code@toks{}% initialise le collecteur de tokens + \ifstarred + {\parse@grouptrue + \ifnexttok{ }% si un espace suit l'étoile + {\afterassignment\parse@i% aller à \parse@i + \let\nxttok= }% après l'avoir mangé + {\parse@i}% sinon, aller à \parse@i + } + {\parse@groupfalse + \parse@i + }% +} +\def\testtoken#1{% macro qui teste le token + \ifxcase{#1} + a{\parseadd{e}}e{\parseadd{i}}i{\parseadd{o}}o{\parseadd{u}} + u{\parseadd{y}}y{\parseadd{a}} + \elseif + \parseadd{#1}% + \endif + \parse@i% aller lire le token suivant +} +\catcode`@12 +\frboxsep=1pt +a) \parse +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues. +\parsestop\medbreak +b) \parse* +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues. +\parsestop +****************** Fin code ****************** + + +****************** Code 345 ****************** +\catcode`\@11 +\def\grab@first#1#2{% + \ifx#1\empty\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\let#2\empty% si #1 est vide, ne rien faire et assigner <vide> à #2 + }% si #1 n'est pas vide + {\def\arg@b{#2}% stocke la macro #2 dans \arg@b + \exparg\ifbracefirst#1% si le 1er token de #1 est "{" + {\expandafter\grab@arg#1\@nil#1% aller lire l'argument avec \grab@arg + } + {% sinon, développer #1 avant de le regarder avec \futurelet : + \expandafter\futurelet\expandafter\nxttok\expandafter\test@nxttok#1\@nil#1% + % puis aller à \test@nxttok + }% + }% +} +\def\test@nxttok{% si le premier token de l'arg #1 de \grab@first est + \ifx\nxttok\sptoken% un espace + \expandafter\grab@spc% aller le lire avec \grab@spc + \else + \expandafter\grab@tok% sinon, lire le token avec \grab@tok + \fi +} +\def\grab@arg#1{% assigne l'argument de \grab@first (mis entre accolades) + \expandafter\def\arg@b{{#1}}% à #2 + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +\expandafter\def\expandafter\grab@spc\space{% + \expandafter\def\arg@b{ }% assigne un espace à #2 de \grab@first + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +\def\grab@tok#1{%% assigne le premier token de l'arg #1 de \grab@first + \expandafter\def\arg@b{#1}% à la macro #2 de \grab@first + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +% assigne tout ce qui reste à lire (moins le "\relax") à la macro +% #1 de \grab@first +\def\assign@tonil#1\@nil#2{\expsecond{\def#2}{\gobone#1}} +\tt +a) \def\foo{1 {2 3} 4}\grab@first\foo\bar + "\meaning\bar"\qquad"\meaning\foo" + +b) \def\foo{ 1 {2 3} 4}\grab@first\foo\bar + "\meaning\bar"\qquad"\meaning\foo"" + +c) \def\foo{{1 2} 3 4}\grab@first\foo\bar + "\meaning\bar"\qquad"\meaning\foo" +****************** Fin code ****************** + + +****************** Code 346 ****************** +\catcode`\@11 +\def\ifstartwith#1#2{% #1=<texte> #2=<motif> + \ifempty{#2} + \firstoftwo% si <motif> est vide, renvoyer vrai + {\ifempty{#1}% si <code> est vide et <motif> non vide + \secondoftwo% renvoyer faux + {\def\startwith@code{#1}\def\startwith@pattern{#2}% + \ifstartwith@i% dans les autres cas, aller à \ifstartwith@i + }% + }% +} +\def\ifstartwith@i{% + \grab@first\startwith@code\first@code% extrait le premier "argument" de <texte> + \grab@first\startwith@pattern\first@pattern% et celui de <motif> + \ifx\first@code\first@pattern\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si les deux arguments élargis sont égaux + \exparg\ifempty\startwith@pattern + \firstoftwo% et que <motif> ne contient plus rien => vrai + {\exparg\ifempty\startwith@code + \secondoftwo% si <texte> ne contient plus rien => faux + \ifstartwith@i% sinon poursuivre les tests + }% + } + \secondoftwo% si les deux argument élargis sont différents, renvoyer faux +} +\catcode`\@12 + +1) \ifstartwith{a b c}{a b}{oui}{non}\quad 2) \ifstartwith{a b}{a b c}{oui}{non}\quad +3) \ifstartwith{ 123 }{ }{oui}{non}\quad 4) \ifstartwith{ 123 }{1}{oui}{non}\quad +5) \ifstartwith{1{2} 3}{12}{oui}{non}\quad 6) \ifstartwith{1{2} 3}{1{2} }{oui}{non}\quad +7) \ifstartwith{{} {}}{ }{oui}{non}\quad 8) \ifstartwith{{} {}}{{} }{oui}{non}\quad +9) \ifstartwith{ {12} a}{ }{oui}{non}\quad10) \ifstartwith{ {12} a}{ {1}}{oui}{non} +****************** Fin code ****************** + + +****************** Code 347 ****************** +\catcode`\@11 +\def\ifcontain#1#2{% #1 contient-il #2? + \def\main@arg{#1}\def\pattern@arg{#2}% stocke le <code> et le <motif> + \ifempty{#2} + \firstoftwo% si #2 est vide => vrai + {\ifempty{#1} + \secondoftwo% si #1 est vide et pas #2 => faux + \ifcontain@i% sinon, aller faire les tests + }% +} +\def\ifcontain@i{% + \exptwoargs\ifstartwith\main@arg\pattern@arg + \firstoftwo% si motif est au début de code => vrai + {\exparg\ifempty\main@arg + \secondoftwo% sinon, si code est vide => faux + {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code + \ifcontain@i% et recommencer + }% + }% +} +\catcode`\@12 +1) \ifcontain{abc def}{c }{oui}{non}\quad 2) \ifcontain{abc def}{cd}{oui}{non}\quad +3) \ifcontain{12 34 5}{1 }{oui}{non}\quad 4) \ifcontain{12 34 5}{ }{oui}{non}\quad +5) \ifcontain{a{b c}d}{b c}{oui}{non}\quad 6) \ifcontain{a{b c}d}{{b c}}{oui}{non}\quad +7) \ifcontain{{} {}}{ }{oui}{non}\quad 8) \ifcontain{{} {}}{{}}{oui}{non} +****************** Fin code ****************** + + +****************** Code 348 ****************** +\catcode`@11 +\newif\ifin@group +\def\ifcontain{% + \ifstarred + {\in@groupfalse\ifcontain@i\ifcontain@star}% + {\ifcontain@i\ifcontain@nostar}} + +\def\ifcontain@i#1#2#3{% #1 = macro à appeler selon étoile ou pas. #2 = code. #3 = motif + \def\main@arg{#2}\def\pattern@arg{#3}% + \ifempty{#3} + \firstoftwo + {\ifempty{#2} + \secondoftwo + #1% aller à \ifcontain@star ou \ifcontain@nostar + }% +} +\def\ifcontain@nostar{% + \exptwoargs\ifstartwith\main@arg\pattern@arg + \firstoftwo% si motif est au début de code => vrai + {\exparg\ifempty\main@arg + \secondoftwo% sinon, si code est vide => faux + {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code + \ifcontain@nostar% et recommencer + }% + }% +} +\def\ifcontain@star{% + \expandafter\ifbracefirst\expandafter{\main@arg}% si code commence par "{" + {\grab@first\main@arg\aux@arg% enlever {<argument>} de main@arg + \begingroup% ouvrir un groupe + \in@grouptrue% mettre le booléen à vrai + \expandafter\def\expandafter\main@arg\aux@arg% assigner "argument" à \main@arg + \ifcontain@star% et recommencer avec ce nouveau \main@arg + }% si code ne commence pas par "{" + {\exptwoargs\ifstartwith\main@arg\pattern@arg% si motif est au début de code + \return@true% renvoyer vrai + {\expandafter\ifempty\expandafter{\main@arg}% si code est vide + {\ifin@group% et que l'on est dans un groupe + \endgroup \expandafter\ifcontain@star% en sortir et recommencer + \else% si on n'est pas dans un groupe, le code a été parcouru + \expandafter\secondoftwo% sans trouver <motif> => renvoyer faux + \fi + }% si code n'est pas vide + {\grab@first\main@arg\startwith@code% manger le 1er "argument" de code + \ifcontain@star% et recommencer + }% + }% + }% +} +\def\return@true{% + \ifin@group% tant qu'on est dans un groupe + \endgroup \expandafter\return@true% en sortir et recommencer + \else + \expandafter\firstoftwo% sinon, renvoyer vrai + \fi +} +\catcode`@12 +1) \ifcontain{ab {c d}ef}{c}{oui}{non}\quad 2) \ifcontain*{ab {c d}ef}{c}{oui}{non} +****************** Fin code ****************** + + +****************** Code 349 ****************** +\catcode`@11 +\newtoks\subst@toks% registre de tokens pour stocker le <texte> modifié +\def\substitute{% + \def\substitute@end{\the\subst@toks}% macro exécutée à la fin + \ifstarred + {\let\recurse@macro\substitute@star \substitute@i}% + {\let\recurse@macro\substitute@nostar\substitute@i}% +} +\def\substitute@i#1#2#3#4{% #1=macro à appeler selon étoile ou pas. + % #2=<texte> #3=<motif> #4=<motif de substi> + \def\code@arg{#2}\def\pattern@arg{#3}\def\subst@arg{#4}% + \subst@toks={}% initialiser le collecteur à vide + #1% aller à \substitute@star ou \substitute@nostar +} +\def\substitute@nostar{% + \exptwoargs\ifstartwith\code@arg\pattern@arg% si le <texte> commence par <motif> + {\eaddtotoks\subst@toks\subst@arg% ajouter <motif subst> + \grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \substitute@nostar% et recommencer + } + {\expandafter\ifempty\expandafter{\code@arg}% + {\substitute@end% sinon, si <texte> est vide => afficher le registre de tokens + } + {\grab@first\code@arg\aux@arg% autrement, manger le 1er "argument" de <texte> + \eaddtotoks\subst@toks\aux@arg% et l'ajouter au registre de tokens + \substitute@nostar% et recommencer + }% + }% +} +\def\substitute@star{% + \expandafter\ifbracefirst\expandafter{\code@arg}% si <texte> commence par "{" + {\grab@first\code@arg\aux@arg% enlever {<argument>} de \code@arg + \begingroup% ouvrir un groupe + \def\substitute@end{% modifier localement la macro exécutée à la fin + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\subst@toks\expandafter% ajouter au registre hors du groupe + {\expandafter{\the\subst@toks}}% ce qui est collecté localement, mis entre {} + \substitute@star% puis recommencer + }% + \subst@toks{}% initialiser à vide + \expandafter\def\expandafter\code@arg\aux@arg% % assigner "argument" au <texte> + \substitute@star% et recommencer avec ce nouveau \code@arg + }% si code ne commence pas par "{" + {\exptwoargs\ifstartwith\code@arg\pattern@arg% si <motif> est au début de <texte> + {\eaddtotoks\subst@toks\subst@arg% ajouter <motif subst> + \grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \substitute@star% et recommencer + } + {\expandafter\ifempty\expandafter{\code@arg}% si <texte> est vide + {\substitute@end% aller à la macro de fin + }% si <texte> n'est pas vide + {\grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \eaddtotoks\subst@toks\aux@code% et l'ajouter au registre + \substitute@star% et recommencer + }% + }% + }% +} +\catcode`@12 +1) \substitute{ab{\bf racada}bra}{a}{W}\qquad +2) \substitute*{ab{\bf racada}bra}{a}{W}\qquad +3) \substitute{12\frbox{\bf 34}56}{\bf}{\it}\qquad +4) \substitute*{12\frbox{\bf 34}56}{\bf}{\it} +****************** Fin code ****************** + + +****************** Code 350 ****************** +\catcode`@11 +\newtoks\subst@toks% registre de tokens pour stocker le <texte> modifié +\def\substitute{% + \def\substitute@end{\the\subst@toks}% macro exécutée à la fin + \ifstarred + {\let\recurse@macro\substitute@star \substitute@i}% + {\let\recurse@macro\substitute@nostar\substitute@i}% +} +\def\substitute@i#1#2#3{% #1=<texte> #2=<motif> #3=<motif de substi> + \def\code@arg{#1}\def\pattern@arg{#2}\def\subst@arg{#3}% + \subst@toks={}% initialiser à vide + \recurse@macro% aller à \substitute@star ou \substitute@nostar +} +\def\substitute@nostar{% + \exptwoargs\ifstartwith\code@arg\pattern@arg% si le <texte> commence par <motif> + {\eaddtotoks\subst@toks\subst@arg% ajouter <motif subst> + \grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \recurse@macro% et recommencer + } + {\expandafter\ifempty\expandafter{\code@arg}% + \substitute@end% sinon, si <texte> est vide => afficher le registre de tokens + {\grab@first\code@arg\aux@arg% autrement, manger le 1er "argument" de <texte> + \eaddtotoks\subst@toks\aux@arg% et l'ajouter au registre de tokens + \recurse@macro% et recommencer + }% + }% +} +\def\substitute@star{% + \expandafter\ifbracefirst\expandafter{\code@arg}% si <texte> commence par "{" + {\grab@first\code@arg\aux@arg% enlever {<argument>} de \code@arg + \begingroup% ouvrir un groupe + \def\substitute@end{% modifier localement la macro exécutée à la fin + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\subst@toks\expandafter% ajouter au registre hors du groupe + {\expandafter{\the\subst@toks}}% ce qui est collecté localement, mis entre {} + \recurse@macro% puis recommencer + }% + \subst@toks{}% initialiser à vide + \expandafter\def\expandafter\code@arg\aux@arg% % assigner "argument" au <texte> + \recurse@macro% et recommencer avec ce nouveau \code@arg + }% si <texte> ne commence pas par "{" : + \substitute@nostar% exécuter macro non étoilée pour une seule itération +} +\def\substitutetocs{% + \ifstarred + {\let\recurse@macro\substitute@star \substitutetocs@i}% + {\let\recurse@macro\substitute@nostar\substitutetocs@i}% +} +\def\substitutetocs@i#1#2#3#4{% #1=<texte> #2=<motif> #3=<motif de substi> #4=\<macro> + \def\substitute@end{\edef#4{\the\subst@toks}}% macro exécutée à la fin + \substitute@i{#1}{#2}{#3}% continuer comme avec \substitute +} +\catcode`@12 +\frboxsep=1pt +1) \substitute{ab{\bf racada}bra}{a}{W}\qquad +2) \substitute*{ab{\bf racada}bra}{a}{W}\qquad +3) \substitute{12\frbox{\bf 34}56}{\bf}{\it}\qquad +4) \substitute*{12\frbox{\bf 34}56}{\bf}{\it} +\medbreak + +1) \substitutetocs{ab{\bf racada}bra}{a}{W}\foo \meaning\foo\par +2) \substitutetocs*{ab{\bf racada}bra}{a}{W}\foo \meaning\foo\par +3) \substitutetocs{12\frbox{\bf 34}56}{\bf}{\it}\foo \meaning\foo\par +4) \substitutetocs*{12\frbox{\bf 34}56}{\bf}{\it}\foo \meaning\foo +****************** Fin code ****************** + + +****************** Code 351 ****************** +\catcode`@11 +\def\vecteur{% + \ifnexttok[% si la macro est suivie d'un crochet + \vecteur@i% aller à la macro à arguments délimités + {\vecteur@i[1]}% sinon, ajouter l'argument optionnel par défaut entre crochets +} +\def\vecteur@i[#1]#2{% #1=arg optionnel #2=arg obligatoire + $(x_{#1},\ldots,x_{#2})$ +} +\catcode`@12 +1) \vecteur{n}\qquad 2) \vecteur[0]{k}\qquad 3) \vecteur[i]j \qquad4) \vecteur[]n +****************** Fin code ****************** + + +****************** Code 352 ****************** +\catcode`@11 +\def\forcemath#1{% compose #1 en mode math, quel que soit le mode en cours + \ifmmode\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {#1}{$#1$}% +} +\def\vecteur{% + \ifnexttok[% si la macro est suivie d'un crochet + \vecteur@i% aller à la macro à arguments délimités + {\vecteur@i[1]}% sinon, ajouter l'argument optionnel par défaut entre crochets +} +\def\vecteur@i[#1]{% + \ifnexttok(% si une parenthèse vient ensuite + {\vecteur@ii[#1]}% aller à la macro à argument délimités + {\vecteur@ii[#1](x)}% sinon, rajouter "x" comme argument optionnel +} +\def\vecteur@ii[#1](#2)#3{% #1 et #2=arg optionnel #3=arg obligatoire + \forcemath{({#2}_{#1},\ldots,{#2}_{#3})}% +} +\catcode`@12 +1) \vecteur{n}\qquad 2) \vecteur[0]{k}\qquad +3) \vecteur[i](y)j \qquad4) \vecteur[](\alpha)n +****************** Fin code ****************** + + +****************** Code 353 ****************** +\catcode`@11 +\def\vecteur{% + \ifnexttok[% si la macro est suivie d'un crochet + \vecteur@bracket% lire cet argument entre crochet + {\ifnexttok(% sinon, si elle est suivie d'une parenthèse + \vecteur@paren % lire cet argument entre parenthèses + {\vecteur@i[1](x)}% sinon, transmettre les arguments par défaut + }% +} +\def\vecteur@bracket[#1]{% + \ifnexttok(% s'il y a une parenthèse après + {\vecteur@i[#1]}% lire la parenthèse + {\vecteur@i[#1](x)}% sinon, donne "x" comme argument par défaut +} +\def\vecteur@paren(#1){% + \ifnexttok[% si le caractère suivant est un crochet + {\vecteur@ii(#1)}% lire l'argument entre crochets + {\vecteur@ii(#1)[1]}% sinon, donner "1" comme argument par défaut +} +\def\vecteur@i[#1](#2)#3{\forcemath{({#2}_{#1},\ldots,{#2}_{#3})}} +\def\vecteur@ii(#1)[#2]{\vecteur@i[#2](#1)}% met les arg optionnels dans l'ordre +\catcode`@12 +1) \vecteur{n}\qquad 2) \vecteur[i](y){j}\qquad 3) \vecteur(y)[i]{j}\qquad +4) \vecteur[0]{k}\qquad 5) \vecteur(\alpha){n} +****************** Fin code ****************** + + +****************** Code 354 ****************** +\catcode`\@11 +\def\foo#1{% #1 -> lit le premier argument obligatoire + \ifnexttok[% si le token suivant est un crochet + {\foo@i{#1}}% aller à \foo@i qui va lire cet argument + {\foo@i{#1}[xxx]}% sinon, transmettre [xxx] à foo@i +} + +\def\foo@i#1[#2]#3#4{% lit l'arg obligatoire + arg optionnel + 2 arg obligatoires + \ifnexttok[ + {\foo@ii{#1}[#2]{#3}{#4}}% + {\foo@ii{#1}[#2]{#3}{#4}[y]}% +} + +\def\foo@ii#1[#2]#3#4[#5]#6{% lit tous les arguments + 1="#1" 2="#2" 3="#3" 4="#4" 5="#5" 6="#6"% +} + +\catcode`\@12 +1) \foo{arg1}{arg2}{arg3}{arg4}\par +2) \foo{arg1}[OPT\_A]{arg2}{arg3}{arg4}\par +3) \foo{arg1}{arg2}{arg3}[OPT\_B]{arg4}\par +4) \foo{arg1}[OPT\_A]{arg2}{arg3}[OPT\_B]{arg4}\medbreak +****************** Fin code ****************** + + +****************** Code 355 ****************** +\catcode`\@11 +\newcount\macro@cnt% numéro à mettre dans le nom des sous macros +\newcount\arg@cnt% compte le nombre d'arguments +\newtoks\param@text% texte de paramètre des macros sous forme "#x" et/ou "[#x]" +\newtoks\arg@text% arguments sous forme "{#x}" et/ou "[#x]" + +\def\newmacro#1{% + % \macro@name construit le <nom> de la macro et éventuellement "@[<chiffre romain>]" + \def\macro@name##1{\expandafter\gobone\string#1\ifnum##1>0 @[\romannumeral##1]\fi}% + \macro@cnt=0 \arg@cnt=0 % initialise les compteurs + \param@text{}\arg@text{}% vide les registres de texte de paramètre et d'argument + \newmacro@i% va voir le prochain token +} + +\def\newmacro@i{\futurelet\nxttok\newmacro@ii}% met le prochain token dans \nxttok... +% ...puis va à la macro : +\def\newmacro@ii{% + \ifxcase\nxttok + [\newmacro@optarg% si le prochain token est un crochet aller à \newmacro@optarg + \bgroup% si c'est un accolade ouvrante + % le texte de paramètre est fini et il faut définir la macro + {\defname{\macro@name\macro@cnt\expandafter}% + \the\param@text}% <- le {<code>} est juste après, il n'est pas encore lu + \elseif% sinon, c'est donc un chiffre + \newmacro@arg% aller à \newmacro@arg + \endif +} + +\def\newmacro@optarg[#1]{% lit la valeur par défaut de l'argument optionnel + % Définit la macro \<nom>@[<nbre>] qui lit tous les arguments (optionnels ou pas) + % jusqu'alors définis à l'aide de \param@text. Puis, cette macro testera si le prochain + % token est un crochet + \expandafter\edef\csname\macro@name\macro@cnt\expandafter\endcsname\the\param@text{% + \noexpand\ifnexttok[% + % si oui : la macro \<nom>@<nbr+1> le lira + {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname + \the\arg@text}% + % si non : transmettre à \<nom>@<nbr+1> l'argument optionnel par défaut lu + {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname + \the\arg@text[\unexpanded{#1}]}% + }% + \advance\arg@cnt 1 % incrémenter le numéro d'argument + % pour ajouter "[#<x>]" à \param@text et à \arg@text + \eaddtotoks\param@text{\expandafter[\expandafter##\number\arg@cnt]}% + \eaddtotoks\arg@text {\expandafter[\expandafter##\number\arg@cnt]}% + \advance\macro@cnt 1 % incrémenter le numéro de nom de macro + \newmacro@i% va voir le token suivant +} + +\def\newmacro@arg#1{% #1=nombre d'arguments obligatoires à ajouter + % boucle qui ajoute "#<x>#<x+1>etc" dans \param@text + % et "{#<x>}{#<x+1>}etc" dans \arg@text + \ifnum#1>\z@ % tant qu'on n'a pas ajouté le nombre de #x nécessaire + \advance\arg@cnt 1 % incrémenter le numéro d'argument + % pour ajouter #x à \param@text et {#x} à \arg@text + \eaddtotoks\param@text{\expandafter##\number\arg@cnt}% + \eaddtotoks\arg@text {\expandafter{\expandafter##\number\arg@cnt}}% + \expandafter\newmacro@arg\expandafter{\number\numexpr#1-1\expandafter}% boucler + \else% si les arguments sont tous ajoutés + \expandafter\newmacro@i% lire le token suivant + \fi +} +\catcode`\@12 +\newmacro\foo 1[xxx]2[y]1{1="#1" 2="#2" 3="#3" 4="#4" 5="#5" 6="#6"} +a) \foo{arg1}{arg2}{arg3}{arg4}\par +b) \foo{arg1}[OPT\_A]{arg2}{arg3}{arg4}\par +c) \foo{arg1}{arg2}{arg3}[OPT\_B]{arg4}\par +d) \foo{arg1}[OPT\_A]{arg2}{arg3}[OPT\_B]{arg4}\medbreak + +\meaning\foo\par +\expandafter\meaning\csname foo@[i]\endcsname\par +\expandafter\meaning\csname foo@[ii]\endcsname +****************** Fin code ****************** + + +****************** Code 356 ****************** +\newmacro\framebox[ULRD]1{% #1 = ULRD (Up, Down, Right, Left) +% ne pas changer le mode H ou V en cours + \hbox{% enferme dans une \hbox + \uppercase{\ifin{#1}L}{\vrule width\frboxrule}{}% réglure gauche + \vtop{% + \vbox{% 1er élément de la \vtop + \uppercase{\ifin{#1}U}{% si la réglure sup doit être tracée + \hrule height\frboxrule% réglure supérieure + \kern\frboxsep% espace haut + } + {}% + \hbox{% + \uppercase{\ifin{#1}L}{\kern\frboxsep}{}% espace gauche + #2% contenu + \uppercase{\ifin{#1}R}{\kern\frboxsep}{}% espace droite + }% + }% puis autres éléments de la \vtop, sous la ligne de base + \uppercase{\ifin{#1}D}{% + \kern\frboxsep% espace bas + \hrule height\frboxrule% réglure inférieure + }% + {}% + }% + \uppercase{\ifin{#1}R}{\vrule width\frboxrule}{}% réglure droite + }% +} +\frboxsep=1pt +Boite \framebox{entière}, \framebox[ud]{Up down}, \framebox[LR]{Left Right}, +\framebox[LU]{Left Up} et \framebox[rd]{Right Down}. +****************** Fin code ****************** + + +****************** Code 357 ****************** +\catcode`\@11 +\newtoks\eargs@toks +\newtoks\eargs@temptoks +\def\eargs[#1]#2{% #1=liste des développements #2=macro + \eargs@toks{#2}% mettre la macro dans le collecteur de tokens + \expandafter\eargs@i\detokenize{#1}\@nil% appeler \eargs@i avec + % la liste des developpements +} + +\def\eargs@i#1\@nil{% #1=liste des n-développements restant + \ifempty{#1}% s'il n' y plus de n-développements + {\the\eargs@toks}% exécuter la macro et ses arguments développés + {\eargs@ii#1\@nil}% sinon appeller la macro qui lit un argument +} + +% #1=n-développement actuel #2=liste des n-développements restants #3=argument lu +\def\eargs@ii#1#2\@nil#3{% + \if+#1% si #1="+", un \edef est demandé pour cet argument + \edef\eargs@tempmacro{{#3}}% le stocker dans une macro temporaire + \else% sinon + \eargs@temptoks={#3}% stocker l'argument dans un registre temporaire + \for\eargs@loop = 1 to #1\do 1 % faire #1 fois : + {\eargs@temptoks=% 1-développer le 1er token du registre temporaire + \expandafter\expandafter\expandafter{\the\eargs@temptoks}% + }% puis le stocker dans la macro temporaire + \edef\eargs@tempmacro{{\the\eargs@temptoks}}% + \fi + \eaddtotoks\eargs@toks\eargs@tempmacro% ajouter le contenu de la macro au collecteur + \eargs@i#2\@nil% appeler \eargs@i avec les n-développements restants +} +\catcode`\@12 +\def\foo#1#2#3#4#5{\detokenize{1="#1" 2="#2" 3="#3" 4="#4" 5="#5"}} +\def\aaa{\bbb}\def\bbb{\ccc}\def\ccc{Bonjour} + +\eargs[0123+]\foo{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}. +****************** Fin code ****************** + + +****************** Code 358 ****************** +\catcode`\@11 +\def\detectmark#1{% #1 est le marqueur + \begingroup + \catcode`#1=13 % rendra #1 actif après la macro + \begingroup% pour les besoins du \lccode + \lccode`\~=`#1 % transforme "~" en " #1 actif" + \lowercase{\endgroup\def~##1~}{\markeffect{##1}}% + \detectmark@i +} +\def\detectmark@i#1{% + #1% exécute le code + \endgroup% ferme le groupe, le marqueur perd son catcode actif +} +\catcode`\@12 +a) \def\markeffect#1{{\bf #1}}% met en gras +\detectmark+{Un +argument+ où les +marqueurs+ sont détectés} +\medskip + +b) \def\markeffect#1{% met dans une boite + \begingroup + \frboxsep=1pt % modifie l'espacement entre texte et encadrement + \frbox{\strut#1}% encadre + \endgroup +} +\detectmark|{Un |argument| où les |marqueurs| sont détectés} +\medskip + +c) \def\markeffect#1{$\vcenter{\hbox{#1}\hbox{#1}}$}% superpose 2 fois +\detectmark`{Un `argument` où les `marqueurs` sont détectés} +****************** Fin code ****************** + + +****************** Code 359 ****************** +\catcode`\@11 +\def\detectmark#1{% + \begingroup + \catcode`#1=13 % rendra #1 actif après la macro + \begingroup% pour les besoins du \lccode + \lccode`\~=`#1 % transforme "~" en " #1 actif" + \lowercase{\endgroup\def~##1~}{\markeffect{##1}}%% + \detectmark@i +} +\def\detectmark@i#1{% + #1% lit le code + \endgroup% ferme le groupe, le marqueur perd son catcode actif +} +\catcode`\@12 +\def\markeffect#1{{\bf #1}}% met en gras +\frbox{\detectmark+{Un +argument+ où les +marqueurs+ sont détectés}} +****************** Fin code ****************** + + +****************** Code 360 ****************** +\catcode`\@11 +\newtoks\alter@toks% collecteur de tokens + +\def\alter#1#2{% #1= délimiteur #2 = macro à altérer + \let\alter@macro#2% sauvegarde la macro + \edef\alter@restorecatcode{% restaurera le catcode de #1 + \catcode`\noexpand#1=\the\catcode`#1 }% + \edef\alter@tmp{\let\noexpand\alter@markertoks= \string#1}% + \alter@tmp% et sauvegarder le délimiteur après avoir mis son catcode à 12 + \edef\alter@tmp{\def\noexpand\alter@readlitterate@i\string#1####1\string#1}% + % développe les \string#1 pour que les arguments délimités aient + % des délimiteurs de catcode 12 + \alter@tmp{% <- comme si on écrivait "\def\alter@readlitterate@i#1##1#1" + \endgroup% après avoir lu ##1 (tokens rendus inoffensifs), fermer le groupe + \addtotoks\alter@toks{{\tt##1}}% ajouter ces tokens + \alter@i% et aller lire le prochain token + }% + \alter@toks{}% initialise le collecteur de tokens + \afterassignment\alter@i% aller lire le premier token après avoir + \let\alter@tmptok= % mangé l'accolade ouvrante de l'<argument> qui suit +} + +\def\alter@i{% lit le prochain token et va à \alter@ii + \futurelet\alter@nxttok\alter@ii}% + +\def\alter@ii{% teste le token qui doit être lu + \ifxcase\alter@nxttok% si le token à lire est + \egroup \alter@stop% "}" : aller à \alterstop@i + \sptoken \alter@readspc% " " : aller à \alter@readspc + \bgroup \alter@readarg% "{" : aller à \alter@readarg + \alter@markertoks \alter@readlitterate% "<delimiteur>" : aller à \alter@readlitterate + \elseif + \alter@readtok% dans les autres cas, aller à \alter@readtok + \endif +} + +\def\alter@readlitterate{% le prochain token est le délimiteur + \begingroup% ouvrir un groupe + \for\alter@tmp=0to255\do{\catcode\alter@tmp=12 }% + % mettre tous les catcodes à 12 + \defactive{ }{\ }% sauf l'espace rendu actif + \doforeach\alter@tmp\in{<,>,-,`,{,},'}% pour chaque motif de ligature + {\unless\if\alter@tmp\alter@markertoks% s'il est différent du délimiteur + % le rendre actif pour éviter la ligature + \expandafter\alter@defligchar\alter@tmp + \fi + }% + \alter@readlitterate@i% puis aller à \alter@readlitterate@i... + % ...qui a été définie dans \alter +} + +\def\alter@defligchar#1{% définit le caractère pour ne pas provoquer de ligature + \defactive#1{\string#1{}}% +} + +\expandafter\def\expandafter\alter@readspc\space{% mange un espace dans le code + \addtotoks\alter@toks{ }% ajoute l'espace + \alter@i% puis lire le token suivant +} + +\def\alter@readarg{% le token qui suit est "{" + \begingroup% ouvrir un groupe + \def\alter@stop@ii{% et modifier localement la macro appelée à la toute fin, + % après que l'accolade fermante ait été mangée (par \alterstop@i) + \expandafter\endgroup% retarder la fermeture de groupe ouvert ci-dessus + \expandafter\addtotoks\expandafter\alter@toks\expandafter + {\expandafter{\the\alter@toks}}% + % pour ajouter hors du groupe ce qui a été collecté à l'intérieur, + % le tout mis entre accolades + \alter@i% puis, lire le token suivant + }% + \alter@toks{}% au début du groupe, initialiser le collecteur + \afterassignment\alter@i% aller lire le prochain token après + \let\alter@tmptok= % avoir mangé l'accolade ouvrante +} + +\def\alter@readtok#1{% le prochain token ne demande pas une action spéciale + \addtotoks\alter@toks{#1}% l'ajouter au collecteur + \alter@i% et aller lire le token suivant +} + +\def\alter@stop{% le token à lire est "}" + \afterassignment\alter@stop@ii% aller à \alter@stop@ii après + \let\alter@tmptok= % avoir mangé l'accolade fermante +} + +\def\alter@stop@ii{% donner à la \<macro> tout ce qui a été récolté + \expandafter\alter@macro\expandafter{\the\alter@toks}% + \alter@restorecatcode% puis restaure le catcode du délimiteur +} +\catcode`@12 + +\frboxsep=1pt +\alter|\frbox{Texte normal - |#& }| - texte normal - |_^ ##| - texte normal} +\alter=\frbox{La macro =\alter= autorise du verbatim dans +des commandes imbriquées \frbox{comme ici =\alter=}.} +****************** Fin code ****************** + + +****************** Code 361 ****************** +\def\hello#1#2{Bonjour #1 et #2 !} +\hello{foo}{bar}\par +\alter|\identity{\hello{macro |{{{\foo|}{macro |\bar}|}} +****************** Fin code ****************** + + +****************** Code 362 ****************** +\def\retokenize#1{% + \immediate\openout\wtest=retokenize.tex % ouvre le fichier + \immediate\write\wtest{\unexpanded{#1}}% y écrit l'argument + \immediate\closeout\wtest% ferme le fichier + \input retokenize.tex % lit le fichier selon les catcodes en vigueur + \unskip% mange l'espace précédemment ajouté qui provient de la fin du fichier +} +\frboxsep=1pt +1) \frbox{Programmer en \catcode`\~=12 \TeX{} est~facile et~utile.}\par +2) \frbox{Programmer en \retokenize{\catcode`\~=12 \TeX{} est~facile} et~utile.}\par +3) \frbox{Programmer en \catcode`\~=12 \retokenize{\TeX{} est~facile} et~utile.} +****************** Fin code ****************** + + +****************** Code 363 ****************** +\frboxsep=1pt +1) \frbox{Programmer \retokenize{\litterate|en \TeX {} est |}facile et utile.}\par +2) \frbox{Programmer \retokenize{\litterate|en \TeX{} est |}facile et utile.} +****************** Fin code ****************** + + +****************** Code 364 ****************** +\frboxsep=1pt +1) \frbox{Programmer en \catcode`\~=12 \TeX{} est~facile et~utile.}\par +2) \frbox{Programmer en \scantokens{\catcode`\~=12 \TeX{} est~facile} et~utile.}\par +3) \frbox{Programmer en \catcode`\~=12 \scantokens{\TeX{} est~facile} et~utile.}\par +4) \frbox{Programmer \scantokens{\litterate|en \TeX {} est facile|} et utile.}\par +5) \frbox{Programmer \scantokens{\litterate|en \TeX{} est facile|} et utile.} +****************** Fin code ****************** + + +****************** Code 365 ****************** +\scantokens{a}b +****************** Fin code ****************** + + +****************** Code 366 ****************** +\begingroup\endlinechar=-1 \scantokens{a}b\endgroup +****************** Fin code ****************** + + +****************** Code 367 ****************** +\edef\foo{\scantokens{Bonjour le monde}}% produit une erreur +****************** Fin code ****************** + + +****************** Code 368 ****************** +\expandafter\def\expandafter\foo\expandafter + {\scantokens{Bonjour le monde}}% produit une erreur +****************** Fin code ****************** + + +****************** Code 369 ****************** + +Voici la macro \string\foo\ : \foo. +****************** Fin code ****************** + + +****************** Code 370 ****************** +\catcode`\@11 +\def\scandef#1#2{% #1=\<macro> #2=<texte> + \begingroup + \endlinechar=-1 % pas de caractère de fin de ligne + \everyeof{\@nil#1\noexpand}% ajoute "\@nil\<macro>\noexpand" avant la fin du fichier + \expandafter\scandef@i\expandafter\relax\scantokens{#2}% +} +\def\scandef@i#1\@nil#2{% "\@nil#2" ont été ajouté par \everyeof + \endgroup% ferme le groupe + \expandafter\def\expandafter#2\expandafter{\gobone#1}% et définit la \<macro> +} +\catcode`@12 +\def\foo{% + Dans tout l'argument de \string\foo, <<~>> est actif + sauf + \catcode`\~12 + \scandef\bar{dans celui de \string\bar : <<~>>}% + \catcode`~13 + \bar +} +\foo +****************** Fin code ****************** + + +****************** Code 371 ****************** +{% +\endlinechar=`\d% insère la lettre "d" à chaque fin de ligne +% le \noexpan(d) est incomplet +Voici la macro \string\foo\ : \foo.% fins de ligne... +}% ...commentées pour éviter le "d" +****************** Fin code ****************** + + +****************** Code 372 ****************** +\catcode`\@11 +\def\cprotect#1{% #1 est la \<macro> + \def\cprotect@i##1{% ##1 est l'<argument> + \endgroup% ferme le groupe précédemment ouvert + #1{\scantokens{##1\noexpand}}% + }% + \begingroup% rend tous les octets de catcode12 + \for\cprotect@temp=0to255\do{\catcode\cprotect@temp=12 }% + \catcode`\{=1 \catcode`\}=2 % sauf "{" et "}" + \cprotect@i% puis, lit l'argument +} +\catcode`@12 +\frboxsep=1.5pt +1) \cprotect\frbox{foo \litterate-&# #^ ^_%- bar}\par +2) \cprotect\frbox{\catcode`\~=12 a~b~c~d}\par +3) \cprotect\frbox{foo \litterate-\bar- \cprotect\frbox{\litterate-&# #-} fin} +****************** Fin code ****************** + + +****************** Code 373 ****************** +\catcode`@11 +\def\Cprotect#1{% #1 est la \<macro> + \def\Cprotect@i##1{% ##1 est la liste des arguments + \endgroup% ferme le groupe précédemment ouvert + \toks0={#1}% met la \<macro> dans le registre de tokens + \Cprotect@ii##1\quark% \quark est mis à la fin des arguments + \the\toks0 % exécute le registre + }% + \begingroup% rend tous les octets de catcode12 + \for\temp@arg= 0 to 255 \do{% changer à 12 tous les catcodes + \unless\ifnum\catcode\temp@arg=1 % sauf si catcode=1 + \unless\ifnum\catcode\temp@arg=2 % ou 2 + \catcode\temp@arg=12 + \fi + \fi}% + \Cprotect@i% puis, lit l'argument +} +\def\Cprotect@ii#1{% #1 est l'argument courant (dépouillé de ses accolades) + \def\temp@arg{#1}% stocke l'argument pour le tester ci dessous : + \unless\ifx\quark\temp@arg% si la fin n'est pas atteinte + % ajouter "{\scantokens{#1\noexpand}}" au registre + \addtotoks{\toks0}{{\scantokens{#1\noexpand}}}% + \expandafter\Cprotect@ii% et recommencer + \fi +} +\catcode`@12 + +\def\test#1#2{Bonjour #1 et #2} +\Cprotect\test{{argument 1 : \litterate-\foo-}{argument 2 : \litterate-\bar-}} +****************** Fin code ****************** + + +****************** Code 374 ****************** +\newdimen\pixelsize \newdimen\pixelsep +\def\pixel{\vrule height\pixelsize width\pixelsize depth0pt } +\def\vblankpixel{\vrule height\pixelsize width0pt depth0pt } +\def\blankpixel{\vblankpixel \vrule height0pt width\pixelsize depth0pt } +\def\gap{\kern\pixelsep} +\pixelsize=3pt \pixelsep=1pt +Essai : +\vbox{% aligne verticalement + \offinterlineskip% annule le ressort d'interligne + \lineskip=\pixelsep\relax% pour le mettre à \pixelsep + \hbox{\pixel\gap\blankpixel\gap\blankpixel\gap\blankpixel\gap\pixel}% 1re ligne + \hbox{\pixel\gap\pixel \gap\blankpixel\gap\pixel \gap\pixel}% 2e ligne +} +****************** Fin code ****************** + + +****************** Code 375 ****************** +\pixelsize=3pt \pixelsep=1pt +Essai : +\vtop{% + \offinterlineskip \lineskip=\pixelsep\relax + \vbox{% + \hbox{\blankpixel\gap\pixel \gap\pixel \gap\pixel}% *** + \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * + \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * + \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * + \hbox{\blankpixel\gap\pixel \gap\pixel \gap\pixel}% *** + }% ----ligne de base + \hbox {\blankpixel\gap\blankpixel\gap\blankpixel\gap\pixel}% * + \hbox {\blankpixel\gap\pixel \gap\pixel }% ** +} +****************** Fin code ****************** + + +****************** Code 376 ****************** +\catcode`\@11 +\begingroup% dans ce groupe : +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\catcode`\^^M=13\relax% le retour à la ligne est actif +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\global\deftok\otherspc{ }% définit un espace de catcode 12 +\xdef\letter@code{% macro contenant le dessin de la lettre "e" + ** +* * +*** +* + ***}% +\endgroup + +\def\makecar@i#1{% #1 = dessin de la lettre avec les caractères "," "*" et " " + \doforeach\current@line\in{#1}% pour chaque ligne dans #1 : + {\ifx\empty\current@line% si la ligne est vide + \addtomacro\pixabove{\hbox{\vblankpixel}}% ajouter une fausse ligne + \else% sinon + \let\pix@line\empty% initialiser le code de la ligne à vide + \expandafter\makecar@ii\current@line\quark% et la construire + \fi + }% +} + +\def\makecar@ii#1{% #1=caractère de dessin de la ligne en cours + \ifxcase#1% si le caractère est + * {\addtomacro\pix@line\pixel}% + \otherspc{\addtomacro\pix@line\blankpixel}% + \endif + \ifx#1\quark% si la fin est atteinte + \addtomacro\pix@line\unkern% annuler le dernier espace interpixel + \eaddtomacro\pixabove{% et encapsuler \pix@line dans une \hbox + \expandafter\hbox\expandafter{\pix@line}}% + \else% si la fin n'est pas atteinte, ajouter l'espace interpixel + \addtomacro\pix@line\gap + \expandafter\makecar@ii% recommencer avec le caractère suivant + \fi +} +\pixelsize=3pt \pixelsep=1pt +\let\pixabove\empty% initialisation de la macro finale à vide +\exparg\makecar@i\letter@code% appelle la macro qui construit \pixabove +La lettre +\vtop{% enferme le tout dans une \vtop : + \offinterlineskip\lineskip=\pixelsep% ajuste l'espace interligne + \vbox{\pixabove}% affiche le caractère créé +}. +****************** Fin code ****************** + + +****************** Code 377 ****************** +\catcode`\@11 +\begingroup% dans ce groupe : +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\catcode`\^^M=13\relax% le retour à la ligne est actif +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\global\deftok\otherspc{ }% définit un espace de catcode 12 +\xdef\letter@code{% macro contenant le dessin de la lettre "g" + *** +* * +* * +* * + ***_ + * + **}% +\endgroup + +\def\makecar#1#2{% #1=nom recevant le code final #2=macro contenant le dessin + \let\pixabove\empty \let\pixbelow\empty \let\pix@line\empty% initialise à vide + \exparg\ifin{#2}_% si le code contient _ + {\expandafter\makecar@iii#2\@nil}% aller à \makecar@iii + {\exparg\makecar@i{#2}}% sinon, à \makecar@i + \edef#1{% définit la macro #1 comme + \vtop{% une \vtop contenant : + \unexpanded{\offinterlineskip\lineskip\pixelsep}% réglage d'espace inter ligne + \vbox{\unexpanded\expandafter{\pixabove}}% \vbox des pixels au-dessus + % de la ligne de base + \unless\ifx\pixbelow\empty% s'il y a des pixels au-dessous de la baseline + \unexpanded\expandafter{\pixbelow}% les ajouter dans la \vtop + \fi + }% + }% +} + +\def\makecar@iii#1_,#2\@nil{% + \makecar@i{#2}% construit la partie au-dessous de la baseline + \let\pixbelow\pixabove% et affecte le code à \pixbelow + \let\pixabove\empty \let\pix@line\empty% ré-initialise + \makecar@i{#1}% construit la partie au-dessus de la baseline +} + +\makecar\lettreg\letter@code +\catcode`\@12 +\pixelsize=3pt \pixelsep=1pt + +Essai : \lettreg +****************** Fin code ****************** + + +****************** Code 378 ****************** +\catcode`\@11 +\begingroup +\expandafter\gdef\csname impact@" "\endcsname{% définit la lettre "espace" + \hskip 4\pixelsize plus.5\pixelsize minus.5\pixelsize\relax}% +\catcode`\^^M=13\relax% le retour à la ligne est actif +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\gdef\impact@alphabet{ +a/ +*** + * + *** +* * +****, +b/ +* +* +*** +* * +* * +* * +***, +% beaucoup de caractères omis +9/ + *** +* * +* * + **** + * + * + ***, +0/ + *** +* * +* * +* * * +* * +* * + ***, +error/ +* * * + * * +* * * + * * +* * * + * * +* * *}% <- commenter la fin de ligne +\endgroup% +% On parcourt le texte de remplacement de \impact@alphabet +\edef\saved@crcatcode{\catcode13=\the\catcode13\relax}% +\catcode`\^^M=13\relax% le retour à la ligne est actif +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\expsecond{\doforeach\letter@name/\letter@code\in}\impact@alphabet% + {\edef\letter@name{\letter@name}% développe la lettre (^^M devient ",") + \edef\letter@code{\letter@code}% développe le code (^^M devient ",") + \exparg\ifstart\letter@name,% si la lettre commence par "," + {\edef\letter@name{\expandafter\gobone\letter@name}}% la retirer + {}% + \exparg\ifstart\letter@code,% si le code commence par "," + {\edef\letter@code{\expandafter\gobone\letter@code}}% la retirer + {}% + \expandafter\makecar\csname impact@"\letter@name"\endcsname\letter@code% + }% +\saved@crcatcode% redonne le catcode de ^^M +% % définit l'espace +\pixelsize=3pt \pixelsep=1pt +% puis, on affiche ce qui a été défini : +a = \csname impact@"a"\endcsname\qquad b = \csname impact@"b"\endcsname\qquad +9 = \csname impact@"9"\endcsname \qquad 0 = \csname impact@"0"\endcsname\qquad +error = \csname impact@"error"\endcsname +****************** Fin code ****************** + + +****************** Code 379 ****************** +\catcode`\@11 +\newskip\letter@skip% ressort mis entre chaque lettre +\def\impactend{\impactend}% définit le quark de fin + +\newmacro\impact[0.2ex][0pt]{% définit la macro à arguments optionnels + \leavevmode% passer en mode horizontal + \begingroup% dans un groupe semi-simple : + \pixelsize=#1\relax \pixelsep=#2\relax% définir ces deux dimensions + \letter@skip=#1 plus.1\pixelsize minus.1\pixelsize\relax% définir espace inter-lettre + \baselineskip=% définir la distance entre les lignes de base + \dimexpr12\pixelsize+7\pixelsep\relax% à 12\pixelsize+7\pixelsep + \lineskiplimit=0pt % si lignes trop serrées + \lineskip=2\pixelsize\relax% les espacer de 2\pixelsize + \impact@i% puis aller voir le prochain token +} + +% va voir le prochain token puis va le tester à \impact@ii +\def\impact@i{\futurelet\nxtletter\impact@ii} + +\def\impact@ii{% + \ifx\nxtletter\impactend% si le token est \let égal à \impactend + \let\donext\impact@endprocess% aller à al macro de fin + \else + \ifx\nxtletter\sptoken% si c'est un espace + \let\donext\impact@spc% aller à \impact@spc + \else + \let\donext\impact@arg% sinon, aller à \impact@arg + \fi + \fi + \donext% faire l'action décidée ci-dessus +} + +% mange un espace (argument délimité) et affiche "\letter@<spc>" +\expandafter\def\expandafter\impact@spc\space{% + \csname impact@" "\endcsname + \impact@i% puis va voir le prochain token +} +% +% lit l'argument suivant +\def\impact@arg#1{% + \csname impact@% affiche + \ifcsname impact@"#1"\endcsname + "#1"% le caractère \impact@"#1" s'il est défini + \else + "error"% sinon \impact@"error" + \fi + \endcsname + \hskip\letter@skip% insérer le ressort inter-lettre + \impact@i% puis, aller voir le prochain token +} +\def\impact@endprocess\impactend{% macro exécuté lorsqque le quark \impactend va être lu + \unskip% annuler le dernier ressort + \par% composer le paragraphe pour prendre en compte + % \baselineskip, \lineskip et \lineskiplimit + \endgroup% et fermer le groupe +} +\catcode`\!=12 % rend le point d'exclamation "gentil" +\impact Programmer en {TEX} est facile et tr{e`}s utile !\impactend\par +\impact[2pt][0.5pt]Programmer en {TEX} est facile et tr{e`}s utile !\impactend +****************** Fin code ****************** + + +****************** Code 380 ****************** +\newdimen\maingraddim \maingraddim=4pt % hauteur des graduations principales +\newdimen\maingradwd \maingradwd=0.5pt % épaisseur des graduations principales +\def\maingradx#1{% + \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous + \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure +} +Début\maingradx{1}suite\maingradx{2}conclusion\maingradx{3}fin. +****************** Fin code ****************** + + +****************** Code 381 ****************** +\maingraddim=4pt \maingradwd=0.5pt +\newdimen\axiswd \axiswd=0.5pt +\newmacro\xaxis[1cm]1[1]1[4]{%% #1= dist #2=xmin #3=inc #4=xmax #5=subdiv + \hbox{% mettre le tout dans une \hbox + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale + \maingradx{\xx}% tracer la graduation et écrire l'abscisse + \kern#1\relax% puis se déplacer vers la droite + }% + }% + \vrule% tracer l'axe des abscisses + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax + depth 0pt\relax % et de profondeur nulle + }% +} +a) \xaxis{-2}{5}suite + +b) \frboxsep=0pt\frbox{\xaxis[1.25cm]{-1}[0.25]{1}}suite +****************** Fin code ****************** + + +****************** Code 382 ****************** +\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\newdimen\subgraddim \subgraddim=2.5pt +\newdimen\subgradwd \subgradwd=0.2pt +% trace un trait de subdivision +\def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }} + +\newmacro\xaxis[1cm]1[1]1[4]{% #1= dist #2=xmin #3=inc #4=xmax #5=subdiv + \hbox{% tout mettre dans une \hbox + \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions + \for\xx=1 to #5-1 \do{% insérer #5-1 fois + \kern\dimsubgrad% une espace secondaire + \subgradx% une graduation secondaire + }% + }% + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale + \maingradx{\xx}% imprimer l'abscisse + \ifdim\xx pt<#4pt % et en débordement à droite, + \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière + \fi + \kern#1\relax% se déplacer vers la droite + }% + }% + \vrule% tracer l'axe des abscisses + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax + depth 0pt\relax % et de profondeur nulle + }% +} +a) \xaxis{-2}{5}[2] + +)b \xaxis[1.25cm]{-1}[0.25]{1}[5] +****************** Fin code ****************** + + +****************** Code 383 ****************** +\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\subgraddim=2.5pt \subgradwd=0.2pt +\def\maingrady#1{% affiche... + \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée... + \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure +} + +% affiche une subdiv +\def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }} +\newmacro\yaxis[1cm]1[1]1[4]{% +% #1= dist #2=ymin #3=inc #4=ymax #5=subdiv + \vbox{% + \offinterlineskip% désactiver le ressort d'interligne + \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions + \for\xx=1 to #5-1 \do{% insérer #5-1 fois + \kern\dimsubgrad% une espace secondaire + \subgrady% une graduation secondaire + }% + }% + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions + \vbox to 0pt{% en débordement vers le bas + \FOR\xx = #4to#2\do-#3{% + \maingrady{\xx}% imprimer l'abscisse + \ifdim\xx pt>#2pt % et en débordement à droite, + \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière + \fi + \kern#1\relax% se déplacer vers la droite + }% + \vss% assure le débordement vers le bas + }% + \clap{\vrule% tracer l'axe des ordonnées + width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 + height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax + depth 0pt\relax % profondeur nulle + }% + }% +} + +Essai : \yaxis{-2}{5}[2]\qquad \frboxsep=0pt \frbox{\yaxis[0.75cm]{-1}[0.5]{3}[5]} +****************** Fin code ****************** + + +****************** Code 384 ****************** +\newdimen\xunit \xunit=1cm +\newdimen\yunit \yunit=0.75cm +\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\subgraddim=2.5pt \subgradwd=0.2pt +\catcode`@11 +\newmacro\graphzone1[1]1[4]1[1]1[4]{% + \leavevmode% quitter le mode vertical + \begingroup% travailler dans un groupe semi-simple + \def\graphxmin{#1}\def\graphxmax{#3}% sauvegarder + \def\graphymin{#5}\def\graphymax{#7}% les + \def\xincrement{#2}\def\yincrement{#6}% arguments + \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% axe "y" dans boite 0 + \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% axe "x" dans boite 1 + \edef\graphboxht{\the\ht0 }% \graphboxh est la hauteur de la zone + \edef\graphboxwd{\the\wd1 }% \graphboxw est la largeur de la zone + \rlap{% annuler la dimension horizontale, et... + \box1 % ...afficher l'axe (Ox) puis + % le trait vertical à l'extrême droite de la zone + \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }% + }% + \rlap{\box0 }% afficher l'axe (Oy) en annulant sa dimension horizontale + \raise\graphboxht% puis monter tout en haut de la zone + % pour tracer le trait horizontal en annulant sa longueur + \rlap{\kern-0.5\axiswd% et en rattrapant le centrage de l'axe des ordonnées + \vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}% + \begingroup% pour lire l'argument "<code>" : + \catcode`\^^M=9\relax % ignorer les retours à la ligne + \graphzone@i% appeler \graphzone@i +} +\def\graphzone@i#1{% lire le <code>... + \endgroup% et fermer le groupe précédemment ouvert + \setbox0\hbox{#1}% mettre les instructions graphique dans la boite 0 + \wd0=\dimexpr\graphboxwd+\axiswd\relax% forcer sa dim horizontale + \ht0=\dimexpr\graphboxht+\axiswd\relax% et verticale + % pour correspondre aux dimension de la zone graphique + \box0 % l'afficher + \endgroup% sortir du groupe initial +} +\catcode`@12 +essai\graphzone{-1}[0.5]{1.5}[5]{-20}[10]{10}[2]{\relax}fin +****************** Fin code ****************** + + +****************** Code 385 ****************** +\def\putat#1#2#3{% + \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}% +} +Essai\putat{0.5cm}{0.3cm}{A}\putat{-0.2cm}{-0.1cm}{B}\putat{0pt}{0.2cm}{C}suite +****************** Fin code ****************** + + +****************** Code 386 ****************** +\def\ifinside#1[#2,#3]{% + \ifnum\sgn{\dimexpr#1pt-#2pt\relax}\sgn{\dimexpr#1pt-#3pt}1=1 + \expandafter\secondoftwo + \else + \expandafter\firstoftwo + \fi +} +1) \ifinside3.5[0.5,4]{vrai}{faux}\qquad +2) \ifinside-0.2[-0.4,-0.3]{vrai}{faux}\qquad +3) \ifinside-0.999[-1,-0.5]{vrai}{faux} +****************** Fin code ****************** + + +****************** Code 387 ****************** +\def\fonction#1{\dimtodec\dimexpr + \decmul{#1}{\decmul{#1}{#1}}pt% x^3 + -\decmul{#1}{#1}pt% -x^2 + -#1pt*3% -3x + +2pt\relax}% +2 +1) \fonction{-4}\qquad% doit afficher -66 +2) \fonction{-1.7}\qquad % doit afficher -0.703 +3) \fonction{1}\qquad% doit afficher -1 +4) \fonction{1.35}% doit afficher -1.412125 +****************** Fin code ****************** + + +****************** Code 388 ****************** +\newmacro\cross[2pt][0.2pt]{% + % #1=dimensions de traits depuis le centre de la croix + % #2=épaisseur des traits + \leavevmode + \vlap{% + \clap{% + \vrule height#2 depth0pt width#1 % 1/2 trait horizontal gauche + \vrule height#1 depth#1 width#2 % trait vertical + \vrule height#2 depth0pt width#1 % 1/2 trait horizontal droit + }% + }% +} +Une croix : \cross{}puis une autre\cross[8pt][0.8pt] +****************** Fin code ****************** + + +****************** Code 389 ****************** +\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\subgraddim=2.5pt \subgradwd=0.2pt +\xunit=1.25cm \yunit=0.75cm + +\def\maingradx#1{% + \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous + \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure +} + +\def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }} + +\newmacro\xaxis[1cm]1[1]1[4]{% + \hbox{% tout mettre dans une \hbox + \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions + \for\xx=1 to #5-1 \do{% insérer #5-1 fois + \kern\dimsubgrad% une espace secondaire + \subgradx% une graduation secondaire + }% + }% + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale + \maingradx{\xx}% imprimer l'abscisse + \ifdim\xx pt<#4pt % et en débordement à droite, + \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière + \fi + \kern#1\relax% se déplacer vers la droite + }% + }% + \vrule% tracer l'axe des abscisses + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax + depth 0pt\relax % et de profondeur nulle + }% +} + +\def\maingrady#1{% affiche... + \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée... + \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure +} + +% affiche une subdiv +\def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }} +% #1= dim entre 2 grad principales #2=abscisse départ #3=incrément +% #4=abscisse arrivée #5=nb intervalles secondaires +\newmacro\yaxis[1cm]1[1]1[4]{% + \vbox{% + \offinterlineskip% désactiver le ressort d'interligne + \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions + \for\xx=1 to #5-1 \do{% insérer #5-1 fois + \kern\dimsubgrad% une espace secondaire + \subgrady% une graduation secondaire + }% + }% + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions + \vbox to 0pt{% en débordement vers le bas + \FOR\xx = #4to#2\do-#3{% + \maingrady{\xx}% imprimer l'abscisse + \ifdim\xx pt>#2pt % et en débordement à droite, + \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière + \fi + \kern#1\relax% se déplacer vers la droite + }% + \vss% assure le débordement vers le bas + }% + \clap{\vrule% tracer l'axe des ordonnées + width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 + height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax + depth 0pt\relax % profondeur nulle + }% + }% +} + +\catcode`@11 +\newmacro\graphzone1[1]1[4]1[1]1[4]{% + \leavevmode% quitter le mode vertical + \begingroup% travailler dans un groupe semi-simple + \def\graphxmin{#1}\def\graphxmax{#3}% sauvegarder + \def\graphymin{#5}\def\graphymax{#7}% les + \def\xincrement{#2}\def\yincrement{#6}% arguments + \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% axe "y" dans boite 0 + \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% axe "x" dans boite 1 + \edef\graphboxht{\the\ht0 }% \graphboxh est la hauteur de la zone + \edef\graphboxwd{\the\wd1 }% \graphboxw est la largeur de la zone + \rlap{% annuler la dimension horizontale, et... + \box1 % ...afficher l'axe (Ox) puis + % le trait vertical à l'extrême droite de la zone + \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }% + }% + \rlap{\box0 }% afficher l'axe (Oy) en annulant sa dimension horizontale + \raise\graphboxht% puis monter tout en haut de la zone + % pour tracer le trait horizontal en annulant sa longueur + \rlap{\kern-0.5\axiswd% et en rattrapant le centrage de l'axe des ordonnées + \vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}% + \begingroup% pour lire l'argument "<code>" : + \catcode`\^^M=9\relax % ignorer les retours à la ligne + \graphzone@i% appeler \graphzone@i +} +\def\graphzone@i#1{% lire le <code>... + \endgroup% et fermer le groupe précédemment ouvert + \xunit=\decdiv1\xincrement\xunit% divise les unités par l'incrément + \yunit=\decdiv1\yincrement\yunit + \setbox0\hbox{#1}% mettre les instructions graphique dans la boite 0 + \wd0=\dimexpr\graphboxwd+\axiswd\relax% forcer sa dim horizontale + \ht0=\dimexpr\graphboxht+\axiswd\relax% et verticale + % pour correspondre aux dimension de la zone graphique + \box0 % l'afficher + \endgroup% sortir du groupe initial +} + +\def\putat#1#2#3{% + \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}% +} + +\newmacro\cross[2pt][0.2pt]{% + \leavevmode + \vlap{% + \clap{% + \vrule height#2 depth0pt width#1 % 1/2 terait horizontal gauche + \vrule height#1 depth#1 width#2 % trait vertical + \vrule height#2 depth0pt width#1 % 1/2 trait horizontal droit + }% + }% +} + +\def\plot(#1,#2){% place "\plotstuff" aux coordonnées #1,#2 + \edef\x@plot{#1}\edef\y@plot{#2}% développer au cas où + \ifinside\x@plot[\graphxmin,\graphxmax]% si #1 est dans les limites + {\ifinside\y@plot[\graphymin,\graphymax]% et si #2 l'est aussi + {\putat% placer aux coordonnées + {\dimexpr\x@plot\xunit-\graphxmin\xunit\relax}% X=(x-xmin)/xinc + {\dimexpr\y@plot\yunit-\graphymin\yunit\relax}% Y=(y-ymin)/yinc + \plotstuff% le contenu de \plotstuff + }% + \relax + } + \relax +} + +\def\showxaxis{% affiche l'axe (Ox) + \ifinside0[\graphymin,\graphymax]% + {\putat\z@{-\graphymin\yunit}{\vlap{\vrule width\graphboxwd height\axiswd}}}% + \relax +} + +\def\showyaxis{% affiche l'axe (Oy) + \ifinside0[\graphxmin,\graphxmax]% + {\putat{-\graphxmin\xunit}\z@{\clap{\vrule width\axiswd height\graphboxht}}}% + \relax +} + +\def\showaxis{\showxaxis\showyaxis}% affiche les deux axes +\catcode`@12 + +%%%%%%%%%%%%%%% 1er exemple : +\xunit=1cm \yunit=0.5cm +\def\fonction#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% + \decmul{#1}{#1}pt-#1pt*3+2pt\relax} +Exemple 1 :\qquad +\graphzone{-2}[0.5]{2.5}[5]{-5}{5}[2]{% + \let\plotstuff\cross% définit ce qu'il faut afficher comme point + \FOR\xx=-2 to 2.5 \do 0.1{% + \plot(\xx,\fonction{\xx})% afficher les points de coordonnées (x ; f(x)) + }% + \putat{5pt}{\dimexpr\graphboxht-10pt\relax}% afficher + {$f(x)=x^3-x^2-3x+2$}% la fonction tracée + \showaxis% et les axes +}\par\medskip +%%%%%%%%%%%%% 2e exemple +\xunit=0.7cm \yunit=\xunit +\def\foncx#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% + \decmul{4}{\decmul{#1}{#1}}pt-#1pt+4pt} +\def\foncy#1{\dimtodec\dimexpr\decmul{#1}{#1}pt-#1pt*2-6pt} +Exemple 2 :\qquad +\graphzone{-12}[2]{12}[2]{-8}[2]{8}[2]{% + \def\plotstuff{\cross[1.25pt]} + \FOR\tt = -5 to 5 \do 0.1 {% + \plot(\foncx\tt,\foncy\tt)% + }% + \putat{5pt}{\dimexpr\graphboxht-20pt\relax}% afficher la fonction paramétrique tracée + {$\left\{\vcenter{\hbox{$x(t)=t^3-4t^2-t-4$}\hbox{$y(t)=t^2-2t-6$}}\right.$}% + \showaxis +} +****************** Fin code ****************** + + +****************** Code 390 ****************** +\def\mandeltest#1#2{% + \def\zx{0}\def\zy{0}% zn=0 + i*0 + \def\zxx{0}\def\zyy{0}% carrés de \zx et \zy + \def\mandelresult{1}% le point appartient à M a priori + \for\ii=1to\maxiter\do1{% + \advance\count255 by1 + \edef\zy{\dimtodec\dimexpr\decmul{\decmul2\zx}\zy pt+#2pt\relax}% + \edef\zx{\dimtodec\dimexpr\zxx pt-\zyy pt+#1pt\relax}%% + \edef\zxx{\decmul\zx\zx}% + \edef\zyy{\decmul\zy\zy}% + \ifdim\dimexpr\zxx pt+\zyy pt\relax>4pt + \def\mandelresult{0}% + \exitfor\ii + \fi + }% +} +\def\mandel#1#2{% #1=points par unité #2=nombre maximal d'itérations + \graphzone{-2}[1]{1}[2]{-1}[1]{1}[2]{% + \def\maxiter{#2}% + \edef\plotstuff{\the\dimexpr\xunit/#1\relax}% taille d'un pixel + \edef\plotstuff{\vrule height\plotstuff width\plotstuff}% + \edef\increment{\decdiv{1}{#1}}% incrément + \count255=0 % compteur des itérations + \FOR\xxx = -2 to 1 \do \increment{% pour chaque + \FOR\yyy = 0 to 1 \do \increment{% pixel du domaine + \mandeltest\xxx\yyy% tester s'il est dans M + \ifnum\mandelresult=1 % si oui, + \plot(\xxx,\yyy)\plot(\xxx,-\yyy)% afficher les 2 points + \fi + }% + }% + \edef\plotstuff{$\scriptstyle\number\count255 $}% affiche la valeur du compteur + \plot(-1.99,0.92)% aux coordonnées (-1.99 ; 0.92) + }% +} +\xunit=3cm \yunit=3cm \mandel{400}{500} +****************** Fin code ****************** + + +****************** Code 391 ****************** +\catcode`\@11 +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres + +\def\formatdecpart#1{% #1=série de chiffres + \ifempty{#1}% si la partie décimale est vide + {}% ne rien afficher + {{,}\formatdecpart@i 1.#1..}% sinon, afficher la virgule et mettre en forme +} + +% #1=compteur de caractères #2= chiffre courant +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatdecpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre + {#4#2}% le mettre en dernière position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatdecpart@i \number\numexpr#1+1.#3.#4#2.}% + }% +} +a) \formatdecpart{1234567}\qquad +b) \formatdecpart{987125}\qquad +c) \formatdecpart{56}\qquad +d) \edef\foo{\formatdecpart{2014}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 392 ****************** +\catcode`\@11 +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres +\def\formatintpart#1{% #1=série de chiffres + \formatintpart@i 1.#1..% appelle la macro récursive +} + +% #1=compteur de caractères #2= chiffre courant +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatintpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre + {#2#4}% le mettre en première position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo + \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatintpart@i 1.#3.\numsep#2#4.}% mettre "\numsep#2" en premier et recommencer + % sinon, mettre #2 en première position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatintpart@i \number\numexpr#1+1.#3.#2#4.}% + }% +} +\catcode`\@12 +a) \formatintpart{1234567}\qquad b) \formatintpart{987125}\qquad +c) \formatintpart{56}\qquad +d) \edef\foo{\formatintpart{2014}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 393 ****************** +\catcode`\@11 +\def\formatintpart#1{% #1=série de chiffres + \expandafter\formatintpart@i\expandafter1\expandafter.\romannumeral\reverse{#1\z@}..% +} + +\catcode`\@12 +a) \formatintpart{1234567}\qquad b) \formatintpart{987125}\qquad +c) \formatintpart{56}\qquad +d) \edef\foo{\formatintpart{2014}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 394 ****************** +\catcode`\@11 +\def\ifnodecpart#1{\if@nodecpart#1.\@nil}% teste si #1 est un entier +\def\if@nodecpart#1.#2\@nil{\ifempty{#2}} + +\def\formatnum#1{% + \ifnodecpart{#1}% s'il n'y a pas de partie décimale + {\formatintpart{#1}}% formatter la partie entière + {\formatnum@i#1\@nil}% sinon, formatter les deux parties +} + +\def\formatnum@i#1.#2\@nil{% + \formatintpart{#1}% formatte la partie entière + \formatdecpart{#2}% et la partie décimale +} + +\catcode`\@12 +a) \formatnum{3.1415926}\qquad +b) \formatnum{1987654.12301}\qquad +c) \edef\foo{\formatnum{0987654.12300}}$\foo$ +****************** Fin code ****************** + + +****************** Code 395 ****************** +\catcode`\@11 +\def\removefirstzeros#1{% + \removefirstzeros@i#1\quark% ajoute "\quark" en dernier +} +\def\removefirstzeros@i#1{% #1=chiffre courant + \ifx\quark#1% fin atteinte donc nombre = 0 + \expandafter0% laisser un zéro + \else + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer + \else% sinon remettre le chiffre #1 et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removefirstzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removefirstzeros@ii#1\quark{#1} +\catcode`\@12 +a) \removefirstzeros{000325478}\qquad +b) \removefirstzeros{00000}\qquad +c) \edef\foo{\removefirstzeros{001000}}\meaning\foo\qquad +d) \long\def\>#1<{\detokenize{#1}} + \expandafter\>\romannumeral\removefirstzeros{0123}< +****************** Fin code ****************** + + +****************** Code 396 ****************** +\long\def\>#1<{\detokenize{#1}} +\expandafter\>\romannumeral-`\@\removefirstzeros{000123}< +****************** Fin code ****************** + + +****************** Code 397 ****************** +\catcode`\@11 +\def\removelastzeros#1{% + \exparg\reverse% inverser après + {\romannumeral-`\.% tout développer + \expandafter\removelastzeros@i% enlever les 0 de gauche après + \romannumeral\reverse{#1\z@}\quark% avoir inversé #1 + }% +} +\def\removelastzeros@i#1{% enlève tous les 0 de gauche + \unless\ifx\quark#1% si la fin n'est pas atteinte + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removelastzeros@i% recommencer + \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removelastzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removelastzeros@ii#1\quark{#1} +\catcode`\@12 +a) \removelastzeros{0003254780}\qquad +b) \removelastzeros{00000}\qquad +c) \edef\foo{\removelastzeros{001000}}\foo\qquad +\long\def\>#1<{\detokenize{#1}} +d) \expandafter\>\romannumeral-`\.\removelastzeros{012300}< +****************** Fin code ****************** + + +****************** Code 398 ****************** +\catcode`\@11 +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres + +\def\formatdecpart#1{% #1=série de chiffres + \ifempty{#1}% si la partie décimale est vide + {}% ne rien afficher + {{,}\formatdecpart@i 1.#1..}% sinon, afficher la virgule et mettre en forme +} + +% #1=compteur de caractères #2= chiffre courant +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatdecpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre + {#4#2}% le mettre en dernière position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatdecpart@i \number\numexpr#1+1.#3.#4#2.}% + }% +} + +\def\formatintpart#1{% #1=série de chiffres + \expandafter\formatintpart@i\expandafter1\expandafter.% + \romannumeral\reverse{#1\z@}..% appelle la macro récursive +} + +% #1=compteur de caractères #2= chiffre à déplacer +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatintpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre à traiter + {#2#4}% le mettre en première position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatintpart@i 1.#3.\numsep#2#4.}% mettre \numsep#2 en premier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatintpart@i\number\numexpr#1+1.#3.#2#4.}% + }% +} +\def\removefirstzeros#1{% + \removefirstzeros@i#1\quark% ajoute "\quark" en dernier +} +\def\removefirstzeros@i#1{% #1=chiffre courant + \ifx\quark#1% fin atteinte donc nombre = 0 + \expandafter0% laisser un zéro + \else + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer + \else% sinon remettre le chiffre #1 et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removefirstzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removefirstzeros@ii#1\quark{#1} + +\def\removelastzeros#1{% + \exparg\reverse% inverser après + {\romannumeral-`\.% tout développer + \expandafter\removelastzeros@i% enlever les 0 de gauche après + \romannumeral\reverse{#1\z@}\quark% avoir inversé #1 + }% +} +\def\removelastzeros@i#1{% enlève tous les 0 de gauche + \unless\ifx\quark#1% si la fin n'est pas atteinte + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removelastzeros@i% recommencer + \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removelastzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removelastzeros@ii#1\quark{#1} + +% renvoie vrai s'il n'y a pas de partie décimale +\def\if@nodecpart#1.#2\@nil{\ifempty{#2}} + +\def\formatnum#1{\formatnum@i1!#1!} + +% #1 = <série de signes> suivie de "1" +% #2 = caractère courant +% #3 = caractères non traités +\def\formatnum@i#1!#2#3!{% + \ifx+#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\ifempty{#3}% si #2=+ et #3 est vide + {\number#1 }% afficher "+1" ou "-1" (il n'y avait que des signes) + {\formatnum@i#2#1!#3!}% sinon, mettre le signe #2 devant #1 + } + {\ifx-#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\ifempty{#3}% si #2=- et #3 est vide + {\number#1 }% afficher "+1" ou "-1" (il n'y avait que des signes) + {\formatnum@i#2#1!#3!}% sinon, mettre le signe #2 devant #1 + }% #2 est le dernier caractère qui n'est pas un signe + ou - + {\ifnum#1<0 -\fi% si "<signes>1" est <0 afficher un "-" + \formatnum@ii{#2#3}% formatter le nombre formé avec "#2#3"" + }% + }% +} + +\def\formatnum@ii#1{% + \if@comma{#1}% si #1 comporte une virgule + {\formatnum@iii#1\@nil}% la remplacer par un "." et recommencer + {\if@nodecpart#1.\@nil% puis si les chiffres restants sont un entier + {\formatintpart{#1}}% formatter l'entier + {\formatnum@iv#1\@nil}% sinon, formatter le nombre + }% +} + +\def\formatnum@iii#1,#2\@nil{\formatnum@ii{#1.#2}} + +\def\formatnum@iv#1.#2\@nil{% formatte le nombre décimal #1.#2 + \exparg\formatintpart{\romannumeral-`\.\removefirstzeros{#1}}% + \exparg\formatdecpart{\romannumeral-`\.\removelastzeros{#2}}% +} + +\def\if@comma#1{\if@comma@i#1,\@nil}% teste la présence d'un virgule dans #1 +\def\if@comma@i#1,#2\@nil{\ifempty{#2}\secondoftwo\firstoftwo} +\catcode`\@12 + +a) \formatnum{3,141592653589793238462643383279502884197169399375105820974944592}\par +b) \formatnum{---+69874}\qquad +c) \formatnum{0032100,98000}\qquad +d) \formatnum{+++010.01100}\qquad +e) \formatnum{-+--+}\qquad +f) \formatnum{---00.0000}\qquad +g) \formatnum{+99,0000}\qquad +h) \formatnum{.123456}\par +i) \edef\foo{\formatnum{-+-+-010500,090900}}\meaning\foo +****************** Fin code ****************** + + +****************** Code 399 ****************** +\setbox0=\vbox{% + \hbox{Première ligne} + \hbox{Deuxième ligne} + \hbox{Avant-dernière ligne} + \hbox{Dernière ligne} +} + +\frboxsep=0pt % aucun espace entre le contenu et l'encadrement +Boite initiale de hauteur \the\ht0 {} : \frbox{\copy0 } +\splittopskip0pt % ne rajouter aucun espace au sommet de la boite restante +\setbox1=\vsplit0 to 22pt % couper la boite à 22pt de hauteur + +Boite 1 de hauteur \the\ht1 {} : \frbox{\box1 } + +Boite 0 de hauteur \the\ht0 {} : \frbox{\box0 } +****************** Fin code ****************** + + +****************** Code 400 ****************** +\setbox0=\vbox{% + \hbox{Première ligne} + \hbox{Deuxième ligne} + \hbox{Avant-dernière ligne} + \hbox{Dernière ligne} +} + +\edef\restorevbadness{\vbadness=\the\vbadness\relax}% restaurera le \vbadness +\vbadness=10000 % plus d'avertissement pour boite verticale +\frboxsep=0pt % aucun espace entre le contenu et l'encadrement +Boite initiale de hauteur \the\ht0 {} : \frbox{\copy0 } +\splittopskip0pt % ne rajouter aucun espace au sommet de la boite restante +\setbox1=\vsplit0 to 22pt % couper la boite à 22pt de hauteur +\setbox1=\vbox{\unvbox1 }% la boite 1 prend la hauteur naturelle +\restorevbadness\relax% restaure le \vbadness + +Boite 1 de hauteur \the\ht1 {} : \frbox{\box1 } + +Boite 0 de hauteur \the\ht0 {} : \frbox{\box0 } +****************** Fin code ****************** + + +****************** Code 401 ****************** +\catcode`@11 +\newbox\remainbox +\newbox\partialbox +\newdimen\cut@ht + +\def\breakpar{% + \par\nointerlineskip% termine le paragraphe précédent + \vskip\frboxsep\relax% et saute une petite espace verticale + \begingroup + \splittopskip\topskip% \topskip en haut des boites coupées + \topskip=0pt % neutraliser le \topskip + % nbre de réglures horizontales contribuant à l'encadrement restant (2 au début) + \def\coeff@rule{2}% + \setbox\remainbox=\vbox\bgroup% compose la boite après avoir... + \advance\hsize by -2\dimexpr\frboxrule+\frboxsep\relax% ajusté sa largeur +} + +\def\endbreakpar{% + \egroup% fin de la composition de la boite + \def\rule@arg{ULR}% prendre l\cut@htes réglures d'encadrement haute, gauche et droite + \splitbox% puis, aller à l'algorithme de coupure + \endgroup% une fois fini, sortir du groupe semi-simple +} + +\def\splitbox{% + \ifvoid\remainbox% si la boite est vide, c'est la fin du processus + \par\nointerlineskip% termine le paragraphe précédent + \vskip\frboxsep\relax% et saute un petit espace vertical + \else% sinon + \expandafter\splitbox@i% aller à \splitbox@i + \fi +} + +\def\splitbox@i{% + \hbox{}% composer un noeud en mode vertical + \nointerlineskip% pas de ressort d'interligne + % calculer la dimension verticale disponible dans la page pour le texte de la boite + \cut@ht=\dimexpr\pagegoal-\pagetotal-(\frboxsep+\frboxrule)*\coeff@rule\relax + % si dimension totale du texte > dimension disponible pour le texte + \ifdim\dimexpr\ht\remainbox+\dp\remainbox>\cut@ht% si une coupure doit être faite + \advance\cut@ht\dimexpr% augmenter Dv de l'espace verticale libérée + +\frboxsep+\frboxrule% par la réglure inférieure qui n'est pas sur cette page + \relax + \edef\old@vbadness{\the\vbadness}% sauvegarder \badness + \vbadness=10000 % désactive les avertissement lors de la coupure + \def\coeff@rule{1}% ne prendre en compte que réglure D + espace D + \setbox\partialbox=\vsplit\remainbox to\cut@ht% coupe à la hauteur calculée + % \partialbox retrouve sa hauteur naturelle + \setbox\partialbox=\vbox{\unvbox\partialbox}% + \vbadness=\old@vbadness\relax% restaure \vbadness + \printpartialbox% imprime la boite partielle + \vfill\eject% et compose la page en cours + \else% si une coupure n'est pas nécessaire : + \setbox\remainbox\vbox{\unvbox\remainbox}% reprendre la hauteur naturelle + \setbox\partialbox=\box\remainbox% \partialbox devient \remainbox + % et cette dernière devient vide + \cut@ht=\dimexpr\ht\partialbox+\dp\partialbox\relax% hauteur à encadrer + \edef\rule@arg{\rule@arg D}% ajouter "D" aux réglures à tracer + \printpartialbox% afficher la boite restante + \fi + \splitbox +} + +\def\printpartialbox{% imprime \partialbox + \expandafter\framebox\expandafter[\rule@arg]{% + \vbox to\cut@ht{\unvbox\partialbox\vss}}% + \def\rule@arg{LR}% ne mettre que les réglures d et g +} + +\def\dummytext#1{% + \for\xx=1to#1\do% composer #1 fois la phrase suivante : + {Ceci est un texte sans aucun int\'er\^et dont le seul but est de meubler + la page artificiellement. + }% +} + +\catcode`@12 +\dummytext{5} +\frboxsep=5pt + +\breakpar + \dummytext{70} +\endbreakpar + +\dummytext{5} +****************** Fin code ****************** + + +****************** Code 402 ****************** +\setbox0=\vbox{% + \hsize=5cm + Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. +} + +Boite 0 : \copy0 % affiche la boite totale +\medbreak + +\edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% appelée après la coupure +\vfuzz=\maxdimen% annule les avertissements pour débordement +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante +\setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur +\restorevfuzz% restaurer \vfuzz +\setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine + +Boites 1+0 : \vbox{% + \offinterlineskip% annule le ressort d'interligne + \box1 % affiche la première ligne + \box0 %affiche les lignes restantes +} +****************** Fin code ****************** + + +****************** Code 403 ****************** +\def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1 +\setbox0=\vbox{% + \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est + de meubler la page de façon artificielle. +} +\edef\htbefore{\the\vdim0}% hauteur de la boite 0 + +Boite 0 : \copy0 % affiche la boite totale +\medbreak + +\edef\restoreparam{% + \vfuzz=\the\vfuzz\relax% sauvegarde le \vfuzz + \splittopskip=\the\splittopskip% et \splittopskip +}% +\vfuzz=\maxdimen% annule les avertissements pour débordement +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante +\setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur +\restoreparam +\setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine + +\edef\intersplitspace{\the\dimexpr\htbefore-(\vdim0+\vdim1)\relax}% +Boites 1+0 : \vbox{% + \offinterlineskip% annule le ressort d'interligne + \box1 % affiche la première ligne + \vskip\intersplitspace\relax% ajoute le ressort perdu à la coupure + \box0 % affiche les lignes restantes +} +****************** Fin code ****************** + + +****************** Code 404 ****************** +\setbox0=\vbox{% + \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est + de meubler la page de façon artificielle. +} + +Boite 0 : \copy0 % affiche la boite totale +\medbreak + +\edef\restoreparam{% + \vfuzz=\the\vfuzz\relax% sauvegarde le \vfuzz + \splittopskip=\the\splittopskip\relax% , le \splittopskip + \savingvdiscards=\the\savingvdiscards\relax% et le \savingvdiscards + }% +\vfuzz=\maxdimen% annule les avertissements pour débordement +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante +\savingvdiscards=1 % autorise la sauvagarde des éléments supprimés +\setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur +\setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine +\restoreparam + +Boites 1+0 : \vbox{% + \offinterlineskip% annule le ressort d'interligne + \box1 % affiche la première ligne + \splitdiscards% affiche les éléments ignorés + \box0 % affiche les lignes restantes +} +****************** Fin code ****************** + + +****************** Code 405 ****************** +\catcode`@11 +\newbox\remainbox% boite contenant le texte total +\newbox\currentline% boite contenant le ligne en cours +\newcount\linecnt% compteur pour numéroter les lignes + +\def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1 + +\newmacro\leftline[0pt]{% définit ce qui se trouve à gauche de chaque ligne + \def\wd@left{#1}% + \def\stuff@left +} + +\newmacro\rightline[0pt]{% définit ce qui se trouve à droite de chaque ligne + \def\wd@right{#1}% + \def\stuff@right +} + +\let\formatline=\identity% par défaut, afficher chaque ligne telle quelle + +% Par défaut : +\leftline[11pt]{$\scriptscriptstyle\number\linecnt$\kern3pt }% numérotation à gauche +\rightline{}% rien à droite + +\def\numlines{% + \par\smallskip + \begingroup% dans un groupe semi-simple + \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante + \linecnt=0 % initialiser le compteur de lignes + \savingvdiscards=1 % autorise la sauvagarde des éléments supprimés + \setbox\remainbox=\vbox\bgroup% compose la boite... + \advance\hsize by% diminuer la \hsize + -\dimexpr\wd@left+\wd@right\relax% de la largeur des contenus +} + +\def\endnumlines{% + \egroup + \offinterlineskip + \split@line +} + +\def\split@line{% + \ifvoid\remainbox% si la boite est vide + \par% fin du processus + \endgroup% fermer le groupe ouvert au début + \else% sinon + \advance\linecnt 1 % incrémente le compteur de lignes + \edef\htbefore{\the\vdim\remainbox}% + \edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% sauvegarde le \vfuzz + \vfuzz=\maxdimen% annule les avertissements pour débordement + \setbox\currentline=\vsplit\remainbox to 0pt % couper la boite à 0pt de hauteur + \restorevfuzz + \setbox\currentline=\vbox{\unvbox\currentline}% redonner à la boite sa hauteur + \edef\intersplitspace{% calcul de l'espace vertical perdu à la coupure + \the\dimexpr\htbefore-(\vdim\remainbox+\vdim\currentline)\relax + }% + \hbox{% en mode vertical et dans une hbox, afficher : + \hbox to\wd@left{\hss\stuff@left}% 1) ce qui est à gauche + \formatline{\box\currentline}% 2) la ligne courante + \hbox to\wd@right{\stuff@right\hss}% 3) ce qui est à droite + }% + \splitdiscards% affiche ce qui a été ignoré à la coupure + \expandafter\split@line% recommencer + \fi +} + +\def\dummytext#1{% + \for\xx=1to#1\do% composer #1 fois la phrase suivante : + {Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. }% +} + +\catcode`@12 +\parindent=2em + +ESSAI 1 : +\numlines + \dummytext{2}\par% 2 phrases + $$1+1=2$$\par% des maths + \dummytext{1}\par% une phrase + \hrulefill\par% un leaders + \dummytext{2}% 2 phrases + \hrule height2pt depth 2pt %une \hrule + \vskip10pt % saute 10pt verticalement + \dummytext{1}% une phrase +\endnumlines\medbreak + +ESSAI 2 : +\leftline{}\rightline{}% rien à gauche et rien à droite +\frboxsep=-\frboxrule% encadrer vers "l'intérieur" +\let\formatline\frbox% lignes encadrées +\numlines + \dummytext{4} +\endnumlines\medbreak + +ESSAI 3 : +\let\formatline\identity +\leftline[7pt]{% + \for\xx= 1 to 2 \do{% insérer 2 fois + \setbox0=\hbox{% % mettre dans une \hbox une \vrule de "bonnes dimensions" + \vrule height\ht\currentline depth\dimexpr\dp\currentline+\intersplitspace + width0.5pt }% + \dp0=\dp\currentline% réajuster la profondeur (c-à-d enlever \intersplitspace) + \box0 % afficher le boite + \kern2pt % et insérer un espace horizontal de 2pt après chaque réglure verticale + }% + \kern2pt % ajouter 2pt de plus entre les lignes et le texte +} + +\rightline[10pt]{\kern5pt $\scriptscriptstyle\number\linecnt$}% numéroter à droite + +\numlines + \dummytext{2} + + $$a^2+b^2=c^2$$ + + \dummytext{2} +\endnumlines +****************** Fin code ****************** + + +****************** Code 406 ****************** +% définition des ressorts "inter-lettre" et "inter-mot" +\newskip\interletterskip \interletterskip=0.25em plus0.05em minus0.05em +\newskip\interwordskip \interwordskip=3\interletterskip\catcode`\@11 +\catcode`@11 +\def\spreadtxt@testtoken#1{% macro qui teste le token + \ifcat\noexpand#1\sptoken% si le token est un espace + \parseadd{% + \unskip% retirer le précédent ressort + \hskip\interwordskip}% et ajouter le ressort inter-mot + \else + \ifcat\noexpand#1a% si le token est une lettre + \parseadd{#1\hskip\interletterskip}% ajouter le ressort inter-lettre + \else + \ifcat\noexpand#1.% si le token est "autre", comme le "." + \parseadd{#1\hskip\interletterskip}% ajouter le ressort inter-lettre + \else% sinon + \parseadd{#1}% ajouter le token lu + \fi + \fi + \fi + \parse@i +} +\def\spreadtxt{% + \ifstarred% si étoilée + {\spreadtxt@i{\parse*}}% appeler \parse* + {\spreadtxt@i{\parse}}% sinon, appeler \parse +} +\def\spreadtxt@i#1#2{% #1= appel "\parse*" ou "\parse" #2 = texte à espacer + \begingroup% dans un groupe + \let\testtoken=\spreadtxt@testtoken% modifier \testtoken + #1#2\parsestop% et appeler \parse + \endgroup +} +\catcode`@12 + +\spreadtxt{Comme on le voit sur cet exemple, une espace est insérée après +{\it chaque} caractère de catcode 10, 11 ou 12.} +\medbreak + +\spreadtxt*{Comme on le voit sur cet exemple, une espace est insérée après +{\it chaque} caractère de catcode 10, 11 ou 12.} +****************** Fin code ****************** + + +****************** Code 407 ****************** +\newskip\interletterskip +\newskip\interwordskip +\catcode`\@11 +\newtoks\spacetxt@toks% le registre qui contient le texte final + +\def\spacetxt{% + \let\spacetxt@endprocess\spacetxt@endnormal + % définit la macro appelée en fin de processus -> a priori : fin normale + \ifstarred% si la macro est étoilée + {\let\spacetxt@recurse\spacetxt@star% définir la macro récursive + \spacetxt@i% et aller à \spacetxt@i + }% sinon + {\let\spacetxt@recurse\spacetxt@nostar% définir la macro récursive + \spacetxt@i% et aller à \spacetxt@i + }% +} + +\newmacro\spacetxt@i[0.3em plus0.07em minus.07em][3\interletterskip]1{% +% arg optionnel #1 et #2 = ressorts inter-lettre et inter--mot +% #3 = texte à espacer + \interletterskip=#1\relax + \interwordskip=#2\relax + \def\spacetxt@code{#3}% met le texte à espacer dans \spacetxt@code + \spacetxt@toks{}% initialiser le registre contenant le texte final + \spacetxt@recurse% aller à la macro récursive précédemment définie +} + +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré + +\def\rightofsc#1#2{% + \exparg\ifin{#1}{#2}% si #1 contient le #2 + {\def\right@of##1#2##2\@nil{\def#1{##2}}% + \expandafter\right@of#1\@nil% appelle la macro auxiliaire + }% + {\let#1=\empty}% sinon, #1 est vide +} + +\def\spacetxt@nostar{% + \exparg\ifempty{\spacetxt@code}% si texte restant est vide + \spacetxt@endprocess% aller à la fin du processus + {\@indivifoundfalse% sinon, a priori, les motifs non réguliers ne sont pas trouvés + % pour chaque \indivi@tmp dans \indivilist + \expsecond{\doforeach\indivi@tmp\in}{\indivilist}% pour chaque motif indivisible + {% si le code commence par le motif courant + \exptwoargs\ifstartwith\spacetxt@code\indivi@tmp + {% l'ajouter dans le registre ainsi que l'espace inter-lettre + \eaddtotoks\spacetxt@toks{\indivi@tmp\hskip\interletterskip}% + % et enlever le motif du texte restant à lire + \expsecond{\rightofsc\spacetxt@code}{\indivi@tmp}% + \@indivifoundtrue% marquer qu'un motif a été trouvé + \doforeachexit% et sortir prématurément de la boucle + }% + \relax% si le code ne commence pas le motif courant -> ne rien faire + }% + \unless\if@indivifound% si aucun motif n'a été trouvé + \grab@first\spacetxt@code\spacetxt@temp% retirer le 1er caractère du texte + \ifx\spacetxt@temp\space% si le 1er caractère est un espace + \addtotoks\spacetxt@toks{% + \unskip% annuler le précédent ressort + \hskip\interwordskip}% ajouter l'espace inter-mot au registre de token + \else% si le 1er caractère n'est pas un espace + % ajouter ce caractère et l'espace inter-lettre au registre de token + \eaddtotoks\spacetxt@toks{\spacetxt@temp\hskip\interletterskip}% + \fi + \fi + \spacetxt@recurse% enfin, continuer le processus + }% +} + +\def\spacetxt@star{% + \exparg\ifempty{\spacetxt@code}% si texte restant est vide + \spacetxt@endprocess% aller à la fin du processus + {% sinon, si texte commence par "{" + \exparg\ifbracefirst{\spacetxt@code}% + {\grab@first\spacetxt@code\spacetxt@temp + % mettre {<argument} dans \spacetxt@temp + \begingroup% ouvrir un groupe + % mettre le contenu de l'argument dans \spacetxt@code + \expandafter\def\expandafter\spacetxt@code\spacetxt@temp + \let\spacetxt@endprocess\spacetxt@endingroup% changer le processus de fin + \spacetxt@toks{}% initialiser + \spacetxt@recurse% exécuter le processus avec ce nouveau texte + }% si le code ne commence pas par "{", aller à \spacetxt@nostar mais comme + \spacetxt@nostar% \spacetxt@recurse vaut \spacetxt@star, n'y faire qu'1 boucle + }% +} + +\def\spacetxt@endnormal{% fin de processus normal + \the\spacetxt@toks% afficher le registre à token + \unskip% et supprimer le dernier ressort +} + +\def\spacetxt@endingroup{% fin du processus dans un groupe : + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\spacetxt@toks\expandafter% ajouter au registre hors du groupe + {\expandafter{\the\spacetxt@toks}}% ce qui est collecté localement mis entre {} + \spacetxt@recurse% puis aller à la macro récursive +} + +\catcode`\@12 +\def\indivilist{é,è}% liste des motifs spéciaux +\spacetxt*{Comme on le voit sur cet exemple, une espace est insérée après +{\it chaque} caractère.} +\medbreak + +\def\indivilist{é,es,au,<<,>>} +\spacetxt[4pt plus.7pt minus.7pt][12pt plus2pt minus2pt]{Ici, les motifs <<es>> et <<au>> +restent indivisibles et la ligature des guillemets devient possible en déclarant "<<" et +">>" comme motifs.} +****************** Fin code ****************** + + +****************** Code 408 ****************** +\catcode`\@11 +\def\ttcode#1{% lit #1, le <délimiteur> de début + \def\ttcode@i##1#1{% ##1 = <texte> entre délimiteurs + \tt% passe en fonte à chasse fixe + \setbox0=\hbox{ }% + \edef\spc@wd{\the\wd0 }% longueur d'un espace + \spacetxt + [.1pt plus0pt minus.1pt]% espace inter-lettre + [\glueexpr\wd0+.3pt plus.1pt minus.1pt\relax]% espace inter-mot + {##1}% le <texte> est composé par \spacetxt + \endgroup + }% + \begingroup + \def\indivilist{<<,>>,{,,},--}% (rajouter à, é, è, etc. en codage UTF8) + \def\do##1{\catcode`##1=12 }% + \dospecials% rend inoffensifs tous les tokens spéciaux + \letactive\ =\space % rend l'espace actif + \ttcode@i% va lire le <texte> et le délimiteur de fin +} +\catcode`\@12 + +\hfill\vrule\vbox{% + \hsize=.75\hsize + Avec \ttcode/\ttcode/, on peut insérer une adresse internet << + \ttcode-http://www.gutenberg.eu.org/Typographie- >> puis repasser en fonte normale + puis \ttcode+même composer un court passage en fonte à chasse fixe -- même si les + coupures de mots se font n'importe où -- et aussi afficher tous les caractères + spéciaux <<{$^ _$#}&+>>, et finir en fonte normale\ldots +}\vrule\hfill\null +****************** Fin code ****************** + + +****************** Code 409 ****************** +\def\ttwide{0.7}% coefficient pour la largeur de composition +\def\ttindent{5}% nombre de caractères d'indentation +\hfill\vrule +\vbox{% + \ttfamily% en TeX, on écrirait "\tt" + \setbox0\hbox{0}% \wd0 est donc la largeur d'un caractère + \parindent=\ttindent\wd0 % réglage de l'indentation + \hsize=\ttwide\hsize % compose sur 70% de la largeur + Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauches + et droites du texte ont été tracées, on constate que les caractères sont + exactement les uns au-dessous d'une ligne à l'autre. Des débordements dans + la marge deviennent alors inévitables, car la largeur de composition (ici + \the\hsize) n'est pas un multiple de la largeur d'un caractère (\the\wd0 ), + le quotient des deux valant environ + \xdef\ttratio{\decdiv{\dimtodec\hsize}{\dimtodec\wd0 }}\ttratio. +}% +\vrule\hfill\null +****************** Fin code ****************** + + +****************** Code 410 ****************** +Fonte normale : \fontname\font\par +{\bf Fonte grasse : \fontname\font\par} +{\it Fonte italique : \fontname\font\par} +{\sc Fonte petites majuscules : \fontname\font} +****************** Fin code ****************** + + +****************** Code 411 ****************** +\font\gras=LinLibertineTB-tlf-t1 at 8pt +\font\ital= LinLibertineTI-tlf-t1 at 8pt +\font\itgras=LinLibertineTBI-tlf-t1 at 8pt +Du texte normal {\gras puis en gras, \ital en italique, +\itgras en italique gras} et retour à la normale. +****************** Fin code ****************** + + +****************** Code 412 ****************** +Code du caractère de coupure = \number\hyphenchar\font\par +Caractère de coupure : "\char\hyphenchar\font" +****************** Fin code ****************** + + +****************** Code 413 ****************** +\def\longtext{Voici une phrase écrite avec des mots insignifiants mais terriblement, + épouvantablement, horriblement et indéniablement longs.} +% créér une macro restaurant le \hyphenchar +\edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% + +%comportement normal, caractère de coupure "-" +1) \vrule\vbox{\hsize=5cm \longtext}\vrule\hfill +% modification du caractère de coupure "W" +2) \vrule\vbox{\hsize=5cm \hyphenchar\font=`W \longtext}\vrule +\medbreak +% interdiction des coupures de mots +3) \vrule\vbox{\hsize=5cm \hyphenchar\font=-1 \longtext}\vrule +\restorehyphenchar +****************** Fin code ****************** + + +****************** Code 414 ****************** +\def\longtext{Voici une phrase écrite avec des mots insignifiants mais terriblement, + épouvantablement, horriblement et indéniablement longs.} + +\edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% +\vrule\vbox{\hsize=5cm \hyphenchar\font=-1 \sloppy \longtext}\vrule +\restorehyphenchar +****************** Fin code ****************** + + +****************** Code 415 ****************** +a) \texttt{\number\hyphenchar\font}\qquad +b) {\ttfamily\number\hyphenchar\font} +****************** Fin code ****************** + + +****************** Code 416 ****************** +\leavevmode +\vbox{% + \hsize=.4\hsize + \Souligne{Police à chasse variable}% + \vskip5pt + espace inter-mot = \the\fontdimen2\font\par + étrirement inter-mot = \the\fontdimen3\font\par + compression inter-mot = \the\fontdimen4\font +}\hfill +\vbox{% + \tt + \hsize=.4\hsize + \Souligne{Police à chasse fixe}% + \vskip5pt + espace inter-mot = \the\fontdimen2\font\par + étrirement inter-mot = \the\fontdimen3\font\par + compression inter-mot = \the\fontdimen4\font +} +****************** Fin code ****************** + + +****************** Code 417 ****************** +\def\ttwide{0.7}\def\ttindent{5}% +\hfill\vrule +\vbox{% + \ttfamily + \hyphenchar\font=`\- % change le caractère de coupure de la fonte en cours + \setbox0\hbox{0}\parindent=\ttindent\wd0 + \hsize=\ttwide\hsize % compose sur 70% de la largeur + Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche + et droite du texte ont été tracées, on constate que les caractères sont + exactement les uns au-dessous des autres d'une ligne à l'autre. Des débordements + dans la marge deviennent inévitables même si les coupures des mots sont + à nouveau rendues possibles.% +}% +\vrule\hfill\null +****************** Fin code ****************** + + +****************** Code 418 ****************** +\frboxsep=0.5pt +\def\printallchars{% + \leavevmode + \for\xx=0 to255\do{% + \vbox{% empiler verticalement + \offinterlineskip% en désactivant le ressort d'interligne + \setbox0\hbox{\frbox{\char\xx}}% + \copy0 % la boite contenant le caractère encadré + \kern1pt% saute 1pt vertical + \hbox to\wd0{\hss$\scriptscriptstyle\xx$\hss}% le numéro + }\hskip0.5em plus1pt minus1pt % saute 0.5em horizontalement + }% +} +\tt +Nom de la fonte = \fontname\font\par +\printallchars +****************** Fin code ****************** + + +****************** Code 419 ****************** +\setbox0=\hbox{\tt\char23} +Largeur = \the\wd0 \qquad Hauteur = \the\ht0 \qquad Profondeur = \the\dp0 +****************** Fin code ****************** + + +****************** Code 420 ****************** +{\tt\xdef\nfont{\the\font}} +Largeur = \the\fontcharwd\nfont23 \qquad Hauteur = \the\fontcharht\nfont23 +\qquad Profondeur = \the\fontchardp\nfont23 +****************** Fin code ****************** + + +****************** Code 421 ****************** +\newmacro\ttstart[5]{% + \begingroup + \tt + \edef\restorefontsettings{% stocke les paramètres de fonte + \hyphenchar\font=\the\hyphenchar\font\relax% le \hyphenchar + \fontdimen2\font=\the\fontdimen2\font\relax% et les paramètres d'espacement + \fontdimen3\font=\the\fontdimen3\font\relax + \fontdimen4\font=\the\fontdimen4\font\relax + }% + \fontdimen3\font=0.30\fontdimen2\font% composante + = 30% de la dimension naturelle + \fontdimen4\font=0.20\fontdimen2\font% composante - = 20% de la dimension naturelle + \hyphenchar\font=`\- % on autorise la coupure des mots (au cas où on utilise latex) + \setbox0\hbox{0}% largeur d'un caractère + \parindent=#1\wd0 % indentation (en nombre de caractères) + \ignorespaces +} +\def\ttstop{% + \restorefontsettings% restaure les paramètres de fonte + \endgroup% et ferme le groupe +} +\hfill\vrule +\def\ttwide{0.70}% +\vbox{% + \hsize=\ttwide\hsize % compose sur 70% de la largeur + \ttstart[5] + Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche + et droite du texte ont été tracées, on constate que les caractères ne sont pas + exactement les uns au-dessous des autres entre les lignes du paragraphe. + Puisque les espaces sont redevenus étirables, les débordements dans la marge + (même s'ils restent possibles), sont bien plus rares et ce d'autant plus que + le nombre d'espaces dans une ligne est grand.% + \ttstop +}% +\vrule\hfill\null +****************** Fin code ****************** + + +****************** Code 422 ****************** +\catcode`\@11 +\newcount\brktt@cnt +\newdimen\brktt@interletter +\newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré + +\def\insert@blankchar{% + \ifstarred\insert@blankchar@ii\insert@blankchar@i +} + +\def\insert@blankchar@i#1{% insère une espace de largeur #1 caractères complets + \ifnum\numexpr#1\relax>0 + \kern\numexpr#1\relax\dimexpr\ttchar@width+\brktt@interletter\relax + \fi +} + +\def\insert@blankchar@ii#1{% insère #1-1 caractères complets + 1 largeur de caractère + \ifnum\numexpr#1\relax>0 + \insert@blankchar@i{#1-1}\kern\ttchar@width + \fi +} + +\def\restart@hbox#1{% + \egroup% feerme la \hbox précédente + \hbox\bgroup% ouvre la suivante + \expsecond{\def\tt@remaintext} + {\romannumeral\removefirstspaces@i{#1}}% initialiser le code à composer + \let\previous@char\space% initialise le caractère précédent + \line@starttrue% aucun caractère n'a encore été imprimé + \brktt@cnt=0\relax% remettre le compteur à 0 +} + +\newmacro\breaktt[0.3em][\hsize]1{% +% arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte +% #3 = texte à espacer + \begingroup% ouvrir un groupe et le fermer à la toute fin + \par% commencer un nouveau paragraphe -> passage en mode vertical + \parindent=0pt% empêche l'indentation + \tt% passer en fonte à chasse fixe + \setbox0 = \hbox{M}% la boite 0 contient un caractère + \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en fonte \tt + \edef\text@width{\the\dimexpr#2\relax}% largeur de composition + % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) + \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère + \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax + % le nombre de caractère par ligne est égal à 1 de plus : + \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% + % calcul de la dimension inter-lettre + \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax + % stocke le texte après avoir enlevé les éventuels espaces extremes : + \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}% + \unless\ifx\tt@remaintext\empty% si le texte à composer n'est pas vide + \hbox\bgroup% démarrer la boite horizontale contenant la première ligne + \insert@blankchar\ttindent% insérer une espace d'indentation + \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés + \line@starttrue% il s'agit du début d'une ligne + \expandafter\breaktt@i% aller à la macro récursive + \fi +} + +\def\breaktt@i{% + \print@nchar\maxchar@num% afficher \maxchar@num caractères + \ifx\tt@remaintext\empty% si texte restant est vide + \egroup% fermer la hbox + \par% aller à la ligne + \endgroup% fermer le groupe ouvert au début + \else + \unless\ifnum\brktt@cnt<\maxchar@num\relax% si la ligne est remplie + \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une + \fi + \expandafter\breaktt@i% enfin, continuer le processus + \fi +} + +\def\print@nchar#1{% affiche #1 caractères pris dans \tt@remaintext + \for\xxx= 1 to #1 \do 1{% + \ifx\tt@remaintext\empty% si le code restant à composer est vide + \exitfor\xxx%sortir de la boucle prématurément + \else + \@indivifoundfalse% sinon, a priori, les motifs de ligature ne sont pas trouvés + % pour chaque \indivi@tmp dans la liste de ligatures + \expsecond{\doforeach\indivi@tmp\in}{\liglist}% + {% si le code commence par la ligature courante + \exptwoargs\ifstartwith\tt@remaintext\indivi@tmp + {\let\previous@char\indivi@tmp% prendre le motif pour caractère courant, + \expsecond{\rightofsc\tt@remaintext}{\indivi@tmp}% l'enlever du texte restant + \@indivifoundtrue% marquer qu'un motif a été trouvé + \doforeachexit% et sortir prématurément de la boucle + }% + {}% si le code ne commence pas le motif courant -> ne rien faire + }% + \unless\if@indivifound% si aucun motif trouvé, + \grab@first\tt@remaintext\previous@char% lire le premier caractère + \fi + \advance\brktt@cnt by 1 % incrémenter le compteur de caractères + \hbox to\ttchar@width{\hss\previous@char\hss}% afficher le caractère lu + \line@startfalse% nous ne sommes plus au début d'une ligne + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie + \kern\brktt@interletter% insérer le ressort inter-lettre + \else + \exitfor\xxx% sinon, sortir de la boucle prématurément + \fi + \fi + }% +} +\catcode`\@12 + +\def\liglist{<<,>>}% liste des motifs de ligature + % ajouter à, é, etc si codage UTF8 + moteur 8 bits +\def\ttindent{3}% valeur de l'indentation' +\vrule\vbox{\breaktt[4pt][.7\hsize]{% +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous des autres d'une ligne à l'autre. Plus aucun +débordement n'a lieu, car une espace correctement calculée est insérée entre +chaque caractère. Les mots, en revanche, sont toujours coupés <<sauvagement>>.% +}% +}\vrule\smallbreak + +\vrule\vbox{\breaktt[1pt][6cm]{% +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous des autres d'une ligne à l'autre. Plus aucun +débordement n'a lieu, car une espace correctement calculée est insérée entre +chaque caractère. Les mots, en revanche, sont toujours coupés <<sauvagement>>.% +}% +}\vrule +****************** Fin code ****************** + + +****************** Code 423 ****************** +\catcode`\@11 +\newcount\brktt@cnt +\newdimen\brktt@interletter +\newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré + +\newmacro\breakttA[0.3em][\hsize]1{% +% arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte +% #3 = texte à espacer + \begingroup% ouvrir un groupe et le fermer à la toute fin + \par% commencer un nouveau paragraphe -> passage en mode vertical + \parindent=0pt% empêche l'indentation + \tt% passer en fonte à chasse fixe + \setbox0 = \hbox{M}% la boite 0 contient un caractère + \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en fonte \tt + \edef\text@width{\the\dimexpr#2\relax}% largeur de composition + % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) + \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère + \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax + % le nombre de caractères par ligne est égal à 1 de plus : + \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% + % calcul de la dimension inter-lettre + \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax + % stocke le texte après avoir enlevé les éventuels espaces extremes : + \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}% + \unless\ifx\tt@remaintext\empty% si le texte à composer n'est pas vide + \hbox\bgroup% démarrer la boite horizontale contenant la première ligne + \insert@blankchar\ttindent% insérer une espace d'indentation + \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés + \line@starttrue% il s'agit du début d'une ligne + \expandafter\breakttA@i% aller à la macro récursive + \fi +} + +\def\breakttA@i{% + \edef\remaining@chars{% calculer le nombre de caractères restant à placer sur la ligne + \numexpr\maxchar@num-\brktt@cnt\relax}% + \len@tonextword% \next@len contient le nombre de caractères du prochain mot + % si le mot + l'eventuel "-" qui le suit ne peut pas loger sur la ligne en cours + \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax + \ifline@start% et si c'est le début d'une ligne + % avertir l'utilisateur + \message{Largeur de composition trop faible pour + \unexpanded\expandafter{\next@word}^^J}% + % et composer tout de même "à la sauvage" jusqu'à la fin de la ligne + \exparg\print@nchar{\number\numexpr\maxchar@num-\brktt@cnt}% + \else% si la ligne en cours n'est pas au début + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne d'espaces + \fi + \exparg\restart@hbox{\tt@remaintext}% commencer une nouvelle ligne + \expandafter\breakttA@i% et poursuivre le processus + % s'il y a assez de place pour accueillir ce qui est avant la prochaine coupure + \else + \print@nchar\next@len% afficher le mot + \ifx\tt@remaintext\empty% si texte restant est vide + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \egroup% fermer la hbox en cours + \par% aller à la ligne + \endgroup% fermer le groupe ouvert au début. Fin du processus + \else% si le texte restant n'est pas vide + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas remplie + \print@nchar{1}% afficher le caractère qui suit le mot (" " ou "-") + \else% si la ligne est remplie + \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une + \fi + \expandafter\expandafter\expandafter\breakttA@i% enfin, continuer le processus + \fi + \fi +} + +\def\leftofsc#1#2{% dans la macro #1, garde ce qui est à gauche de #2 + \def\leftofsc@i##1#2##2\@nil{\def#1{##1}}% + \expandafter\leftofsc@i#1#2\@nil +} + +\def\len@tonextword{% stocke dans \next@len le nombre de caractères avant + % le prochain point de coupure dans \tt@remaintext + \let\next@word\tt@remaintext% copie \tt@remaintext dans la macro temporaire \next@word + \leftofsc\next@word{ }% ne prend que ce qui est avant le prochain espace + \exparg\ifin\next@word{-}% si le mot contient un tiret + {\leftofsc\next@word{-}% prendre ce qui est à gauche de ce tiret + \def\extra@char{1}% il y a un caractère de plus à loger après le mot + }% sinon, le caractère après le mot est un espace + {\def\extra@char{0}% qu'il ne faut pas compter + }% + \setbox0=\hbox{\next@word}% enfermer le mot dans une boite + % et en calculer le nombre de caractères = dim(boite)/dim(caractère) + \edef\next@len{\number\numexpr\dimexpr\wd0 \relax/\dimexpr\ttchar@width\relax\relax}% +} +\catcode`\@12 + +\def\liglist{<<,>>}% liste des motifs de ligature (mettre à, é, etc si codage UTF8) +\def\ttindent{3} + +\vrule\vbox{\breakttA[3pt][8cm]{% +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<~sauvagement~>>. +}}\vrule\medbreak + +\leavevmode\vrule\vbox{\breakttA[1pt][5cm]{% +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<~sauvagement~>>. +}}\vrule\qquad\def\ttindent{0}% +\vrule\vbox{\breakttA[0.6pt][2cm]{mot-composé mot-clé passe-droit au-dessus +là-bas remonte-pente vingt-huit Notre-Dame-de-Lourdes Mont-Blanc +Saint-Jean-de-Luz}}\vrule +****************** Fin code ****************** + + +****************** Code 424 ****************** +\newmacro\zerocompose[]2{% +% #1=code à exécuter avant la composition +% #2=registre de boite recevant le résultat +% #3= texte à composer en largeur 0pt + \setbox#2=\vbox{% + #1% code a exécuter (changement de fonte par exemple) + \hfuzz=\maxdimen% annule les avertissements pour débordement horizontaux + \hbadness=10000 % annule les avertissements pour mauvaise boite horizontale + \pretolerance=-1 % désactive la première passe (celle sans coupures) + \tolerance=10000 % passe avec coupures acceptée + \hyphenpenalty=-10000 % favorise fortement les copures de mots + \lefthyphenmin=2 \righthyphenmin=3 % longueur mini des fragments de début et fin + \clubpenalty=0 % pas de pénalité supplémentaire après la première ligne + \interlinepenalty=0 % pas de pénalité inter-ligne + \widowpenalty=0 % pas de pénalité supplémentaire avant la dernière ligne + \exhyphenpenalty=0 % ne pas pénaliser une coupure explicite + \leftskip=0pt \rightskip=0pt % désactive les éventuels ressorts latéraux + \everypar={}% désactive l'éventuel \everypar + \parfillskip=0pt plus1fil % règle le \parfillskip par défaut + \hsize=0pt % largeur de composition = 0pt + \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% + \hyphenchar\font=`\- % impose "-" comme caractère de coupure + \noindent % pas d'indentation + passage en mode horizontal + \hskip0pt \relax% premier noeud horizontal pour permettre la coupure de la suite + #3\par% compose #3 + \restorehyphenchar% restaure le caractère de coupure + }% +} +\zerocompose{0}{Programmation}% +\leavevmode\box0 +\hskip 5cm +\zerocompose{0}{Composer en largeur nulle}% +\box0 +****************** Fin code ****************** + + +****************** Code 425 ****************** +\def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. } +\setbox0=\vtop{% définit la boite 0 + \hsize=8cm + \dummytext\dummytext +} + +a) Boite pleine : \copy0 + +b) \setbox0=\vtop{% + \unvbox0 % boite précédemment composée + \global\setbox1=\lastbox% et capture la dernière ligne dans la boite 1 + }% + Dernière ligne = |\hbox{\unhbox1 }|\par% redonne à box1 sa largeur naturelle + Boite restante = \box0 +****************** Fin code ****************** + + +****************** Code 426 ****************** +\def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. } +\setbox0=\vtop{% définit la boite 0 + \hsize=8cm + \dummytext\dummytext +} + +\setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} +\setbox1=\hbox{\unhbox1 } +1) |\box1| + +\setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} +\setbox1=\hbox{\unhbox1 } +2) |\box1| + +\setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} +\setbox1=\hbox{\unhbox1 } +3) |\box1| +****************** Fin code ****************** + + +****************** Code 427 ****************** +\setbox0=\vbox{\hsize=2.5cm Programmer en TeX est facile}% +\showboxbreadth=5 \showboxdepth=3 \tracingonline=1 +\showbox0 +****************** Fin code ****************** + + +****************** Code 428 ****************** +\newbox\tempbox +\def\reversebox#1{% affiche le contenu de la boite verticale #1 + \setbox#1=\vbox{% + \unvbox#1% composer la boite #1 + \unskip\unpenalty% retirer ce qui n'est pas capturable par \lastbox + \global\setbox0=\lastbox% la boite 1 devient la dernière ligne + }% + |\hbox{\unhbox0 }|\par% afficher la boite 1 dans sa largeur d'origine + \ifvoidorempty{#1}% si le registre #1 contient une boite vide + \relax% ne rien faire + {\reversebox{#1}}% sinon, rencommencer +} +\def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. } +\setbox\tempbox=\vbox{% définit la boite 0 + \hsize=8cm \dummytext\dummytext +} + +\reversebox\tempbox +****************** Fin code ****************** + + +****************** Code 429 ****************** +% compose "Programmation" en largeur 0 dans la boite 0 : +\zerocompose{0}{Programmation}% +Boite initiale : \copy0 \par% compose la boite résultante +\vfuzz=\maxdimen% annule les avertissements pour débordements +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante +\setbox1 =\vsplit0 to 0pt % coupe la boite 0 à 0pt +{% dans un groupe (où la boite 0 sert de brouillon) + \setbox0 =\vbox{% affecter à la boite brouillon + \unvbox1 % la boite 1 composée dans sa largeur naturelle + \unskip\unpenalty% annule ce qui n'est pas une boite + \global\setbox2 =\lastbox% affecte globalement la dernière (et unique) ligne + % à la boite 1 + }% +}% ferme le groupe +\setbox2=\hbox{\unhbox2}% rend à la boite 2 sa largeur naturelle +Première ligne = "\box2 "% compose la boite 2 +****************** Fin code ****************** + + +****************** Code 430 ****************** +\catcode`@11 +\def\hyphlengths#1#2{%#2 = macro contenant les longueurs de coupures du mot #1 + \begingroup + \zerocompose + [\tt% passer en fonte à chasse fixe + \setbox\z@\hbox{M}\xdef\ttwidth{\the\wd\z@}% mesurer la largeur des caractères + ]\z@{#1}% compose en 0pt dans la boite 0 + \let#2 = \empty% initialise la macro #2 + \def\cumul@length{0}% le cumul des longueurs initialisé à 0 + \vfuzz=\maxdimen% annule les avertissements pour débordement + \splittopskip=\z@ % ne rajouter aucun espace au sommet de la boite restante + \loop + \setbox1=\vsplit\z@ to \z@% couper la boite à 0pt de hauteur + {\setbox\z@=\vbox{\unvbox1 \unskip\unpenalty\global\setbox1=\lastbox}}% + \setbox1=\hbox{\unhbox1 }% + \unless\ifvoid\z@% si la boite 0 n'est pas encore vide + \edef\cumul@length{% mettre à jour \cumul@length + \number\numexpr + \cumul@length + +% ajouter le quotient "largeur syllabe/largeur d'1 caractère" + \wd1/\dimexpr\ttwidth\relax + -1% et soustraire 1 (le caractère "-") + \relax + }% + % ajouter à #2 la virgule et le cumul actuel +1 + \edef#2{% définir la macro #2 : + #2% reprendre le contenu de #2 + \ifx#2\empty\else,\fi% ajouter "," si #2 non vide + \number\numexpr\cumul@length+1\relax% et le cumul + }% + \repeat% et recommencer + \expsecond{% avant de fermer le groupe + \endgroup + \def#2}{#2}% définit #2 hors du groupe +} +\catcode`\@12 +a) \hyphlengths{programmation}\foo liste = "\foo"\par +b) \hyphlengths{typographie}\foo liste ="\foo"\par +c) \hyphlengths{j'entendrai}\foo liste ="\foo"\par +d) \hyphlengths{vite}\foo liste ="\foo" +****************** Fin code ****************** + + +****************** Code 431 ****************** +\catcode`\@11 +\newcount\brktt@cnt +\newdimen\brktt@interletter +\newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré + +\let\breakttB=\breakttA + +\def\breakttA@i{% + \edef\remaining@chars{% calculer le nombre de caractères restant à placer sur la ligne + \numexpr\maxchar@num-\brktt@cnt\relax}% + \len@tonextword% calculer dans \next@len le nombre de caractères avant le prochain mot + \def\next@hyphlen{0}% initialiser la longueur de coupure possible du mot + % si le mot + l'eventuel "-" qui le suit ne rentre pas sur ce qui reste de la ligne + \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax + \hyphlengths\next@word\list@hyphlengths% bâtir la liste des longueurs de coupures + \unless\ifx\list@hyphlengths\empty% si une coupure est possible + \expsecond{\doforeach\xx\in}{\list@hyphlengths}% les examiner toutes + {% si la coupure examinée peut loger sur la ligne + \unless\ifnum\xx>\remaining@chars\relax% + \let\next@hyphlen\xx% la stocker + \fi% pour que \next@hyphlen soit maximal + }% + \fi + \fi + \ifnum\next@hyphlen>0 % si une coupure est nécessaire et a été trouvée + \let\next@len\next@hyphlen% mettre à jour la longueur du mot à placer + \fi + % si le mot + l'eventuel "-" qui le suit ne peut pas loger sur la ligne en cours + \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax + \ifline@start% si c'est le début d'une ligne + % avertir l'utilisateur + \message{Largeur de composition trop faible pour + \unexpanded\expandafter{\next@word}^^J}% + % et composer tout de même jusqu'à la fin de la ligne + \exparg\print@nchar{\number\numexpr\maxchar@num-\brktt@cnt}% + \else% sinon + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \fi + \exparg\restart@hbox{\tt@remaintext}% ré-ouvrir une boite + \expandafter\breakttA@i% et poursuivre le processus + % s'il y a la place pour accueillir ce qui est avant la prochaine coupure + \else + \ifnum\next@hyphlen>0 % si une coupure de mot doit être faite + \exparg\print@nchar{\number\numexpr\next@len-1}% afficher les lettres de la syllabe + \print@hyphchar% et le caractère de coupure + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \exparg\restart@hbox{\tt@remaintext}% ré-ouvrir une boite + \expandafter\expandafter\expandafter\breakttA@i% et continuer le processus + \else% si pas de coupure dans le mot + \print@nchar\next@len% afficher le mot + \ifx\tt@remaintext\tt@emptytext% si texte restant est vide + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \egroup% fermer la hbox + \par% aller à la ligne + \endgroup% fermer le groupe ouvert au début. Fin du processus + \else% si le texte restant n'est pas vide + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas remplie + \print@nchar{1}% afficher le caractère qui suit le mot + \else% si la ligne est remplie + \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une + \fi + \expandafter\expandafter\expandafter\breakttA@i% enfin, continuer le processus + \fi + \fi + \fi +} + +\def\print@hyphchar{% + \advance\brktt@cnt by 1 % augmenter le compteur de caractères + \hbox to\ttchar@width{\hss-\hss}% afficher "-" + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie + \kern\brktt@interletter\relax% insérer le ressort inter-lettre + \fi +} + +\catcode`\@12 + +\def\liglist{<<,>>}% liste des motifs de ligature (mettre à, é, etc si codage UTF8) +\def\ttindent{3} + +\vrule\vbox{\breakttB[3pt][8cm]{% +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<sauvagement>>. +}% +}\vrule + +\vrule\vbox{\breakttB[1pt][5cm]{% +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<sauvagement>>. +}% +}\vrule +****************** Fin code ****************** + + +****************** Code 432 ****************** +Voici un algorithme élémentaire : +\algorithm[\#]{Algorithme {\bf MinMax}}| +macro ~Min_Max~ (#1) % afficher la valeur max et min d'une liste de valeurs #1 + $V_{max}:=-\infty$ % $-\infty$ ou la plus petite valeur possible + $V_{min}:=+\infty$ % $+\infty$ ou la plus grande valeur possible + ~Pour~ chaque $x$ dans (#1) + ~Si~ $x>V_{max}$ + ~alors~ $V_{max}:=x$ + ~FinSi~ + ~Si~ $x<V_{min}$ + ~alors~ $V_{min}:=x$ + ~FinSi~ + ~FinPour~ + Afficher "$V_{min}$ et $V_{max}$" +~FinMin_Max~| +Suite du texte... +****************** Fin code ****************** + + +****************** Code 433 ****************** +\def\algoindent{1cm}% +\begingroup +\parindent=0pt % pas d'indentation +\defactive\^^M{\leavevmode\par}% rend le retour charriot actif +\defactive\^^I{\hskip\algoindent\relax}% tabulation active +Pas d'indentation ici\ldots + Voici une première ligne + une seconde plus indentée + retour à l'indentation normale + \for\xx=1to10\do{un long texte } + pour terminer, la dernière ligne +\endgroup +****************** Fin code ****************** + + +****************** Code 434 ****************** +\def\algoindent{1cm}% +\begingroup +\leftskip=0pt \rightskip=0pt plus1fil \relax % initialise les ressorts latéraux +\parindent=0pt % pas d'indentation +\defactive\^^M{\leavevmode\par\leftskip=0pt }% rend le retour charriot actif +\defactive\^^I{\advance\leftskip by \algoindent\relax}% tabulation active +Pas d'indentation ici\ldots + Voici une première ligne + une seconde plus indentée + retour à l'indentation normale + \for\xx=1to10\do{un long texte } + pour terminer, la dernière ligne +\endgroup +****************** Fin code ****************** + + +****************** Code 435 ****************** + % compteur de lignes pour l'algorithme +\def\algoindent{1cm}% +\begingroup +\algocnt=1 % initialise le compteur à 1 +\leftskip=0pt \rightskip=0pt plus1fil\relax% initialise les ressorts à 0pt +\parindent=0pt % pas d'indentation +\everypar={\llap{$\scriptstyle\number\algocnt$\kern\dimexpr4pt+\leftskip\relax}}% + +\defactive\^^M{\leavevmode\par\advance\algocnt by1 \leftskip=0pt }% retour charriot actif +\defactive\^^I{\advance\leftskip by \algoindent\relax}% tabulation active +Pas d'indentation ici\ldots + Voici une première ligne + une seconde plus indentée + retour à l'indentation normale + \for\xx=1to10\do{un long texte } + pour terminer, la dernière ligne +\endgroup +****************** Fin code ****************** + + +****************** Code 436 ****************** +\newif\ifnumalgo +\newcount\algocnt +\numalgotrue + +\def\algoindent{2em} +\def\algorule{.4pt}% épaisseur des réglures de début et de fin + +\def\algohrulefill{% remplit avec une ligne horizontale + \leavevmode + \leaders\hrule height\algorule\hfill + \kern0pt % rajoute un noeud au cas où la commande est en fin de ligne +} + +\newmacro\algorithm[\\,\#,\{,\}]2{% + \medbreak + \begingroup% ouvre un groupe (sera fermé à la fin de l'algorithme) + \algocnt=1 % initialise le compteur de lignes + \leftskip=0pt \rightskip=0pt plus1fil\relax% initialise les ressorts latéraux + \parindent=0pt % pas d'indentation + %%%%%%%%%%%% affichage du titre %%%%%%% + \algohrulefill% remplir avec une ligne horizontale + \ifempty{#2}% si #2 est vide + {}% ne rien insérer + {% sinon + \lower.5ex\hbox{ #2 }% insérer le titre abaissé de 0.5ex + \algohrulefill% insérer une ligne horizontale + }% + \par% aller à la ligne + \nointerlineskip% ne pas insérer le ressort d'interligne + \kern7pt % et sauter 7pt verticalement + \nobreak% empêcher une coupure de page + %%%%%%%%%%%%%% fin du titre %%%%%%%%%% + % + %%%%%%%%%%%%%% rend les caractères actifs %%%%%%%%%%%%%% + \def~##1~{\begingroup\bf##1\endgroup}% + \defactive:{% rend ":" actif + \futurelet\nxttok\algoassign% \nxttok = token suivant + }% + \def\algoassign{% suite du code de ":" + \ifx=\nxttok% si ":" est suivi de "=" + \ifmmode% si mode math + \leftarrow% afficher "\leftarrow" + \else% si mode texte + ${}\leftarrow{}$% passer en mode math pour "\leftarrow" + \fi + \expandafter\gobone% manger le signe "=" + \else% si ":" n'est pas suivi de "="' + \string:% afficher ":" + \fi + }% + \ifnumalgo% si la numérotation est demandée, + \everypar={% définir le \everypar + \llap{$\scriptstyle\number\algocnt$\kern\dimexpr4pt+\leftskip\relax}% + }% + \fi + \doforeach\currentchar\in{#1}{\catcode\expandafter`\currentchar=12 }% + \def\algocr{% définit ce que fait ^^M + \color{black}% repasse en couleur noire + \rm% fonte droite + \leavevmode\par% termine le paragraphe + \advance\algocnt by1 % incrémente le compteur de lignes + \leftskip=0pt % initialise le ressort gauche + } + \letactive\^^M=\algocr% + \defactive\^^I{\advance\leftskip by \algoindent\relax}% + \defactive\ {\hskip1.25\fontdimen2\font\relax}% espace = 25% de + que + % la largeur naturelle + \defactive\%{\it\color{gray}\char`\%}% passe en italique et gris puis affiche "%" + \defactive_{\ifmmode_\else\string_\fi}% + \defactive#3{% rend le token #3 actif (il sera rencontré à la fin de l'algorithme) + \everypar{}% neutralise le \everypar + \par% aller à la ligne + \nointerlineskip% ne pas insérer le ressort d'interligne + \kern7pt % et sauter 7pt verticalement + \nobreak% empêcher une coupure de page + \algohrulefill% tracer la ligne de fin + \endgroup% ferme le groupe ouvert au début de l'algorithme + \smallbreak% saute un petit espace vertical + }% + %%%%%%%%%%%%%%%%% fin des caractères actifs %%%%%%%%%%%%%%%% + \sanitizealgo% va manger les espaces et les ^^M au début de l'algo +} + +\def\sanitizealgo{\futurelet\nxttok\checkfirsttok}% récupère le prochain token + +\def\checkfirsttok{% teste le prochaun token + \def\nextaction{% a priori, on considère que la suite est " " ou "^^M" donc + \afterassignment\sanitizealgo% aller à \sanitizealgo + \let\nexttok= % après avoir mangé ce "^^M" ou cet espace + }% + \unless\ifx\nxttok\algocr% si le prochain token n'est pas un ^^M + \unless\ifx\space\nxttok% et si le prochain token n'est pas un espace + \let\nextaction\relax% ne rien faire ensuite + \fi + \fi + \nextaction% faire l'action décidée ci-dessus +} + +Voici un algorithme élémentaire +\algorithm[\#]{Algorithme {\bf MinMax}}| +macro ~Min_Max~ (#1) % afficher la valeur max et min d'une liste de valeurs #1 +W$V_{max}:=-\infty$ % $-\infty$ ou la plus petite valeur possible +W$V_{min}:=+\infty$ % $+\infty$ ou la plus grande valeur possible +W~Pour~ chaque $x$ dans (#1) +WW~Si~ $x>V_{max}$ +WWW~alors~ $V_{max}:=x$ +WW~FinSi~ +WW~Si~ $x<V_{min}$ +WWW~alors~ $V_{min}:=x$ +WW~FinSi~ +W~FinPour~ +WAfficher "$V_{min}$ et $V_{max}$" +~FinMin_Max~| +Suite du texte... +****************** Fin code ****************** + + +****************** Code 437 ****************** +a) \meaning a\par +b) \meaning {\par +c) \meaning _\par +d) \meaning\def\par % une primitive +e) \meaning\baselineskip\par% une primitive +f) \long\def\foo#1#2.#3{#1 puis #2 et #3 !}\meaning\foo +****************** Fin code ****************** + + +****************** Code 438 ****************** +\def\foo#1#2{% + \def\argA{#1}\show\argA% débogage + Bonjour #1 et #2} +\foo{{\bf X}}{Y} +****************** Fin code ****************** + + +****************** Code 439 ****************** +\def\foo#1#2{% + \showtokens{#1}% débogage + Bonjour #1 et #2} +\foo{{\bf X}}{Y} +****************** Fin code ****************** + + +****************** Code 440 ****************** +\newtoks\footoks +\footoks={foo bar} +\show\footoks +****************** Fin code ****************** + + +****************** Code 441 ****************** +\showthe\baselineskip% registre-primitive +\newtoks\footoks \footoks={foo bar} \showthe\footoks +\newcount\foocount \foocount=987 \showthe\foocount +\newdimen\foodimen \foodimen=3.14pt \showthe\foodimen +\newskip\fooskip \fooskip=10 pt plus 1pt minus 1fil \showthe\fooskip +\newbox\foobox \foobox\hbox{foo} \showthe\foobox +****************** Fin code ****************** + + +****************** Code 442 ****************** +\def\foo#1,#2\endfoo{Bonjour #1 et #2} +\tracingmacros=1 +\foo moi,toi\endfoo +\tracingmacros=0 +****************** Fin code ****************** + + +****************** Code 443 ****************** +\tracingcommands=2 +Foo \hbox{et \ifnum2>1 bar\else toi\fi}\par +Suite +\tracingcommands=0 +****************** Fin code ****************** + + +****************** Code 444 ****************** +\def\i{\advance\count255 1 } +\tracingrestores=1 +\count255=2 {\i\i\i} +\tracingrestores=0 +****************** Fin code ****************** + + +****************** Code 445 ****************** +\def\i{\advance\count255 1 } \def\gi{\global\i} +\tracingrestores=1 +\count255=2 {\i\i\gi\i\gi\i} +\tracingrestores=0 +****************** Fin code ****************** + + +****************** Code 446 ****************** +\catcode`@11 +\def\ifnodecpart#1{\ifnodecpart@i#1.\@nil} +\def\ifnodecpart@i#1.#2\@nil{\ifempty{#2}} +\def\decadd#1#2{% #1 et #2=nombre à additionner + \ifnodecpart{#1}% si #1 est un entier + {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr + {\number\numexpr#1+#2\relax}% + {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 + } + {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2 + {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 + {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes + }% +} +\def\decadd@i#1.#2\@nil#3.#4\@nil{% affiche les parties entières et décimales reçues + $x_a=#1\quad y_a=#2\qquad x_b=#3\quad y_b=#4$ +} +\catcode`@12 +a) \decadd{5}{9.4}\par +b) \decadd{-3.198}{-6.02}\par +c) \decadd{0.123}{123} +****************** Fin code ****************** + + +****************** Code 447 ****************** +\def\addzeros#1#2/#3.#4#5/#6.#7/{% + Arguments reçus : #1#2/#3.#4#5/#6.#7/\par% afficher ce que la macro reçoit + \ifempty{#2}% si #1 est le dernier chiffre de y + {\ifempty{#5}% et si #4 est le dernier chiffre de y2 + {Résultat final : \detokenize{{#3#1}{#6#4}{#70}}}% afficher le résultat final + {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 + } + {\ifempty{#5}% si #4 est le dernier chiffre de y2 + {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 + {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides + }% +} +\addzeros457/.689714/.1/ +****************** Fin code ****************** + + +****************** Code 448 ****************** +\catcode`\@11 +\def\addzeros#1#2/#3.#4#5/#6.#7/{% + \ifempty{#2}% si #1 est le dernier chiffre de y1 + {\ifempty{#5}% et si #4 est le dernier chiffre de y2 + {{#3#1}{#6#4}{#70}}% redonner les 3 arguments + {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 + } + {\ifempty{#5}% si #4 est le dernier chiffre de y2 + {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 + {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides + }% +} +\def\decadd#1#2{% #1 et #2=nombre à additionner + \ifnodecpart{#1} + {\ifnodecpart{#2}{\number\numexpr#1+#2\relax}{\decadd@i#1.0\@nil#2\@nil}} + {\ifnodecpart{#2}{\decadd@i#1\@nil#2.0\@nil}{\decadd@i#1\@nil#2\@nil}}% +} +\def\decadd@i#1.#2\@nil#3.#4\@nil{% + \expandafter\decadd@ii + \romannumeral-`\0\addzeros#2/.#4/.1/% se développe en 3 arguments + {#1} + {#3}% +} +\def\decadd@ii#1#2#3#4#5{% +% #1 et #2=parties décimales (mêmes longueurs); +% #3=seuil de retenue; #4 et #5=parties entières + \exptwoargs{\decadd@iii{#3}}% envoyer le seuil de retenue tel quel + % sommer les parties décimales signées + {\number\numexpr\true@sgn{#4}#1+\true@sgn{#5}#2\relax}% + % et les parties entières + {\number\numexpr#4+#5\relax}% +} +\def\decadd@iii#1#2#3{% + seuil de retenue = #1 \qquad nombre = "#3"."#2"% +} +\catcode`\@12 +a) \decadd{6.7}{3.498}\par +b) \decadd{1.67}{-4.9}\par +c) \decadd{3.95}{2.0005}\par +d) \decadd{1.007}{2.008}\par +e) \decadd{-7.123}{3.523} +****************** Fin code ****************** + + +****************** Code 449 ****************** +\catcode`\@11 +\def\format@dec#1#2{% #1=partie décimale #2=seuil de retenue + \expandafter\gobone% le \gobone agira en dernier, + \romannumeral-`\0% mais avant, tout développer + \expandafter\reverse\expandafter% et retarder le \reverse de fin + % pour développer son argument qui + {\number% développe tout et évalue avec \number + \expandafter\reverse\expandafter% l'inversion de + {\number\numexpr\abs{#1}+#2\relax}% |#1|+#2 + }% +} +a) \format@dec{710}{100000}\par +b) \format@dec{6}{100}\par +c) \format@dec{-12300}{1000000} +\catcode`\@12 +****************** Fin code ****************** + + +****************** Code 450 ****************** +\catcode`@11 +\def\true@sgn#1{\ifnum#11<\z@-\fi} +\def\decadd#1#2{% #1 et #2=nombre à additionner + \romannumeral-`\.% tout développer jusqu'à l'affichage du nombre (\decadd@iv) + \ifnodecpart{#1}% si #1 est un entier + {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr + {\numexpr#1+#2\relax}% + {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 + } + {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2 + {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 + {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes + }% +} +\def\decadd@i#1.#2\@nil#3.#4\@nil{% + \expandafter\decadd@ii + \romannumeral-`\0\addzeros#2/.#4/.10/% se développe en 3 arguments + {#1} + {#3}% +} +\def\decadd@ii#1#2#3#4#5{% +% #1 et #2=parties décimales (mêmes longueurs) +% #3=seuil de retenue; #4 et #5=parties entières + \exptwoargs{\decadd@iii{#3}}% envoyer le seuil de retenue tel quel + % sommer les parties décimales signées + {\number\numexpr\true@sgn{#4}#1+\true@sgn{#5}#2\relax}% + % et les parties entières + {\number\numexpr#4+#5\relax}% +} +\def\decadd@iii#1#2#3{% #1=seuil de retenue #2=partie décimale #3= partie entière + \ifnum\true@sgn{#2}\true@sgn{\ifnum#3=\z@#2\else#3\fi}1=-1 % si les signes sont + % différents + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\exptwoargs\decadd@iv% transmettre les arguments modifiés : + {\number\numexpr#3-\true@sgn{#3}1}% #3:=#3-sgn(#3)1 + {\number\numexpr#2-\true@sgn{#2}#1}% #2:=#2-sgn(#2)10^n + {#1}% + }% si les signes sont égaux + {\ifnum\abs{#2}<#1 % et si abs(y)<10^n + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\decadd@iv{#3}{#2}{#1}% tranmettre les arguments tels quels + } + {\exptwoargs\decadd@iv% sinon + {\number\numexpr#3+\true@sgn{#3}1}% #3:=#3+sgn(#3)1 + {\number\numexpr#2-\true@sgn{#2}#1}% #2:=#2-sgn(#2)10^n + {#1}% + }% + }% +} +\def\decadd@iv#1#2#3{% affiche le décimal "#1.#2" + % le développement maximal initié par le \romannumeral de \decadd est actif + \ifnum#1=\z@\ifnum#2<\z@% si #1=0 et #2<0 + \expandafter\expandafter\expandafter% transmettre le développement à \number + -% puis afficher le signe "-" + \fi\fi + \number#1% affiche #1 qui est la somme des parties entières + % poursuivre le développement initié par \number + \unless\ifnum#2=\z@% si la partie décimale est différente de 0 + \antefi% se débarrasser de \fi + \expandafter.% afficher le "." décimal après avoir + \romannumeral-`\0\format@dec{#2}{#3}% correctement géré les 0 de #2 + \fi +} +\def\addzeros#1#2/#3.#4#5/#6.#7/{% + \ifempty{#2}% si #1 est le dernier chiffre de y1 + {\ifempty{#5}% et si #4 est le dernier chiffre de y2 + {{#3#1}{#6#4}{#7}}% redonner les 3 arguments + {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 + } + {\ifempty{#5}% si #4 est le dernier chiffre de y2 + {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 + {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides + }% +} +\def\format@dec#1#2{% #1=partie décimale #2=seuil de retenue + \expandafter\gobone% le \gobone agira en dernier, + \romannumeral-`\0% mais avant, tout développer + \expandafter\reverse\expandafter% et retarder le \reverse de fin + % pour développer son argument qui + {\number% développe tout et évalue avec \number + \expandafter\reverse\expandafter% l'inversion de + {\number\numexpr\abs{#1}+#2\relax}% abs(#1)+#2 + }% +} +\catcode`@12 +a) $-3.78+1.6987=\decadd{-3.78}{1.6987}$\par +b) $3.56-3.06=\decadd{3.56}{-3.06}$\par +c) $4.125+13.49=\decadd{4.125}{13.49}$\par +d) $-0.99+1.005=\decadd{-0.99}{1.005}$\par +e) $16.6-19.879=\decadd{16.6}{-19.879}$\par +f) $5.789-0.698=\decadd{5.789}{-0.698}$\par +g) $0.123-0.123=\decadd{0.123}{-0.123}$\par +h) $3.14-16.4912=\decadd{3.14}{-16.4912}$\par +i) $0.1-0.98=\decadd{0.1}{-0.98}$\par +j) $2.43+7.57=\decadd{2.43}{7.57}$\par +h) \edef\foo{\decadd{1.23}{9.78}}\meaning\foo\par +j) \detokenize\expandafter\expandafter\expandafter{\decadd{3.14}{-8.544}} +****************** Fin code ****************** + + +****************** Code 451 ****************** +a) \pdfstrcmp{foo}{bar}\qquad +b) \pdfstrcmp{bar}{foo}\qquad +c) \def\foo{ABC}\pdfstrcmp{\foo}{ABC}\qquad +d) \edef\foo{\string_}\pdfstrcmp{1_2}{1\foo2}\qquad +e) \pdfstrcmp{\string\relax}{\relax} +****************** Fin code ****************** + + +****************** Code 452 ****************** +\edef\tempcompil{\number\pdfelapsedtime}% +Depuis le début de la compilation, il s'est écoulé \tempcompil{} secondes d'échelle, +soit \convertunit{\tempcompil sp}{pt} secondes. +****************** Fin code ****************** + + +****************** Code 453 ****************** +%%%%%%%%%% definition de \loop...\repeat comme plain-TeX %%%%%%%%%% +\def\loop#1\repeat{\def\body{#1}\iterate} +\def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next} +\let\repeat=\fi +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\newcount\testcnt +\pdfresettimer% remet le compteur à 0 + \testcnt=0 + \loop % Test no 1 + \ifnum\testcnt<100000 \advance\testcnt 1 + \repeat +Temps 1 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \TeX)\par +%%%%%%%%%%%% definition de \loop...\repeat comme LaTeX %%%%%%%%%%%% +\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}% + \iterate \let\iterate\relax} +\let\repeat\fi +\pdfresettimer% remet le compteur à 0 + \testcnt=0 + \loop % Test no 2 + \ifnum\testcnt<100000 + \advance\testcnt 1 + \repeat +Temps 2 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \LaTeX)\par +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\pdfresettimer% remet le compteur à 0 + \for\ii=1 to 100000\do{}% Test no 3 +Temps 3 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle for) +****************** Fin code ****************** + + +****************** Code 454 ****************** +%%%%%%%%%%%% definition de \loop...\repeat comme LaTeX %%%%%%%%%%%% +\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}% + \iterate \let\iterate\relax} +\let\repeat\fi +\pdfresettimer% remet le compteur à 0 + \def\ii{0}% + \loop % Test no 1 + \ifnum\ii<100000 + \edef\ii{\number\numexpr\ii+1\relax}% + \repeat +Temps 1 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \LaTeX)\par +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\pdfresettimer% remet le compteur à 0 + \for\ii=1 to 100000\do{}% Test no 2 +Temps 2 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle for) +****************** Fin code ****************** + + +****************** Code 455 ****************** +\newcount\testcnt +\pdfresettimer +\testcnt=0 +\for\ii=1 to 100000\do{\advance\testcnt1 } +Temps 1 : \convertunit{\pdfelapsedtime sp}{pt} s (incrémentation compteur) + +\pdfresettimer +\def\foo{0}% +\for\ii=1 to 100000\do{\edef\foo{\number\numexpr\foo+1\relax}}% +Temps 2 : \convertunit{\pdfelapsedtime sp}{pt} s (incrémentation du texte de remplacement) +****************** Fin code ****************** diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/apprendre-a-programmer-en-tex.tex b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/apprendre-a-programmer-en-tex.tex new file mode 100644 index 00000000000..529d26e7980 --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/apprendre-a-programmer-en-tex.tex @@ -0,0 +1,19358 @@ +% !TeX spellcheck = fr_FR +% !TeX encoding = ISO-8859-1 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% Code source du livre %% +%% « Apprendre à programmer en TeX » %% +%% %% +%% ___ %% +%% %% +%% © 2014 Christian Tellechea %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% ___________________________________________________________________________ +%| | +%| Encodage ISO 8859-1 (latin1) | +%| À compiler avec pdflatex en mode pdf | +%|___________________________________________________________________________| + +\RequirePackage{etex} +\documentclass[fontsize=10pt,chapterprefix=true]{scrbook} + +\newwrite\wtest +\newread\rtest + +\usepackage[latin1]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[ + paperwidth=16.8275cm,paperheight=26.035cm, + headsep=.5cm,headheight=1cm, + footskip=1cm, + twoside, + lmargin=2.2cm, + rmargin=2.8775cm, + tmargin=2.5cm, + bmargin=3cm + ]{geometry}% largeur texte = 16.8275-(2.8775+2.2)=11,75 cm + +\usepackage[table]{xcolor} +\definecolor{shadecolor}{rgb}{0.86078,0.86078,0.86078}% couleur de fond des environnements "regle" + +\usepackage{ + metalogo, + tikz, + xspace, + dashbox, + fancybox, + enumitem, + longtable, + framed, + graphicx, + amsmath, + needspace +} +\xspaceaddexceptions{\footnote\verb)} +\usetikzlibrary{decorations} +\usepgflibrary{decorations.pathmorphing} +% ____________________________________________________________________________ +%| | +%| Macros définies dans le livre | +%| | +\input progtexmacro.tex% | +%| | +%|____________________________________________________________________________| + +\makeatletter + +% ____________________________________________________________________________ +%| | +%| environnements "liste" | +%| | +\newlist{algo}{enumerate}{4} +\setlist[algo]{leftmargin=*,labelindent=\main@parindent,itemsep=0pt,topsep=4pt,parsep=1.5pt,before=\small} +\setlist[algo,1]{label=\arabic*),topsep=2pt,ref=\arabic*} +\setlist[algo,2]{label=\alph*),topsep=1pt,ref=\thealgoi.\alph*} +\setlist[algo,3]{label=\roman*),topsep=1pt,ref=\thealgoii.\roman*} +\setlist[algo,4]{label=--,topsep=1pt,ref=\thealgoiii.\roman*} + +\setlist[enumerate]{leftmargin=*,labelindent=\main@parindent,itemsep=0.3pt,topsep=1pt,parsep=1pt,partopsep=0pt} + +\setlist[itemize]{leftmargin=*,labelindent=\main@parindent,itemsep=0.3pt,topsep=1pt,parsep=1pt,partopsep=0pt,label=--} +%| | +%| fin environnements "liste" | +%|____________________________________________________________________________| + +\usepackage[bottom]{footmisc} + +\usepackage{scrlayer-scrpage} +\pagestyle{scrheadings} +\clearscrheadfoot +\cehead{\leftmark} +\cohead{\rightmark} +\lehead{\llap{\normalfont\bfseries\thepage\kern4mm }} +\rohead{\rlap{\normalfont\bfseries\kern4mm \thepage}} +\automark[chapter]{part} +\renewcommand\partmarkformat{\ifnum\arabic{part}>0 \normalfont\biolinum\small\itshape\thepart. \fi} +\renewcommand\chaptermarkformat{\normalfont\biolinum\small\itshape\thechapter. } + +% ____________________________________________________________________________ +%| | +%| Polices | +%| | +\usepackage{amssymb,textcomp,eurosym,libertine} +\usepackage[libertine]{newtxmath} +\renewcommand*\ttdefault{LinuxLibertineMonoTC-TLF} +\DeclareRobustCommand*\libLegacyGlyph[1]{{\ifcsname fxl@#1\endcsname\@nameuse{fxl@#1}\else\errmessage{glyphe fxl@#1 non défini}\fi}} +\DeclareRobustCommand*\bioLegacyKeyGlyph[1]{{\ifcsname fxk@#1\endcsname\@nameuse{fxk@#1}\else\errmessage{glyphe fxk@#1 non défini}\fi}} +\newcommand*\DeclareTextGlyphX[5]{\@namedef{#1@#4}{{\fontfamily{#3}\fontencoding{#2}\selectfont\char#5\relax}}} +\DeclareTextGlyphX{fxk}{U}{fxke1}{T_a_b}{117} +\DeclareTextGlyphX{fxk}{U}{fxke1}{E_n_t_e_r}{118} +\DeclareTextGlyphX{fxk}{U}{fxke1}{A_l_t_G_r}{114} +\DeclareTextGlyphX{fxk}{U}{fxk00}{A}{65} +\DeclareTextGlyphX{fxk}{U}{fxk00}{R}{82} +\DeclareTextGlyphX{fxk}{U}{fxk00}{seven}{55} +\DeclareTextGlyphX{fxl}{U}{fxle0}{Q_u}{72} +\DeclareTextGlyphX{fxl}{U}{fxle0}{uniE007}{7} +\newcommand*\Qu{\libLegacyGlyph{Q_u}}% <- pour éviter de créer une fonte virtuelle +%| | +%| fin Polices | +%|____________________________________________________________________________| + +\usepackage{lettrine} +\usepackage[ + protrusion=true, + expansion=true, + stretch=10, + shrink=5, + tracking=true, + final, + babel=true]{microtype} +\usepackage[autolanguage]{numprint} +% ____________________________________________________________________________ +%| | +%| Mises en forme | +%| | +\let\lcodedelim=\langle +\let\rcodedelim=\rangle +\DeclareRobustCommand\codeelement[1]{% met en forme <"#1"> + \ensuremath\lcodedelim\textit{#1}\ensuremath\rcodedelim +} +\let\Verb=\verb +\def\def@visible@space{% + \setbox0\hbox{x}% + \def\visible@space{% + \leavevmode + \kern.1ex + \vrule width0.4pt height0.5ex depth0.2ex\relax + \vrule width\dimexpr\wd0-.8pt-.2ex\relax height\dimexpr.4pt-.2ex\relax depth0.2ex\relax + \vrule width0.4pt height0.5ex depth0.2ex\relax + \kern.1ex + }% +} +\def\verb{% + \relax\ifmmode\hbox\else\leavevmode\null\fi + \bgroup + \ttfamily + \hyphenchar\font=-1 + \for\ii=0 to 255 \do {\catcode\ii=12 }% + \catcode`\~13 + \obeylines \verbatim@font \@noligs \hyphenchar\font=-1 + \ifstarred{\def@visible@space\letactive\ =\visible@space\catcode`\~12 \verb@i}{\letactive\ =\space\catcode`\~12\frenchspacing\verb@i}% +} +\def\verb@i#1{% + \def\verb@ii##1#1{\verb@iii{##1}}% + \verb@ii +} +\catcode`\>=13 +\catcode`\<=13 +\def\verb@element#1>{\codeelement{#1}} +\def\verb@iii#1{% + \ifin{#1}<% + {\def\temp@l{#1}\let\temp@r\temp@l% + \leftofsc\temp@l<% + \rightofsc\temp@r<% + \exparg\ifin\temp@r>% + {\addtomacro\temp@l\verb@element + \eaddtomacro\temp@l\temp@r + \exparg\verb@iii\temp@l + } + {#1\egroup}% + } + {#1\egroup}% +} +\catcode`\>=12 +\catcode`\<=12 + +\newcount\exercice@cnt +\exercice@cnt\z@ + +\newmacro\defactive[\def]1{% + \catcode`#2=13 + \begingroup + \lccode`~=`#2 + \lowercase{\endgroup#1~}} + +\newcounter{exemple}[part] + +\newenvironment{centrage}[1][3.5pt] + {\ifvmode\removelastskip\fi + \list{}{\leftmargin\z@\parsep\z@\topsep#1\relax}% + \centering\item\relax + }% + \endlist + +\newcommand\boxtoken[2][\string]{% + \begingroup + \fboxsep\boxtokensep + \fbox{\ttfamily\vphantom{Ailpj}#1#2}% + \endgroup + \xspace +} +\newcommand*\boxtokensep{0.3pt} + +\newcommand*\TeXhead{$\ggg$} +\newcommand*\Hex[1]{\hbox{$\mathsf{#1}_{\scriptscriptstyle\mathsf h}$}} +\newcommand*\bin[1]{\textit{#1}} +\newcommand*\latin{\texttt{latin1}\xspace} +\newcommand*\utf{\texttt{UTF8}\xspace} + +\newenvironment{exercice} + {% + \needspace{3.5\baselineskip}% + \setlist[enumerate]{leftmargin=*,labelindent=\main@parindent,itemsep=0pt,topsep=1pt,parsep=1pt}% + \parindent\z@ + \parskip2pt plus.5pt minus.5pt + \global\advance\exercice@cnt\@ne + \bigbreak + \small\noindent + \rule{1.25ex}{1.25ex}\kern1ex \textsc{\textbf{Exercice \number\exercice@cnt}}\par\nobreak + } + {\ifvmode + \vskip\dimexpr2pt-\baselineskip-\lastskip\relax + \fi + \hfill\rule{1.25ex}{1.25ex}\bigbreak} + +\FrameSep=5pt +\newcount\cnt@regle +\newwrite\reglewrite +\immediate\openout\reglewrite=regles.txt +\immediate\write\reglewrite{% + \string\newcount\string\recapreglecnt\string^^J% + \string\recapreglecnt=0\relax + \string^^J% +} +\newenvironment{regle}[1][Règle]{% + \global\advance\cnt@regle\@ne + \begingroup + \gdef\titre@regle{#1}% + \for\xxx=0 to 255\do{\catcode\xxx=12 }% + \captureregle + } + {} +\begingroup\edef\temp{\endgroup\def\noexpand\captureregle##1\string\end\detokenize{{regle}}} +\temp{% + \endgroup + \newlinechar`\^^M + \immediate\write\reglewrite{% + \string\begin{recapregle}#1\string\end{recapregle}\string^^J% + }% + \begingroup + \parskip2pt plus.25pt minus.25pt + \setlist[itemize]{leftmargin=2em,itemsep=0pt,topsep=1pt,parsep=1pt,label=--}% + \newlinechar`\^^M + \everyeof{\noexpand}% + \makeatletter + \scantokens{% + \parindent\z@ + \shaded + {\bfseries\scshape\number\cnt@regle{} - \titre@regle}% + \par\nobreak + #1% + \endshaded + }% + \endgroup + \end{regle}% +}% + +\newenvironment{recapregle}% + {\bigbreak + \global\advance\recapreglecnt 1 + \let\label=\gobone + \small + \parindent=0pt + \parskip1pt plus.25pt minus.25pt + \setlist[itemize]{leftmargin=3.5em,itemsep=0pt,topsep=1pt,parsep=1pt,label=--}% + \textbf{\number\recapreglecnt\relax.\kern0.33333em}% + } + {} + +\newcommand\solution[1][]{% + \needspace{3\baselineskip}% + \smallbreak\noindent%\incorrectiontrue + {\fboxsep\z@\fboxrule.5pt \fbox{\vrule height1ex width\z@\kern1ex }}% + \hspace{1ex}\textsc{\textbf{Solution\ifempty{#1}{}{ #1}}}\par\nobreak +} + +% Centre la ligne de code en mode verbatim +% Utilisation : \centrecode*-code- +% "*" facultative. Si présent, les espaces sont rendus visibles +% code est un code se trouvant entre 2 caractères identiques. +% Si * : ne tient pas compte des "<" et ">" +\def\centrecode{\def\space@def{\defactive\ {\space}}\centrecode@} + +\def\centrecodespc{\def\space@def{\catcode`\ =12 }\centrecode@} + +\def\centrecode@{% + \par\unskip + \begingroup + \parskip\z@ + \catcode`\¤=13 + \begingroup + \lccode`\~`\¤\relax + \lowercase{\endgroup\long\def~##1~}{% + \begingroup + \catcode`\{=1 \catcode`\}=2 \catcode`\#=6 \catcode`\\=0 \catcode`\$=3 + \catcode`\f=11 + \endlinechar=-1 + \scantokens{##1}% + \endgroup + }% échappement de ¤...¤ + \parindent\z@\let\do\@makeother\dospecials + \defactive\,{\kern\z@\string,}% + \defactive\-{\kern\z@\string-}% + \defactive\^^M{\normalfont\ttfamily\color{black}\cr{}}% + \defactive\^^I{{ }{ }{ }{ }}% tab = 4 espaces + \defactive\%{\itshape\color{gray}\char`\% }% + \space@def + \@ifstar + {\let\centrecode@iii=\centrecode@star + \defactive\<{\string<{}}% + \defactive\>{\string>{}}% + \centrecode@i + } + {\let\centrecode@iii=\centrecode@nostar + \centrecode@i + }% +} + +\def\centrecode@i#1{% + \def\centrecode@ii##1#1{\centrecode@iii{##1}}% + \centrecode@ii} + +\def\centrecode@nostar#1{% #1 est le code + \ifin{#1}<% + {\def\temp@l{#1}\let\temp@r\temp@l% + \leftofsc\temp@l<% + \rightofsc\temp@r<% + \exparg\ifin\temp@r>% + {\addtomacro\temp@l\centrecode@element + \eaddtomacro\temp@l\temp@r + \exparg\centrecode@nostar\temp@l + }% + {\centrecode@star{#1}}% + }% + {\centrecode@star{#1}}% +} + +\def\centrecode@star#1{% + \centrage{}\leavevmode\vbox{\small\ttfamily\halign{##\hfill\cr#1\cr\crcr}}\endcentrage + \endgroup\ignorespaces +} +\def\centrecode@element#1>{\codeelement{#1}} + +% Utilisation : \indentcode*[dim]-code- +% "*" facultative. Si présente, les lignes sont numérotées +% [dim] est l'indentation du code par rapport au début des lignes. 3em par défaut +% code se trouve entre 2 caractères identiques et peut comporter des retours à la ligne +\newif\ifindencodenum +\indencodenumfalse +\newcount\indentcode@cnt +\newcommand*\indentcode{% + \begingroup + \indentcode@cnt=\z@ + \let\do\@makeother\dospecials + \catcode`\?12 \catcode`\!12 + \@ifstar + \indentcode@i + {\defactive\ { }% + \indentcode@i + }% +} + +\newcommand*\indentcodeactivechars[1]{% + \defactive\^^M{\par\hspace{#1}}% + \defactive\^^I{{ }{ }{ }{ }}% tab = 4 espaces + \catcode`\¤=13 + \begingroup + \lccode`\~`\¤\relax + \lowercase{\endgroup\long\def~##1~}{% + \begingroup + \catcode`\{=1 \catcode`\}=2 \catcode`\#=6 \catcode`\\=0 \catcode`\$=3 + \catcode`\f=11 + \endlinechar=-1 + \scantokens{##1}% + \endgroup + }% échappement ¤...¤ +} + +\newcommand*\indentcode@i[1][\main@parindent]{% + \parindent\z@ + \parskip\z@ + \ttfamily\small + \ifindencodenum + \everypar{\advance\indentcode@cnt\@ne\llap{\footnotesize$\scriptstyle\number\indentcode@cnt$\space}}% + \fi + \par\smallskip\nobreak\noindent\hspace{#1}% + \indentcodeactivechars{#1}% + \indentcode@ii +} + +\def\indentcode@ii#1{% + \expandafter\defactive\csname#1\endcsname{\smallbreak\endgroup\ignorespaces}% +} + +\newcommand*\boxcs[2]{% + {\boxput*(0,1){\fboxsep\z@\colorbox{white}{\leavevmode\kern1pt\footnotesize\string#1\kern1pt}}{\fboxsep=7pt\fbox{#2}}}% +} + +\newcount\node@cnt +\newcommand*\jumptok[2][\expandatfer]{% + \begin{tikzpicture}[baseline,inner sep=1pt,outer sep=0pt] + \def\exp@tok{\ttfamily\string#1}% + \def\end@tok{#2}% + \small + \makeatletter + \@makeother\{\@makeother\}\@makeother\#% + \node@cnt\z@ + \def\previous@node{n@0}% + \node[anchor=base,fill=black!25](n@0){\strut\exp@tok}; + \jump@tok +} + +\def\jump@tok#1#2#3{% + \advance\node@cnt\@ne + \node[anchor=base west,at=(\previous@node.base east)](jumped@tok){\ttfamily\strut\string#1}; + \if\end@tok\noexpand#3% + \node[draw,anchor=base west,at=(jumped@tok.base east)](n@\number\node@cnt){\ttfamily\strut\string#2}; + \draw[-stealth,shorten >=2pt](\previous@node) to[out=30,in=150] (n@\number\node@cnt); + \end{tikzpicture}% + \expandafter\@gobbletwo + \else + \node[fill=black!25,anchor=base west,at=(jumped@tok.base east)](n@\number\node@cnt){\strut\exp@tok}; + \draw[-stealth,shorten >=2pt](\previous@node) to[out=30,in=150] (n@\number\node@cnt); + \edef\previous@node{n@\number\node@cnt}% + \expandafter\jump@tok + \fi{#2}{#3}% +} + +\def\showcodes#1{% + \hbox{% + \texttt{\string#1}% + (\number`#1\relax\thinspace\string;\thinspace\number\catcode`#1\relax)% + }% + \xspace +} + +\newbox\testbox +\newif\ifshow@dimen +\newcommand*\dimenbox{\@ifstar{\show@dimenfalse\dimenbox@i}{\show@dimentrue\dimenbox@i}} +\newcommand*\dimenbox@i[2][100pt]{% + \setbox\testbox\hbox{\fontsize{#1}{#1}\selectfont #2}% + \begin{tikzpicture}[inner sep=0pt,outer sep=0pt,minimum size=0pt,baseline,line width=0.2pt] + \node[anchor=base west,draw,inner sep=-0.1pt]at(0,0)(boite){\fontsize{#1}{#1}\selectfont#2}; + \ifshow@dimen + \draw[gray,overlay]([xshift=-.8cm]boite.base west)--([xshift=.5cm]boite.base east)node[pos=0,anchor=south west,black,outer sep=1pt]{\tiny ligne de}node[pos=0,anchor=north west,black,outer sep=1pt]{\tiny base}; + \ifdim\dp\testbox>5pt + \draw[stealth-stealth,overlay]([xshift=.4cm]boite.base east)--([xshift=.4cm]boite.south east)node[pos=0.5,anchor=west,outer sep=3pt]{\ttfamily\scriptsize\string\dp=\the\dp\testbox}; + \else + \path([xshift=.4cm]boite.base east)--([xshift=.4cm]boite.south east)node[pos=0.5,anchor=west,outer sep=3pt]{\ttfamily\scriptsize\string\dp=\the\dp\testbox}; + \fi + \draw[stealth-stealth,overlay]([xshift=.4cm]boite.base east)--([xshift=.4cm]boite.north east)node[pos=0.5,anchor=west,outer sep=3pt]{\ttfamily\scriptsize\string\ht=\the\ht\testbox}; + \draw[stealth-stealth]([yshift=.2cm]boite.north west)--([yshift=.2cm]boite.north east)node[pos=0.5,anchor=south,outer sep=3pt]{\ttfamily\scriptsize\string\wd=\the\wd\testbox}; + \fi + \draw[fill,black,overlay](boite.base west)circle(1pt);% + \end{tikzpicture}% +} +%| | +%| fin mises en forme | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Commande \showcode | +%| | +\newbox\strut@box +\newif\if@codeline +\newif\if@errcode +\newif\if@comment +\newcount\code@line +\newcount\code@count +\newwrite\codewrite +\immediate\openout\codewrite=progtexcode.txt +\exactwrite\codewrite|%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% Codes donnés dans le livre %% +%% « Apprendre à programmer en TeX » %% +%% %% +%% Encodage ISO 8859-1 %% +%% _____ %% +%% %% +%% © 2014 Christian Tellechea %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Les codes et les macros commentés donnés dans ce fichier sont diffusés sous +% la licence LaTeX project public license (LPPL) 1.2 +% +% https://www.latex-project.org/lppl/lppl-1-2/ +% https://www.latex-project.org/lppl/lppl-1-2.txt +% +% Attention : ce fichier n'a pas vocation à être compilé +\endinput| +\let\shownumline\@codelinetrue +\let\hidenumline\@codelinefalse +\shownumline +\newcommand\encadre[2]{%0 = encadre 1=début 2=milieu 3=fin + \leavevmode + \frboxrule=0.4pt \frboxsep=3pt + \setbox\z@\hbox{\kern\frboxsep#2\kern\frboxsep}% + \hbox{% + \vrule width\frboxrule + \vtop{% + \vbox{% + \ifnum#1<2 + \hbox to\wd\z@{% + \bfseries\footnotesize + \hrulefill + \kern.5em + \setbox\z@\hbox{Code \ifnum\arabic{part}>\z@\no\thepart-\fi\number\code@count}% + \ht\z@\z@ \dp\z@\z@ + \lower.5ex\box\z@ + \kern.5em + \hrulefill + }% + \fi% réglure supérieure + \kern\frboxsep% espace haut + \box\z@ + }% + \kern\dimexpr\frboxsep+.5ex\relax% espace bas + \ifnum#1=0 \hrule height\frboxrule\fi% réglure inférieure + \ifnum#1=3 \hrule height\frboxrule\fi + }% réglure inférieure + \vrule width\frboxrule + }% +} + +\newcommand*\showcode{\@errcodefalse\showcode@i} + +\newcommand*\errcode{\@errcodetrue\showcode@i} + +\def\showcodeactivechars{} + +\newcommand*\showcode@i[1][]{% + \needspace{3.5\baselineskip}% + \global\advance\code@count1 + \edef\saved@state{% + \tolerance=\the\tolerance\relax + \emergencystretch=\the\emergencystretch\relax + \hfuzz=\the\hfuzz + \vfuzz=\the\vfuzz + }% + \medbreak + \begingroup + \parindent\z@ + \parskip\z@ + \code@line\z@ + \def\aux@code{#1}% + \begingroup + \for\xx=0 to 255 \do{\catcode\xx=12 }% + \showcode@ii +} + +\def\assign@execcode#1\@nil{\xdef\execcode@code{#1}} + +\newcommand*\showcode@ii[1]{% + \def\showcode@iii##1#1{% ##1 est le code où tous les catcodes sont de 12 + \catcode`\¤=13 + \begingroup + \lccode`\~=`\¤ + \lowercase{\endgroup\def~####1~}{}% + \newlinechar`\^^M + \everyeof{\@nil}% + \expandafter\assign@execcode\scantokens{##1}% + \immediate\write\codewrite{^^J^^J% + ****************** Code \number\code@count \space******************^^J% + \execcode@code + ****************** Fin code ******************}% + \endgroup + \def\showcode@code{##1}% + \afterassignment\showcode@iv + \if@errcode\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi + {\def\err@message} + {\let\err@message\@empty}% + }% + \showcode@iii +} +\definecolor{codegray}{gray}{0.375}% gris pour les commentaires dans \showcode +\newcommand*\showcode@iv{% + \def\FrameCommand{\encadre0}% + \def\FirstFrameCommand{\encadre1}% + \def\MidFrameCommand{\encadre2}% + \def\LastFrameCommand{\encadre3}% + \topsep1.5ex %espace avant l'encadrement + \par\nointerlineskip\nobreak + \edef\restorevbadness{\vbadness=\the\vbadness\relax}% + \vbadness=10000 + \MakeFramed{\advance\hsize-\width\FrameRestore}% + \begingroup + \footnotesize + \everyeof{\noexpand}% + \begingroup + \if@codeline\everypar{\num@code}\fi + \gray@line + \let\do\@makeother\dospecials + \catcode`\¤=13 + \begingroup + \lccode`\~`\¤\relax + \lowercase{\endgroup\long\def~##1~}{% + \begingroup + \catcode`\{=1 \catcode`\}=2 \catcode`\#=6 \catcode`\\=0 \catcode`\$=3 + \catcode`\f=11 \catcode`\ =10 + \def\idxmp{% + \relax + \edef\saved@at@catcode{\the\catcode`\@}% + \catcode`\@11 + \@ifstar + {\let\print@idx\@gobble\index@macro@perso} + {\let\print@idx\@gobble\index@macro@perso}% + }% + \def\idx{% + \relax\makeatletter + \@ifstar + {\let\print@idx\@gobble\idx@}% + {\let\print@idx\@gobble\idx@}% + }% + \scantokens{##1}% + \endgroup + }% échappement ¤...¤ + \defactive\%{\unexpanded{\@commenttrue\itshape\color{codegray}\%}}% commentaire + \defactive\^^M{\unexpanded{\if@comment\normalfont\ttfamily\color{black}\fi\@commentfalse}\par\leavevmode}% retour charriot + \normalfont\renewcommand*\ttdefault{LinuxLibertineMonoTCC-TLF}\ttfamily + \setbox\z@\hbox{0}\edef\tt@spc{\hskip\the\wd\z@\relax}% + \catcode`\ =13 + \begingroup + \lccode`\~`\ \relax + \lowercase{\endgroup\let~\tt@spc}% + \defactive\^^I{\tt@spc\tt@spc}% tabulation + \defactive\,{\string,{}}% ligatures + \defactive\<{\string<{}}% + \defactive\>{\string>{}}% + \defactive\-{\string-{}}% + \defactive\`{\string`{}}% + \defactive f{\string f{}}% + \showcodeactivechars% éventuelles modif de caractères actifs + \endlinechar-1 + \scantokens\expandafter{\showcode@code}% + \endgroup + \removelastskip\par\nointerlineskip\nobreak + \ligneH{\kern4pt }% + \par%\nobreak + \if@errcode\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi + {\ttfamily\err@message} + {\catcode`\¤=13 + \begingroup + \lccode`\~`\¤\relax + \lowercase{\endgroup\def~##1~}{}% + \xdef\showcodehisze{\the\hsize}% + \newlinechar`\^^M + \saved@state + \microtypesetup{expansion=false,protrusion=false,tracking=false}% + \aux@code + \edef\nstiflevel@a{\number\currentiflevel}% + \scantokens\expandafter{\execcode@code}\relax + \edef\nstiflevel@b{\number\currentiflevel}% + \ifnum\nstiflevel@a=\nstiflevel@b\relax\else\errmessage{Erreur de if : \number\code@count}\fi + }% + \par\nointerlineskip\nobreak + \endgroup + \endMakeFramed + \restorevbadness + \endgroup + \medbreak +} + +\newcommand*\ligneH[2][4pt]{% + \vbox{\offinterlineskip\hsize\linewidth + \null + #2% + \hrule width\linewidth\kern#1\relax\null% 4pt = espace entre ligne et ce qu'il y a dessous + }% +} + +\newcommand*\gray@line{% + \everypar\expandafter{% + \the\everypar + \edef\color@line{\noexpand\colorbox{gray!\ifodd\code@line 35\else 20\fi}}% ou "50\else 35" pour l'impression + \setbox\strut@box\hbox{\vphantom{Àpj}}% + \dp\strut@box\dimexpr\dp\strut@box+1.25pt\relax + \ht\strut@box\dimexpr\ht\strut@box+1.25pt\relax + \setbox\strut@box\hbox{% + \fboxsep\z@ + \color@line{\box\strut@box\kern\dimexpr\linewidth-7.5pt\relax}% + }% + \wd\strut@box\z@ + \dp\strut@box\dimexpr\dp\strut@box-1.25pt\relax + \ht\strut@box\dimexpr\ht\strut@box-1.25pt\relax + \box\strut@box + }% +} + +\newcommand*\num@code{% + \hb@xt@7.5pt{% + \hss + \global\advance\code@line\@ne + \tiny\number\code@line + \kern2.5pt}% +} + +\newcommand*\defline[1]{\xdef#1{\number\code@line}} +%| | +%| fin \showcode | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Mise en forme tdm, sectionnement | +%| | +\usepackage{titletoc} + +\newcommand*\partminitoc{% + \edef\parksip@saved{\the\parskip}\parskip0pt + \vspace{1.5cm}% + \normalsize\normalfont + \startcontents[part] + \begingroup + \noindent\bfseries\large + Sommaire\hfill\null\par\vskip3pt + \endgroup + \hrule height1pt depth0pt + \smallskip + \renewcommand*\l@chapter[2]{% + \ifnum\c@tocdepth>\m@ne + \addpenalty{-\@highpenalty}% + \vskip 2pt \@plus\p@ + \@tempdima1.5em + \begingroup + \parindent \z@ \rightskip \@pnumwidth + \parfillskip-\@pnumwidth + \leavevmode \normalfont + \advance\leftskip\@tempdima + \hskip-\leftskip + {##1}\nobreak\dotfill\nobreak\hb@xt@\@pnumwidth{\hss ##2}\par + \penalty\@highpenalty + \endgroup + \fi + } + \printcontents[part]{part-}{0}{\setcounter{tocdepth}{0}}% + \vskip5pt + \hrule height1pt depth0pt%\titlerule[1pt] + \parskip\parksip@saved\relax +} + +\def\defpartcomment{\def\part@comment} +\defpartcomment{} + +\renewcommand*\partformat{% + \normalfont\large\sffamily\bfseries\scshape + \hfill\MakeUppercase{\partname}\hfill\null + \vskip.5cm + \setbox\z@\hbox{\normalfont\Huge\sffamily\bfseries + \libLegacyGlyph{uniE007}% + }% + \hfill + \ifnum\c@part>0 + {\normalfont\Huge\sffamily\bfseries + \libLegacyGlyph{uniE007}% + }% + \else + \vphantom{\box\z@}% + \fi + \hfill\null\endgraf + \fontsize{20}{20}\selectfont\centering +} + +\renewcommand*\partheadendvskip{% + \partminitoc + \vfill + \unless\ifx\part@comment\@empty + \noindent\hfill + \begin{minipage}{0.65\linewidth} + \biolinum + \rightskip0pt \leftskip0pt \parindent\z@ + \hsize5cm + \small\null\part@comment\par + \end{minipage}% + \global\let\part@comment\@empty + \vskip2cm + \fi +} +\addtokomafont{chapterprefix}{\raggedleft} +\addtokomafont{chapter}{\huge\bfseries\scshape\raggedleft} +\renewcommand*\chapterformat{% + \hfill + \hbox{\normalfont\bfseries\Large\color{gray}\chapappifchapterprefix{\nobreakspace}\fontsize{72}{80}\selectfont\lower .5ex\hbox{\thechapter}} + \vskip1cm +}% +\renewcommand*\chapterheadendvskip{\vskip2cm } +\renewcommand*\chapterheadstartvskip{\vskip0.2\vsize} +%| | +%| fin mise en forme tdm, sectionnement | +%|____________________________________________________________________________| + +\usepackage[french]{babel} +\frenchbsetup{StandardLists=true,og=«,fg=»} +\selectlanguage{french} + +% ____________________________________________________________________________ +%| | +%| Mise en forme notes bas de page | +%| | +\parindentFFN\parindent +%| | +%| fin mise en forme notes bas de page | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Index | +%| | +\usepackage{imakeidx}% index multiples +\makeindex[ + title=Index général, + columns=2, + intoc=true, + program=texindy, + options=-M xindstyle.xdy -L french +] +\indexsetup{toclevel=part} +\makeindex[ + name=macros, + title=Index des macros définies dans ce livre, + columns=2, + intoc=true, + program=texindy, + options=-M xindstyle.xdy -L french +] + +\immediate\openout\wtest=xindstyle.xdy + +\exactwrite\wtest|(require "page-ranges.xdy") +(define-location-class "arabic-page-numbers" + ("arabic-numbers") :min-range-length 1) +(markup-locclass-list :open "\quad " :sep ", ") +(define-attributes (("etc"))) +(markup-locref :open "\hyperpage{" :close "}, \ldots" :attr "etc") +(define-crossref-class "voir" :unverified) +(markup-crossref-list :class "voir" :open "\textit{voir} " :sep ", ") +(define-crossref-class "voiraussi" :unverified) +(markup-crossref-list :class "voiraussi" :open "\textit{voir aussi} " :sep ", ") +(define-location-class-order ( + "roman-page-numbers" + "arabic-page-numbers" + "alpha-page-numbers" + "Roman-page-numbers" + "Alpha-page-numbers" + "see" + "voir" + "voiraussi")) +(markup-locref :open "\hyperpage{" :close "}") +(markup-locref :open "\hyperpage{" :close "}" :attr "hyperpage")| + +\immediate\closeout\wtest +\usepackage{xstring} + +\let\list@macro@perso\empty% liste des macros perso déjà rencontrées + +\catcode`\W=3 +\def\noindexlist{W}% liste des entrées à ignorer lors de la mise en index + +\long\def\forbidindex#1{% n'indexe plus l'entrée "#1" jusqu'à ordre contraire + \expandafter\global\expandafter\long\expandafter\def\expandafter\noindexlist\expandafter{\noindexlist#1W}% ajoute l'entrée à la liste +} + +\long\def\allowindex#1{% autorise l'index de l'entrée "#1" + \long\def\allowindex@i##1W#1W##2\allowindex@i{\global\long\def\noindexlist{##1W##2}}% + \expandafter\ifin\expandafter{\noindexlist}{W#1W}% + {\allowindex@i#1\allowindex@i}% + {}% +} +\catcode`\W=11 + +\newcommand\idxmp{% + \relax + \edef\saved@at@catcode{\the\catcode`\@}% + \catcode`\@11 + \@ifstar{\let\print@idx\@gobble\index@macro@perso}{\let\print@idx\@firstofone\index@macro@perso}% +} + +\let\oldparagraph=§ +\letactive §\idxmp + +\def\index@macro@perso#1{% + \edef\temp@{\noexpand\substtocs\noexpand\temp@{\string#1}{\string @}}\temp@{\char64 }% + \edef\temp@{\expandafter\gobone\temp@}% + \exptwoargs\ifin\list@macro@perso{\string#1}% + {\print@idx{\leavevmode\hbox{\texttt{\string#1}}}% macro déjà rencontrée + } + {\xdef\list@macro@perso{\list@macro@perso\string#1}% première apparition + \print@idx{\leavevmode\hbox{\texttt{\string#1}${}^{{}\scriptscriptstyle\star{}}$}}% + }% + \index@macro@perso@i +} + +\newcommand*\index@macro@perso@i[1][]{% + \index[macros]{\temp@ @\texttt{\char92 \temp@}#1}% + \restoreexpandmode + \catcode`\@=\saved@at@catcode\relax + \xspace +} + +\edef\print@macro@list{\string\LaTeX\string\eTeX} + +\let\list@indexentry\empty% liste des entrées d'index + +\def\left@@of#1#2{% dans la sc #1, garde ce qui est à gauche de #2 + \def\left@@of@i##1#2##2\@nil{\def#1{##1}}% + \expandafter\left@@of@i#1#2\@nil +} + +\newcommand\add@toindexentry[1]{% ajoute #1 à la liste d'entrées ou pas + \edef\temp@{\detokenize\expandafter{\string#1}}% + \expandafter\left@@of\expandafter\temp@\string|% ne prend que ce qui est avant | + \expandafter\left@@of\expandafter\temp@\string!% ne prend que ce qui est avant ! + \exptwoargs\ifin{\list@indexentry}{\temp@,} + \relax + {\xdef\list@indexentry{\list@indexentry\temp@, }}% +} + +\newcommand\idx{\relax\makeatletter\@ifstar{\let\print@idx\@gobble\idx@}{\let\print@idx\@firstofone\idx@}} + +\catcode`\W=3 +\newcommand\idx@[2][]{% + \saveexpandmode\expandarg + \let\savedindex\index + \expandafter\ifin\expandafter{\noindexlist}{W#2W}{\let\index\gobone}{}% + \expandafter\if@controlsequence\expandafter{\firstto@nil#2\@nil}% si #2 est une séquence de contrôle¤§*\firstto@nil¤ + {\let\repeat\relax + \add@toindexentry{#2}% + \edef\temp@{\expandafter\gobone\string#2}% + \long\edef\temp@a{\meaning#2}% + \long\edef\temp@b{\string#2}% + \let\xx@space\xspace + \ifx\temp@a\temp@b% si #2 est une primitive + \expandafter\ifx\firstto@nil#2\@nil\ % #2 = \<espace> ? + \index{\char\number`#2 @\quitvmode\hbox to0pt {\hss${}^*$}\texttt{\char92 \char32 }#1}% + \print@idx{\texttt{\char92 \char32 }}% + \else + \index{\temp@ @\quitvmode\hbox to0pt {\hss${}^*$}\texttt{\char92 \temp@}#1}% + \print@idx{\texttt{\char92 \temp@}}% + \fi + \else + \expandafter\expandafter\expandafter\ifempty + \expandafter\expandafter\expandafter{\expandafter\gobtwo\string#2}% si caractère de contrôle + {\index{\char\number\expandafter`\temp@ "@@\texttt{\char92 \char\number\expandafter`\temp@}#1}% + \print@idx{\texttt{\char92 \temp@}}% + \let\xx@space\relax + }% + {\StrSubstitute\temp@{\string @}{\char64 }[\temp@]% remplace les @ + \IfSubStr\print@macro@list{\string#2}% + {\index{\temp@ @\protect#2#1}% si macro dans liste + \print@idx{#2}% + }% + {\index{\temp@ @\texttt{\char92 \temp@}#1}% + \print@idx{\texttt{\char92 \temp@}}% + }% + }% + \fi + \restoreexpandmode + \let\repeat\fi + \makeatother + \let\index\savedindex + \xx@space + }% + {\restoreexpandmode + \index{#2\ifx\@empty#1\@empty\else #1\fi}% + \print@idx{#2}% + \let\index\savedindex + \makeatother + }% +} +\catcode`\W=11 + +\newcommand\if@controlsequence[1]{% + \begingroup + \escapechar`\\ + \edef\first@char{\expandafter\firstto@nil\string#1\@nil}%¤ + \edef\escape@char{\expandafter\firstto@nil\string\relax\@nil}% + \ifx\first@char\escape@char + \endgroup\expandafter\firstoftwo + \else + \endgroup\expandafter\secondoftwo + \fi +} + +\def\visiblespace{ } + +% met en index un seul caractère de la forme <char> ou \<char> +\newcommand\cidx{\relax\@ifstar{\let\print@idx\@gobble\cidx@}{\let\print@idx\@firstofone\cidx@}} +\newcommand\cidx@[2][]{% + \if@controlsequence{#2}% si c'est une macro + {\ifnum`#2=`\ % espace ? + \index{\char\number`#2 @\textvisiblespace#1}% + \add@toindexentry{ }% + \print@idx{\texttt{\char92 \char32 }}% + \else + \index{\char\number`#2 @\texttt{\char\number`#2}#1}% + \expandafter\add@toindexentry\expandafter{\expandafter\char\number`#2}% + \print@idx{\texttt{\char\number`#2}}% + \fi + \xspace + } + {\StrSubstitute{#2}"{""}[\temp@@]% + \index{\temp@@ @\texttt{\string#2}#1}% + \add@toindexentry{#2}% + }% +} + +\def\idxdetokenize{\begingroup\catcode`\^12 \idxdetokenize@i} +\def\idxdetokenize@i#1{\endgroup\detokenize{#1}} +% met en index le second argument "détokénizé" +\newcommand\verbidx{\begingroup\catcode`\^12 \@ifstar{\let\print@idx\@gobble\verbidx@}{\let\print@idx\@firstofone\verbidx@}} +\newcommand\verbidx@[2][]{% + \index{#2@{\ttfamily\protect\idxdetokenize{#2}}#1}% + \print@idx{{\ttfamily\idxdetokenize{#2}}}% + \add@toindexentry{#2}% + \endgroup +} + +% met en index les primitives de test "\if...", "\else", "\fi" +\newcommand\tidx{\relax\@ifstar{\let\print@idx\@gobble\tidx@i}{\let\print@idx\@firstofone\tidx@i}} +\newcommand\tidx@i[2][]{% #2 est le nom de la primitive SANS le caractère d'échappement + \index{#2@\quitvmode \hbox to0pt {\hss ${}^*$}\texttt{\char92 #2}#1}% + \add@toindexentry{#2}% + \print@idx{\texttt{\char92 #2}}% +} +%| fin index | +%|____________________________________________________________________________| + +\let\grandsaut\bigbreak +\newdimen\main@parindent + +\usepackage[ + hyperindex = false, + plainpages = true, + pdfauthor = {Christian~TELLECHEA}, + pdftitle = {Apprendre~à~programmer~en~TeX}, + pdfsubject = {Présentation~des~concepts~de~base~et~apprentissage~de~la~programmation~en~langage~TeX}, + pdfcreator = {LaTeX} + ]{hyperref} + +\@addtoreset{chapter}{part} + +\makeatother +\begin{document} +\defactive«{\char19 \penalty10000 \FBguillspace\relax\ignorespaces}% +\defactive»{\unskip\penalty10000 \FBguillspace\relax\char20 }% +\pagestyle{scrheadings}% +\parindent=15pt +\parskip0pt plus.3pt +\hyphenation{cons-truire cons-truite cons-truites cons-truction cons-tructions cons-truit exem-ple exem-ples con-state con-stater con-sta-ta-tion}% +\widowpenalty=600 +\clubpenalty=600 +\interfootnotelinepenalty=10000 +\csname main@parindent\endcsname=\parindent +\let\oldit=\it \let\it=\itshape +\let\oldbf=\bf \let\bf=\bfseries +\let\oldrm=\rm \let\rm=\normalfont +\let\oldtt\tt \let\tt=\ttfamily +\let\oldsc=\sc \let\sc=\scshape% éviter les avertissements d'obsolescence +\raggedbottom +\newcommand\drawbox[1]{% + \leavevmode + \begingroup + \tikz\draw[fill,black,overlay](0.35pt,0pt)circle(1pt);% + \fboxsep0pt \fboxrule0.2pt + \kern-\fboxrule\fbox{#1}\kern-\fboxrule + \endgroup} +\def\messageavert{message d'avertissement} +\startcontents[tableofcontents] +\startcontents[part] + +% ____________________________________________________________________________ +%| | +%| Début du livre | +%| | +\frontmatter +\pagestyle{empty} +\null\pagebreak +\null\pagebreak +%%%%%%%%%%%% Page de titre %%%%%%%%% +\title{\huge Apprendre à programmer en \TeX{}} +\author{\small Christian \textsc{Tellechea}} +\date{\small Septembre 2014} +\def\largeurtext{85}% +\uppertitleback{% + \leavevmode\null\hfill + \vtop{% + \hsize=.\largeurtext\textwidth + \footnotesize + \parskip=3pt + Ce livre est « autoédité », c'est-à-dire que la conception, les relectures et les corrections ont été faites par l'auteur. Malgré le soin qui y a été apporté, des fautes et des erreurs peuvent encore exister. + + Toute erreur, omission ou toute proposition d'amélioration peut être signalée.\medbreak + + Email de l'auteur :\par + \href{mailto:unbonpetit@netc.fr}{\texttt{unbonpetit@netc.fr}}\bigbreak + + Dépôt du code source, fichiers pdf et fichiers annexes :\par + \href{https://framagit.org/unbonpetit/apprendre-a-programmer-en-tex/}{\texttt{https://framagit.org/unbonpetit/apprendre-a-programmer-en-tex/}}\bigbreak + + Remontées de bugs, améliorations :\par + \href{https://framagit.org/unbonpetit/apprendre-a-programmer-en-tex/issues}{\texttt{https://framagit.org/unbonpetit/apprendre-a-programmer-en-tex/issues}} + \vskip1.5cm + + Les codes et algorithmes figurant dans ce livre sont donnés sans \emph{aucune garantie} en vue de leur utilisation dans le cadre d'une activité professionnelle ou commerciale. + }% + \hfill\null +} + +\lowertitleback{% + \leavevmode\null\hfill + \vtop{% + \hsize=.\largeurtext\textwidth + \small + ISBN 978-2-9548602-0-6\medskip + + \textcopyright{} Christian \textsc{Tellechea}, 2014\medskip + + \scriptsize + Révision \no2a, 26/04/2019\medskip + + Photo couverture \textcopyright{} Christian~\textsc{Tellechea}\par + Photo 4\ieme{} couverture \textcopyright{} Willi~\textsc{Heidelbach}. + \medbreak + Le code source au format texte, ce fichier au format pdf qui en résulte après compilation, les fichiers au format texte générés par la compilation ainsi que les fichiers texte et pdf nécessaires à la compilation sont placés sous la licence «LaTeX Project Public License Version 1.2» qui est consultable à l'adresse suivante \href{https://www.latex-project.org/lppl/lppl-1-2.txt}{\texttt{https://www.latex-project.org/lppl/lppl-1-2.txt}} + }% + \hfill\null +} + +\dedication{% + \vskip3cm + \hfill + \vtop{% + \hsize=.47\linewidth + \parindent=0pt + \raggedleft\itshape\small + Un langage de programmation est censé être une façon conventionnelle de donner des ordres à un ordinateur. Il n'est pas censé être obscur, bizarre et plein de pièges subtils (ça, ce sont les attributs de la magie).\medskip + + \normalfont Dave \textsc{Small}% + }% +} + +\extratitle{% + \null\vskip0pt plus1fill + \hfill + \hbox{\Large\biolinum Apprendre à programmer en \TeX{}}% + \hfill\null + \vskip0pt plus3fill + \null +} + +\maketitle + +\pagestyle{plain}\setcounter{page}{1} +\chapter*{Préface} + +Il est déjà bien difficile de maitriser \TeX{} comme langage de composition\ldots{} C'est un vrai défi que d'en connaitre suffisamment les méandres pour l'utiliser comme langage de programmation\footnote{\TeX{} a été créé pour la composition et si son créateur l'a été doté de ce qu'il faut pour programmer, ce n'est pas par choix, mais parce qu'il a été obligé de le faire (un peu à contrec\oe ur) !}. Et, c'est une mission quasi impossible que d'écrire un livre là-dessus, j'en parle en connaissance de cause ! D'ailleurs, bien peu de choses existent sur le sujet. Non pas que les gens sachant programmer en \TeX{} soient rares, ils sont simplement assez sages et modestes pour ne pas s'embarquer dans une telle galère. Inconscient, insouciant et trop sûr de moi, j'ai commencé à écrire sans mesurer les difficultés auxquelles j'allais me heurter. Je ne sais toujours pas quelle folie m'a poussé à poursuivre cette aventure jusqu'au bout. Même si un peu de vanité a certainement joué un rôle, ma motivation n'a jamais été de montrer que « moi, je peux »; il ne s'agit pas d'un péché d'orgueil. Le but poursuivi, moteur plus fort que les difficultés rencontrées au cours de cette aventure, a été d'aider ceux qui, comme moi lorsque j'étais débutant en programmation \TeX, restent devant un code écrit en \TeX{} comme devant un texte en chinois ancien : plongés dans un abime d'incompréhension. + +\Qu iconque connait un peu \TeX{} a dû sourire en lisant la citation de Dave~\textsc{Small}. Et a dû se dire qu'avec \TeX{} bien plus qu'avec d'autres langages, on allait forcément parler de magie noire à un moment ou à un autre ! Car il est vrai que \TeX{} est un langage difficile, tortueux et réputé comme tel. On ne compte plus ceux qui font tout pour ne pas s'y frotter, ceux qui le haïssent ou le rejettent ni ceux qui, bien que l'utilisant, se traitent de fous tant des choses apparemment simples sont en réalité compliquées. \TeX{} est bourré de défauts, collectionne les lacunes, a une syntaxe à coucher dehors tant elle est irrégulière, mais malgré tout cela, on se prend à l'apprécier un peu comme on pourrait apprécier les défauts d'un ami de longue date, taciturne et bourru. Tout comme cet ami pourrait envier certains côtés aux « nouveaux communicants » dont nous sommes désormais entourés, \TeX{} ne soutient pas non plus la comparaison avec les langages modernes. Mais, l'utiliser pour programmer procure un plaisir certain ainsi qu'une sensation de liberté et de créativité qu'on ne retrouve que dans une moindre mesure avec les langages de programmation récents. + +J'invite donc mon lecteur, armé de patience et de persévérance, à entreprendre ce périple qui s'annonce ardu, mais riche de nouveautés et d'originalité. La meilleure chose que je lui souhaite est d'arriver à ses fins et par là même, atteindre aussi le but dans lequel ce livre a été écrit : acquérir une autonomie dans la programmation en \TeX. Ce livre, qui n'est qu'une simple introduction à la programmation en \TeX, sera peut-être un départ pour de nouveaux horizons qui, après beaucoup de pratique et sans utilisation de magie noire --~c'est garanti !~--, le mèneront, qui sait, à devenir un \TeX gourou\ldots + +% ____________________________________________________________________________ +%| | +%| Table des matières | +%| | +\cleardoublepage +%%%%%%%%% Table des matières %%%%%%% +\begingroup + \huge\centering + {\null\vspace{2cm}\bfseries\scshape Table des matières}\par + {\Huge\bfseries\libLegacyGlyph{uniE007}}\bigbreak + \normalfont\normalsize + \makeatletter + \renewcommand*\l@chapter{\@dottedtocline{0}{0em}{1.5em}} + \printcontents[tableofcontents]{sommaire-}{-1}{\setcounter{tocdepth}{2}} + \clearpage +\endgroup +%| | +%| Fin table des matières | +%|____________________________________________________________________________| + +\pagestyle{scrheadings} +\mainmatter + +\chapter*{Conventions adoptées dans ce livre} +\addcontentsline{toc}{part}{Conventions adoptées dans ce livre} + +\subsubsection{Les codes} +Par souci de pédagogie, les codes sont très nombreux. Ils sont présentés de telle sorte que le code précède l'affichage qu'il génère. En voici un, le premier d'une longue série : + +\showcode/Voici le code en \TeX{} du premier exemple.% ceci est un commentaire + +On peut observer l'affichage qu'il produit juste au-dessous !/% + +Sauf pour des cas très exceptionnels qui seront signalés, il ne peut y avoir de «triche» sur l'affichage : il est très exactement le résultat du code qui se trouve au-dessus. En effet, les codes sont tapés \emph{une seule fois} dans le code source de ce livre. Une macro est chargée de stocker ce code pour ensuite fonctionner en deux temps. Elle l'affiche tel quel dans la partie haute du cadre puis laisse \TeX{} le compiler et afficher le résultat dans la partie basse. + +Les codes donnés produisent donc les résultats affichés, mais le lecteur doit garder en tête les limitations suivantes : + +\begin{itemize} + \item malgré les relectures effectuées sur chaque code, ils ne sont pas garantis sans «bogue». Certains bogues étant particulièrement difficiles à débusquer, il serait même très étonnant que certains n'en contiennent pas ! + \item dans la plupart des cas, les codes donnés ne sont qu'une façon de faire \emph{parmi beaucoup d'autres}, le nombre d'alternatives tendant à augmenter avec la complexité de la tâche à exécuter. Ces codes sont donc à considérer comme \emph{une} solution et non pas comme \emph{la} solution. Ils n'ont donc pas valeur de méthode absolue qui serait meilleure que toute autre; + \item ils ne fonctionnent que pour un certain domaine de validité (souvent implicite) des arguments de leurs macros, car élargir ce domaine suppose des complications qu'il est long et fastidieux de programmer. On peut donc toujours trouver des cas plus ou moins spéciaux où les arguments contiennent des choses qui feront «planter» une macro. + + Par exemple, si une macro attend un nombre entier et qu'on lui donne la lettre «a» comme argument, \TeX{} va protester avec l'erreur «\texttt{missing number}». Par ailleurs, des caractères spéciaux comme «\verb|#|» seul, «\verb|%|», etc. vont inévitablement provoquer des plantages s'ils sont écrits dans les arguments des macros. Sauf dans les cas où ces caractères spéciaux sont gérés, les macros attendent des arguments «gentils», c'est-à-dire constitués de caractères ayant un code de catégorie de 10, 11 ou 12 (voir page~\pageref{catcode.table}); + \item les codes proposés ne sont pas toujours \emph{optimisés}, c'est-à-dire que leur vitesse d'exécution tout comme la consommation de mémoire qu'ils requièrent peuvent être améliorées. Ils répondent avant tout à deux contraintes, celle d'illustrer la notion qui est en train d'être abordée tout en utilisant des notions précédemment vues et celle de ne pas inutilement compliquer le code. +\end{itemize} + +J'invite le lecteur à \emph{lire} les codes proposés, même s'ils sont rébarbatifs. Lire n'est pas survoler ! Lorsque les codes deviennent assez complexes, il faut vraiment faire un effort mental important et se plonger dedans afin de comprendre comment ils fonctionnent. Les commentaires insérés dans le code, toujours très nombreux, fournissent une aide pour cela, mais la gymnastique mentale consistant à analyser l'enchainement des instructions et comprendre comment évoluent les arguments des macros reste nécessaire. + +\subsubsection{Les exercices} +Les exercices, également en grand nombre et de difficulté variable, sont souvent proposés au lecteur au fur et à mesure que de nouvelles notions sont présentées. Ils sont précédés d'un «{\footnotesize\rule{1.25ex}{1.25ex}}» et immédiatement suivis de leur solution, choix discutable, mais qui s'est imposé. Voici à ce propos le premier exercice : + +\begin{exercice} +\Qu el argument principal peut-on trouver pour justifier que les exercices soient immédiatement suivis de la solution ? + +\solution +Se décider a été difficile, mais qu'y a-t-il de plus lassant que de passer son temps à tourner les pages pour aller débusquer à la fin du livre la solution d'un exercice que, bien souvent, on n'arrive pas à résoudre ? Ou qu'on ne \emph{souhaite} pas résoudre ! Sur ce point, l'expérience un peu pénible du \TeX book (que vous avez bien sûr déjà lu et relu !) a été décisive et m'a poussé à présenter les choses différemment. Il me semble que le lecteur, supposé adulte et ayant un certain contrôle de lui-même, peut se retenir de lire la solution s'il souhaite vraiment chercher un exercice. +\end{exercice} + +\subsubsection{Éléments de code} +Les chevrons ''$\lcodedelim$`` et ''$\rcodedelim$`` ont vocation à symboliser des éléments dont la nature est exprimée par les mots se trouvant entre ces chevrons. Ainsi, \verb|<nombre>| est censé représenter\ldots{} un nombre ! Selon la même notation, on aurait \verb|\<macro>|, \verb|<texte>|, \verb|<code>| (pour du code source en \TeX{}), \verb|<car>| (pour un caractère), \verb|<dimension>|, etc. +\grandsaut + +Un espace dans le code source peut être mis en exergue, soit parce qu'il revêt une importance particulière, soit parce sa présence est importante et qu'il doit être clairement être visible. Dans ces cas, il est noté «\verb*| |». + +Un caractère isolé peut parfois être encadré pour qu'il soit aisément identifiable, comme l'apostrophe inverse «{\def\boxtokensep{0.5pt}\boxtoken `}». + +Lorsqu'il peut y avoir ambigüité sur les caractères composant le nom d'une commande ou bien parce que nom contient des caractères inhabituels, le nom est encadré comme dans «\hbox{\verb|\|\fboxsep1pt \fbox{\ttfamily a\string_1\string\b7}}» (car une commande peut tout à fait porter un nom pareil !). + +\subsubsection{Les macros propres à ce livre} +Tout au long de ce livre, des macros sont définies dans des codes ou dans des exercices. Lorsqu'elles sont définies, elles sont suivies du signe «$\star$» mis en exposant comme dans \verb|\gobone|${}^{{}\star{}}$. Elles disposent d'un index à part (voir page~\pageref{index.macros}) où l'on peut aisément trouver le numéro de page où chacune d'entre elles a été mentionnée. Lorsqu'elles sont mentionnées dans l'index général, elles sont également suivies de «$\star$». + +\subsubsection{Anglicismes} +Si à mon grand regret l'anglais --~que dis-je, le \emph{globish} !~-- tend à s'imposer comme langue universelle de communication entre personnes ne parlant pas la même langue, il s'est depuis longtemps imposé comme langue en programmation. Il est en effet indéniable que les mots-clés de presque tous les langages de programmation sont en anglais. Ceci a conduit à employer des mots anglais pour désigner des notions couramment utilisées dans le monde de la programmation. J'ai donc fait le choix facile, pour quelques mots, d'utiliser la version anglaise. + +\begin{centrage} +\footnotesize +\begin{tabular}{>\itshape llp{0.55\linewidth}}\\\hline + \normalfont Anglais&Français&Signification\\\hline + \idx{token}&entité lexicale&séquence d'un ou plusieurs caractères du code source reconnue et assimilée par \TeX{} comme un élément indivisible selon les règles de l'analyseur lexical qui seront exposées dans ce livre.\\ + tokenization&segmentation&s'applique à une portion du code source : transformation des caractères du code source en tokens.\\ + catcode&code de catégorie&s'applique à un token : entier sur 4 bits (0 à 15) affecté à un token lorsqu'il est lu par \TeX.\\ + \idx{package}&extension&mot générique signifiant « fichier externe permettant d'accroitre les possibilités de \TeX{} ». Bien souvent, il s'agit de code source mettant à disposition des macros permettant de réaliser des tâches complexes.\\\hline +\end{tabular} +\end{centrage} + +\subsubsection{Les règles} +Au fur et à mesure de la progression, des règles précisant la façon de fonctionner de \TeX{} sont inscrites dans des cadres sur fond gris. Toutes ces règles sont reprises en annexe à partir de la page~\pageref{recapregle}. + +\subsubsection{Orthographe rectifiée} +Lorsque j'ai vu la moue un peu dubitative des rares personnes ayant lu quelques passages de ce livre, j'y ai vu l'embarras de ceux qui, me découvrant incapable d'écrire quelques phrases sans faute, se préparaient à me faire des remarques sur mon orthographe fautive. + +J'ai fait le choix, comme l'indique le logo ci-dessous, d'utiliser l'orthographe rectifiée de la réforme de 1990. + +\begin{centrage} + \includegraphics[scale=0.3333]{vignette.png} +\end{centrage} + +Par conséquent, la phrase « \emph{nous sommes surs que la boite va disparaitre}» est orthographiquement correcte. Il ne manque aucun accent circonflexe, même s'il en fallait trois avant la réforme. \Qu e l'orthographe rectifiée soit encore actuellement considérée comme fautive par la majorité des personnes me laisse un peu interrogatif. Malgré plus de deux décennies pendant lesquelles elle a été \emph{recommandée}, le fait qu'elle soit si peu reconnue et pratiquement absente de l'enseignement ne s'explique pour moi que par le rapport excessivement rigide et crispé que nous entretenons avec notre orthographe. + +Bien évidemment, ce livre étant 100\% \emph{fait-maison}, je ne peux le garantir exempt de coquilles, d'accords fautifs ou de lourdeurs. Je présente d'ores et déjà mes excuses à mes lecteurs pour les désagréments que cela leur procurera. +%| | +%| Fin début du livre | +%|____________________________________________________________________________| + + +% ____________________________________________________________________________ +%| | +%| Introduction | +%| | +\addpart{Introduction} +\chapter{\TeX{}, la brute et le truand} +\section{Les spécificités de \TeX} +Le programme \texttt{tex} est conçu pour lire du code source (le plus souvent enregistré dans un fichier de type «texte») puis «compiler» ce code afin de produire un document affichable disponible dans un fichier de type \texttt{dvi}\idx*{fichier!dvi}. Un tel programme, transformant du code source en document affichable, est appelé «\emph{\idx{moteur}}\footnote{On dit aussi parfois «\emph{compilateur}».}». Le langage \TeX{}\footnote{Il ne faut pas confondre le \emph{moteur} \texttt{tex}\idx*{moteur!tex} qui est exécuté par un ordinateur et le \emph{langage} \TeX{}. Ce langage met à disposition des commandes pour produire un document, mais aussi des commandes pour \emph{programmer}. C'est d'ailleurs le but de ce livre que d'expliquer comment utiliser ces dernières.} met à disposition des commandes qui sont appelées \emph{\idx{primitive}s}, tout en permettant de \emph{définir} d'autres commandes, appelées «\emph{macros-instructions}» ou «\emph{macros}». Compte tenu de leur puissance, ces dernières sont d'un emploi extrêmement courant et sont donc pratiquement indispensables. Elles peuvent être définies par l'utilisateur à l'intérieur même du code source, mais peuvent aussi être contenues dans un fichier externe appelé extension ou «\emph{\idx{package}}» qui sera lu à un endroit du code source choisi par l'utilisateur. Dernière possibilité, des macros peuvent également être précompilées pour être incorporées dans un exécutable appelé «\emph{\idx{format}}» qui embarque le langage \TeX{} dans sa totalité ainsi que des macros précompilées. Ainsi, lorsqu'on lance l'exécutable d'un format, ces macros sont déjà définies avant que le début du code source ne soit lu. + +Les formats les plus connus et les plus utilisés\footnote{On ne peut pas vraiment parler de fréquence d'utilisation des formats les plus « célèbres », car n'importe qui peut se construire son propre \idx{format} à la carte. La man\oe uvre à suivre ne sera pas décrite dans ce livre.} sont :% TODO : vérifier qu'un saut de page n'a pas lieu ici + +\begin{itemize} + \item plain-\TeX\idx*{format!plain-\TeX}, dont l'exécutable est «\texttt{tex}», où les macros «\texttt{plain}» ont été définies par l'auteur de \TeX{} lui-même. Il est donc à noter que lorsqu'on lance le programme \texttt{tex}, en plus du langage \TeX{} \emph{nu}, on dispose aussi de toutes les macros de \texttt{plain}; + \item \LaTeX\idx*{format!\LaTeX}, dont l'exécutable est «\texttt{latex}», qui est un recueil de macros, écrites et mises en cohérence à l'origine par \idx*{Lamport Leslie}Leslie~\textsc{Lamport} et dont le but est de fournir des macros de haut niveau pour la composition; + \item Con\TeX t\idx*{format!Con\TeX t} qui est un peu à part des deux précédents en ceci qu'il n'est pas basé sur le moteur \texttt{tex}\idx*{moteur!tex} mais sur un autre moteur permettant l'accès au langage \emph{\idx{lua}}; + \item pour mémoire, en voici de très peu connus ou très anciens, mais qui ont le mérite d'exister : \texttt{phyzzx}, \texttt{psizzl}, \texttt{Lollipop} et \texttt{TeXsis}. Le plus original de la bande est sans doute \texttt{StarTeX} qui a une approche très éloignée de tous les autres puisque la syntaxe des commandes se rapproche au plus près de celle de l'\texttt{HTML} ! +\end{itemize} + +Le langage \TeX{} et ses formats sont réputés produire des documents de grande qualité typographique, mais \TeX{} est vieux. Son concepteur, \idx*{Knuth Donald}Donald~\textsc{Knuth}, l'a publié en 1982\footnote{Une version plus récente «\TeX90» a été publiée en 1990 où de nouvelles primitives ont été incorporées et où la lecture du code est désormais sur 8 bits au lieu de 7 pour «\TeX82».}, époque où aucun des langages « modernes » n'existait encore. Pour le programmer, il a utilisé un vieux langage s'il en est, le Pascal. \TeX{} est assurément très stable, probablement exempt de bug, mais indéniablement \emph{très vieux} à l'échelle du temps informatique. On peut d'ailleurs s'étonner qu'entre 1982 et le début des années 2000, une aussi longue période se soit écoulée sans évolution majeure. Certes, entre temps, un nouveau moteur «\eTeX\idx*{moteur!etex}» a vu le jour où de nouvelles primitives ont ajouté des fonctionnalités bien pratiques qui n'existaient pas dans le langage \TeX{} initial. Plus récemment encore, le moteur «pdf\TeX\idx*{moteur!pdftex}» a été écrit par Hàn Th\'{ê} \textsc{Thành}\idx*{Hàn Thê Thành} et de nouvelles fonctionnalités ont été ajoutées, tout en conservant celles de \eTeX\idx*{moteur!etex}. Mais ce qui fait la particularité de pdf\TeX\idx*{moteur!pdftex} est qu'il est capable de produire \emph{directement} un fichier \verb|pdf|\idx*{fichier!pdf} à partir du code source sans passer par le fichier \texttt{dvi} jusqu'alors incontournable. +\grandsaut + +Au-delà de son âge, mais peut-être en sont-ce des conséquences, \TeX{} a deux particularités qui le rendent peu attractif, voire carrément repoussant pour beaucoup de personnes qui s'y essaient. + +Tout d'abord, la façon intrinsèque que \TeX{} a de fonctionner, certes très originale, ne ressemble pas à celle qui est devenue la norme dans la plupart des langages de programmation. Ceci est \emph{extrêmement} déroutant et conduit bien souvent à une incompréhension puis un rejet de ce langage, même pour des programmeurs avertis. Car avec \TeX{}, les méthodes de programmation changent et donc, c'est aussi la façon de penser un algorithme qui change ! + +Pour résumer à l'extrême, \TeX{} est un langage \emph{de macros}, c'est-à-dire que les appels aux macros provoquent le remplacement de ces macros (et de leurs arguments) par un nouveau code. Ce code --~ou « \idx{texte de remplacement} »~-- est celui qui a été donné à la macro lors de sa définition. De plus, et c'est une originalité supplémentaire en \TeX, lorsqu'on définit une macro, non seulement on définit son texte de remplacement, mais on définit \emph{aussi} la syntaxe avec laquelle elle devra être appelée. Enfin, et c'est là un des points les plus difficiles, \TeX{} ne procède à \emph{aucune évaluation}, ni du \idx{texte de remplacement} lors de la définition d'une macro, ni des arguments qui lui sont donnés lorsqu'elle est appelée. + +Voici un exemple qui peut illustrer cette absence d'évaluation. Pour comprendre le problème, tenons-nous à l'écart de la syntaxe de \TeX{} où les commandes doivent commencer par «\verb|\|» et les arguments doivent être entre accolades. Adoptons plutôt une syntaxe «générique» où les commandes sont écrites normalement et où les arguments se trouvent entre parenthèses. Imaginons qu'une macro «\verb|left|» ait été créée et qu'elle admette deux arguments. Le premier de type chaine de caractères alphanumériques, noté «\verb|A|» et le second de type entier, noté «\verb|i|» : le résultat de cette macro est constitué par ce qui se trouve à gauche du caractère \no\verb|i| de la chaine \verb|A|. Par exemple, le code : + +\centrecode-left(bonjour)(4)- + +\noindent donnerait les caractères se trouvant à gauche de 4\ieme{} lettre, c'est-à-dire «\verb|bon|». + +De la même façon, admettons qu'il existe aussi une macro «\verb|right|» qui elle, renvoie ce qui se trouve à droite du caractère \no\verb|i| dans la chaine \verb|A|. Et donc : + +\centrecode-right(bonjour)(4)- + +\noindent donnerait les caractères «\verb|our|». + +Voici maintenant un raisonnement extrêmement répandu en programmation et c'est d'ailleurs le c\oe ur du problème. N'importe quel programmeur pense --~avec raison~-- que pour isoler ce qui se trouve entre les caractères \no2 et \no5 dans la chaine «\verb|bonjour|» (c'est-à-dire les caractères «\verb|nj|»), il suffit d'écrire : + +\centrecode|right(left(bonjour)(5))(2)| + +Ce raisonnement suppose que les arguments de \verb|right|--~ici en particulier le premier~-- ont + été évalués \emph{avant} que la macro n'entre en jeu. Ce premier argument qui est «\verb|left(bonjour)(5)|» serait donc remplacé par son résultat «\verb|bonj|» et finalement, après cette évaluation, tout se passe comme si on avait l'appel : + +\centrecode|right(bonj)(2)| + +\noindent Or, cette évaluation n'a pas lieu avec \TeX{} ! Essayons de nouveau d'appréhender ce code, mais adoptons un esprit \TeX ien : + +\centrecode|right(left(bonjour)(5))(2)| + +\noindent \TeX{} exécute le code de gauche à droite donc, lorsqu'il exécute la macro \verb|right|, il va lui transmettre ses deux arguments qui sont «\verb|left(bonjour)(5)|» et «\verb|2|». Et donc, les caractères renvoyés seront tout ce qui se trouve à droite de la 2\ieme{} lettre du premier argument, c'est-à-dire «\verb|ft(bonjour)(5)|». + +Le simple fait que le premier argument ne soit pas évalué conduit non seulement à un résultat radicalement différent, mais surtout implique que si l'on souhaite un comportement «normal», c'est au programmeur d'intégrer à la macro un mécanisme qui se charge d'évaluer cet argument avant que le code utile de la macro ne traite le résultat. Pour ceux qui viennent d'autres langages, cette charge de travail supplémentaire est souvent ressentie comme une corvée, sans compter que le code s'en trouve alourdi et donc moins lisible. + +Une bonne partie des problèmes que rencontrent les personnes non habituées à \TeX{} vient de la confusion qu'ils font entre : +\begin{itemize} + \item l'affichage qui résulte d'un code; + \item ce code lorsqu'il est dans l'argument d'une macro. +\end{itemize} + +L'explication de cette confusion tient au fait que la plupart des langages ont un mécanisme --~appelé «\idx{préprocesseur}»~-- qui \emph{lit, analyse, évalue puis remplace les arguments par leurs résultats}, et ce avant que la macro ne soit exécutée. Avec \TeX{}, ce préprocesseur est réduit à sa plus simple expression : il ne fait que \emph{lire} et \emph{tokenizer} cet argument\footnote{Dans ce cas, on a affaire à un préprocesseur de bas niveau, le plus bas qui soit, et on parle de «préprocesseur lexical».}. + +\Qu elle que soit l'affinité --~ou l'adoration~-- que l'on éprouve envers \TeX{}, on est bien obligé de reconnaitre que cette façon de faire n'est pas \emph{naturelle}. Car notre cerveau est lui aussi doté d'un \idx{préprocesseur} intelligent qui agit de façon semblable à celui des langages de programmation «normaux». Par exemple, n'apprend-on pas en mathématiques que : + +\[\text{si }f(x)=x^2\text{\quad alors\quad}f(2\times3-1)=25\] + +\noindent Avant tout calcul concernant la fonction $f$, ne procède-t-on pas spontanément à l'évaluation de l'argument «$2\times3-1$» pour le remplacer par le résultat, c'est-à-dire le nombre $5$ ? Ce faisant, ne procède-t-on pas comme les langages de programmation modernes qui, eux aussi, évaluent les arguments des macros ? + +Tout ceci conduit donc à une première règle, probablement une des plus importantes de toutes : + +\label{regle.argument}\begin{regle} +Si un code quelconque, notons-le \hbox{\verb|<|$x$\verb|>|}, produit un affichage \hbox{\verb|<|$y$\verb|>|} alors, sauf cas très particulier, il n'est pas équivalent d'écrire \hbox{\verb|<|$x$\verb|>|} ou \hbox{\verb|<|$y$\verb|>|} dans l'argument d'une macro. +\end{regle} +\grandsaut + +L'autre inconvénient de \TeX{} qui rebute les personnes habituées à des langages plus modernes est que, pris comme un langage de programmation, \TeX{} est doté du \emph{strict minimum} ! Pour parler avec modération, l'\emph{extrême minimalisme}\footnote{D'autres, moins bienveillants, parleraient de «pauvreté».} de ce langage est totalement déroutant, car toutes les facilités auxquelles les langages modernes nous ont habituées depuis longtemps ne sont tout simplement pas implémentées dans \TeX{}. On ne parle pas ici de structures évoluées comme de pointeurs ou pire, de programmation orientée objet. Non, on parle de structures de contrôle des plus \emph{basiques}. Il va falloir renoncer à la création de variables typées, aux opérateurs logiques bien pratiques lors des tests («\texttt{or}», «\texttt{and}», «\texttt{not}»), aux boucles («\texttt{for}», «\texttt{while}», etc.), aux tableaux, aux nombres décimaux et aux opérations scientifiques, ainsi qu'à beaucoup d'autres choses. En \TeX{}, on doit se contenter de récursivité, de tests simples, de nombres entiers et des quatre opérations arithmétiques, et encore avec quelques limitations pour ces dernières. Et si l'on veut profiter de structures plus évoluées, il faut les programmer soi-même ! Heureusement, cela est toujours possible, car \TeX{}, dans son minimalisme, a été doté de tout ce qu'il faut pour construire n'importe quel algorithme, aussi complexe soit-il. On qualifie un tel langage de «complet». On aborde d'ailleurs dans ce livre la programmation de structures de contrôle plus évoluées que celles directement accessibles par \TeX. + +Évidemment, lorsqu'on programme en \TeX{}, on ne se trouve pas au niveau de l'assembleur\footnote{Le «langage machine» est le langage de plus bas niveau qui soit, c'est-à-dire le moins compréhensible par un humain : ce sont des commandes directement compréhensibles par le microprocesseur qui sont des assemblages de bits ou si l'on préfère, des suites de 0 et de 1. + +Ces suites de bits, traduites en instructions aisément mémorisables par un humain, constituent le langage «assembleur».} car tout de même, \TeX{} est un langage de bien plus haut niveau. En revanche, ce qui est certain, c'est qu'on programme à un niveau beaucoup plus bas que celui de tous les langages actuels. Cela oblige donc à revenir aux fondamentaux de la programmation, et ce retour aux sources est parfois vécu comme une régression par les personnes venant d'autres langages. Pourtant, contrairement à ce que l'on peut penser, revenir aux concepts de base de la programmation aide à élaborer des algorithmes plus efficaces et mieux construits (ceci étant vrai quel que soit le langage utilisé), oblige à mieux comprendre les méthodes sous-jacentes aux structures complexes et en fin de compte, fait accomplir des progrès en algorithmique tout comme en compréhension de la façon dont fonctionne un ordinateur. + +\section{La relève} +Ces contraintes intrinsèques à \TeX{} font que, petit à petit, de plus en plus de gens gravitant autour de la sphère \TeX{} --~des utilisateurs de \LaTeX{} pour la plupart~-- ont admis que ce langage était devenu obsolète, tant pour la composition que pour la programmation. À leurs yeux, il devenait convenable, sinon de le cacher aux yeux des utilisateurs, au moins de proposer des alternatives considérées comme viables. + +Le problème est que le \idx[!\LaTeX]{format} \texttt{latex} offre une collection de macros précompilées appelées \LaTeX{} dont le but est de proposer des commandes de haut niveau pour la composition mais hélas, \emph{presque rien} pour la programmation ! Pour pallier ce manque, les utilisateurs de \LaTeX{} se sont vus proposer des extensions spécialisées qui rendent la vie du programmeur plus simple et qui mettent à disposition des commandes permettant de créer facilement des boucles ou autres structures de contrôle de plus haut niveau. + +Le progrès que cela représente est considérable, car la facilité d'utilisation est au rendez-vous. Il devient ainsi \emph{très} facile de faire appel à une boucle de type «\verb|for|». Bien sûr, chaque \idx{package} a sa propre syntaxe et ses propres instructions. Mais finalement, beaucoup d'entre eux ont une intersection non vide qui est la mise à disposition de commandes sur les boucles et les tests notamment. Étant donné le grand nombre de packages disponibles pour la seule programmation, il est presque impossible de connaitre les instructions de tous les packages. Le travers que cela créé est que certains utilisateurs deviennent totalement dépendants d'un package auquel ils sont habitués, quitte à ce que cela confine parfois à l'absurde. Un des exemples les plus frappants est le package «ti\textit kz» dont le but est de proposer des commandes de très haut niveau pour faire des dessins vectoriels. C'est un package d'une taille absolument gigantesque qui compte à peu près \numprint{300000}\footnote{À comparer avec les \numprint{8000} lignes du code de \LaTeX{} !} lignes de code, écrit en \TeX{} et en perpétuelle évolution et qui propose, en plus de commandes de dessin vectoriel, des outils qui facilitent la vie en programmation. Bien entendu, on y trouve des commandes qui permettent de construire des boucles. L'absurde de la situation est que parfois, certains utilisateurs dépendants de ti\textit kz et incapables de programmer par ignorance des commandes de \TeX{}, en viennent à charger un \idx{package} aussi volumineux pour ne se servir que d'une instruction en vue de créer une boucle, le tout en dehors de tout contexte de dessin. Certes, la solution fonctionne, mais l'efficacité n'est pas vraiment optimale. +\grandsaut + +Le projet qui est actuellement en pleine phase d'expérimentation est \LaTeX3\index{format!\LaTeX3|(}. Il s'agit d'un projet qui relève un défi très ambitieux, celui de remplacer et d'améliorer l'actuelle version de \LaTeX. Ce projet, en maturation intense depuis de nombreuses années et animé par une équipe de codeurs très expérimentés, arrive actuellement à un stade où des pans entiers, bien que toujours en évolution, deviennent utilisables. Le pan qui émerge en ce moment est carrément un nouveau langage de programmation entièrement écrit en \TeX{} ! Comme \TeX{} est un langage complet, tout est possible, même le plus inattendu ! Ce nouveau langage de programmation offre d'innombrables possibilités que \TeX{} ne proposait pas, certaines extrêmement ardues à programmer. De plus, les auteurs ont essayé autant qu'ils le pouvaient d'inclure un \idx{préprocesseur}, actionnable à volonté et agissant sur les arguments des macros individuellement. Un code écrit dans ce nouveau langage devient donc beaucoup plus épuré et lisible que du code \TeX{}, autant par la présence de ce \idx{préprocesseur} que par l'extraordinaire diversité des macros mises à disposition. Celles-ci couvrent des domaines aussi variés que : + +\begin{itemize} + \item toutes les structures de contrôle basiques : tests, boucles; + \item nombres décimaux en virgule fixe ou flottante et les opérations arithmétiques et scientifiques sur ces nombres ; + \item recherche, remplacement dans une chaine de caractères avec la possibilité de faire appel à des critères dont la syntaxe est proche de celle des «expressions régulières\footnote{Une expression régulière est une suite de signes, appelés «motif», qui décrit selon une syntaxe précise des règles auxquelles doit satisfaire une chaine de caractère lors d'une recherche. + + Un livre entier serait à peine suffisant pour explorer le monde des expressions régulières. Ce qu'il faut savoir est qu'elles sont très largement utilisées autant pour leur compacité que pour leur puissance.}»; + \item tri de données ; + \item etc. +\end{itemize} + +Le revers de la médaille est qu'il faut apprendre ce nouveau langage dont la syntaxe est suffisamment éloignée de celle de \TeX{} pour dérouter, voire rebuter un grand nombre d'utilisateurs habitués depuis des années voire des décennies à celle de \TeX{} ou \LaTeX. Ce nouveau langage se veut en effet beaucoup plus régulier que ne l'est \TeX, à tel point qu'il peut apparaitre fade, aseptisé et domestiqué en regard de l'original qui, par ses défauts un peu folkloriques, peut être ressenti comme davantage original et attachant. Le point le plus discutable est que \TeX{}, le langage sous-jacent, est volontairement rendu totalement invisible pour l'utilisateur final de \LaTeX3, toujours selon la volonté que \TeX{} doit être caché à l'utilisateur, car jugé trop difficile, obsolète et contraignant. \LaTeX3 créé finalement une dichotomie entre les utilisateurs. Les uns, pragmatiques, désireux de savoir et prêts à apprendre un nouveau langage, se moquent éperdument du \emph{vrai} langage qui se trouve sous cette «couche», trop contents d'avoir enfin à leur disposition un langage au comportement quasi «normal». Les autres, par curiosité intellectuelle et par désir de comprendre, ou tout simplement par rejet de \LaTeX3, n'y adhèreront pas et préfèreront ouvrir le capot afin de voir ce qui se joue en coulisses. + +Il va sans dire que ces derniers devront, si ce n'est déjà fait, se mettre à \TeX{} et que les autres doivent sans tarder lire la documentation de \LaTeX3\index{format!\LaTeX3|)}. + +\section{La révolution} +Mais que ces utilisateurs ne se ruent pas trop vite sur \LaTeX3, car il y a autre chose. Depuis l'année 2005, un nouveau moteur est en train de voir le jour. Il s'agit de lua\TeX{}\idx*[|(]{moteur!luatex}, un moteur où bien sûr le langage \TeX{} est toujours accessible, mais où est également rendu possible l'accès au langage «\texttt{lua}». Ce langage, créé en 1993 a justement été écrit pour être embarqué dans des applications pour en étendre les possibilités. Lua\idx*{lua} est un langage \emph{interprété} mais surtout, il possède tout ce que \TeX{} n'a pas et donc tout ce que les langages modernes ont! Parmi les fonctionnalités les plus essentielles, on peut citer : + +\begin{itemize} + \item accès facile et «naturel» aux structures de contrôle habituelles : boucles (\texttt{repeat}, \texttt{while}, \texttt{for}), tests simples, tests composés avec des opérateurs logiques sur les conditions; + \item fonctions dont les arguments sont évalués par un \idx{préprocesseur}; + \item nombres, qui sont d'un seul type, les décimaux à virgule flottante. Ceux-ci sont assortis des opérations arithmétiques et d'une bibliothèque de fonctions mathématiques; + \item variables typées (chaines de caractères, booléens, tables); + \item accès aux fonctions du système (accès aux entrées/sorties, aux fonctions de date, d'heure); + \item etc. +\end{itemize} + +L'accès à \texttt{lua} peut donc grandement faciliter la vie d'un programmeur pour écrire des algorithmes qui autrement, seraient difficiles à coder en \TeX. Cela ne doit tout de même pas faire oublier que certains algorithmes \emph{doivent} être codés en \TeX, car c'est le langage qui a le dernier mot en matière de composition. + +Mais il y a bien plus important. Le plus étonnant est que \texttt{lua} permet aussi l'accès aux mécanismes les plus intimes de \TeX{}. Les auteurs ont réussi le tour de force d'implémenter \texttt{lua} à un niveau tellement bas qu'il est possible d'agir sur le programme \texttt{tex} lui-même et d'accéder à des endroits auparavant inaccessibles et non modifiables (on dit qu'ils sont codés «en dur»). Cet accès ouvre des possibilités inouïes, car lors de la compilation et par l'intermédiaire de \texttt{lua}, il devient possible de modifier ou contrôler des comportements très fins de \TeX, le rendant extrêmement configurable, bien plus qu'il ne l'était. Il devient possible, par exemple, d'effectuer ces actions qui étaient impossibles avant : + +\begin{itemize} + \item relever les positions de toutes les espaces inter-mots d'un paragraphe afin de vérifier que certaines d'entre elles, au-delà d'un certain nombre, ne soient pas adjacentes et alignées verticalement, créant ainsi ce que l'on appelle en typographie des «rivières» ; + \item lors de l'affichage, remplacer chaque occurrence d'un groupe de caractères par un autre, par exemple, tous les «onsieur» par «adame»; + \item etc. +\end{itemize}\idx*[|)]{moteur!luatex} + +\section{Et le bon dans tout ça?} +Bien évidemment, de \TeX{}, \LaTeX3 et lua\TeX, aucun n'est le bon, la brute ou le truand ! Chacun a ses propres caractéristiques et chacun cohabite avec les deux autres sans qu'aucun ne soit en compétition avec un autre. Au contraire, il faut se réjouir puisque le monde de \TeX{}, déjà d'une extrême complexité, mais presque figé depuis 1982 est en pleine effervescence et promet maintenant de façon certaine que des choses impossibles auparavant ne le seront plus. L'avenir s'ouvre riche en perspectives et sans rêver, ne peut-on pas imaginer, d'ici quelques années, un \idx{format} basé sur le moteur lua\TeX{}\idx*{moteur!luatex} contenant les macros précompilées de \LaTeX3, le tout formant une unité réunissant les trois protagonistes qui sera d'une puissance et d'une souplesse jusqu'alors inconnues ? Peut-on déjà mesurer l'immense bond en avant que constituera cette symbiose ? Et que feront les packages de cette nouvelle ère s'ils savent exploiter les trois langages, quels miracles seront-ils capables de réaliser ? + +\chapter{Avant de commencer} +\section{La programmation \TeX{} oui, mais pour qui ?} +Finalement, à qui s'adresse ce livre ? +\grandsaut + +Tout d'abord aux utilisateurs de plain-\TeX, \LaTeX{} ou d'un autre \idx{format} qui souhaitent acquérir un peu d'autonomie dans la programmation en \TeX. Car un jour ou l'autre c'est inévitable, on en a besoin, ne serait-ce que parce que les packages disponibles n'ont pas ne couvrent pas tous les cas ou parce qu'on ignore qu'un package répondant à un besoin précis existe. \Qu 'y a-t-il de plus frustrant que de renoncer en se sachant incapable d'écrire le moindre code non linéaire, surtout lorsqu'on en a un urgent besoin ? \Qu elle satisfaction intellectuelle retire-t-on lorsqu'au moindre problème urgent et incontournable, on est contraint de poser une question de programmation \TeX{} sur un forum spécialisé ? Est-ce vraiment valorisant d'attendre une réponse, « copier-coller » le code donné par un connaisseur sans en comprendre le fonctionnement et se dire qu'au prochain problème, il faudra poser une question à nouveau ? + +Ce livre s'adresse aussi aux curieux, à ceux désireux d'apprendre, ceux qui souhaitent soulever un coin du voile. En effet, \TeX{} est réputé comme étant un langage réservé à quelques «initiés», voire carrément inaccessible si l'on n'est pas «\TeX-compatible». La lecture de ce livre dissipera un peu l'aura quasi mystique qui l'enveloppe --~c'est en tout cas un des buts recherchés~--, assouvissant les légitimes interrogations que se posent les «non-initiés» à son sujet. + +Il s'agit aussi de faire redécouvrir qu'il existe d'autres voies en programmation et celle que prend \TeX{} est des plus originales. Car finalement, tous les «poids lourds» des langages modernes ne tendent-ils pas à se ressembler tous ? Ne proposent-ils pas un peu tous les mêmes fonctionnalités, au moins jusqu'à un certain niveau ? Il est assez excitant de découvrir un langage de macros tellement ceux-ci sont rares\footnote{Le langage « \texttt{M4} » est également un langage de macros.}. L'apprentissage d'un tel langage est excitant d'abord parce que cela étend la culture informatique, mais surtout parce cela ouvre de nouvelles voies en programmation en suscitant des méthodes de programmation nouvelles. Il est d'ailleurs reconnu que les langages de macros sont puissants, même s'ils procèdent à des remplacements \emph{aveugles} de macros par leur \idx{texte de remplacement}. Ce manque d'évaluation se paie par des erreurs parfois difficiles à corriger, car surgissant à des moments ou à des endroits très éloignés de l'origine du problème. +\grandsaut + +Bien sûr, nulle part nous n'apprendrons à programmer des macros aussi complexes que celles de \LaTeX3, nous resterons beaucoup plus modestes. Le but de ce livre est de donner les clés pour comprendre \emph{comment} il faut s'y prendre. La compréhension de quelques concepts doit pousser, au fur et à mesure de la lecture, à coder soi-même et essayer de voler de ses propres ailes. Il est en effet primordial, quel que soit le niveau atteint, de toujours expérimenter par soi-même, car se contenter d'une lecture linéaire ne peut suffire. Imaginerait-on qu'un joueur débutant de bridge, jeu complexe s'il en est, deviendrait subitement un bon joueur après avoir simplement lu d'un trait un ouvrage sur le sujet ? La réponse est évidemment négative, car tester, modifier, adapter, expérimenter, se « planter », apprendre de ses erreurs, recommencer, recommencer encore fait partie de tout apprentissage. Cela est valable dans tous les domaines, et en programmation --~surtout en \TeX~-- peut-être encore davantage ! +\section{Ce qu'il faut savoir} +Il ne serait pas raisonnable d'aborder le côté langage de \emph{programmation} de \TeX{} sans avoir jamais pratiqué \TeX{}, \LaTeX{} ou un autre \idx{format} en tant que langage de \emph{composition}. Non, vraiment pas raisonnable. Cette pratique est indispensable, ne serait-ce que pour s'imprégner de la syntaxe et se familiariser avec l'outil. Ce livre ne s'adresse donc pas au débutant de la première heure, sauf inconscient ou masochiste qui souhaiterait se dégouter à tout jamais de \TeX{} ! + +Mais tout compte fait et nous allons le voir ci-dessous, les choses à savoir ne sont pas légion. + +\subsection{\TeX{} et la composition} +Il faut avoir une petite idée de la façon dont \TeX{} compose du texte. Le schéma mental le plus proche de la réalité est de se dire que \TeX{} fonctionne par placement de boites sur la page. La géométrie des boites de \TeX{} est assez facile à comprendre. Voici la lettre «g», écrite en très grand et dont on a tracé les contours de la \emph{boite englobante}\idx*{boite!englobante} qui traduit les dimensions avec lesquelles \TeX{} voit une boite : + +\begin{centrage} +\dimenbox[75]{g}\global\setbox\testbox\hbox{\unhbox\testbox} +\end{centrage} + +Comme on peut le voir, la boite englobante\idx*{boite!englobante} a trois dimensions\idx*{boite!dimension}\idx*{boite!géométrie}, tout comme n'importe quelle boite dans le monde de \TeX{}\idx*{boite!géométrie} :% +\begin{itemize} + \item une largeur qui est ici de \the\wd\testbox; + \item une hauteur, de \the\ht\testbox + \item une profondeur égale à \the\dp\testbox +\end{itemize} + +\TeX{} exprime naturellement les dimensions en \emph{points}\idx*{pt (unité)}\idx*{unité!pt} (mais nous le verrons, il y a bien d'autres unités possibles), et un centimètre\idx*{unité!cm}\idx*{cm (unité)} contient exactement \texttt{\the\dimexpr1cm\relax}. + +La hauteur et la profondeur s'étendent verticalement de part et d'autre de la \emph{\idx{ligne de base}}. C'est une ligne horizontale théorique autour de laquelle s'organise la géométrie d'une boite. Pour les boites contenant les caractères, sa position pratique coïncide avec le bas des caractères sans jambage comme «a,», «b», «c», etc.\medbreak + +{% +\noindent\setbox0\hbox{\Large Programmer en \TeX{} est \emph{facile}.}% +\hfill +\rlap{\kern-1cm\vrule height0.2pt depth0.2pt width\dimexpr\wd0+2cm}% +\box0 +\hfill\null +}% +\medbreak + +Chaque boite a un point particulier, appelé \emph{\idx{point de référence}} situé sur la frontière gauche de la \idx*{boite!englobante}boite englobante. Ce point de référence est, dans la boite contenant «g», représenté par un point «\tikz\draw[fill,black](0.35pt,0pt)circle(1pt);». Les boites sont donc correctement placées verticalement parce que leur point de référence vient sur la \idx{ligne de base} de la ligne courante, et sont positionnées les unes à la suite des autres parce que le \idx{point de référence} de la boite suivante est situé sur la frontière droite de la boite précédente. + +\TeX{} procède par à-coups, composant tour à tour des paragraphes\idx*[|(]{paragraphe!composition}. La mécanique fonctionne de cette manière : lorsque la commande \idx\par est rencontrée, le paragraphe qui vient d'être lu et qui est jusqu'alors stocké en mémoire est « composé ». C'est le moment où \TeX{} appréhende le paragraphe dans sa totalité pour décider où se feront les coupures de ligne. Chaque ligne de texte est ainsi constituée + +\begin{itemize} + \item de boites contenant les caractères\footnote{Pour être clair, \TeX{} ne connait à aucun moment le dessin de chaque caractère. Il ne connait que les dimensions de la boite qui le contient, qui est la boite englobante.}. Dans un mot, chaque boite est adjacente à la précédente; + \item d'espaces qui peuvent, dans une certaine mesure, s'étirer ou se comprimer de telle sorte que la largeur de la ligne soit exactement celle de la largeur du paragraphe. La dernière ligne du paragraphe, moins large que ne l'est le paragraphe, a des espaces qui ont leur largeur naturelle. +\end{itemize} + +Pendant tout le paragraphe, \TeX{} positionne cette \emph{liste} d'éléments à la suite les uns des autres de façon horizontale pour former chaque ligne : on dit qu'il fonctionne en «mode horizontal\idx*{mode!horizontal}». Ce mode est d'ailleurs celui dans lequel il se trouve pour la plupart du temps lorsqu'il compose du texte. + +La composition proprement dite du paragraphe enferme chaque ligne dans une boite et les boites contenant les lignes sont positionnées \emph{verticalement} les unes sous les autres, chacune étant séparée de sa voisine par un « \idx{ressort d'interligne} » (page~\pageref{ressort.interligne}). + +\begingroup +\clubpenalty=0 % pas de pénalité supplémentaire après la première ligne +\interlinepenalty=0 % pas de pénalité inter-ligne +\widowpenalty=0 % pas de pénalité supplémentaire avant la dernière ligne +\leftline{} +\let\formatline\frbox +\frboxrule=0.3pt +\frboxsep=-\frboxrule% encadrer vers "l'intérieur" +\numlines +On peut observer ce \idx{ressort d'interligne} dans ce paragraphe où la boite englobante\idx*{boite!englobante} de chaque ligne est tracée. On peut constater que la boite englobante d'une ligne ne touche pas celle de ses voisines : l'espace vertical qui les sépare est l'espace interligne. Si cet espace était nul, les lignes seraient beaucoup trop serrées et cela rendrait la lecture très pénible. +\endnumlines +\endgroup + +Après avoir formé le paragraphe (et donc après avoir exécuté la commande \idx\par), le mode\idx*{mode!vertical} vertical est en vigueur, puisque \TeX{} vient de placer les lignes du paragraphe précédent verticalement : il est donc prêt à positionner d'autres éléments verticalement les uns sous les autres. Bien évidemment, si un nouveau paragraphe commence juste après, \TeX{} repasse en mode horizontal\idx*{mode!horizontal} dès le premier caractère rencontré. + +Au fur et à mesure que les lignes et autres objets remplissent verticalement la page, il arrive un moment où un certain seuil de remplissage est atteint et \TeX{} décide où se situe la coupure de page. La page destinée à être composée est à ce moment enfermée dans une ultime boite. Cette boite signe la fin de l'imbrication des boites en poupées gigognes et est envoyée vers le fichier de sortie.\idx*[|)]{paragraphe!composition}% + +\subsection{\Qu elques commandes utiles} +\subsubsection{\texttt{\textbackslash par} et les changements de mode} +La plus courante de toutes les primitives est sans doute \idx\par puisque sans elle, il n'y aurait pas de \idx{paragraphe} et donc pas de typographie. Ajoutons à ce qui a été dit ci-dessus la règle suivante : + +\label{par.consecutifs}\begin{regle} +Lorsque \TeX{} est en mode vertical\idx*{mode!vertical}, la commande \idx[!mode vertical]\par\idx*{mode!vertical} est sans effet. + +Par conséquent, si l'on se trouve en mode horizontal\idx*{mode!horizontal} et si plusieurs commandes \idx[!consécutifs]\par se suivent, seule la première est utilisée pour composer le paragraphe en cours et les autres sont ignorées. +\end{regle} + +On peut quitter le mode vertical\idx*{mode!vertical} sans afficher aucun caractère avec la macro de plain-\TeX{} \idx\leavevmode. + +D'autres primitives commandent à \TeX{} le passage en mode horizontal\idx*{mode!horizontal}, mais font aussi autre chose : +\begin{itemize} + \item \idx\indent insère au début de la liste horizontale une espace de valeur \idx\parindent, qui est la primitive contenant la dimension du retrait de la première ligne d'un paragraphe (plain-\TeX{} initialise cette dimension à \texttt{20pt} en procédant à l'assignation «\verb*|\parindent=20pt |»); + \item \idx\noindent demande à ce que le prochain paragraphe\idx*{paragraphe!indentation} ne soit pas indenté, c'est-à-dire que sa première ligne ne commence pas en retrait. +\end{itemize} + +\subsubsection{Espaces sécables et insécables} +La primitive \idx\kern, suivie d'une dimension, insère dans la liste courante une espace insécable\idx*{espace!insécable} de la dimension voulue. Cette espace est insécable, car aucune coupure (de page ou de ligne) ne pourra se faire sur cette espace. Il est important de noter que \idx\kern obéit au mode en cours; l'espace est horizontale si \TeX{} est en mode horizontal\idx*{mode!horizontal} et sera verticale sinon. Voici ce qu'il se passe si l'on écrit \verb|A\kern 5mm B| : + +\begin{centrage} + A\kern 5mm B +\end{centrage} + +\noindent L'espace de \texttt{5mm} se trouve entre le bord droit de la boite englobante\idx*{boite!englobante} de « A » et le bord gauche de la boite englobante de « B ». + +Pour insérer une espace verticale supplémentaire de \numprint[mm]5 + entre deux lignes, il suffit de passer en mode vertical\idx*{mode!vertical} avec \idx\par pour que \idx\kern agisse dans ce mode. Ainsi, \verb|A\par\kern 5mm B| se traduit par : + +\begin{centrage} + A\par\kern 5mm B +\end{centrage} +\grandsaut + +L'espace entre le bas de la lettre «A» et le haut de la lettre «B» est légèrement supérieure à \numprint[mm]5 car le \idx{ressort d'interligne} s'ajoute à l'espace spécifiée par \idx\kern.\grandsaut + +Si l'on veut insérer des espaces \emph{sécables}\idx*{espace!sécable}, c'est-à-dire susceptibles d'être \emph{remplacées}\footnote{Il faut bien lire « remplacées », car une espace sécable est supprimée si elle se trouve à un endroit de coupure. Les espaces sécables les plus courantes sont les espaces tapés dans le code pour séparer les mots.} par une coupure de ligne ou de page, il faut utiliser soit \idx\hskip, soit \idx\vskip. La première lettre désignant le mode dans lequel on veut que cette espace soit insérée et commandant le passage dans ce mode. Ces deux primitives doivent être suivies d'une dimension de ressort (voir page~\pageref{dimension.ressort}). Plain-\TeX{} met à disposition deux macros \idx\quad et \idx\qquad insérant respectivement une espace horizontale sécable\idx*{espace!sécable} de \verb|1em| et de \verb|2em|. L'unité \verb|em|\idx*{unité!em} dépend du contexte : c'est la dimension horizontale de la boite englobante\idx*{boite!englobante} de lettre «\verb|m|», composée dans la fonte en cours. + +La macro \idx\smallskip insère verticalement à l'aide de \idx\vskip l'espace élastique stockée dans le registre \idx\smallskipamount et qui vaut dans plain-\TeX{} «\texttt{3pt plus 1pt minus 1pt}». Les variantes \idx\medskip et \idx\bigskip insèrent des espaces verticales égales respectivement à 2 fois et 4 fois \idx\smallskipamount. + +Les macros \idx\smallbreak, \idx\medbreak et \idx\bigbreak se comportent comme leurs s\oe urs en «skip» mais elles vérifient tout d'abord que la précédente espace verticale qui les précède est inférieure à celle qui doit être insérée. Dans ce cas seulement, ces macros favorisent une coupure de page (par l'insertion d'une \idx{pénalité} de $-50$, $-100$ ou $-200$), suppriment la précédente espace verticale et insèrent l'espace par l'intermédiaire de \idx\smallskip, \idx\medskip et \idx\bigskip. + +\subsubsection{\texttt{\textbackslash relax}} +La primitive \idx\relax est la plus simple à comprendre puisqu'elle ne provoque aucune action. Elle est cependant utile notamment pour marquer clairement la fin d'un nombre ou d'une dimension et évite que \TeX{} n'aille chercher des choses au-delà. Elle fait aussi partie, mais de façon optionnelle, de la syntaxe des primitives \idx\numexpr et \idx\dimexpr de \idx\eTeX{} (voir pages~\pageref{numexpr} et \pageref{dimexpr}). + +\subsubsection{Commandes de fontes} +Les commandes suivantes, programmées dans le format plain-\TeX, agissent comme des « bascules » et sélectionnent une forme de fonte : + +\begin{centrage} +\small +\begin{tabular}{ccc}\hline + Commande \TeX & Équivalent \LaTeX & Exemple\\\hline + \idx\bf & \idx\bfseries & \bf fonte en gras\\ + \idx\it & \idx\itshape & \it fonte italique\\ + \idx\tt & \idx\ttfamily & \tt fonte à chasse fixe\\ + \idx\sc & \idx\scshape & \sc fonte petite majuscule\\\hline +\end{tabular} +\end{centrage} + +Par ailleurs, en \idx*{mode!mathématique}mode mathématique, la primitive \idx\scriptstyle fonctionne également comme une bascule au niveau de la taille de la fonte qui est affichée dans la taille des exposants. De la même façon, \idx\scriptscriptstyle sélectionne une taille encore plus petite, celle des « exposants d'exposants » : + +\showcode/3 tailles : $% entre en mode mathématique (espaces ignorés) +1^{2^3}% 2 est en taille "\scriptstyle" et 3 en taille "\scriptscriptstyle" +$% fin du mode math¤\idx*{mode!mathématique}¤ + +normal $1$, petit $\scriptstyle 2$, très petit $\scriptscriptstyle 3$.¤\idx*\scriptstyle\idx*\scriptscriptstyle¤/ + +\subsubsection{Les boites} +Venons-en maintenant aux \idx{boite}s, non pas celles que \TeX{} construit automatiquement lors de la composition, mais de celles qui sont mises à disposition de l'utilisateur pour qu'il puisse enfermer dedans un contenu arbitraire. + +\TeX{} met à disposition trois primitives permettant d'enfermer du matériel dans des boites : +\begin{itemize} + \item \idx\hbox\verb|{<contenu>}| enferme le \verb|<contenu>| dans une boite horizontale, étant entendu que ce contenu ne peut être composé qu'en mode horizontal\idx*{mode!horizontal} et doit donc être exempt de commande spécifiquement verticale (\idx\par ou \idx\vskip y sont interdits puisque passant en mode vertical\idx*{mode!vertical}); + \item \idx\vbox\verb|{<contenu>}| construit une boite verticale dans laquelle chaque élément du \verb|<contenu>| est empilé sous l'élément précédent et où règne le mode vertical \emph{interne}\idx*{mode!vertical interne}. Le \idx{point de référence} de la boite ainsi obtenue coïncide avec celui du dernier élément (le plus bas donc). Autrement dit, la \idx{ligne de base} de la \idx\vbox coïncidera donc avec la \idx{ligne de base} de l'élément le plus bas. Par conséquent, s'il y a $n$ éléments dans la \idx\vbox, les $n-1$ premiers seront placés au-dessus de la \idx{ligne de base} de la ligne courante comme le montre la figure ci-dessous ; + \item \idx\vtop\verb|{<contenu>}| procède comme \verb|\vbox| sauf que le \idx{point de référence} de la \idx\vtop coïncide avec celui du \emph{premier} élément (le plus haut). Les $n-1$ derniers éléments sont donc au-dessous de la \idx{ligne de base} de la ligne courante. +\end{itemize} + +Voici deux constructions, l'une avec \idx\vbox et l'autre avec \idx\vtop où les frontières des boites ont été rendues visibles et où les points de références de chaque boite ont été symbolisés par un point «\tikz\draw[fill,black](0.35pt,0pt)circle(1pt);». Pour les besoins de l'exemple, chaque boite contient trois éléments. + +\begin{centrage}\footnotesize + Voici une \idx\vbox : \drawbox{\vbox{\hbox{\drawbox{La première ligne}}\hbox{\drawbox{La deuxième}}\hbox{\drawbox{Et la troisième}}}} puis une \idx\vtop : \drawbox{\vtop{\hbox{\drawbox{La première ligne}}\hbox{\drawbox{La deuxième}}\hbox{\drawbox{Et la troisième}}}} +\end{centrage} + +Le mode en cours dans ces boites est le mode \emph{interne}\idx*{mode!interne}. Il s'agit du mode horizontal interne\idx*{mode!horizontal interne} pour \idx\hbox et du mode vertical interne\idx*{mode!vertical interne} pour \idx\vbox et \idx\vtop.\grandsaut + +Une boite construite avec l'une des trois primitives \idx\hbox, \idx\vbox ou \idx\vtop est \emph{insécable}. Il n'existe donc aucun mécanisme qui permet de couper une boite ainsi construite\label{boite.insecable}. Il est donc nécessaire d'être prudent sur les débordements\idx*{dépassement (marge)} qui peuvent en résulter : + +\begin{itemize} + \item en mode horizontal\idx*{mode!horizontal}, ces débordements ont lieu dans la marge de droite ; + \item en mode vertical interne\idx*{mode!vertical interne}, ils ont lieu dans la marge inférieure. +\end{itemize} + +{\hfuzz=\maxdimen Pour illustrer ce propos, voici ce qui arrive si on tente d'enfermer ces \hbox{\bfseries mots en gras} dans une \idx\hbox. On peut constater que la coupure de ligne n'a pu se faire et que la boite dépasse dans la marge de droite. Cela signifie que malgré tous ses efforts pour comprimer ou étirer les espaces, couper des mots, mixer ces possibilités pour examiner plusieurs solutions, \TeX{} n'a pu composer le paragraphe\idx*{paragraphe!composition} pour faire en sorte que cette \idx\hbox se trouve entre les frontières du texte. Dans le cas présent, cet échec est dû à la trop grande largeur de la boite et au fait qu'elle se situe à un endroit proche du début du paragraphe, où la latitude d'organiser différemment ce qui précède est assez réduite. \TeX{} avertit l'utilisateur qu'une anomalie dans la composition du paragraphe a eu lieu par un message dans le \idx[!log]{fichier} \verb|log|: c'est le fichier produit par \TeX{} lors de la compilation d'un code source. Ce fichier contient des informations de compilation et a le même nom que le fichier compilé mais porte l'extension «\texttt{.log}». Concernant ce débordement, on lit dans ce ficher «\texttt{Overful \string\hbox{} (9.74199pt too wide) in paragraph}», ce qui signifie qu'une boite horizontale (la boite horizontale dont il est question est ici la ligne en cours) est trop large de 9.74199pt par rapport à la largeur du texte.\par} + +\section{Demandez le programme} +Le chemin choisi pour aborder la programmation en \TeX{} va sembler, c'est sûr, long et ingrat. Il obéit cependant à une logique qui apparaitra plus tard au lecteur. Les parties I et II ne sont pas consacrées à la programmation, mais aux nombreuses spécificités du langage \TeX{} sans lesquelles il est impossible de construire le moindre programme. C'est par souci de clarté et pour bâtir des bases solides que ces spécificités sont abordées \emph{avant} d'entrer dans le vif du sujet. Renoncer à ces explications préliminaires aurait conduit à d'innombrables digressions, chacune apparaissant à une nouvelle spécificité, et troublant plus le discours qu'autre chose. Ainsi, la partie I montrera comment et selon quelles règles \TeX{} lit le code source pour le transformer en matière exploitable que sont les \idx{token}s. La partie suivante exposera les nombreuses spécificités des macros de \TeX{} et comment les construire afin de maitriser cet outil fondamental de programmation. + +Ce n'est qu'ensuite, à la partie III, que nous nous lancerons dans le grand bain et commencerons à bâtir des algorithmes. Nous verrons, à l'aide d'algorithmes relativement peu complexes, quelles méthodes de programmation nous pouvons mettre en \oe uvre en \TeX{} et quels écueils nous devons éviter. La partie IV s'inscrit dans la continuité de la partie III, mais les méthodes employées diffèrent en ce sens que la granularité de la lecture du code effectuée par les macros sera plus fine. + +Enfin, la dernière partie sera consacrée à des algorithmes un peu plus complexes mettant à profit les notions vues jusqu'alors. +%| | +%| Fin introduction | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Partie 1 | +%| | +\defpartcomment{\lettrine[lines=3,slope=4pt,nindent=0pt]{\libertineInitialGlyph{A}}{\kern -5pt vant} d'entrer dans le vif du sujet, il nous faut comprendre un des mécanismes fondamentaux de \TeX{}, le premier dont il est question lorsque \TeX{} fonctionne : comment lit-il le code qui est tapé par l'utilisateur ? Et qu'est-ce réellement du code tapé par l'utilisateur ? Les réponses à ces questions non triviales permettent non seulement de lever une petite partie du mystère qui entoure le fonctionnement de \TeX{} mais surtout posent des bases essentielles.} + +\part{\TeX{} et le code source} +\chapter{Comment \TeX{} lit-il le code source ?} +Pour bien répondre à cette question, il faut d'abord définir ce qu'est du «code tapé par l'utilisateur». Ce code, la plupart du temps stocké dans un fichier, est considéré et lu par \TeX{} \idx{octet}\footnote{Un octet est une suite de 8 bits. Cette suite peut être comprise comme un nombre constitué de 8 chiffres en base 2. Si l'on convertit ce nombre en base 10 alors, ce nombre peut varier de $2^0-1$ à $2^8-1$, c'est-à-dire de 0 à 255 ou \Hex{00} à \Hex{FF} en hexadécimal.} par \idx{octet}; c'est d'ailleurs ainsi qu'il est stocké sur les disques durs de nos ordinateurs. Ainsi la résolution de lecture de \TeX{} est toujours la même, le code tapé par l'utilisateur est vu par \TeX{} comme une suite d'\idx{octet}s. +\grandsaut + +Un «\idx{éditeur}» est un logiciel totalement indépendant de \TeX{} dont les fonctions les plus basiques sont de permettre la saisie de \emph{texte} et la sauvegarde de ce texte dans un fichier. Il existe des quantités d'éditeurs\footnote{Citons pour les plus reconnus vim et emacs du côté de UNIX/GNU-linux et notepad++ du côté de Windows.} différents, avec des fonctions plus ou moins nombreuses, parfois pléthoriques, certains étant même spécialisés dans la saisie de code \TeX\footnote{Là aussi, il y a du monde : auc\TeX{} qui est une extension d'emacs pour le spécialiser pour \TeX/\LaTeX, \TeX macs, kile, \TeX maker, \TeX works, etc.}. Lorsqu'on demande à l'éditeur d'enregistrer dans un fichier le texte que l'on a tapé, l'éditeur a la charge de \emph{convertir} les caractères tapés par l'utilisateur en une suite d'\idx{octet}s qui seront enregistrés sur le disque dur sous forme d'un fichier texte. + +Pour comprendre comment cette conversion est faite et quelle est la signification de ces \idx{octet}s, il faut savoir à quel «\idx{encodage}» obéit l'éditeur lors de cette conversion. Un \idx{encodage} est une convention qui lie de façon biunivoque un caractère à un (parfois plusieurs) \idx{octet}. Il est important de comprendre que c'est l'\emph{utilisateur}, en paramétrant son éditeur, qui décide quel \idx{encodage} il désire utiliser, même si évidemment, un choix par défaut est toujours sélectionné si l'on n'a pas fait la démarche de modifier le paramètre adéquat. +\grandsaut + +L'\idx{encodage} ASCII\idx*{encodage!ASCII}\footnote{ASCII est l'acronyme pour «American Standard Code for Information Interchange».} est commun à l'immense majorité des autres \idx{encodage}s ce qui fait que l'ASCII joue le rôle de plus petit dénominateur commun. Cette convention ASCII ne s'occupe que des \idx{octet}s compris entre 0 et 127 (ou \Hex{00} et \Hex{7F} en hexadécimal). \TeX{} reconnait nativement l'ASCII dont on voit dans la table ci-dessous la correspondance entre \idx{octet}s et caractères. Pour toutes les implémentations de \TeX{}, la relation entre \idx{octet} et caractère affichable qui figure dans cette table est valide : + +\def\carnonaff{\itshape} +\begin{centrage} + \noindent + \footnotesize + \def\?#1{\ttfamily\bfseries x#1}% + \newcount\charcnt \charcnt=-17 + \def\!{\global\advance\charcnt1 }% + \def\:{\char\charcnt}% + \let\^\carnonaff% marque un caractère non affichable + \newcolumntype G{m{\dimexpr(\linewidth-18\arrayrulewidth)/17\relax}}% + \begin{tabular}{|@{}>{\columncolor{gray!40}[0pt][0pt]\hfill\ttfamily\bfseries}G<{x\hfill\null}@{}|*{16}{@{}>{\!\hfill\vrule height3.3ex depth.8ex width0pt }G<{\hfill\unless\ifnum\charcnt<0 \vtop{\kern1.5pt\normalfont\tiny\llap{\number\charcnt\kern.5pt}\kern1pt}\fi\null}@{}|}} + \hline + \rowcolor{gray!40}[0pt][0pt]\multicolumn1{|@{}G@{}|}{\cellcolor{white}} + &\?0 &\?1 &\?2 &\?3 &\?4 &\?5 &\?6 &\?7 &\?8 &\?9 & \?A& \?B&\?C &\?D &\?E & \?F\\\hline + 0 & \^NUL&\^SOH &\^STX &\^ETX &\^EOT &\^ENQ &\^ACK &\^BEL & \^BS &\^HT & \^LF & \^VT & \^FF & \^CR & \^SO & \^SI \\\hline + 1 & \^DLE&\^DCI & \^DC2& \^DC3& \^DC4& \^NAK& \^SYN& \^ETB& \^CAN& \^EM & \^SUB& \^ESC& \^FS & \^GS & \^RS & \^US \\\hline + 2 & SP & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: \\\hline + 3 & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: \\\hline + 4 & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: \\\hline + 5 & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: \\\hline + 6 & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: \\\hline + 7 & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \: & \^DEL\\\hline + \end{tabular} +\end{centrage} + +Dans ce tableau, les cases grisées portent les deux chiffres hexadécimaux qui formeront le numéro de l'octet. Le nombre en bas à droite de chaque case est le numéro de l'octet en base 10. Par exemple, le caractère «A» correspond à l'octet \Hex{41} dont la valeur décimale est 65. + +On remarque que quelques octets codent des caractères non affichables. Ceux-ci sont affichés en italique\footnote{Les acronymes dans les cases des caractères non imprimables correspondent à des noms qui ont été donnés à ces octets lors de la création de cet \idx{encodage}. Pour la plupart, ces octets étaient insérés dans des flux de données lors de télétransmission et avaient une signification liée à la communication entre deux ordinateurs distants. Par exemple, l'octet {\carnonaff EOT\/} signifie « End Of Transmission» c'est-à-dire « Fin de Transmission», {\carnonaff NAK\/} signifie «Negative Acknowledge» pour «Accusé de réception négatif». Ces fonctions sont évidemment sans objet pour un traitement de texte comme \TeX{}. Ces caractères n'ont donc pas de signification particulière pour \TeX{} à l 'exception de quelques caractères non imprimables dont la fonction est restée la même : {\carnonaff HT\/} pour «tabulation horizontale» (le caractère inséré avec la touche «Tab» \bioLegacyKeyGlyph{T_a_b}), {\carnonaff LF\/} pour «saut de ligne» et {\carnonaff CR\/} pour «retour charriot».} dans la table. Ils ont des codes hexadécimaux compris entre \Hex{00} et \Hex{1F} inclus auxquels il faut rajouter le caractère de code \Hex{7F}. +\grandsaut + +Pendant la compilation, à chaque fois que \TeX{} lit un \idx{octet} --~pour l'instant, disons par abus de langage un «caractère»~--, il lui affecte une bonne fois pour toutes un «code de catégorie» ou «\idx{catcode}» et de fait, chaque caractère lu dans le fichier a donc \emph{deux} codes pour \TeX: +\begin{itemize} + \item le \idx{code de caractère}, c'est-à-dire la valeur de l'\idx{octet} lu dans le code source et qui est lié à la table ASCII vue précédemment; + \item le code de catégorie\idx*{catcode} qui est affecté par \TeX{} lors de sa lecture. Ces codes de catégorie sont des entiers qui vont de 0 à 15 et qui donnent au caractère des propriétés qui sont celles de la catégorie à laquelle il appartient. +\end{itemize} + +Voici les différentes \idx{catégories} ainsi que pour chacune, les caractères qui les peuplent par défaut : + +\begin{centrage} + \label{catcode.table}\small + \let\^\carnonaff% marque un caractère non affichable + \LTpre\parskip \LTpost\parskip\idx*{catcode!table des catcodes} + \begin{longtable}{>{\leftskip0ptplus1fill \rightskip\leftskip}m{1.5cm}m{4.5cm}m{3cm}}\hline + + N°{} de\par catégorie & Signification & Caractères\par concernés\\\hline\endhead + 0 & \idx{caractère d'échappement} & \texttt\textbackslash\\ + 1 & début de groupe & \ttfamily\{\\ + 2 & fin de groupe & \ttfamily\}\\ + 3 & délimiteur de mode mathématique\idx*{mode!mathématique} & \ttfamily\$\\ + 4 & tabulation d'alignement & \ttfamily\&\\ + 5 & retour charriot & \^LF\\ + 6 & caractère de paramètre & \ttfamily\#\\ + 7 & exposant & \ttfamily\string^\\ + 8 & indice & \ttfamily\string_\\ + 9 & caractère ignoré & \^NUL\\ + 10 & \idx{espace} & SP et \^HT\\ + 11 & lettres & \texttt{A...Z}\quad et\quad\texttt{a...z}\\ + 12 & autres caractères &\\ + &\qquad-- chiffres & \ttfamily0 1 2 3 4 5 6 7 8 9\\ + &\qquad-- signes de ponctuation & \ttfamily\string: . \string; , \string? \string! \string' \string"\\ + &\qquad-- signes mathématiques & \ttfamily + - * / = < >\\ + &\qquad-- autres signes &\ttfamily [ ] ( ) | @ \string`\\ + 13 & caractère actif & \ttfamily\string~\\ + 14 & caractère de commentaire & \ttfamily\%\\ + 15 & caractère invalide & \^DEL\\ + \hline + \end{longtable} +\end{centrage} + +Dans toute la suite du livre, sauf indication contraire, lorsqu'il est dit par abus de langage « lettre » ou bien « accolade ouvrante », il est sous-entendu « caractère de catcode 11\idx*{catcode!11 (lettre)} » ou « caractère de catcode 1\idx*{catcode!1 (accolade)} ». + +Comme on le voit, certaines catégories sont très peuplées (celles dont le numéro est 11 ou 12) alors que d'autres ne contiennent qu'un seul élément. Ceci s'explique par le fait que seules les catégories de numéro 10, 11 et 12 comportent des caractères \emph{affichables} pour \TeX. Les autres ne comportent que des caractères qui commandent une action à \TeX{}, par exemple de passer en mode mathématique\idx*{mode!mathématique} pour le caractère «\verb-$-». Comme il n'y a pas de raison pour que plusieurs caractères revêtent cette propriété, il est logique de ne trouver qu'un seul caractère dans la catégorie n°3. Le même raisonnement s'applique aux autres catégories ne comportant qu'un seul caractère. + +L'\idx{espace}, bien qu'affichable, se trouve dans une catégorie à part, car il obéit à des règles différentes que celles des autres caractères affichables, notamment lors de la lecture du code source : + +\label{espaces.consecutifs}\begin{regle} +Lorsque plusieurs espaces\idx*{espace!consécutifs} (ou plus généralement «caractères de catcode 10»\idx*{espace!catcode 10})\idx*{catcode!10 (espace)} se suivent dans le code source, \TeX{} ne prend en compte que le premier d'entre eux et ignore les autres. + +Si une ligne de code source commence par des espaces\idx*{espace!début de ligne}, ceux-ci sont ignorés. + +Plain-\TeX{} et \LaTeX{} assignent au caractère de tabulation ({\carnonaff HT\/}) le catcode de 10 ce qui signifie que ce caractère revêt toutes les propriétés de l'espace. +\end{regle} + +Pour fixer les idées, supposons que \TeX{} ait à lire le code suivant : + +\centrecodespc-\hbox{Et $x$ vaut 3,5.}- + +\noindent Au fur et à mesure de sa lecture, il attribuera à chaque caractère le code de catégorie en vigueur au moment où il le lit et agira en conséquence. Ce code de catégorie est attribué de façon \emph{définitive} et ne peut pas être changé ensuite ; il s'agit d'un mécanisme incontournable sur lequel il faut insister : + +\label{catcode.inalterable}\begin{regle} + Dès que \TeX{} lit un caractère, il lui affecte de façon inaltérable un \idx*{catcode}code de catégorie. +\end{regle} + +Si lors de la lecture, les catcodes en vigueur sont ceux exposés dans le tableau ci-dessus, voici sous chaque caractère, le catcode qui lui sera affecté : + +\def\falsespcverb{\leavevmode\vrule width0.2pt height .5ex depth 0pt + \vrule width.75em height0.4pt depth 0pt + \vrule width0.2pt height .5ex depth 0pt } +\begingroup + \hfill + \def~#1#2{% + \hbox{% + \footnotesize + \vtop{\offinterlineskip\hbox to1em{\hss\ttfamily\strut#1\hss}\hbox to1em{\hss\tiny#2\hss}}% + }% + } + ~\textbackslash0~h{11}~b{11}~o{11}~x{11}% + ~\{1~E{11}~t{11}~\falsespcverb{10}~{\string$}{3}~x{11}% + ~{\string$}{3}~\falsespcverb{10}~v{11}~a{11}% + ~u{11}~t{11}~\falsespcverb{10}~3{12}~,{12}~5{12}~.{12}% + ~\}2\hfill\null +\endgroup +\grandsaut + +Un caractère peut être tapé sous forme habituelle comme lorsqu'on tape un «\verb-a-», mais on peut aussi utiliser «\verbidx[|(]{^^}». En effet, si \TeX{} voit deux caractères \emph{identiques} dont le code de catégorie est \number\catcode`\^{} (celui du caractère de mise en exposant) et si ces deux caractères sont suivis d'un caractère dont le code --~appelons-le $c$~-- est compris entre 0 et 63 inclus, alors \TeX{} lit le tout comme étant le caractère dont le code est $c+64$. Par exemple, «\verb-^^:-» est le caractère «^^:». Il est rigoureusement équivalent de taper «^^:» ou «\verb-^^:-» et ce, quel que soit l'endroit où l'on se trouve dans le code source. + +Symétriquement, si le code de caractère $c$ est compris entre 64 et 127 inclus, \TeX{} remplace le tout par le caractère de code $c-64$. Et donc, il est équivalent de taper «\verb-^^z-» ou «\string^^z». + +Ce qui est écrit dans les deux paragraphes précédents nous conduit donc à cette règle : + +\begin{regle} +Lorsqu'on écrit «\verb-^^<car>-» dans le code source, tout se passe donc comme si \TeX{} voyait le caractère se trouvant 4 lignes plus haut ou plus bas dans la table ASCII, selon que \verb-<car>- se trouve dans la partie basse ou haute de cette table. + +Si les \emph{deux} caractères qui suivent \verb-^^- forment un nombre hexadécimal à deux chiffres minuscules pris parmi «\verb-0123456789abcdef-», alors \TeX{} remplace ces 4 caractères par le caractère ayant le code hexadécimal spécifié. Avec cette méthode, on peut donc facilement accéder à n'importe quel caractère. Le caractère «z» peut aussi s'écrire «\verb-^^7a-». +\end{regle} + +Ainsi, le caractère «t» étant apparié via \verb-^^- avec un caractère affichable (en l'occurrence «4» qui se trouve 4 lignes plus haut dans la table ASCII), «t» peut donc être tapé dans le code de 3 façons équivalentes : «\verb-t-» ou «\verb-^^4-» ou «\verb-^^74-». Si l'on voulait rendre le code mystérieux ou difficilement lisible, voici comment on pourrait écrire le mot «tex» : +\begin{itemize} + \item \verb-^^4^^%^^8- + \item \verb-^^74^^65^^78- +\end{itemize} + +La seule petite ambigüité qui subsiste dans cette belle mécanique est que l'on ne peut pas écrire un «t» sous la forme «\verb|^^4|» puis le faire suivre du caractère «5». En effet, cela formerait la suite de caractères «\verb|^^45|» que \TeX{} interprèterait comme étant le caractère de code hexadécimal \Hex{45} qui est «E»\verbidx*[|)]{^^}. + +Au-delà de l'originalité de cette méthode alternative pour écrire des caractères dans le code source, ce qu'il faut en retenir est qu'elle peut être très utile pour écrire des caractères non affichables qui de toute façon, seraient difficilement accessibles au clavier. Ainsi, le caractère de tabulation {\carnonaff HT} s'écrit «\verbidx[ (tabulation)]{^^I}», le retour charriot {\carnonaff CR} «\verbidx[ (retour charriot)]{^^M}», etc. +\grandsaut + +On peut aussi \emph{afficher} un caractère avec la primitive \idx\char suivie du numéro du code de caractère. Ainsi, \verb|\char97| affiche un «\char97» mais au contraire de la méthode avec \verb|^^|, \verb|\char97| ne peut pas être utilisé dans le code source à la place d'une lettre. Autant «\verb|\p^^61r|» et «\verb|\par|» sont équivalents, autant il n'est pas question d'écrire «\verb|\p\char65r|» à la place de \verb|\par| dans le code source, car ceci serait interprété comme la commande \verb|\p|, suivie de \verb|\char65|, suivi de «r». +\grandsaut + +Enfin, il faut définir ce qu'est une «ligne de code source». Pour \TeX{}, une ligne de code est constituée par les caractères qui se trouvent avant la prochaine « marque de \idx{fin de ligne} »\label{marque.fin.ligne}. Cette marque est une convention qui dépend du système d'exploitation : + +\begin{itemize} + \item pour Windows, une marque de fin de ligne est constituée des octets \Hex{0D}\Hex{0A}, c'est-à-dire de {\carnonaff CR} suivi de {\carnonaff LF} (noté «CRLF»); + \item pour GNU-linux, cette marque est l'octet \Hex{0A} (LF); + \item enfin, pour MAC OS X, cette marque est l'octet \Hex{0D} (CR). +\end{itemize} + +L'exécutable \texttt{pdftex} considère comme marque de fin de ligne les séquences suivantes : LF, CR, CRLF et LFCR. Les \idx[!fin de ligne]{espace}s qui précèdent la marque de \idx{fin de ligne} sont supprimés en même temps que cette marque et \TeX{} insère à la place un caractère spécial dont le code de caractère est contenu dans la primitive \idx\endlinechar. Habituellement, ce code vaut \number`\^^M{}, celui du caractère retour charriot\verbidx*[ (retour charriot)]{^^M}. + +\label{catcode.5} +\begin{regle} +Dans la ligne courante, si un caractère de catcode \number\catcode`\%\relax{} (caractère de commentaire, généralement \cidx[ (caractère de commentaire)]\%) ou \number\catcode`\^^M\relax{} (retour charriot, généralement \verb|^^M|\verbidx*[ (retour charriot)]{^^M}) apparait, alors, ce caractère ainsi que tout ce qui va jusqu'à la fin de la ligne est ignoré, y compris le caractère de code \idx\endlinechar inséré à la fin de la ligne. +\end{regle} + +Les caractères de catcode 5 obéissent à une règle particulière : + +\label{regle.catcode5}\begin{regle} +Un caractère de catcode 5 est interprété comme un \idx[!catcode 5]{espace}.\idx*{catcode!5 (retour charriot)}\idx*{espace!catcode 5} + +Deux caractères de catcode 5 consécutifs sont transformés en la séquence de contrôle \idx\par\verbidx*[ consécutifs]{^^M}. +\medbreak + +Cela signifie qu'un retour charriot\verbidx*[ (retour charriot)]{^^M} est vu comme un \idx[!fin de ligne]{espace}, sauf s'il est précédé du caractère \cidx\% et que deux retours charriots consécutifs (symbolisés dans le code source par une ligne vide) seront équivalents à \idx\par. +\end{regle} + +On peut localement modifier le caractère de code \idx\endlinechar pour obtenir des effets spéciaux en tenant compte de la règle suivante : + +\begin{regle} +Si \idx\endlinechar est négatif ou supérieur à 255, aucun caractère n'est inséré aux fins de lignes. +\end{regle} + +Voici une illustration de cette règle où l'on teste le comportement normal, puis on met \idx\endlinechar à $-1$ et enfin à \number`\X{} (qui est le code de caractère de «X») : + +{\endlinechar13 +\showcode|%%% comportement normal %%% +a) comportement normal : +Ligne 1 +Ligne 2 + +Ligne 3 +\medbreak¤\idx*\medbreak¤ +b) Aucun caractère de fin de ligne :¤\idx*{caractère de fin de ligne}¤ +%%% aucune insertion en fin de ligne %%% +\endlinechar=-1 ¤\idx*\endlinechar¤ +Ligne 1 +Ligne 2 + +Ligne 3\endlinechar13 + +\medbreak¤\idx*\medbreak¤ +c) "X" comme caractère de fin de ligne :¤\idx*{caractère de fin de ligne}¤ +%%% "X" est inséré à chaque fin de ligne +\endlinechar=88 +Ligne 1 +Ligne 2 + +Ligne 3|} + +On peut notamment remarquer que lorsque \idx\endlinechar est négatif, tout se passe comme si les lignes se terminaient toutes par le \idx[|voir{«\texttt{\char37}»}]{caractère de commentaire} \cidx\%, de catcode \number\catcode`\%. + +\chapter{Octets dont le bit de poids fort est 1} +Venons-en maintenant à la partie de la table des caractères qui se situe après le numéro 128, c'est-à-dire dont la représentation en base 2 est \bin{1xxxxxxx} et dont le bit le le plus à gauche, celui qui a le plus de «poids\footnote{Dans l'écriture moderne des nombres, dite «de position», les chiffres ont une influence liée à leur position dans le nombre. En écriture décimale, nous avons pris comme convention qu'un nombre, par exemple écrit avec 3 chiffres «$\overline{\text{\textit{abc}}}$», est égal à la somme $a\times100+b\times10+c\times1$. Le coefficient d'un chiffre (ou son «poids») est une puissance de 10 dont l'exposant est d'autant plus grand que la place du chiffre est à gauche : $10^2$, $10^1$ et $10^0$. On dit que le chiffre $a$ a un «poids» fort puisque plus important que celui des chiffres $b$ ou $c$. + +En binaire, c'est le même principe sauf que les poids sont les puissances de 2. Si un \idx{octet} s'écrit $\overline{\text{\textit{abcdefgh}}}$, sa valeur en base 10 est $a\times2^7+b\times2^6+\cdots+g\times2^1+h\times2^0$. Le bit \bin{a} est le bit de poids fort tandis que \bin{h} est celui de poids faible.}», vaut 1. Cette partie de la table n'est pas dans la convention ASCII et donc, dépend de la convention que l'on veut se donner. Pour nous en France, deux de ces conventions sont utilisées et dépendent du codage avec lequel a été enregistré le code source. Comme on l'a dit, cet \idx{encodage} doit être un surensemble de l'ASCII qui est commun à tous les \idx{encodage}s. + +Historiquement, plusieurs façons de compléter la table ASCII ont vu le jour, chacune donnant naissance à un \idx{encodage} à part entière. Aucune harmonisation n'a jamais été faite et plusieurs \idx{encodage}s sur 8 bits cohabitent encore à l'heure actuelle. Les plus connus pour les langues occidentales sont : + +\begin{itemize} + \item \texttt{ansinew} a émergé comme \idx{encodage} des systèmes d'exploitation Windows; + \item \texttt{applemac} a été choisi dans la sphère de la marque Apple jusqu'à un passé récent; + \item \texttt{latin1}\idx*[|(]{encodage!latin1} a été utilisé dans le monde Unix/Linux. +\end{itemize} + +Selon ces trois encodages, le caractère «é» est respectivement placé à l'octet \Hex{E9}, \Hex{8E} et \Hex{E9}. +\grandsaut + +Intéressons-nous à l'\idx{encodage} «\latin» ou «\hbox{ISO 8859-1}»\idx*{encodage!latin1}. Cet \idx{encodage} a été créé pour être utilisé avec les langues européennes. Par exemple, l'octet \number"E0{} (ou \Hex{E0} en hexadécimal) code la lettre «à». On dispose ainsi de tous les caractères nécessaires pour la langue française\footnote{À l'exception notable de «\oe », «\OE » et «\"Y». Il manque également quelques caractères pour d'autres langues.}. Il faut noter que l'extension pour \LaTeX{} «\verb-inputenc-»\idx*{package!inputenc} chargée avec l'option «\verb-latin1-» rend plusieurs caractères actifs (et donc met leur catcode à 13) lorsque l'\idx{encodage} est \latin\idx*{encodage!latin1}. Le caractère «à» en fait partie et donc, ce caractère se comporte comme une commande : \verb-inputenc- lui fait exécuter un code qui au final, imprime le caractère «à». Il est immédiat que du fait qu'il est actif, le caractère «à» n'appartient pas à la catégorie des lettres. + +Voici la table de l'\idx{encodage} \latin. Seuls les \idx{octet}s supérieurs ou égaux à 128 y figurent puisque ceux qui sont inférieurs à 128 sont ceux de la table ASCII : + +\begin{centrage} + \catcode`\µ=12 + \tabcolsep0pt + \footnotesize + \par\noindent + \def\?#1{\ttfamily\bfseries x#1}% + \charcnt=\numexpr128-17 + \def\:{\global\advance\charcnt1 }% + \def\!{\cellcolor{gray!40}}% + \let\^\carnonaff% marque un caractère non affichable + \newcolumntype G{m{\dimexpr(\linewidth-18\arrayrulewidth)/17\relax}}% + \begin{tabular}{|@{}>{\columncolor{gray!40}[0pt][0pt]\hfill\ttfamily\bfseries}G<{x\hfill\null}@{}|*{16}{@{}>{\:\hfill\vrule height3.3ex depth.8ex width0pt }G<{\hfill\unless\ifnum\charcnt<128 \vtop{\kern1.5pt\normalfont\tiny\llap{\number\charcnt\kern.5pt}\kern1pt}\fi\null}@{}|}} + \hline + \rowcolor{gray!40}[0pt][0pt]\multicolumn1{|@{}G@{}|}{\cellcolor{white}} + &\?0 &\?1 &\?2 &\?3 &\?4 &\?5 &\?6 &\?7 &\?8 &\?9 & \?A& \?B&\?C &\?D &\?E &\?F \\\hline + 8 & \^PAD& \^HOP&\^BPH &\^NBH &\^IND &\^NEL &\^SSA &\^ESA &\^HTS &\^HTJ &\^VTS &\^PLD &\^PLU &\^RI &\^SS2 &\^SS3 \\\hline + 9 & \^DCS& \^PU1& \^PU2& \^STS& \^CCH& \^MW &\^SPA& \^EPA& \^SOS& \^SGCI& \^SCI&\^CSI &\^ST &\^OSC &\^PM & \^APC\\\hline + A &\scalebox{0.8}[1]{NBSP}&^^a1&^^a2&^^a3&^^a4&^^a5&^^a6&\oldparagraph&^^a8&^^a9&^^aa&^^ab&\!$^^ac$&-&^^ae&^^af \\\hline + B &^^b0&\!$^^b1$&\!$^^b2$&\!$^^b3$&^^b4&\!$^^b5$&$^^b6$&^^b7&^^b8&\!$^^b9$&^^ba&^^bb&^^bc&^^bd&^^be&^^bf\\\hline + C &^^c0&^^c1&^^c2&^^c3&^^c4&^^c5&^^c6&^^c7&^^c8&^^c9&^^ca&^^cb&^^cc&^^cd&^^ce&^^cf\\\hline + D &^^d0&^^d1&^^d2&^^d3&^^d4&^^d5&^^d6&\!$^^d7$&^^d8&^^d9&^^da&^^db&^^dc&^^dd&^^de&^^df\\\hline + E &^^e0&^^e1&^^e2&^^e3&^^e4&^^e5&^^e6&^^e7&^^e8&^^e9&^^ea&^^eb&^^ec&^^ed&^^ee&^^ef\\\hline + F &^^f0&^^f1&^^f2&^^f3&^^f4&^^f5&^^f6&\!$^^f7$&^^f8&^^f9&^^fa&^^fb&^^fc&^^fd&^^fe&^^ff\\\hline + \end{tabular} +\end{centrage} +\grandsaut + +Ici encore, cette partie de la table comporte des caractères non affichables. Ils se situent entre les codes \Hex{80} et \Hex{9F} inclus auxquels il faut rajouter le caractère de code \Hex{AD}. Ceux qui se trouvent dans des cases grisées sont affichables, mais ils doivent être utilisés en mode mathématique\idx*{mode!mathématique}. +\grandsaut + +Bien sûr, certains de ces caractères ne sont pas directement accessibles au clavier. En fait, les caractères accessibles au clavier dépendent de la configuration de chacun. Par exemple, sur \emph{mon} clavier, avec \emph{mon} système d'exploitation et le pilote correspondant au modèle du clavier, avec le mappage du clavier sélectionné dans les options du système d'exploitation, je peux accéder au caractère «\textttæ» avec la combinaison de touches \bioLegacyKeyGlyph{A_l_t_G_r}+\bioLegacyKeyGlyph{A}. Pour le caractère «$¶$», c'est avec \bioLegacyKeyGlyph{A_l_t_G_r}+\bioLegacyKeyGlyph{R}. Et ainsi de suite\ldots{} En revanche, certains caractères dans la table ci-dessus me sont inaccessibles, sauf à les taper via \verb-^^-. +\grandsaut + +L'\idx{encodage} «\utf\idx*[|(]{encodage!UTF8}\footnote{L'acronyme \texttt{UTF8} signifie «Unicode Transformation Format 8 bits».}» permet de coder beaucoup plus que 256 caractères avec l'inévitable conséquence que certains d'entre eux seront codés sur plusieurs octets. Le principe de cet \idx{encodage} est assez simple et on peut le décrire ici : +\begin{itemize} + \item si un caractère a un code inférieur à 128, alors il est codé par le même \idx{octet} que dans le \idx{encodage} ASCII; + \item sinon, il est codé sur plusieurs octets : le nombre de bits de poids fort non nuls (au maximum 4) du premier octet indique le nombre d'octets pris au total pour l'\idx{encodage} du caractère. Par exemple, si un \idx{octet} vaut \bin{1110xxxx} en binaire, les 3 bits de poids fort suivis d'un 0 indiquent que ce caractère est codé sur 3 octets, les 2 octets restants ayant obligatoirement les bits de poids fort \bin{10}, c'est-à-dire qu'ils sont de la forme \bin{10xxxxxx}. + + Finalement, ce caractère sera donc codé sur 3 octets ayant cette forme : \bin{1110xxxx 10xxxxxx 10xxxxxx}. Il y a donc 16 bits non contraints sur 24. Le «code propre» du caractère sera constitué des 16 bits \bin x. +\end{itemize} + +Si l'on s'intéresse par exemple au caractère «à», celui-ci est codé en \utf par les 2 octets \Hex{C3} \Hex{A0}. Écrits en binaire, on obtient \bin{\textbf{110}00011} \bin{\textbf{10}100000} où les bits contraints sont écrits en gras. Il en résulte que le code propre du caractère «à» est \bin{00011100000}. Si on élimine les zéros inutiles de gauche, on obtient \bin{11100000} qui est l'\idx{octet} \Hex{E0}, celui que l'on retrouve dans la table de l'\idx{encodage} \latin pour la lettre «à». En effet, en \utf, les caractères ayant des codes propres compris entre 128 et 255 \emph{doivent} être les mêmes que les caractères qui ont ces codes dans la table de \latin. Enfin, le symbole «\geneuro» est codé sur 3 \idx{octet}s en UFT8 : \Hex{E2} \Hex{82} \Hex{AC} qui s'écrit en binaire \bin{\textbf{1110}0010} \bin{\textbf{10}000010} \bin{\textbf{10}101100} et qui donne un code propre de \bin{10000010101100} soit \Hex{20AC}. + +Pour \TeX{}, le principe est le même qu'avec l'\idx{encodage} \latin, il faut rendre actifs tous les \idx{octet}s qui ont une écriture binaire \bin{110xxxxx} ou \bin{1110xxxx} ou \bin{11110xxx} et dont les bits non contraints «\bin x» correspondent au début du code des caractères que \TeX{} va prendre en charge. Ensuite, ces caractères actifs vont lire le (ou les) \idx{octet}(s) suivant(s) et traduire le tout de façon à faire imprimer le caractère voulu. +\grandsaut + +Ce qu'il faut retenir est que, aussi bien en \latin qu'en \utf, les lettres accentuées et les caractères ne figurant pas dans la table ASCII ne sont pas des octets dont le code de catégorie est 11 et n'appartiennent donc pas à la catégorie des lettres. De plus, pour ceux qui utilisent l'\idx{encodage} \utf qui tend à devenir la norme, il ne faut pas perdre de vue que lorsqu'on écrit la lettre «à» dans le code, nous ne voyons qu'un seul caractère, mais celui-ci est stocké sur \emph{deux} octets dans le fichier et donc, \TeX{} voit \emph{deux} choses. Il n'y a donc pas équivalence entre «octet» et «caractère» et dans tous les cas, \TeX{} ne lit pas le code caractère par caractère, mais octet par octet. + +Cette dernière affirmation n'est vraie que pour les trois moteurs \TeX{} (celui écrit par \idx*{Knuth Donald}Donald~\textsc{Knuth}), $\varepsilon$\TeX{}\idx*{moteur!etex} et pdf\TeX{}\idx*{moteur!pdftex}. Outre ces « ancêtres », encore majoritairement utilisés malgré leur âge, il existe désormais des moteurs plus récents qui gèrent nativement l'\idx{encodage} \utf : \XeTeX{} et lua\TeX{}. Ces deux moteurs, entre autres possibilités, lisent le code source caractère \utf par caractère \utf et il peut donc arriver que plusieurs octets soient lus en un coup pour former une entité unique. Malgré le progrès que cela constitue, on considèrera dans ce livre que l'on utilise un moteur fonctionnant octet par \idx{octet}.\idx*[|)]{encodage!UTF8}\idx*[|)]{encodage!latin1} + +\chapter{Codes de catégorie}\idx*[|(]{catcode}\idx*[|(]{\catcode} +Le sujet est vaste et technique puisqu'il touche à l'un des mécanismes les plus intimes de \TeX. Nous nous contenterons pour l'instant de voir l'essentiel, quitte à y revenir plus loin au fur et à mesure de nos besoins. Rappelons que les «catégories» de \TeX{} sont au nombre de 16 et regroupent des caractères ayant des propriétés communes aux yeux de \TeX{}. Le code de catégorie d'un octet lu est la première chose que \TeX{} examine, avant même le nombre correspondant à l'octet lui-même puisque le code de catégorie détermine la façon dont \TeX{} doit réagir face à cet \idx{octet}. +\grandsaut + +Tout d'abord, pour accéder au code de catégorie d'un caractère, il faut utiliser la primitive \idx\catcode suivie du \emph{\idx{code de caractère}}. L'ensemble constitue une représentation interne de l'entier étant le code de catégorie du caractère concerné. La commande \idx\number\label{number} convertit la représentation interne d'un nombre en la représentation affichable habituelle, c'est-à-dire en base 10 et en chiffres arabes. Par exemple, si on veut afficher le code de catégorie de «\cidx\$» dont le code de caractère est \number`\$, on va écrire «\verb-\number\catcode36-» et on obtiendra «\number\catcode36 ». + +On peut trouver gênant de mettre explicitement le code du caractère après la primitive \idx\catcode. En effet, cela oblige à aller le chercher dans la table des caractères ASCII puis le convertir en décimal. Cette dernière étape, la conversion hexadécimal vers décimal, n'a pas lieu d'être faite car \TeX{} comprend nativement les nombres hexadécimaux\footnote{Où les lettres qui le composent \emph{doivent} être majuscules.}. Pour indiquer qu'un nombre est écrit en base 16, il faut le faire précéder du caractère «\cidx[ (notation hexadécimale)]\"» et pour traduire le tout en nombre en écriture arabe en base 10, il faut mettre devant l'ensemble la primitive \idx\number{}: + +\showcode/a) \number"1A \qquad b) \number"AB3FE \qquad c) \number"78 ¤\idx*\number\idx*\qquad¤/ + +Pour obtenir le code de catégorie du caractère «\cidx\$» dont le code de caractère est \Hex{24}, on peut donc écrire \verb-\number\catcode"24-. + +Dans le même ordre d'idée et bien que cela soit assez rarement utile, il faut savoir que \TeX{} comprend aussi les nombres en notation \emph{octale}, c'est-à-dire écrits en base 8. Pour indiquer qu'un nombre est écrit en base 8, il faut le faire précéder de l'apostrophe «\cidx[ (notation octale)]\'» : + +\showcode/a) \number'15 \qquad b) \number'674 \qquad c) \number'46¤\idx*\number\idx*\qquad¤/ + +Malgré ce côté pratique, il faut toujours aller voir la table ASCII pour chercher le code de caractère. Heureusement, on peut facilement convertir un caractère en son code écrit en base 10. Pour cela, s'agissant des caractères de catcode 11\idx*{catcode!11 (lettre)} ou 12\idx*{catcode!12 (autre)}, il faut faire précéder le caractère de l'apostrophe inverse \boxtoken{`}\cidx*[ (notation de nombre)]{\`} ce qui donnerait «\verb-\number`a-» pour afficher le code de caractère de «a». Un moyen encore plus sûr qui fonctionne pour \emph{tous} les caractères, quels que soient leurs catcodes, est de faire précéder le caractère de l'apostrophe inverse \emph{et} du caractère d'échappement comme dans cet exemple: + +\showcode/a) \number`\a \quad %code de caractère de "a"¤\idx*\number\idx*\quad¤ +b) \number`\\ \quad % code de caractère de "\" +c) \number`\$ \quad % code de caractère de "$" +d) \number`\ \quad % code de caractère de l'espace +e) \number`\5 \quad % code de caractère de "5" +f) \number`\{ \quad % code de caractère de l'accolade ouvrante +g) \number`\} % code de caractère de accolade fermante/ + +Il devient facile d'afficher le code de catégorie de n'importe quel caractère : + +\showcode/a) \number\catcode`\a \quad %code de catégorie de "a"¤\idx*\catcode¤ +b) \number\catcode`\\ \quad % code de catégorie de "\"¤\idx*\number¤ +c) \number\catcode`\$ \quad % code de catégorie de "$" +d) \number\catcode`\ \quad % code de catégorie de l'espace +e) \number\catcode`\5 \quad % code de catégorie de "5" +f) \number\catcode`\{ \quad % code de catégorie de l'accolade ouvrante¤\idx*\quad¤ +g) \number\catcode`\} % code de catégorie de accolade fermante/ + +Afficher un code de catégorie n'est pas réellement utile. Ce qui l'est plus est de \emph{modifier} le code de catégorie d'un caractère, nous verrons dans quels buts plus loin. Pour ce faire, on écrit\idx*{\catcode} : +\centrecode-\catcode`\<caractère>=<nombre>- + +\noindent où \verb-<nombre>- est un entier de 0 à 15. Après que \TeX{} a exécuté ce code, à chaque fois que \TeX{} lira le \verb-<caractère>-, il lui assignera le code de catégorie égal au \verb-<nombre>-. Supposons par exemple que nous souhaitions que le caractère «\verb|W|» ne soit plus dans la catégorie des lettres, mais revête le code de catégorie de 3 pour utiliser indifféremment «\verb|W|» ou «\cidx\$» pour basculer en mode mathématique\idx*{mode!mathématique}. Voici comment procéder : + +\showcode/Ici, W est une lettre...\par +\catcode`\W=3 % W devient le signe de bascule en mode math¤\idx*\catcode\idx*{mode!mathématique}¤ +Wx+y=3W\par +$a+b=cW\par +W2^3=8$\par +\catcode`\W=11 % W redevient une lettre¤\idx*\catcode¤ +De nouveau, W est une lettre.../ + +Il est important de restaurer le code de catégorie initial à la fin de la zone où l'on souhaite avoir le comportement voulu sous peine de s'exposer à des erreurs de compilation ou des effets indésirables plus tard. + +Symétriquement, on aurait aussi bien pu assigner le code de catégorie 11 (ou 12) au caractère «\verb-$-» de façon à pouvoir l'afficher comme on le fait d'une lettre ou d'un caractère affichable : +\showcode/$3+4=7$ \par % $ est la bascule math +\catcode`\$=12 % $ devient un caractère affichable¤\idx*\catcode¤ +$ s'affiche sans problème : $\par +\catcode`\$=3 % $ redevient la bascule math¤\idx*\catcode¤ +$4+3=7$/ + +\begin{exercice} +Inventer un procédé pour que, dans une zone, seuls les premiers mots de chaque ligne soient affichés. +\solution +Il suffit de modifier le code de catégorie de l'\idx[!catcode 5]{espace} pour qu'il devienne 5. D'après la règle vue à la page~\pageref{catcode.5}, dans une ligne, tout ce qui se trouve après un caractère de catcode 5\idx*{catcode!5 (retour charriot)} est ignoré : + +\showcode|Voici les premiers mots de chaque ligne : + +\catcode`\ =5 % l'espace est déormais de catcode 5¤\idx*\catcode¤ +Cette première ligne sera tronquée... + +La deuxième aussi ! + +Et la dernière également. +\catcode`\ =10 % l'espace reprend son catcode¤\idx*\catcode¤ + +Le comportement normal est restauré.| +\end{exercice} + +Il faut retenir que les codes de catégorie attachés à chaque caractère de la table des catcodes de la page~\pageref{catcode.table} ne sont que des valeurs \emph{par défaut} qui peuvent être facilement modifiées comme on l'a vu dans les exemples précédents. On peut donc parler de «régime de catcode», c'est-à-dire qu'à chaque moment, chacun des 256 octets aura un catcode qui lui sera attribué. En règle générale, si l'on modifie le catcode d'un octet, il est sage de restaurer les codes de catégorie de la table des catcodes au-delà de la zone où l'on veut faire des modifications. En effet, il est admis que les catcodes «naturels» de cette table doivent rester la règle.\idx*[|)]{catcode}\idx*[|)]{\catcode} +%| | +%| Fin partie 1 | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Partie 2 | +%| | +\defpartcomment{\lettrine[lines=3,nindent=0pt]{\libertineInitialGlyph{D}}{ans} cette partie, nous allons nous intéresser à ce qui fait l'essentiel du code lorsqu'on programme, les commandes. Contrairement à d'autres langages de programmation où leur comportement est assez intuitif, elles revêtent avec \TeX{} des propriétés originales. Par leur intermédiaire, nous découvrirons l'essentiel de ce qu'il faut savoir pour commencer à programmer c'est-à-dire la façon intrinsèque qu'a \TeX{} de fonctionner et comment contrôler ce fonctionnement. Après ces prérequis, nous pourrons alors aborder la partie suivante où nous commencerons les premiers exercices de programmation.} +\part{Commandes} + +\chapter[\Qu 'est-ce qu'une commande ?]{Qu'est-ce qu'une commande ?} +Lorsqu'on utilise \TeX{}, les séquences de contrôle\idx*{séquence de contrôle} sont des mots constitués de lettres (caractères de catcode 11) qui débutent par le \idx{caractère d'échappement} «\cidx[ (caractère d'échappement)]\\». Ce caractère spécial peut être changé pour en utiliser un autre, mais cette man\-\oe uvre ne sera pas décrite pour l'instant. Ainsi, lorsque \TeX{} lit le code que l'on a tapé, toujours linéairement de gauche à droite, forme une \idx{séquence de contrôle} à l'aide des lettres, majuscules ou minuscules, qui suivent le caractère d'échappement «\cidx\\», la casse étant prise en compte. \TeX{} considère que le nom de la \idx{séquence de contrôle} se termine au dernier caractère qui est une lettre . Une fois que ce nom est construit, il devient une entité unitaire indivisible, une «unité lexicale» ou «\emph{\idx{token}}» au même titre qu'un \idx{octet} dans le code si celui-ci ne sert pas à écrire une \idx{séquence de contrôle}. + +\begin{regle} +Une \idx{séquence de contrôle} commence par le caractère d'échappement de catcode 0\idx*{catcode!0 (caractère d'échappement)} qui est habituellement «\cidx\\» et se termine à la fin de la plus longue série de lettres (caractères de catcode 11\idx*{catcode!11 (lettre)}) qui le suit. +\end{regle} + +Les séquences de contrôle sont de deux types : +\begin{itemize} + \item les «commandes», que l'on appellera aussi «macros», ont été définies soit par l'utilisateur, par un format ou par une extension. Ces commandes ont un \idx{texte de remplacement}, c'est le code \TeX{} qui les définit, ou si l'on préfère, le code \TeX{} qu'elles «contiennent». Ce texte de remplacement peut évidemment contenir d'autres séquences de contrôle ; + \item les \idx{primitive}s, qui sont codées en dur dans le programme binaire \texttt{tex}, et qui représentent en fait le «vocabulaire» de base avec lequel on doit se débrouiller pour faire tout le reste. Les primitives n'ont pas de \idx{texte de remplacement} et sont destinées à être exécutées de façon binaire par le programme \texttt{tex}. +\end{itemize} + +\label{regle.espace}\begin{regle} +Voici des règles concernant les espaces et les séquences de contrôle : + \begin{enumerate} + \item dans le code qui est tapé, tout \idx{espace} qui suit une unité lexicale de type \idx{séquence de contrôle} est ignoré; + \item si plusieurs espaces \idx*{espace!consécutifs}se suivent \emph{dans le code}, seul le premier est pris en compte et les autres sont ignorés (ceci est un rappel de la règle page~\pageref{espaces.consecutifs}); + \item il découle des deux premiers points que, quel que soit le nombre d'espaces qui suivent une \idx{séquence de contrôle}, ceux-ci sont ignorés. + \end{enumerate} +\end{regle} + +Lorsqu'un utilisateur définit une commande, il lui donne un \emph{\idx{texte de remplacement}} qui sera utilisé à la place de cette commande lorsqu'elle sera « exécutée » par \TeX{}. La primitive qui permet de définir ce texte de remplacement est «\idx[|(]\def». Elle doit être suivie du nom de la commande et du texte de remplacement entre \idx{accolade}s : + +\centrecode-\def\foo{Bonjour}- + +Après cette définition, à chaque fois que \TeX{} «exécutera» le token \verb|\foo| dans le code source, il le remplacera dans sa mémoire par son texte de remplacement qui est «Bonjour» et continuera à lire le code obtenu en tenant compte de ce remplacement. Cet endroit dans sa mémoire s'appelle «\idx{pile d'entrée}». Pour écrire un espace après \verb-\foo- qui ne soit pas ignoré, il faut trouver un moyen, invisible à l'affichage, pour que l'espace affiché après la macro ne lui soit pas consécutif. On peut envelopper la macro dans des accolades ou la faire suivre de \verb-{}-\cidx*\{\cidx*\} de façon à ce que l'espace se situant \emph{après} l'accolade fermante ne soit pas ignoré comme il le serait après une \idx{séquence de contrôle}. On peut également mettre après \verb|\foo| la macro \idx\space dont le \idx{texte de remplacement} est un espace. Dans ce cas, le dernier point de la règle précédente ne s'applique pas, car ce qui suit \verb|\foo| n'est pas un espace mais une \idx{séquence de contrôle}, et peu importe son texte de remplacement. + +\showcode/\def\foo{Bonjour}% définit le texte de remplacement de \foo +a) \foo Alice.\qquad% espace ignoré¤\idx*\qquad¤ +b) {\foo} Bob.\qquad% espace non ignoré +c) \foo{} Chris.\qquad% espace non ignoré +d) \foo\space Daniel.% \space est remplacé par un espace¤\idx*\space¤/ + +\begin{regle} +Lorsque \TeX{} assigne un \idx{texte de remplacement} à une macro avec \idx\def, ce texte de remplacement n'est pas exécuté, il est juste converti en \idx{token}s et rangé quelque part dans la mémoire de \TeX{} pour être ressorti plus tard pour venir en replacement de la macro, et éventuellement être exécuté à ce moment. + +Le texte de remplacement est très peu \emph{analysé} lorsqu'il est stocké avec \verb|\def|, ce qui veut dire que si une erreur est contenue dans le texte de remplacement, elle ne sera pas détectée au moment où \idx\def agit mais plus tard lors du remplacement et de l'exécution proprement dite. + +Une des rares choses que \TeX{} vérifie dans le \idx{texte de remplacement} d'une macro est qu'un caractère de \idx*[!15 (invalide)]{\catcode}catcode 15\idx*{catcode!15 (invalide)} n'y figure pas (nous verrons plus loin les autres vérifications que \TeX{} effectue). +\end{regle} + +\section{Accolades et groupes} +Les \cidx*\{\cidx*\{\idx{accolade}s des lignes 3 et 4 délimitent des «\idx[|(]{groupe}s\footnote{On dit aussi parfois «\idx[!simple]{groupe} simple».}». Un groupe est une zone où les modifications effectuées et où les définitions faites restent \emph{locales} à ce groupe et sont détruites lors de sa fermeture pour revenir à l'état antérieur à l'ouverture du groupe. En revanche, les \idx{accolade}s obligatoires utilisées avec la primitive \idx\def de la ligne 1 ne délimitent pas un groupe. Elles délimitent la portée du \idx{texte de remplacement}. On peut donc considérer qu'il y a deux types d'\idx{accolade}s. D'une part celles qui sont obligatoires et font partie de la syntaxe d'une primitive comme celles de la primitive \idx\def qui délimitent le \idx{texte de remplacement} et d'autre part celles, non obligatoires, qui servent de frontières à un groupe. + +Un \idx[!semi-simple]{groupe} semi-simple\footnote{Le nom « groupe simple » étant déjà pris pour les groupes entre accolades, il y a fort à parier que D.~\textsc{Knuth}, par jeu et par affinité pour les mathématiques, ait baptisé « groupe semi-simple» un groupe délimité par \texttt{\textbackslash begingroup} et \texttt{\textbackslash endgroup}.} est compris entre les instants où \TeX{} exécute les primitives \idx\begingroup et \idx\endgroup. Comme dans les groupes, les modifications et les définitions dans un groupe semi-simple sont locales et sont restaurées à leur état antérieur à sa fermeture. + +\begin{regle} +On peut créer des zones où les modifications faites aux macros et autres paramètres de \TeX{} sont locales. Ces zones portent le nom de \idx{groupe}s. + +Un \idx{groupe} est délimité : +\begin{itemize} + \item soit par une \idx{accolade} ouvrante et une accolade fermante auquel cas le groupe est qualifié de « simple » ; + \item soit par les primitives \idx\begingroup et \idx\endgroup et dans ce cas, le groupe est dit « semi-simple». +\end{itemize} +Il est entendu qu'un groupe ouvert avec une accolade ne peut être fermé qu'avec une accolade et il en est de même avec \idx\begingroup et \idx\endgroup. + +À l'intérieur d'un groupe, les assignations sont locales à ce groupe et sont restaurées à leur état antérieur lors de la fermeture du groupe. Pour qu'une assignation soit \emph{globale}, c'est-à-dire pour qu'elle survive à la fermeture du groupe, il faut faire précéder la commande d'assignation de la primitive \idx\global. + +Les groupes délimités par accolades et les groupes semi-simples peuvent être emboités, mais \emph{ne doivent pas} se chevaucher. +\end{regle} + +\begin{exercice} +Puisqu'un groupe délimité par accolades et un groupe semi-simple ont les mêmes fonctionnalités, pourquoi existe-t-il deux façons différentes de délimiter un groupe ? +\solution +Tout d'abord, abondance de biens ne nuit pas ! + +Ensuite, il y a des cas où une \idx{accolade} ne peut pas délimiter un début ou une fin groupe, c'est dans le texte de remplacement d'une macro. Par exemple, si l'on voulait qu'une macro \verb|\startbold| ouvre un groupe et écrive en gras et que la macro \verb|\stopbold| stoppe ce fonctionnement, on serait obligé d'utiliser \idx\begingroup et \idx\endgroup : + +\showcode|\def\startbold{\begingroup \bf} +\def\stopbold{\endgroup} +Voici \startbold du texte en gras\stopbold{} et la suite.| + +En effet, cela constituerait une erreur que d'écrire : + +\centrecode|\def\startbold{{\bf} +\def\stopbold{}}| + +\noindent car le texte de remplacement de \verb|\startbold| irait jusqu'à la prochaine accolade équilibrée et serait + +\centrecode|{\bf} \def\stopbold{}| + +\noindent Il n'y aurait pas d'erreur de compilation, mais la portée de \verb|\bf| serait nulle puisque cette macro est seule dans son groupe et \verb|\stopbold|, ayant un texte de remplacement vide, n'aurait aucune action. +\end{exercice} + +\begin{exercice} +Décrire ce qui se passe exactement et quel va être l'affichage si l'on définit une macro \verb|\foo| dans ce contexte : + +\centrecode-\begingroup +\def\foo{Hello \endgroup world !} +\foo- + +\solution +Déjà, la définition sera \emph{locale} au groupe semi-simple initié par \idx\begingroup et la macro \verb|\foo| sera détruite dès qu'un \idx\endgroup sera exécuté pour revenir à ce qu'elle était avant le groupe semi-simple (éventuellement indéfinie). Le \idx\endgroup de la ligne 2 n'est pas «exécuté» par \TeX{} puisque ce \idx\endgroup est stocké dans le \idx{texte de remplacement} de la macro \verb|\foo|. Il ne ferme donc pas le groupe semi-simple lorsque \TeX {} exécute la ligne \no2 du code. + +Lorsque \TeX{} rencontre \verb|\foo| à la 3\ieme{} ligne du code, il se trouve toujours dans le groupe semi-simple ouvert par \idx\begingroup et la définition faite la ligne au-dessus est toujours en vigueur. \TeX{} remplace donc \verb|\foo| par son texte de remplacement, ce remplacement se faisant dans la \idx{pile d'entrée} de \TeX{} : + +\centrecode-Hello \endgroup world !- + +\noindent \TeX{} va continuer à lire ce qu'il y a dans sa pile et va donc afficher \verb*|Hello | puis, en rencontrant \idx\endgroup, détruira la définition locale de \verb|\foo| enfin continuera à lire ce qui est dans sa pile et affichera : \verb|world !|. + +On voit donc que dans ce cas, la macro \verb|\foo| porte dans son \idx{texte de remplacement} la primitive \idx\endgroup qui provoquera son autodestruction !\idx*[|)]{groupe}\idx*[|)]\def +\end{exercice} + +\begin{regle} +La primitive \idx\aftergroup\verb|<token>| permet de stocker un \verb|<token>| dans une pile spéciale de la mémoire de \TeX pour que ce \idx{token} soit lu et exécuté juste après être sorti du groupe courant, que ce groupe soit simple ou semi-simple. + +Si plusieurs commandes \idx\aftergroup sont rencontrées dans un même groupe, les \idx{token}s mis en mémoire seront lus dans l'ordre où ils ont été définis. Ainsi, écrire + +\centrecode-\aftergroup<x>\aftergroup<y>-\idx*\aftergroup + +se traduira par «\verb|<x><y>|» après la fermeture du groupe. +\end{regle} + +\showcode/\def\foo{foo} +\begingroup A\aftergroup\foo B\endgroup\par +{A\aftergroup X\aftergroup\foo B}/ + +La primitive \idx\aftergroup trouve une utilité lorsque la composition en italique est demandée dans un groupe (qui en délimite la portée). En effet, pour éviter que le dernier caractère en italique (qui est donc penché vers la droite) ne soit trop près du caractère du mot suivant (qui est vertical), il convient d'insérer à la fin du groupe une \idx{correction d'italique}, surtout si les deux caractères sont des lettres hautes comme «l», «f» ou une parenthèse fermante. Cette correction, effectuée en \TeX{} par la primitive \idx[ (correction d'italique)]\/, augmente légèrement l'espace entre ces deux caractères. La différence est très fine, mais elle existe : + +\showcode|1) (un {\it cheval})\par% sans correction d'italique¤\idx*\it¤ +2) (un {\it\aftergroup\/cheval})\par% avec correction d'italique¤\idx*[ (correction d'italique)]\/\idx*\aftergroup\idx*\it¤ +% on peut définir une macro \itacorr qui effectue automatiquement la correction +\def\itacorr{\it\aftergroup\/}¤\idx*\aftergroup¤ +3) (un {\itacorr cheval})% avec correction d'italique| + +\section{Primitive \ttfamily{\textbackslash let}}\label{let} +\idx*[|(]{\let}Pour copier le \idx[!copie]{texte de remplacement} d'une macro vers une autre, on utilise la primitive \verb-\let- qui prend comme arguments deux unités lexicales de type \idx{séquence de contrôle}. Par exemple, écrire \verb-\let\foo=\bar- copiera le texte de remplacement de la macro \verb-\bar- vers \verb-\foo- en écrasant celui de cette dernière si elle était déjà définie. Le signe «=» entre les deux séquences de contrôle est facultatif et sera souvent omis désormais\footnote{En réalité, le signe égal peut aussi être suivi d'\emph{un} espace facultatif.}. Une fois cette copie faite, peu importe ce que sera le futur de \verb-\bar-, la copie a lieu à l'instant où \verb-\let- est rencontré et si \verb-\bar- est redéfinie par la suite, cela n'affectera pas le texte de remplacement de \verb-\foo-. + +\showcode/\def\bar{Je suis bar.} +\let\foo\bar % \foo devient égal à \bar +\def\bar{ABC}% \bar est redéfinie +\foo\par% affiche "Je suis bar" +\bar% affiche "ABC"/ + +Lorsqu'on écrit \verb-\let\foo=\bar-, si \verb-\bar- est une primitive, alors \verb-\foo- devient un \emph{alias} pour cette primitive et pour \TeX{}, elles sont identiques. Il est important de noter que ni \idx\def ni \verb-\let- ne préviennent d'aucune façon l'utilisateur qu'il écrase une \idx{séquence de contrôle} et que sa définition précédente sera perdue. Et tant pis si c'est une primitive. Si par malheur on écrit \verb-\let\def\foo-, alors, à partir de ce moment, la primitive \verb-\def- sera écrasée et il ne sera donc plus possible de définir de commande. +\begin{exercice} +\Qu e fait le code suivant : \verb-\let\foo\let\foo\bar\foo- ? +\solution +En procédant comme \TeX{} et en ne lisant que ce dont on a besoin, on a d'abord \verb-\let\foo\let- et donc \verb-\foo- devient égale à la primitive \verb-\let-. + +Ensuite, on a \verb-\foo\bar\foo- mais comme \verb-\foo- est devenue un alias de \verb-\let-, tout se passe comme si l'on écrivait \verb-\let\bar\let- et donc, \verb-\bar- devient aussi égale à \verb-\let-. + +Le code rend donc \verb-\foo- et \verb-\bar- égales à \verb-\let-. +\end{exercice} + +La commande \verb-\let- peut aussi être utilisée pour définir des «caractères implicites\idx*{caractère implicite}». La syntaxe ne change pas, mais au lieu de la seconde \idx{séquence de contrôle}, on met un caractère. Si on écrit par exemple \verb-\let\foo=a-, la \idx{séquence de contrôle} \verb-\foo- devient un «\texttt a implicite». Cela signifie qu'elle produira un «\texttt a» à l'affichage et que lorsque \TeX{} teste cette \idx{séquence de contrôle} (nous verrons comment plus tard), il la voit égale à «\texttt a». Il est bien évident que \verb-\foo- ne peut pas être utilisée à la place de «\texttt a» n'importe où dans le code. Si on veut écrire \verb-\par-, on ne peut pas écrire \verb-\p\foo r- ! + +La définition d'un caractère implicite via \verb|\let| ne peut se faire que pour les caractères \emph{inoffensifs} que l'on peut rencontrer, c'est-à-dire ayant un catcode 11\idx*{catcode!11 (lettre)} ou 12\idx*{catcode!12 (autre)}, ainsi que pour ceux ayant le catcode 1\idx*{catcode!1 (accolade)}, 2, 3, 4, 6, 7, 8. Il est par exemple impossible de définir un «\verb|%|» ou un «\verb|\|» implicite avec \verb|\let|. Pour l'espace, cela requiert un plus haut niveau de \TeX nicité que nous verrons plus loin (page~\pageref{sptoken}). + +Pour les \idx[!implicite]{accolade}s ouvrantes et fermantes (dont les catcodes sont respectivement 1\idx*{catcode!1 (accolade)} et 2), il existe des tokens implicites\idx*[!implicite]{token} prédéfinis très importants qui sont \idx\bgroup et \idx\egroup. Ils sont définis par plain-\TeX{} de la façon suivante : + +\begin{centrage} +\small\verb-\let\bgroup={-\kern2cm\verb-\let\egroup=}- +\end{centrage} + +\noindent Ces \emph{\idx[!implicite]{accolade}s implicites} peuvent jouer le même rôle de délimiteur de groupe que les accolades explicites «\verb-{-»\cidx*\{ et «\verb-}-»\cidx*\{ mais dans la grande majorité des cas, elles ne sont \emph{pas} interchangeables avec les accolades explicites. Par exemple, elles ne peuvent pas être utilisées pour dans la syntaxe de \idx\def pour délimiter le texte de remplacement d'une macro. +\grandsaut + +À ce stade, on peut donc faire un point \TeX nique sur tout ce qui sert à délimiter un \idx{groupe} à savoir : +\begin{itemize} + \item la paire \idx\begingroup\linebreak[1]\verb|...|\linebreak[1]\idx\endgroup; + \item les accolades explicites «\cidx\{» et «\cidx\}» ; + \item les accolades implicites \idx\bgroup\linebreak[1]\verb|...|\linebreak[1]\idx\egroup. +\end{itemize} + +Tout d'abord, un \idx{groupe} ouvert par \idx\begingroup ne peut être refermé que par \idx\endgroup et donc, le \idx[!semi-simple]{groupe} semi-simple est totalement indépendant du groupe ouvert par des accolades. Les choses sont différentes pour les groupes ouverts et fermés par «\cidx\{» et «\cidx\}» ou par «\idx\bgroup» et «\idx\egroup». Tout d'abord, s'il n'existe aucune contrainte d'équilibrage d'accolades, «\cidx\{» et \idx\bgroup sont interchangeables pour ouvrir un groupe et «\cidx\}» et \idx\egroup le sont pour le fermer. Enfin, il y a quelques \emph{rares} primitives où les accolades explicites et implicites sont interchangeables dans leur syntaxe : + +\begin{itemize} + \item les primitives \idx\hbox, \idx\vbox et \idx\vtop attendent après elles un texte entre accolades pour le composer dans une boite (pour en savoir plus, voir la section \ref{boites} page~\pageref{boites} et suivantes). + + Ce sont les seules primitives où l'on peut indifféremment utiliser des accolades explicites ou implicites, aussi bien pour ouvrir que pour fermer. Par conséquent, «\verb|\hbox{a}|» et «\verb|\hbox\bgoup| \verb|a\egroup|» sont équivalents; + \item certaines primitives que nous verrons et utiliserons plus loin ont une propriété encore plus curieuse\ldots{} + + Ces primitives doivent être immédiatement suivies d'une accolade ouvrante. Les plus connues sont \idx\toks\verb|<nombre>|, \idx\lowercase, \idx\uppercase et pour celles de \idx\eTeX{}, \idx\detokenize et \idx\unexpanded. Elles tolèrent indifféremment «\verb|{|» ou «\idx\bgroup» pour l'accolade ouvrante. En revanche, l'accolade fermante \emph{doit} être une accolade explicite. + + Ainsi, «\idx\lowercase\verb|{Foo}|» et «\idx\lowercase\verb|\bgroup Foo}|» sont acceptés et sont équivalents. +\end{itemize} + +\begin{exercice} +Si l'on écrit le code ci-dessous, va-t-on obtenir «$a^3=b_5$» ? + +\centrecode|\let\swichmath=$ \let\expo= ^ \let\ind _ \let\egal= = +\swichmath a\expo3\egal b\ind5$| + +\solution +Oui car tous les tokens implicites\idx*[!implicite]{token} définis ici le sont avec la bonne syntaxe (le signe \verb-=- et l'espace facultatif qui le suit sont présents ou pas) et surtout, les catcodes des tokens implicites permettent une telle définition. +\end{exercice} + +\begin{exercice} +\Qu e va t-il se passer si l'on définit la macro \verb-\foo- ainsi : \verb-\let\foo={hello}- +\solution +\TeX{} va d'abord lire \verb-\let\foo={- et donc, \verb-\foo- va devenir une accolade implicite. + +Ensuite, le reste du code à lire est «\verb-hello}-». Tout va bien se passer jusqu'à l'accolade fermante. La première accolade ouvrante ayant été lue et absorbée par le \verb-\let-, celle-ci n'a pas été prise en compte dans le comptage interne à \TeX{} concernant l'équilibrage des accolades, l'accolade fermante devient orpheline et \TeX{} va nous gratifier d'un «\verb-Too many }'s-». Cette dernière erreur ne surviendra pas si auparavant, une accolade $x$ a été ouverte sans encore être fermée par une accolade fermante; dans ce cas, l'accolade qui suit «\verb|hello| équilibrera l'accolade $x$ ouverte préalablement à ce code. + +Pour \emph{définir} une macro et lui donner un texte de remplacement, on ne doit utiliser que \idx\def puis le nom de la macro puis le texte de remplacement entre accolades. +\end{exercice} + +La primitive \verb-\let- est parfois utile pour faire une sauvegarde d'une commande de façon à pouvoir la restaurer à son état initial après y avoir apporté des modifications. Prenons un mauvais exemple et décidons de modifier la macro \verb-\TeX- pour qu'elle n'affiche plus «\TeX» mais «tEx». Nous allons d'abord définir avec \verb-\let- un alias de \verb-\TeX- que nous appelons \verb-\TeXsauve- puis nous pourrons modifier la commande \verb-\TeX- et utiliser cette commande avec sa nouvelle définition. À la fin, avec \verb-\let- à nouveau, nous la restaurerons pour revenir au comportement initial : + +\showcode/Initialement, c'est \TeX.\par +\let\TeXsauve\TeX% sauvegarde +\def\TeX{tEx}% redéfinition +Ici, on a modifié \TeX.\par +\let\TeX\TeXsauve% retauration +De nouveau, c'est \TeX./\idx*[|)]{\let} + +\section{Les primitives \texttt{\textbackslash csname} et \texttt{\textbackslash endcsname}}\idx*[|(]\csname\idx*[|(]\endcsname +Pour écrire une \idx{séquence de contrôle}, on peut bien sûr faire précéder son nom du caractère «\verb|\|», c'est que nous avons fait jusqu'ici\ldots{} Mais il y a une autre méthode pour créer une \idx{séquence de contrôle}. On peut utiliser la paire de primitives \verb-\csname- et \verb-\endcsname- qui utilisent les caractères qui se trouvent entre elles pour les convertir en \idx{séquence de contrôle}. Par exemple, si \TeX{} rencontre \verb-\csname-\linebreak[1]\verb*- foo-\linebreak[1]\verb-\endcsname-\footnote{L'espace qui suit \texttt{\string\csname} est ignoré en vertu de la règle vue à la page~\pageref{regle.espace}}, dans un premier temps il construira l'unité lexicale «\verb|\foo|» et dans un deuxième temps, il lui substituera son texte de remplacement, tout ceci étant effectué dans la mémoire de \TeX{}, dans la \idx{pile d'entrée}. À l'affichage, l'effet est le même que si l'on avait directement écrit \verb-\foo-, mais dans les entrailles de \TeX{}, il y a une étape de plus, celle de former la \idx{séquence de contrôle} avec ce qui se trouve entre \verb-\csname- et \verb-\endcsname-. L'avantage de cette méthode est que l'on peut utiliser des caractères normalement interdits dans le nom de commandes, c'est-à-dire des tokens dont le catcode est différent de 11, celui des lettres. On peut donc construire des séquences de contrôle dont le nom comporte des espaces, des chiffres, des signes d'opérations, de ponctuation ainsi que d'autres caractères réservés dont les codes de catégorie sont spéciaux (\verb|&|, \verb|#|, \verb|^|, \verb|_|, \verb|$|). + +Un autre avantage non négligeable est que si jamais la \idx{séquence de contrôle} formée par \verb-\csname-\ldots\verb-\endcsname- n'est pas définie, \TeX{} la rend égale à \idx\relax qui est une primitive dont l'action est de ne rien faire. Ainsi, écrire \verb-\foobar- dans le code alors que cette macro n'a pas été définie par \idx\def ou \idx\let provoquera une erreur de compilation «\texttt{Undefined control sequence}». En revanche, écrire \verb-\csname foobar\endcsname- revient à invoquer la primitive \idx\relax et il ne se passera donc rien. + +Le revers de la médaille est que, pour \TeX{}, former une \idx{séquence de contrôle} avec \verb-\csname-\ldots\verb-\endcsname- est \emph{beaucoup} plus lent que de l'écrire directement avec le caractère d'échappement. Si c'est possible, il vaut donc mieux éviter d'utiliser cette méthode dans les boucles des programmes. + +\begin{exercice} +Après avoir défini la macro \verb-\foo- avec \verb-\def\foo{Bonjour}-, pourquoi rien ne s'affiche lorsqu'on écrit «\verb-\csname foo \endcsname-»? +\solution +Parce que l'espace qui suit le «foo» est pris en compte pour construire le nom de la \idx{séquence de contrôle}. Ainsi, écrire \verb-\csname-\linebreak[1]\verb*- foo -\linebreak[1]\verb-\endcsname- construit la commande {\fboxsep0.3pt\fbox{\ttfamily\textbackslash foo\textvisiblespace}} qu'il serait fastidieux de construire autrement. Celle-ci étant indéfinie, la construire avec \verb-\csname-\ldots\verb-\endcsname- la rend égale à \idx\relax et il ne se passe donc rien. +\end{exercice} +\begin{exercice} +\label{non.dev.csname}Pourquoi écrire \idx\let\verb-\salut\csname foo\endcsname- pour rendre égale \verb|\salut| à \verb|\foo| provoque une erreur de compilation ? +\solution +L'erreur survient parce que \TeX{} lit le code de gauche à droite en prenant en compte uniquement ce dont il a besoin. Il lit donc d'abord \idx\let\verb-\salut\csname-. Ce faisant, il rend donc la commande \verb-\salut- égale à \verb-\csname-. Ayant effectué cette opération, il poursuit avec la suite du code qui est \verb-foo\endcsname-. Les lettres «foo» ne vont pas lui poser de problème, il les affichera normalement, mais il va rencontrer la commande \verb-\endcsname- sans avoir rencontré le \verb-\csname- qui initiait le début de la construction du nom de la commande. Il va donc émettre le message d'erreur «\texttt{Extra \string\endcsname}». + +Pour faire ce que nous voulions, il faudrait forcer \TeX{} à construire la \idx{séquence de contrôle} \verb|\foo| à partir de \verb*-\csname -\linebreak[1]\verb- foo-\linebreak[1]\verb-\endcsname- avant qu'il ne passe la main à \verb-\let-. Pour provoquer cette action, il faudrait sauter les deux tokens \verb-\let\salut- pour provoquer cette action et revenir où l'on était avant les sauts pour reprendre la lecture du code dans l'ordre normal. Pour effectuer cette man\-\oe uvre non linéaire qui déroge à la règle de lecture du code, il faut avoir recours à la primitive \idx\expandafter que nous verrons plus loin. +\end{exercice} +\begin{exercice} +On a vu comment définir une commande avec \idx\def. Existe-t-il un moyen d'annuler une définition et faire qu'une commande préalablement définie redevienne indéfinie ? +\solution +Il n'existe pas de primitive \verb-\undef- qui rendrait une commande indéfinie \emph{et} l'enlèverait de la table de hashage\footnote{Il s'agit d'une structure interne à \TeX{} où sont stockés les appariements entre les noms des macros et l'endroit de la mémoire où sont stockés leurs textes de remplacement.}. + +On peut cependant combler ce manque. En effet, si l'on définit une commande à l'intérieur d'un \idx{groupe} simple ou semi-simple, la définition est locale au groupe et est perdue à sa fermeture. Par conséquent, si la commande était indéfinie avant l'ouverture du groupe, elle le redevient après sa fermeture. + +Si l'on souhaite définir une commande de façon globale, la primitive \idx\global, lorsqu'elle est placée devant une assignation, rend cette assignation globale c'est-à-dire qu'elle survit à la fermeture du groupe. La primitive \idx\gdef se comporte comme \idx\global\verb|\def|. +\end{exercice}\idx*[|)]\csname\idx*[|)]\endcsname + +\section{Caractère de contrôle}\idx*[|(]{caractère de contrôle} + +\begin{regle} +Lorsque le caractère d'échappement «\cidx\\» est suivi d'un caractère dont le catcode n'est pas 11\idx*{catcode!11 (lettre)}, celui des lettres, seul ce caractère est pris en compte et \TeX{} forme ce que l'on appelle un «caractère de contrôle». +\end{regle} + +Pour former un caractère de contrôle, on peut donc utiliser des caractères de catcode 12\idx*{catcode!12 (autre)} comme par exemple «\verb|\.|», «\verb|\*|», «\verb|\5|» ou «\verb|\@|». Il devient surtout possible d'employer des caractères dont le catcode est plus sensible comme «\verb|\\|», «\verb|\{|», «\verb|\}|», «\verb|\^|», «\verb|\_|», «\verb|\&|», «\verb|\~|», «\verb|\%|». Ces derniers, à l'exception de \verb|\\|\footnote{La macro \texttt{\string\\} est définie par plain-\TeX{} car elle est utilisée comme macro auxiliaire pour définir une autre macro et la définition qui lui est donnée à cette occasion est laissée en l'état. Toujours est-il qu'elle n'affiche pas «\texttt{\textbackslash}», qui s'obtient avec «\texttt{\textbackslash char\number`\\}» ou mieux avec «\texttt{\textbackslash char`\textbackslash\textbackslash}». + +Du côté de \LaTeX{}, la macro \texttt{\string\\} est définie pour commander d'aller à la ligne pendant le paragraphe en cours et est redéfinie dans plusieurs environnements (centering, raggedright, raggedleft, eqnarray, tabbing, array, tabular).}, sont définis par plain-\TeX{} pour afficher le caractère qui forme leur nom, caractère qu'il serait plus difficile d'afficher sinon. Pour cela, la primitive \idx\chardef est employée avec cette syntaxe : + +\centrecode-\chardef\<macro>=<code de caractère>- + +\noindent Si l'on s'intéresse au code des macros du format plain-\TeX{} qui est contenu dans le fichier «\verb|plain.tex|»\idx*{fichier!plain.tex}, on découvre par exemple à la ligne \no 651 que le caractère de contrôle \idx\& est défini par + +\centrecode-\chardef\&=`\&- + +\noindent ce qui a pour effet de rendre équivalent (à la manière de \idx\let) le caractère de contrôle \verb|\&| à «\idx\char\verb|`\&|». + +\begin{regle} +Contrairement aux séquences de contrôle dont le nom est formé de lettres, les \idx{espace}s qui suivent les « caractères de contrôle » ne sont pas ignorés. La seule exception est la primitive \idx\ , qui ajoute un espace à la liste courante. En effet, si cette dernière est suivie d'un espace, alors deux \idx[!consécutifs]{espace}s se suivent dans le code et une autre règle stipule que le second est ignoré. +\end{regle} + +\begin{exercice} +En ayant fait les définitions «\verb*-\def\9{\ \:}-» et «\verb*-\def\:{ \ X}-», quel affichage obtiendra-t-on avec «\verb*-a\9 fin-» ? +\solution +On obtient la lettre «a» suivie du texte de remplacement de \verb-\9-. Celui-ci commence par \verb*-\ - (espace qui sera affiché) suivi de \verb-\:-. Le texte de remplacement de \verb|\:| commence par : \verb*- - (espace qui sera affiché) puis \verb*-\ - (le deuxième espace est ignoré) suivi d'un «\verb|X|». Et enfin, l'espace après \verb*-\9 - sera affiché avant le mot «fin». On obtient donc «a\verb*- -X\verb*- -fin». +\end{exercice} + +\begin{exercice} +\Qu elle différence y t-il entre «\verb-\+-» et «\idx\csname\verb|+|\idx\endcsname» + ? +\solution +Il n'y a pas de différence à l'affichage sauf si le caractère qui suit dans le code est un espace. Il sera comptabilisé avec \verb-\+- et ignoré avec \idx\csname\verb|+|\idx\endcsname car l'espace qui suit la \idx{séquence de contrôle} \idx\endcsname est ignoré. + +De plus, si le caractère de contrôle \verb-\+- n'a pas été défini, écrire \verb-\+- provoque une erreur à la compilation de type «\texttt{Undefined control sequence}». Au contraire, avec \idx\csname\verb|+|\idx\endcsname, il n'y aurait pas d'erreur car lorsque \idx\csname construit une \idx{séquence de contrôle} non définie, elle la rend égale à \idx\relax, et ici, tout se passerait comme si on avait écrit \idx\let\verb|\+=|\idx\relax. +\end{exercice} +\begin{exercice} +\Qu elle différence y a-t-il entre «\idx\def\verb-\foo{A}-» et «\idx\let\verb-\foo=A-» ? +\solution +À l'affichage, dans les deux cas, \verb-\foo- produit un «A». Par contre, en interne, les choses sont différentes\ldots + +Lorsque \verb-\foo- est définie avec \idx\def, \TeX{} la \emph{remplace} par un «A». + +Lorsque \verb-\foo- est définie avec \idx\let, aucun remplacement n'est fait puisque \verb-\foo- \emph{est} un «A» et n'a aucun texte de remplacement. +\end{exercice} + +Le fait que les commandes caractères prennent en compte les espaces qui les suivent permet de définir un espace implicite §\sptoken de cette façon\footnote{C'est ainsi que \texttt{\string\@sptoken} est défini dans le code du noyau \LaTeX.} :\label{sptoken} + +\centrecodespc/\def\:{\let\sptoken= }¤§*\sptoken¤ +\: / + +\noindent Toute l'astuce réside dans le fait que l'espace qui suit \verb|\:| à la 2\ieme{} ligne est pris en compte. La man\-\oe uvre mérite une petite explication. La première ligne définit un texte de remplacement pour la macro \verb|\:|. À la deuxième ligne, \TeX{} va remplacer \verb|\:| par son texte de remplacement ce qui va donner : + +\begin{centrage} +\small\boxtoken{\let\string\sptoken= }\verb*| |% +\end{centrage} + +Pour plus de clarté, le texte de remplacement de \verb|\:| est encadré. Dans ce texte de remplacement, l'espace qui suit le signe \verb|=| est l'espace facultatif qui est permis avec la primitive \idx\let. Cet espace, faisant partie de la syntaxe de \idx\let, est ignoré et donc, \idx\let rend §\sptoken égal au token suivant qui est «\verb*| |», l'espace qui suit le caractère de contrôle \verb|\:|. + +Dans le code ci-dessus, le second espace n'est pas ignoré car les deux espaces ne sont pas consécutifs dans le code source. Ils sont consécutifs après que \TeX{} ait remplacé \verb|\:| par son texte de remplacement. La règle de la page~\pageref{regle.espace} ne s'applique donc pas. + +\begin{exercice} +Comment serait défini §\sptoken si l'on écrivait naïvement \idx\let\verb*-\sptoken= - ? +\solution +Comme on l'a vu, le premier espace, facultatif, fait partie de la syntaxe de \idx\let et ne peut donc pas servir à définir §\sptoken. Le second est ignoré puisque la règle veut que deux espaces consécutifs dans le code n'en forment qu'un. + +Dans ce cas, §\sptoken sera un alias pour le token qui \emph{suivra dans le code} et que l'on n'a pas précisé dans l'énoncé de l'exercice. Il faut noter que si ce qui suit est un saut de ligne, §\sptoken sera un alias de la commande \idx\par car deux sauts de lignes consécutifs sont équivalents à la commande \idx\par : + +\showcode/\let\sptoken= %2 espaces avant le "%"¤§*\sptoken¤ + +La commande \sptoken compose le paragraphe/ +\end{exercice}\idx*[|)]{caractère de contrôle} + +\section{Caractères actifs}\idx*[|(]{caractère actif} +Un caractère est dit \emph{actif} lorsqu'il revêt les mêmes propriétés qu'une commande : on peut le définir avec \idx\def et, lorsqu'il est rencontré par \TeX{}, son texte de remplacement lui est substitué. En contrepartie, il ne fait plus partie de la catégorie de lettres et n'est donc pas autorisé dans les noms des séquences de contrôle. + +\begin{regle} +Un caractère est \emph{actif} lorsque son code de catégorie vaut 13\idx*[!13 (actif)]\catcode. Dans ce cas, on peut lui donner un texte de remplacement comme on le fait pour une macro. +\end{regle} + +Par défaut, aussi bien avec plain-\TeX{} qu'avec \LaTeX, seul le caractère «\cidx\~» est actif fait les choses suivantes : +\begin{enumerate} + \item interdire une coupure en spécifiant une haute \idx{pénalité} (avec la primitive \idx\penalty suivie d'un entier élevé, par exemple \numprint{1000}); + \item appeler la primitive \idx\ qui affiche une espace. +\end{enumerate} +Ainsi, plain-\TeX{} dit : + +\centrecode-\def~{\penalty1000 \ }- + +\noindent La forte \idx{pénalité} de \texttt{1000} interdit toute coupure et le tout forme donc une « \idx[!insécable]{espace} insécable », où l'adjectif \emph{insécable} exprime que cette espace ne pourra donner lieu à une coupure de ligne. +\grandsaut + +À notre tour, supposons que l'on souhaite rendre le caractère «\verb|W|» actif. Nous le ferons à l'intérieur d'un groupe, car il est prudent de limiter la portée des modifications des codes de catégorie. + +Nous savons que \idx\catcode\verb-`\<car>=<nombre>- modifie le catcode d'un caractère, où \verb-<nombre>- est un nombre de 0 à 15\footnote{Le caractère «\texttt`» est l'apostrophe \emph{inverse} et s'obtient parfois en tapant la combinaison de touches \bioLegacyKeyGlyph{A_l_t_G_r}+\bioLegacyKeyGlyph{seven}. Pour exprimer le nombre égal au code de catégorie d'un caractère, on peut écrire \verb|\`<car>| ou \verb|`<car>|. La seconde écriture est cependant moins recommandable puisque l'on s'expose à des erreurs lorsque \verb|<car>| a un catcode spécial (0 ou \number\catcode`\%).}. Ici, on va donc écrire \verb-\catcode`\W=13-. Et ensuite, il suffit de définir le texte de remplacement du caractère actif \verb|W|, par exemple «wagons» : + +\showcode/{% début du groupe +\catcode`\W=13 \def W{wagons}¤\idx*\catcode¤ +Les W constituent le train. +}% fin du groupe/ + +Voici la règle corroborant ce que l'on constate dans l'exemple ci-dessus : + +\begin{regle} +Un \idx{espace} après un caractère actif est pris en compte. +\end{regle} + +Il est plus difficile est de créer une commande qui rende le caractère \verb|W| actif. Appelons \verb-\actiw- cette commande et donnons-nous le cahier des charges suivant : elle devra rendre le caractère \verb|W| actif et lui donner le texte de remplacement «wagons». Nous placerons un \idx\begingroup avant d'appeler cette macro et un \idx\endgroup à la fin du texte où l'on souhaite que le caractère \verb|W| soit actif. + +Pour définir cette commande \verb-\actiw-, on ne peut pas écrire directement ceci : + +\centrecode-\def\actiw{\catcode`\W=13 \def W{wagons}}- + +\noindent En effet, lorsque \TeX{} remplace \verb|\actiw| par son texte de remplacement, \emph{tous} les tokens dans ce texte de remplacement ont leurs catcodes figés depuis que la définition a été faite. Et donc, le «\verb|W|» qui suit le \verb|\def| a un catcode de 11. + +Par conséquent, lorsque ce texte de remplacement sera exécuté, \TeX{} va trouver \verb-\def W{wagons}- où \verb|W| est une lettre (de catcode 11\idx*{catcode!11 (lettre)}), et il va se plaindre de ne pas trouver une \idx{séquence de contrôle} après le \idx\def en affichant le message d'erreur «\texttt{Missing control sequence}». + +Il faut comprendre que l'ordre «\verb|\catcode`\W=13|» qui est dans texte de remplacement ne peut pas agir sur les «\verb|W|» de ce texte de remplacement puisque leurs catcodes sont figés, mais agira sur le code qui reste à lire \emph{après} \verb|\actiw|. + +Pour sortir de ce mauvais pas, il faut rendre \verb|W| actif \emph{avant} que \TeX{} ne lise le texte de remplacement de \verb-\wagon-. Pour limiter la portée de cette opération, on va donc le faire dans un groupe et utiliser \idx\gdef, pour que la définition soit globale et survive à la fermeture du groupe. Voici la façon correcte de définir \verb-\actiw- : + +\showcode/\begingroup \catcode`\W=13 ¤\idx*\begingroup\idx*\endgroup¤ +\gdef\actiw{%¤\idx*\gdef¤ + \catcode`\W=13 ¤\idx*\catcode¤ + \def W{wagons}} +\endgroup +a) Les trains ne sont pas constitués de W !\par +b) \begingroup\actiw Ici, les W forment les trains.\endgroup\par +c) Ici, les W sont redevenus des lettres./ + +\begin{exercice} +Définir deux séquences de contrôle \verb-\>- et \verb-\<- telles qu'entre elles, les mots soient espacés de \numprint[mm]5. Le comportement normal doit être rétabli ensuite. + +On utilisera la primitive \idx\hskip suivie d'une dimension pour ordonner à \TeX{} d'insérer une espace horizontale de la valeur de la dimension. +\solution +Comme on l'a vu, on ne peut pas écrire directement ce code comme ci-dessous pour définir la macro \verb-\>- : + +\centrecode-\def\>{\begingroup\catcode`\ =13 \def {\hspace{5mm}}}- + +\noindent puisqu'au moment où cette ligne est lue par \TeX{}, l'espace a son code de catégorie naturel de 10. Celui-ci ne pourra plus être changé par la suite. + +Voici la bonne façon de procéder : il suffit de rendre la commande \verb-\<- égale à \idx\endgroup ce qui clôturera le processus initié par \verb-\>- qui avait ouvert le groupe semi-simple : + +\showcode/\begingroup¤\idx*\begingroup¤ + \catcode`\ =13 % rend l'espace actif¤\idx*\catcode¤ + \gdef\>{\begingroup¤\idx*\gdef¤ + \catcode`\ =13 + \def {\hskip5mm\relax}}¤\idx*\hskip\idx*\relax¤ +\endgroup +\let\<=\endgroup¤\idx*\let¤ +a) Du texte normal\par +b) \>Des mots très espacés\<\par +c) Du texte normal/ +\end{exercice} + +La présence d'un \idx\relax après la dimension de \verb|5mm| stoppe la lecture de la dimension. En effet, comme nous le verrons plus loin, une dimension peut avoir des composantes étirables qui commencent par le mot-clé «\verb|plus|». Sans le \idx\relax, si jamais un espace actif était suivi du mot «\verb|plus|», alors \TeX{} penserait que ce mot fait partie intégrante de la dimension et irait chercher encore plus loin la composante étirable. Le \verb|\relax| agit donc ici comme une sécurité. +\grandsaut + +Une application utile des caractères actifs est de rendre les signes de ponctuation haute actifs. C'est ce que fait l'extension pour \LaTeX{} «\verb-babel-» chargée avec l'option «\verb-frenchb-» de Daniel \textsc{Flipo}\idx*{package!babel (frenchb)}. Les signes de ponctuation \emph{haute} «\string!», «\string?», «\string:» et «\string;» sont rendus actifs de façon à insérer une espace fine insécable avant eux. Nous allons imiter ce comportement et rendre la virgule active. L'idée est de la programmer pour qu'elle supprime un éventuel espace avant elle, affiche le caractère «\verb|,|», insère une espace justifiante (c'est-à-dire étirable) et enfin, ne tienne pas compte des espaces qui pourraient suivre. Pour effectuer ces actions, certaines primitives sont nécessaires : +\begin{itemize} + \item si ce qui précède est un espace étirable, la primitive \idx\unskip le supprime ; + \item \idx\string transforme le token qui suit en un ou plusieurs tokens dont les codes de catégorie sont 12. Cette primitive est utile pour rendre n'importe quel \idx{token} inoffensif et donc immédiatement affichable. Par exemple, «\verb-\string#-» produit «\string#» de même que «\verb-\string\def-» produit «\string\def». Lorsque la virgule sera active, «\verb-\string,-» affichera une virgule non active; + \item \idx\ignorespaces demande à \TeX{} d'ignorer tous les espaces qui vont suivre et d'avancer sa tête de lecture jusqu'au prochain caractère qui n'est pas un espace. +\end{itemize} + +\showcode/\begingroup¤\idx*\begingroup¤ + \catcode`\,=13 \def,{\unskip\string,\space\ignorespaces}¤\idx*\unskip\idx*\string\idx*\space\idx*\ignorespaces\idx*\catcode¤ + La rue assourdissante autour de moi hurlait. + + Longue , mince,en grand deuil , douleur majestueuse , + + Une femme passa ,d'une main fastueuse + + Soulevant,balançant le feston et l'ourlet ; + +\endgroup\medbreak\hfill Charles {\sc Baudelaire}¤\idx*\endgroup\idx*\medbreak\idx*\hfill\idx*\sc¤/ + +\begin{exercice} +Par défaut, le caractère inséré par \TeX{} à chaque \idx{fin de ligne} est le retour charriot, qui s'écrit «\verb-^^M-\verbidx*[ (retour charriot)]{^^M}» et qui a comme code de catégorie \number\catcode`\^^M. La règle de \TeX{} vue page~\pageref{regle.catcode5} veut que \emph{deux} retours charriots consécutifs (ou plus généralement deux tokens consécutifs de catcode \number\catcode`\^^M) soient équivalents à la commande \idx\par\idx*{catcode!5 (retour charriot)}. + +Reprendre l'exemple précédent en faisant en sorte qu'un seul retour charriot suffise à aller à la ligne suivante. +\solution +Il suffit de rendre le caractère \verb-^^M- actif et le rendre égal à \idx\par avec un \idx\let : + +\showcode/\begingroup¤\idx*[|etc]\begingroup\forbidindex\begingroup¤ + \catcode`\,=13 \def,{\unskip\string, \ignorespaces}¤\idx*\unskip\idx*\string\idx*\ignorespaces\idx*\catcode¤ + \catcode`\^^M=13 \let ^^M\par¤\idx*\let¤ % rend le retour charriot égal à \par + La rue assourdissante autour de moi hurlait. + Longue , mince,en grand deuil , douleur majestueuse , + Une femme passa , d'une main fastueuse + Soulevant,balançant le feston et l'ourlet ; +\endgroup\medbreak\hfill Charles {\sc Baudelaire}¤\idx*[|etc]\endgroup\forbidindex\endgroup\idx*\medbreak\idx*\hfill\idx*\sc¤/ +\end{exercice} + +\begin{exercice} +Dans un texte à l'intérieur d'un groupe, inventer un procédé qui effectue un remplacement par permutation circulaire des voyelles, c'est-à-dire qui remplace a par e, e par i, i par o, o par u, u par y et y par a. +\solution +\label{permutation.voyelles}La difficulté vient du fait qu'une fois qu'un caractère est rendu actif, il devient semblable à une \idx{séquence de contrôle} et on ne peut plus s'en servir pour former le nom d'une \idx{séquence de contrôle}. C'est donc une méthode peu recommandable, il en existe d'ailleurs de meilleures. Il n'empêche qu'en l'état actuel de nos connaissances, nous ne pouvons procéder qu'en touchant aux codes de catégorie. + +Avant de toucher aux catcodes des voyelles, on va donc définir des séquences de contrôle \verb-\AA-, \verb-\EE-, etc. qui, à l'aide de la commande \idx\let vont devenir des lettres implicites, «a» pour \verb-\AA-, «e» pour \verb-\EE-, etc. + +Ensuite, les commandes \idx\catcode et \verb|\let| contenant des voyelles, on ne pourra pas y faire appel après avoir modifié les codes de catégorie. On va donc définir avec \verb-\let- des séquences de contrôle équivalentes dont les voyelles seront en majuscule. On aura donc \verb-\lEt- pour \verb-\let- et \verb-\cAtcOdE- pour \verb-\catcode-. + +On peut ensuite rendre chaque voyelle active et la mettre \verb|let|-égale à \verb-\AA-, \verb-\EE-, etc. selon la lettre que l'on veut obtenir. + +\showcode[\def\`##1{{\accent0 ##1}}\def\'##1{{\accent1 ##1}}]/{% ouvre un groupe + \let\AA=a \let\EE=e \let\II=i \let\OO=o \let\UU=u \let\YY=y¤\idx*\let¤ + % sauvegarder avant de modifier le catcode de a et e : + \let\lEt=\let \let\cAtcOdE=\catcode¤\idx*\catcode¤ + \cAtcOdE`\a=13 \lEt a=\EE \cAtcOdE`\e=13 \lEt e=\II + \cAtcOdE`\i=13 \lEt i=\OO \cAtcOdE`\o=13 \lEt o=\UU + \cAtcOdE`\u=13 \lEt u=\YY \cAtcOdE`\y=13 \lEt y=\AA + Ce texte devenu \`a peine reconnaissable montre que le r\'esultat + contient des sonorit\'es catalanes, corses ou grecques assez + inattendues. +}% ferme le groupe/ + +Les contorsions nécessaires pour arriver à nos fins montrent que l'on ne touche pas aux codes de catégorie sans se créer de réelles difficultés et sans prendre de gros risques si l'on contrôle mal la portée des modifications. Changer un code de catégorie de caractère doit donc rester exceptionnel et rester réservé à des buts très spécifiques. En effet, des méthodes plus sures existent, même si elles ont aussi d'autres inconvénients. + +Une optimisation du code serait d'utiliser le caractère déjà actif «\cidx\~» comme alias de \idx\catcode. Bien sûr, on y perd en lisibilité\ldots{} On peut aussi profiter de la permutation circulaire pour définir chaque voyelle rendue active comme alias de la prochaine voyelle, non encore rendue active. On économise ainsi des séquences de contrôle pour les lettres implicites. On n'en a besoin que d'une seule, \verb-\AA-, lettre implicite pour «a» : + +\showcode[\def\`##1{{\accent0 ##1}}\def\'##1{{\accent1 ##1}}]/{% + \let\AA=a \let\lEt=\let \let~=\catcode¤\idx*\catcode\idx*\let\cidx*\~¤ + ~`a=13 \lEt a=e ~`e=13 \lEt e=i ~`i=13 \lEt i=o + ~`o=13 \lEt o=u ~`u=13 \lEt u=y ~`y=13 \lEt y=\AA + Ce texte devenu \`a peine reconnaissable... +}/ +\end{exercice}\idx*[|)]{caractère actif} + +\section{Signification d'une commande} +Il est possible de demander à \TeX{} la « signification » d'une \idx[|etc]{séquence de contrôle}\forbidindex{séquence de contrôle}. Pour cela, la primitive \idx\show écrit dans le \idx[!log]{fichier} log le \idx{texte de remplacement} de la macro ou simplement son nom si c'est une primitive. Cette primitive, lorsqu'elle est utilisée à bon escient dans le code permet un débogage efficace par la lecture du \idx[!log]{fichier} \verb|log|. Pour diriger les informations délivrées par \idx\show dans le flux de lecture de \TeX{} pour par exemple les afficher dans le document final, il faut utiliser la primitive \idx\meaning. + +\showcode/a) \def\foo{Bonjour}\meaning\foo\par¤\idx*\meaning¤ +b) \let\bar=\foo\meaning\bar\par% \foo est "copiée" vers \bar +c) \def\baz{\foo}\meaning\baz\par +d) \catcode`\W=13 \def W{Wagons}% W est un caractère actif¤\idx*\catcode¤ + \meaning W\par +e) \meaning\def% \def est une primitive¤\idx*\meaning¤/ + +Il est important d'insister entre la différence qui existe entre \verb|\let\bar\foo| (cas b) et \verb|\def\baz{\foo}| (cas c). Dans le premier cas, \verb|\bar| est rendue égale à \verb|\foo| et à partir de ce moment, \verb|\bar| a comme texte de remplacement «Bonjour», indépendamment de ce que devient \verb|\foo|. En revanche, lorsqu'on écrit \verb|\def\baz{\foo}|, la macro \verb|\baz| a comme texte de remplacement \verb|\foo| et donc, à chaque fois qu'elle est exécutée, elle dépend de ce qu'est \verb|\foo| à ce moment-là. + +\begin{exercice} +Mettre en évidence à l'aide de la primitive \idx\meaning que 2 retours charriots\verbidx*[ (retour charriot)]{^^M} consécutifs sont interprétés par \TeX{} comme étant la primitive \verb-\par-. +\solution +Il suffit de définir une commande dont le texte de remplacement est constitué de 2 retours charriots consécutifs et de faire afficher à \TeX{} la signification de cette commande, voire faire suivre \verb|\meaning| de deux retours à la ligne : + +\showcode/a) \def\foo{ + +}Signification de \string\foo{} : \meaning\foo¤\idx*\string\idx*\meaning¤ + +b) Signification de deux retours charriots consécutifs : \meaning + / +\end{exercice} + +\begin{regle} +La primitive \idx\show écrit dans le \idx[!log]{fichier} \verb|log| la « signification » du token\idx*[!signification]{token} qui la suit : + \begin{enumerate} + \item si ce token est une macro (ou un caractère actif), le texte «\texttt{macro->}» suivi du \idx{texte de remplacement} de cette macro est écrit; + \item si ce token est une primitive, qui par définition n'a pas de \idx{texte de remplacement}, le nom de la primitive est écrit; + \item sinon, \TeX{} écrit un texte bref (caractérisant le catcode du token) suivi du token. + \end{enumerate} +La primitive \idx\meaning écrit les mêmes choses que \idx\show sauf qu'elle les écrits dans le flux de lecture de \TeX{}, dans la \idx{pile d'entrée}. +\end{regle} + +Voici ce que provoque \verb|\meaning| pour les tokens de chaque catégorie qu'il est possible de mettre après \verb|\meaning| : +\begingroup +\def~{\penalty\@M \ } +\showcode/a) \meaning\relax\par% primitive¤\idx*\meaning¤ +b) \meaning {\par% catcode 1 +c) \meaning }\par% catcode 2 +d) \meaning $\par% catcode 3 +e) \meaning &\par% catcode 4 +g) \meaning #\par% catcode 6 +h) \meaning ^\par% catcode 7¤\idx*{catcode!7\space(exposant)}¤ +i) \meaning _\par% catcode 8 +j) \meaning a\par% catcode 11 (une lettre)¤\idx*{catcode!11\space(lettre)}¤ +k) \meaning +\par% catcode 12 (caractère "autre")¤\idx*{catcode!12\space(autre)}¤ +l) \meaning ~\par% catcode 13 (caractère actif)¤\idx*{catcode!13\space(actif)}¤/% +\endgroup\iffalse$\fi + +\begin{exercice} +On ne peut pas afficher la signification d'un espace (catcode 10\idx*{catcode!10 (espace)}) puisqu'écrire «\idx\meaning\verb*| |» ferait que l'espace qui suit la primitive serait ignoré et \idx\meaning prendrait le token d'après. Comment s'y prendre pour afficher la signification d'un espace avec \idx\meaning ? +\solution +On peut employer la même astuce qu'avec §\sptoken de la page~\pageref{sptoken}. On s'y prend + ici avec \idx\let, en enfermant le tout dans un groupe semi-simple : + +\showcode/\begingroup% dans un groupe¤\idx*\begingroup¤ + \let\*=\meaning% rendre \* égal à \meaning¤\idx*\meaning¤ + \* %<- espace pris en compte +\endgroup% fermer le groupe¤\idx*\endgroup¤/ + +En revanche, on ne peut pas écrire après \idx\meaning les caractères de catcode suivants : +\begin{itemize} + \item 0 car le caractère d'échappement s'apparie toujours avec autre chose pour former une \idx{séquence de contrôle}. Un token de catcode 0\idx*{catcode!0 (caractère d'échappement)} ne peut exister comme entité seule; + \item 5 car ce caractère est vu comme un espace; + \item 9 car c'est le numéro de la catégorie des caractères «ignorés» et le token serait aussi ignoré, même après \idx\meaning; + \item 14 car un caractère de commentaire est ignoré ainsi que tout ce qui va jusqu'à la fin de la ligne; + \item 15 car un caractère « invalide » n'est pas permis et provoque une erreur de compilation lorsqu'il est vu par \TeX{}. +\end{itemize} +\end{exercice} + +\section{Le caractère d'échappement}\idx*[|(]{caractère d'échappement} +Par défaut, le caractère d'échappement est «\cidx\\», c'est à ce caractère que \TeX{} reconnait le début d'une \idx{séquence de contrôle}. Comme presque tout dans \TeX{}, ce caractère est modifiable. En effet, tout caractère de catcode 0\idx*{catcode!0 (caractère d'échappement)} est, par définition, un caractère d'échappement. Supposons qu'à l'intérieur d'un groupe semi-simple, on donne à «\verb-*-» le code de catégorie 0 et à «\verb-\-» le code de catégorie 12 qui est celui des caractères «autres». Ayant fait ces modifications, nous devrons mettre «\verb-*-» devant les séquences de contrôle et pourrons alors afficher «\verb-\-» directement puisqu'il sera devenu un caractère inoffensif : + +\showcode/\begingroup¤\idx*\begingroup¤ +\catcode`\*0 ¤\idx*\catcode¤ +\catcode`\\12 % \ n'est plus un caractère d'échappement +*def*foo{bar} +*LaTeX{} et la macro *string*foo : *foo. + +L'ancien caractère d'échappement "\" et \TeX. +*endgroup/ + +\begin{exercice} +Concernant l'exemple précédent : +\begin{enumerate} + \item Aurait-on pu écrire à la ligne 3 ceci : «\verb-*catcode`*\12-»\idx*\catcode ? + \item Si l'on avait utilisé \idx*\gdef\verb-*gdef- pour que la macro \verb-*foo- survive à la fermeture du groupe, aurait-on pu l'appeler par la suite avec \verb-\foo- ? +\end{enumerate} +\solution +\begin{enumerate} + \item Après la ligne \no2, il y a 2 caractères d'échappement qui sont «\verb-\-» et «\verb-*-». Tant qu'ils ont ce pouvoir, ils sont interchangeables. La réponse est donc oui. + + En revanche, la ligne 2 fait perdre ce pouvoir au caractère «\verb-\-» ce qui fait qu'ensuite, seul «\verb-*-» peut être utilisé comme caractère d'échappement jusqu'à ce que le groupe soit fermé par \verb-*endgroup-\idx*\endgroup. + \item N'importe quel caractère ayant le catcode 0\idx*{catcode!0 (caractère d'échappement)} peut être utilisé pour amorcer le nom d'une macro. En temps normal, seul «\verb-\-» est disponible donc la réponse est oui. +\end{enumerate} +\end{exercice} + +On peut observer que \idx\string, qui convertit le nom d'une \idx{séquence de contrôle} en caractères de catcode 12\idx*{catcode!12 (autre)}, ne tient pas compte de la modification du caractère d'échappement puisque l'on obtient \verb-\foo- et non pas \verb-*foo-. Cela dit, ce comportement est un peu normal car s'il y avait plusieurs caractères d'échappement, \TeX{} serait bien ennuyé pour en choisir un. Pour schématiser, on peut considérer que le choix d'un caractère d'échappement par son catcode concerne un mécanisme d'\emph{entrée} pour \TeX{} puisqu'il s'agit d'une règle concernant la lecture du code tapé par l'utilisateur. La primitive \idx\string appliquée aux macros, au contraire, concerne un mécanisme de sortie puisqu'elle provoque la conversion d'une donnée interne de \TeX{} (le nom de la macro) en caractères immédiatement affichables. Ces deux mécanismes se représentent le caractère d'échappement par des moyens indépendants. Pour \idx\string, il faut le définir via la primitive \idx\escapechar qui attend après elle le code de caractère du caractère d'échappement que l'on souhaite. Dans l'exemple précédent pour que le caractère d'échappement affiché par \idx\string soit «\verb-@-», il suffit de rajouter juste après le \idx\begingroup : + +\begin{centrage} + \small\verb|\espcapechar=64 |\qquad ou bien\qquad \verb|\escapechar=`\@| +\end{centrage} + +Notons que tout comme \idx\string, les primitives \idx\meaning et \idx\show tiennent compte de la modification du \idx\escapechar. Enfin, si l'on assigne à \idx\escapechar un code inférieur à 0 ou supérieur à 255, alors aucun caractère d'échappement ne précèdera le nom de la macro. + +\showcode/\def\foo{\bar} +\begingroup¤\idx*\begingroup¤ +a) \escapechar=-1 \meaning\foo\qquad% pas de caractère d'échappement¤\idx*\escapechar\idx*\meaning\idx*\qquad¤ +b) \escapechar=`\@ \meaning\foo\qquad% "@" est le caractère d'échappement +\endgroup¤\idx*\endgroup¤ +c) \meaning\foo% caractère d'échappement par défaut/ + +\begin{exercice}\label{construction.nom.etrange} +Définir une commande dont le nom est \verb-\-\boxtoken{1\string\2\string\a} et dont le texte de remplacement est «foo» et vérifier en appelant cette commande que la définition est bien correcte. +\solution +Il va falloir agir dans un groupe, modifier les catcodes de 1, 2 et \cidx\\ pour les mettre à 11, catégorie des lettres, seules autorisées dans les noms de macros. Comme «\cidx\\» est le caractère d'échappement, il va aussi falloir en définir un autre, choisissons «\verb-|-». Comme toutes ces man\-\oe uvres ont lieu dans un groupe, nous utiliserons \verb-|gdef-\idx*\gdef pour que la définition de la macro survive à la fermeture du groupe. + +Le problème ici est que l'on doit donner \emph{en dernier} à «\verb|1|» le catcode 11. En effet, si on le faisait avant, «\verb|1|» deviendrait pour \TeX{} une \emph{lettre} et lorsqu'il rencontrerait \verb-|catcode`|2=11-, le «\verb|11|» serait, à ses yeux, deux lettres qui ne peuvent former un nombre, car les tokens qui forment les nombres doivent avoir un catcode de 12\idx*{nombre!catcode des chiffres} ! Nous aurions droit à une erreur de compilation du type «\texttt{Missing number}». + +Enfin, pour appeler la macro à l'extérieur du groupe, on doit utiliser la paire \idx\csname...\linebreak[1]\idx\endcsname dans laquelle on prendra soin de transformer le caractère d'échappement \verb-\- en un caractère inoffensif de catcode 12\idx*{catcode!12 (autre)} avec la primitive \idx\string. + +\showcode/\begingroup + \catcode`\|=0 |catcode`|\=11 |catcode`|2=11 |catcode`|1=11 ¤\idx*\catcode\forbidindex\catcode¤ + |gdef|1\2\a{foo}¤\idx*\gdef¤ +|endgroup¤\idx*\endgroup¤ +Voici la macro : \csname 1\string\2\string\a\endcsname¤\idx*\string\idx*\csname\idx*\endcsname¤/ + +Dans ce cas, ces manipulations de catcode, assez dangereuses et plutôt chatouilleuses, sont fort heureusement inutiles avec la primitive \idx\expandafter que nous verrons plus loin. Il s'agit d'un exemple purement pédagogique\ldots +\end{exercice}\idx*[|)]{caractère d'échappement} + +\section{Une autre façon de stocker des tokens} +Arrivés à ce point, nous savons qu'une macro peut servir à stocker des tokens. Ces tokens sont placés dans le texte de remplacement de la macro lors de sa définition avec \verb|\def|. Bien qu'on les utilise aussi dans ce but, les possibilités d'une macro ne se limitent pas à ce simple stockage et c'est ce que nous allons découvrir dans les chapitres suivants. + +Si l'on recherche uniquement le côté « stockage de tokens », \TeX{} propose un autre type de structure : les registres de tokens\idx*[|(]{registre!token}\idx*[|(]{token!registre}. Ce sont de zones de mémoire (au nombre de 256 pour \TeX{} et \numprint{32768} pour \idx\eTeX) auxquelles on accède à l'aide de la primitive \idx\toks suivie du numéro de registre. Ainsi, \verb|\toks17| fait référence au registre de token \no17. Il est admis que le registre \no0 sert de registre brouillon et peut être utilisé sans trop de risque n'importe où. + +\TeX{} met aussi à disposition la primitive \idx\toksdef et par exemple, écrire + +\centrecode-\toksdef\foo=17-\idx*\toks + +\noindent rend \verb|\foo| équivalent à \verb|\toks17| de telle sorte que le registre \no17 peut être désigné par la \idx{séquence de contrôle} \verb|\foo|. L'équivalence dont il est question ici ressemble à celle de \idx\let en ce sens que \verb|\foo| n'aura pas de texte de remplacement, mais \emph{sera} \verb|\toks17|. + +Il est déconseillé d'utiliser un numéro non nul arbitrairement et sans précaution, car il se pourrait que ce registre soit déjà utilisé et on pourrait écraser son contenu et provoquer des dysfonctionnements par la suite. Plain-\TeX{} propose la macro \idx\newtoks\verb-\<nom>- où le \verb-\<nom>- sera une \idx{séquence de contrôle} créée par \TeX{} et qui, par l'intermédiaire de la primitive \idx\toksdef, rendra \verb|\<nom>| équivalent à \idx\toks suivi du prochain numéro de registre inutilisé. Grâce à ce système, on peut donc demander en toute sécurité l'allocation d'un registre de tokens et y faire référence ensuite par un nom, ce qui est bien plus pratique à retenir qu'un numéro. + +Pour effectuer une assignation à un registre de tokens, il faut écrire + +\centrecode-<registre de token>= {<ensemble de token>}- +\noindent ou bien +\centrecode-<registre de token>= <registre de token>- + +\noindent où \verb|<registre de token>| est soit une \idx{séquence de contrôle} définie par \idx\toksdef ou \idx\newtoks, soit explicitement \idx\toks\verb|<numéro>|. Le signe «=» et l'espace qui le suit sont facultatifs et seront souvent omis. + +Pour extraire du registre ce qu'il contient, on doit faire appel à la primitive \idx\the suivie du \verb|<registre de token>|. Voici un exemple où est sont créés deux registres \verb-\foo- et \verb|\bar| : + +\showcode/\newtoks\foo% allocation d'un nouveau registre¤\idx*\newtoks¤ +\foo={Bonjour le monde}% assignation +Contenu du registre {\tt\string\foo} : \the\foo.¤\idx*\the\idx*\tt\idx*\string¤ + +\newtoks\bar% allocation d'un autre registre +\bar=\foo% assigne à \bar les tokens du registre \foo +Contenu du registre {\tt\string\bar} : \the\bar.¤\idx*\string¤/ + +Comme pour les séquences de contrôle, si l'assignation d'un registre à tokens se fait à l'intérieur d'un groupe, elle est annulée lors de sa fermeture à moins de faire précéder l'assignation de \idx\global\idx*[|)]{registre!token}\idx*[|)]{token!registre}\idx*{assignation!registre de tokens}. + +\chapter{Arguments d'une commande} +Les commandes vues jusqu'à présent étaient immuables en ce sens que leur texte de remplacement était figé une bonne fois pour toutes. Avec cette limite, elles ne seraient ni très puissantes ni très intéressantes. Heureusement, les commandes peuvent admettre des « arguments », variables eux, qui seront lus chaque fois que la commande sera appelée et qui seront insérés à des endroits du texte de remplacement choisis par l'utilisateur. + +\section{\Qu 'est ce qu'un argument ?} + +\begin{regle} + Un \idx*{argument}argument d'une macro est : + + \begin{itemize} + \item soit un \idx{token} c'est-à-dire un caractère (un \idx{octet}) ou une \idx{séquence de contrôle}; + \item soit le code qui se trouve entre deux accolades, étant entendu que ce code est un ensemble de tokens équilibrés en accolades ouvrantes et fermantes. + \end{itemize} +\end{regle} + +\noindent Par exemple, si on les considère comme des arguments de commandes : +\begin{itemize} + \item «\verb-a-» représente un argument qui est «\verb-a-»; + \item «\verb-abc-» représente 3 arguments qui sont «\verb-a-», «\verb-b-» et «\verb-c-»; + \item «\verb*-ab c-» constitue 3 arguments aussi, les mêmes que ci-dessus. En effet, dans une liste d'arguments, un espace sous forme d'unité lexicale est ignoré. Pour qu'il soit pris en compte l'espace \emph{doit} être mis entre accolades : «\verb*- -» n'est pas un argument alors que «\verb*-{ }-» en est un. + \item «\verb-{abc}-» est un argument unique qui est «abc»; + \item «\verb|\foo\bar|» sont deux arguments, «\verb|\foo|» et «\verb|\bar|»; + \item «\verb|{\foo\bar}|» est un seul argument qui est «\verb|\foo\bar|»; + \item enfin, «\verb*-{\def\AA{a}}{ }{c{d}e}{f}-» est constitué de quatre arguments : + \begin{enumerate} + \item \verb-\def\AA{a}- + \item \verb*- - + \item \verb-c{d}e- + \item \verb-f- + \end{enumerate} +\end{itemize} + +\label{argument.espace}\begin{regle} +Un espace \emph{non entouré d'accolades} est ignoré en tant qu'argument.\idx*[!argument]{espace}\idx*[!espace]{argument} +\end{regle} + +Il faut retenir que les accolades ne servent qu'à délimiter l'argument et n'en font pas partie. Lorsque \TeX{} lit cet argument, il lit le texte se trouvant à intérieur des accolades et dépouille donc l'argument de ses éventuelles accolades extérieures\idx*[!délimiteur d'argument]{accolade}. + +\begin{regle} +Lorsque des accolades délimitent un argument d'une macro, elles ne jouent pas le rôle de délimiteur de \emph{groupe}\idx*{argument!accolades}. +\end{regle}\idx*[|(]{argument!accolades} + +\begin{exercice} +Si on les considère comme arguments d'une macro, «\verb-a-» et «\verb-{a}-» sont-ils différents ? Et si oui, en quoi ? +\solution +Non, s'ils sont les arguments d'une macro, «\verb-a-» et «\verb-{a}-» sont des arguments rigoureusement identiques et donc, si \verb|\foo| est une macro admettant un seul argument, il sera équivalent d'écrire +\begin{itemize} + \item \verb*|\foo a| + \item \verb|\foo{a}| +\end{itemize} + +En revanche, si l'on souhaite donner la macro \verb|\bar| comme argument, il y aura une différence entre +\begin{itemize} + \item \verb*|\foo\bar| + \item \verb|\foo{\bar}| +\end{itemize} +Dans le premier cas, si un espace est à suivre, il sera ignoré tandis qu'il sera bien pris en compte dans le second cas puisque venant à la suite d'une accolade et non pas d'une \idx{séquence de contrôle}. + +Certains prennent l'habitude de ne jamais entourer d'accolades un argument constitué d'une seule unité lexicale. Il reste à savoir si cette habitude est bonne ou pas\ldots{} Je pencherais plutôt pour dire qu'elle est bonne puisqu'elle facilite la lisibilité en ne surchargeant pas le code d'accolades inutiles. Cependant, la présentation du code étant souvent une affaire de gouts personnels, d'autres trouveraient des arguments imparables pour préférer le contraire ! +\end{exercice}\idx*[|)]{argument!accolades} + +Pour définir une commande qui admet des arguments\idx*{argument!token de paramètre (\char`\#)|etc}, on utilise le token «\cidx[ (token de paramètre)]\#», de catcode \number\catcode`\#, dit «token de paramètre\idx*[!de paramètre]{token}» que l'on fait suivre d'un entier qui représente le numéro de l'argument. Cet entier doit être compris entre 1 et 9, ce qui signifie qu'une macro ne peut admettre que 9 arguments au maximum. + +Voici une macro «\verb|\hello|», très simple, qui admet deux arguments et qui affiche « Bonjour \verb|<argument 1>| et \verb|<argument 2>|.» et qui compose le paragraphe courant à l'aide de \idx\par : + +\showcode/\def\hello#1#2{Bonjour #1 et #2.\par}% définition +\hello ab% #1=a et #2=b +\hello a b% #1=a et #2=b +\hello{foo}{bar}% #1=foo et #2=bar +\hello foobar% #1=f et #2=o +% (le reste "obar" est lu après que la macro est terminée)/ + +Comme on le constate, écrire + +\centrecode-\def\hello#1#2{Bonjour #1 et #2.\par}- + +\noindent signifie que la commande \verb-\foo- admet deux arguments qui seront écrits «\verb-#1-» et «\verb-#2-» dans le \idx{texte de remplacement} de la macro. Lorsque \TeX{} va rencontrer \verb|\hello|, il lira \emph{aussi} les deux arguments qui suivent la commande. Il remplacera ces trois choses par le \idx{texte de remplacement} de la macro où chaque \verb-#1- sera l'exact contenu du premier argument et chaque \verb-#2- celui du second. +\grandsaut + +Lorsqu'on utilise \idx\def pour définir une macro, ce qui est situé entre le nom de la macro et l'accolade ouvrante qui délimite son texte de remplacement est appelé «\idx{texte de paramètre}». Dans l'exemple ci-dessus, ce texte de paramètre est \verb-#1#2- et spécifie que la commande \verb-\foo- admet 2 arguments. Dans le texte de paramètre, les entiers qui suivent les caractères \verb-#- doivent commencer à 1 pour le premier et aller en croissant de un en un. Il faut également que les arguments rencontrés dans le \idx{texte de remplacement} existent \emph{aussi} dans le \idx{texte de paramètre} sinon, \TeX{} émet un message d'erreur «\verb|Illegal parameter number in definition of \<macro>|\footnote{Nombre de paramètres incorrect dans la définition de \texttt{\char`\\\codeelement{macro}}.}». On ne peut donc pas écrire + +\centrecode-\def\foo#1#2{Bonjour #1, #2 et #3}- + +\noindent car l'argument \verb|#3| n'est pas spécifié dans le \idx{texte de paramètre}. + +\begin{regle} +Une macro peut accepter de 0 à 9 arguments\idx*{argument!nombre d'arguments}. + +Pour le spécifier, on indique juste après \idx\def, dans le «\idx{texte de paramètre}», les arguments les uns à la suite des autres sous la forme «\verb|#1|», «\verb|#2|», et ainsi de suite jusqu'au nombre d'arguments que l'on veut donner à la macro. + +Le \idx{texte de paramètre} est strict sur l'ordre des arguments, mais en revanche, il n'y a aucune contrainte sur l'ordre et l'existence des arguments dans le \idx{texte de remplacement} de la macro. On peut donc les y écrire dans l'ordre que l'on veut, autant de fois que l'on souhaite, voire ne pas en écrire certains. +\end{regle} + +\begin{exercice} +Comment s'y prendre pour qu'une commande traite plus de 9 arguments, par exemple 10 ? + +Programmer une macro \verb-\tenlist- qui lit 10 arguments (qui peuvent être par exemple les 10 premières lettres de l'alphabet) et les affiche de cette façon : + +\begin{centrage}(a,b,c,d,e,f,g,h,i,j)\end{centrage} + +\solution +On ne peut pas, \emph{stricto sensu}, programmer une macro qui admet 10 arguments, c'est une limitation intrinsèque à \TeX. Mais rien n'empêche d'en programmer une qui admet 9 arguments, traiter ces arguments comme on l'entend et une fois fini, passer la main à une autre commande qui admettra un argument, le lira et le traitera à son tour. + +On va donc écrire : + +\centrecode-\def\tenlist#1#2#3#4#5#6#7#8#9{% + (#1,#2,#3,#4,#5,#6,#7,#8,#9\endlist +} +\def\endlist#1{,#1)}- + +Lorsque \TeX{} rencontrera la macro \verb-\tenlist-, il l'absorbera ainsi que les 9 arguments suivants (et uniquement eux) et il aura donc avancé juste devant le 10\ieme{} argument. Il procèdera à la substitution de la macro et ses arguments par le texte de remplacement et poursuivra sa lecture en tenant compte de la substitution. Ce texte de remplacement va procéder à l'affichage des 9 premiers arguments en les séparant par une virgule, puis \TeX{} rencontrera à la fin du \idx{texte de remplacement} la macro \verb-\endlist-. Comme elle n'est suivie de rien dans le texte de remplacement, elle ira chercher son argument plus loin dans le code, et ce qu'il y a après justement, c'est le 10\ieme{} argument. + +\showcode/\def\tenlist#1#2#3#4#5#6#7#8#9{(#1,#2,#3,#4,#5,#6,#7,#8,#9\endlist} +\def\endlist#1{,#1)} +Une liste \tenlist abcdefghij de lettres./ +\end{exercice} + +Voici l'exemple de la macro \verb-\foo- que l'on a défini précédemment et que l'on met à l'épreuve dans différents cas : + +\showcode/\def\foo#1#2{Bonjour #1 et #2.\par} +\begingroup\tt% passer en fonte à chasse fixe¤\idx*\begingroup\idx*\tt¤ +a) \foo{monsieur}{madame} +b) \foo{{\bf toi}}{moi} +c) \foo{}{{}} +d) \foo{ }{ } +e) \foo{$\pi$} {$\delta$} +f) \foo ab +g) \foo X Y +\endgroup% fin de la fonte à chasse fixe¤\idx*\endgroup¤/ + +\noindent Le cas b contient un argument lui-même entre accolades, c'est-à-dire dans un groupe. En effet, si les accolades intérieures n'existaient pas, cet argument serait «\verb-\bf moi-» et la macro \idx\bf qui ordonne le passage en fonte grasse ne serait pas contenue dans un groupe pour en limiter sa portée. Tel qu'il est écrit à la ligne 4, le second argument est donc \verb-{\bf moi}- et donc le texte de remplacement de \verb-\foo{toi}{{\bf moi}}- est : + +\centrecode-Bonjour toi et {\bf moi}.\par- + +On remarque ensuite au cas c que les deux arguments ne produisent rien. Le premier parce que c'est un + \idx[!vide]{argument} vide et le second parce que c'est \verb-{}- qui est un groupe vide qui lui non plus ne produit non plus aucun affichage. Pour mieux comprendre ce qui se passe à cette ligne 5, le texte de remplacement de \verb-\foo{}{{}}- est : + +\centrecodespc-Bonjour et {}.\par- + +L'argument \verb|#1|, qui est vide, se trouve entre les deux premiers espaces qui par conséquent deviennent des espaces consécutifs, mais ils ne le sont pas \emph{dans le code source} ! La règle de \TeX{} concernant les \idx[!consécutifs]{espace} consécutifs ne s'applique donc pas comme on peut l'observer à l'affichage. + +\begin{regle} +Si l'argument \verb|<i>| d'une macro est vide, cela équivaudra dans le texte de remplacement de la macro à ce que \verb-#<i>- n'existe pas\idx*{argument!vide}. +\end{regle} + +Le sens d'un argument «vide» n'est pas évident. On entend ici que l'argument vide est ce qui est entre les accolades dans «\verb-{}-». Si l'on préfère, un argument vide n'est constitué d'aucun token. + +Ce qui se passe au cas d est tout aussi intéressant. Les deux arguments sont des espaces \emph{entre accolades}, ils sont donc pris en compte. Le texte de remplacement est ici : + +\begin{centrage} +\small\verb*-Bonjour -\fboxsep0.5pt\fbox{\ttfamily\vphantom A\textvisiblespace}\verb*- et -\fbox{\ttfamily\vphantom A\textvisiblespace}\verb-.- +\end{centrage} + +\noindent Les arguments \verb-#1- et \verb-#2-, qui sont des espaces, ont été encadrés. L'espace du code «\verb*- -», suivi de l'espace qui provient de l'argument {\fboxsep0.5pt\fbox{\ttfamily\vphantom A\textvisiblespace}}, ne sont pas des espaces consécutifs \emph{du code}. La règle qui veut que deux espaces consécutifs du code n'en font qu'un \emph{ne s'applique pas}. À l'affichage, on a donc \emph{trois} espaces entre «Bonjour» et «et» puis deux entre «et» et le point final. +\grandsaut + +Lorsque \TeX{} lit le \idx{texte de paramètre} puis le \idx{texte de remplacement} d'une macro pour la définir et stocker les tokens qui résultent de cette lecture, il entre dans ce qu'on appelle la \emph{\idx{lecture à grande vitesse}}. Très peu de vérifications sont faites à ce moment : + +\begin{enumerate} + \item dans le \idx{texte de paramètre}, la déclaration des arguments \verb|#<chiffre>| n'est acceptée que si les \verb|<chiffres>| commencent à 1 et vont en croissant de un en un; + \item une occurrence de \verb|#<chiffre>| dans le \idx{texte de remplacement}, symbolisant l'emplacement d'un argument, est acceptée uniquement si cet argument a été déclaré dans le \idx{texte de paramètre}; + \item \TeX{} vérifie que les macros rencontrées dans le \idx{texte de remplacement} ne sont pas déclarées «\idx\outer» (voir page~\pageref{outer}); + \item la \idx{lecture à grande vitesse} s'arrête lorsque l'accolade ouvrante marquant le début du \idx{texte de remplacement} est équilibrée par une accolade fermante. À ce moment, le stockage du \idx{texte de remplacement} sous forme de tokens a lieu dans la mémoire de \TeX. +\end{enumerate} + +L'argument d'une commande ne peut pas contenir la primitive \idx\par. Cette interdiction est en fait un mécanisme de sécurité pour que, si jamais on a oublié de fermer l'accolade qui marque la fin d'un argument, \TeX{} ne tente pas de lire indéfiniment (jusqu'à la fin du document) cet argument : au premier \idx\par rencontré, la compilation est stoppée avec affichage d'un message d'erreur, ce qui limite la zone où l'on doit chercher où il manque une accolade fermante dans le code source. Il est parfois nécessaire de passer outre cette protection si l'on souhaite écrire une macro dont un argument est susceptible de contenir \idx\par. Pour déclarer une macro de ce type, il faut faire précéder \idx[|etc]\def\forbidindex\def{} de la primitive \idx\long. Dans ce cas, on dit que la macro est « longue » par opposition aux autres macros « courtes ». + +Il faut noter que si on définit un alias pour \idx\par comme plain-\TeX{} le fait avec \verb-\let\endgraf=\par-, alors, l'alias \idx\endgraf est permis dans l'argument d'une macro même si celle-ci n'est pas déclarée \idx\long. En effet, les arguments d'une macro sont eux aussi lus à grande vitesse\idx*{lecture à grande vitesse} avant d'être insérés sous forme de tokens aux endroits qui leur sont réservés dans le texte de remplacement. Lors de cette \idx{lecture à grande vitesse}, \TeX{} ne tient pas compte de la \verb|\let| égalité; il ne fait que chercher \verb|\par|. L'alias \verb|\endgraf| est parfois bien pratique puisqu'avec cet artifice, tout se passe comme si l'on pouvait mettre un \verb|\par| dans les macros courtes sans avoir besoin de les réécrire pour qu'elles soient longues. + +\begin{regle} +Une macro ne peut admettre la primitive \idx\par dans aucun de ses arguments. Elle admet en revanche toute \idx{séquence de contrôle} \verb|\let|-équivalente à \verb|\par|, par exemple \idx\endgraf, définie par plain-\TeX. + +Pour définir une macro capable d'accepter \idx\par dans ses arguments, on doit faire précéder \verb|\def| de la primitive \idx\long. +\end{regle} + +\section{Perte d'informations lors de la « tokénization »}\label{perte.information.lecture} +Examinons maintenant comment et quand sont lues les « composantes » d'une macro, à savoir son texte de remplacement et ses arguments, et surtout quelles sont les conséquences de cette lecture. + +Tout d'abord, il est utile de comprendre que plusieurs moments différents interviennent lorsqu'on utilise une macro : + +\begin{itemize} + \item le moment où elle est définie; + \item le (ou les) moment(s) où elle est appelée. +\end{itemize} + +\begin{regle} +Lorsqu'une macro est \emph{définie}, les arguments figurant dans le \idx{texte de remplacement} sous la forme \verb|#<chiffre>| (où «\verb|#|» symbolise un token ayant le catcode \number\catcode`\#\relax{} au moment de la définition) sont des endroits où \TeX{} mettra les arguments lus plus tard lorsque la macro sera appelée. + +Lors de sa \idx{lecture à grande vitesse}, le reste du texte de remplacement est « tokénizé » c'est-à-dire que les caractères composant le code source sont transformés en tokens et les catcodes qui leur sont assignés obéissent au régime en cours lorsque la définition a lieu. Ces tokens sont stockés dans la mémoire de \TeX. +\medskip + +Lorsqu'une macro est \emph{appelée} et qu'elle \emph{lit} ses arguments, ceux-ci sont lus à grande vitesse\idx*{lecture à grande vitesse} et sont tokénizés selon le régime de catcode en vigueur cette macro est appelée (et qui peut donc différer de celui en cours lors de sa définition). Ils sont ensuite insérés aux endroits où \verb|#<chiffre>| était écrit dans le texte de remplacement. Les tokens du \idx{texte de remplacement} ne sont pas modifiés et restent ce qu'ils étaient lors de la définition. +\end{regle} + +Plus généralement, quels que soient le moment et les circonstances, lorsque \TeX{} lit du code source, les caractères du code source sont transformés en tokens. Cette transformation est faite selon le régime de catcode en vigueur au moment de la lecture et des règles spécifiques à chaque catégorie sont alors appliquées. Il est important de savoir que transformer des caractères en tokens provoque d'irréversibles pertes d'informations. En voici quelques-unes : + +\begin{enumerate} + \item plusieurs \idx[!consécutifs]{espace}s consécutifs sont lus comme un espace unique; + \item les \idx[!début de ligne]{espace}s au début d'une ligne du code source sont ignorés ; + \item les espaces qui suivent une \idx{séquence de contrôle} sont ignorés; + \item un caractère de \idx*{catcode!5 (retour charriot)}catcode 5 (en général le retour charriot «\verb|^^M|») est lu comme un espace\idx*[!5 (retour charriot)]\catcode\verbidx*[ (retour charriot)]{^^M}; + \item deux caractères consécutifs de catcode 5 sont lus comme la primitive \idx\par\verbidx*[ consécutifs]{^^M}; + \item deux caractères identiques de catcode \number\catcode`\^{}\idx*{catcode!7 (exposant)} (en général le caractère de mise en exposant «\verb|^|») suivis d'un caractère ou de deux chiffres hexadécimaux sont lus comme un caractère unique\verbidx*{^^}\iffalse i\fi; + \item le caractère de code \idx\endlinechar est inséré à chaque fois qu'une \idx{fin de ligne} de code source est rencontrée ; + \item dans une ligne de code source, tout ce qui se trouve après un caractère de catcode \number\catcode`\%{} est ignoré (le caractère de commentaire est en général «\cidx\%»). +\end{enumerate} + +Par exemple, supposons qu'une macro \verb|\foo| est définie de cette façon sous le régime de catcode par défaut : + +\centrecodespc|\def\foo{Programmer en \TeX {} est facile}| + +\noindent Par la suite, en examinant le texte de remplacement de \verb|\foo|, il est impossible de savoir combien d'espaces il y avait après «\texttt{est}» dans le code source. Il est également impossible de savoir si \verb|\TeX| était suivi d'un espace, de plusieurs ou d'aucun. Et donc, une façon \emph{strictement équivalente} de définir \verb|\foo| aurait été : + +\centrecodespc|\def\foo{Program^^6der en \TeX{} est facile}| + +La primitive \idx\meaning peut nous aider à le constater. + +\showcode|\def\foo{Programmer en \TeX {} est facile} +\meaning\foo\par¤\idx*\meaning¤ +\def\foo{Program^^6der en \TeX{} est facile} +\meaning\foo¤\idx*\meaning¤| + +\begin{regle} +Lorsque \TeX{} lit du code source, d'irréversibles pertes d'informations ont lieu lorsque des \emph{caractères} sont transformés en \emph{\idx{token}s}. +\end{regle} + +Cette règle s'applique bien évidemment lorsque \TeX{} lit du code pour le stocker (que ce soit dans une macro ou dans un registre de tokens) ou pour l'exécuter. + +\begin{exercice} +Donner un autre exemple de perte d'information lorsque \TeX{} lit du code. +\solution +Si deux caractères ont le catcode 0\idx*{catcode!0 (caractère d'échappement)} (par exemple «\verb|\|» et «\verb-|-»), voici deux façons parfaitement équivalentes de définir une macro \verb|foo| : + +\centrecodespc-\def\foo{Programmer en |TeX{} est facile}- + +\noindent et + +\centrecodespc-\def\foo{Programmer en \TeX{} est facile}- + +Si par ailleurs, "\verb|<|" et "\verb|>|" ont comme catcode 1\idx*{catcode!1 (accolade)} et 2, alors, cette façon est également équivalente : + +\centrecodespc*-\def\foo<Programmer en \TeX<> est facile}- + +Une autre perte d'information est que les caractères ayant un catcode égal à 9 sont ignorés. +\end{exercice} + +\begin{exercice} +Comment faudrait-il s'y prendre pour que la lecture du code soit \emph{réversible}, c'est-à-dire pour qu'aucune perte d'information n'ait lieu ? +\solution +Il faudrait que le régime de catcode en vigueur soit très spécifique et que tous les caractères ayant un catcode de 0, 1, 2, 5, 6, 9, 10, 13, 14 et 15 aient un catcode de 12. On peut aussi carrément utiliser le régime où \emph{tous} les octets ont un catcode égal à 12. +\end{exercice} + +\section{\Qu elques commandes utiles} +Prenons maintenant le temps de définir des commandes utiles en programmation. Certes, il est encore tôt pour comprendre leur intérêt, mais nous le verrons plus tard, leur emploi est fréquent. + +La plus simple de toutes est pourtant très utile : elle lit un argument qu'elle fait disparaitre. Appelons-la\footnote{Cette macro est appelée {\ttfamily\string\@gobble} dans le noyau \LaTeX.} §\gobone pour «gobble one» qui signifie «mange un». Le code qui permet sa définition est d'une simplicité extrême : + +\centrecode-\def\gobone#1{}- + +\noindent Le texte de remplacement est vide, ce qui revient à dire que lorsque \TeX{} rencontre \verb-\gobone-, il lit également l'argument qui suit et remplace le tout par le texte de remplacement, c'est-à-dire rien. + +Pour rester dans la simplicité, voici une commande qui admet un argument et dont le texte de remplacement est cet argument. Appelons-la\footnote{Son nom dans le noyau \LaTeX{} est \texttt{\string\@firstofone}} §\identity\label{identity} : + +\centrecode-\long\def\identity#1{#1}- + +Voici maintenant deux commandes très utilisées. Elles admettent deux arguments. Le texte de remplacement de §\firstoftwo est son premier argument et celui §\secondoftwo est son second argument\footnote{Elles sont appelées \texttt{\string\@firstoftwo} et \texttt{\string\@secondoftwo} dans le noyau \LaTeX.}\label{firstoftwo} + +\centrecode-\long\def\firstoftwo#1#2{#1} +\long\def\secondoftwo#1#2{#2}¤§*\secondoftwo¤- + +On pourrait aller plus loin et définir, pourquoi pas, \verb|\thirdofsix| qui irait chercher le 3\ieme{} argument d'une série de 6 : + +\centrecode-\long\def\thirdofsix#1#2#3#4#5#6{#3}- + +\noindent Mais ces variantes sont un peu inutiles, car ce sont les macros §\firstoftwo et §\secondoftwo qui sont le plus souvent utilisées. Pourquoi est-il plus utile et fréquent de choisir entre \emph{deux} arguments ? L'explication tient au fait que ces macros sont utilisées après un test pour choisir une des alternatives, l'une correspondant au fait que le test est vrai et l'autre qu'il est faux. + +\begin{exercice} +Pour manger les deux arguments \verb-{<arg1>}- et \verb-{<arg2>}-, le code suivant ne fonctionne pas : +\centrecode-\gobone\gobone{<arg1>}{<arg2>}- + +Pourquoi ? Comment faudrait-il programmer la macro §\gobtwo pour qu'elle mange et fasse disparaitre les deux arguments qui la suivent ? +\solution +\TeX{} lit le code \emph{de gauche à droite}, il remplace donc dans un premier temps §\gobone et son argument, le deuxième §\gobone, par le texte de remplacement de §\gobone, c'est-à-dire rien. Les deux §\gobone disparaissent et il reste les deux arguments entre accolades qui seront affichés tous les deux ! + +La macro §\gobtwo se programme ainsi : \verb-\def\gobtwo#1#2{}- +\end{exercice} + +\begin{exercice} +\Qu 'obtient-on à l'affichage si on écrit : + +\centrecode-\gobone1\secondoftwo2{3\gobtwo}\firstoftwo45\secondoftwo{67}{\gobone890}¤§*\secondoftwo¤- + +\solution +Procédons de gauche à droite, comme \TeX{} : +\begin{itemize} + \item «\verb-\gobone 1-» disparait; + \item «\verb-\secondoftwo2{3\gobtwo}-» a comme texte de remplacement son 2\ieme{} argument qui est «\verb-3\gobtwo-». «3» est donc affiché et le \verb-\gobtwo- va manger les 2 arguments qui le suivent. Ils se trouvent en dehors de l'argument en cours et ce sont «§\firstoftwo\verb-4-» qui sont donc mangés; + \item «5» est affiché; + \item «\verb-\secondoftwo{67}{\gobone890}-» a pour texte de remplacement son 2\ieme{} argument qui est «\verb-\gobone890-». Le \verb|\gobone| mange le «8» qui disparait et il reste donc les tokens «90» qui seront affichés. +\end{itemize} + +On obtient donc «\verb-3590-» +\end{exercice} +\begin{exercice} +On constate que les macros §\gobone et §\secondoftwo donnent les mêmes résultats : + +\showcode/a) \gobone XY - \secondoftwo XY\par¤§*\gobone§*\secondoftwo¤ +b) \gobone{ab}{xy} - \secondoftwo{ab}{xy}\par +c) \gobone{123}4 - \secondoftwo{123}4\par +d) \gobone A{BCD} - \secondoftwo A{BCD}¤§*\gobone§*\secondoftwo¤/ + +Existe-t-il une différence entre les macros §\gobone et §\secondoftwo et si oui, laquelle ? +\solution +Il y a une différence, et de taille! On peut le voir sur cet exemple : + +\showcode/\gobone {a}{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ?¤\cidx*\~¤ + +\secondoftwo{a}{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ?¤§*\secondoftwo¤/ + +À la première ligne, lorsque §\gobone est développée, «\verb|\gobone{a}|» disparait et il reste ceci qui n'a pas encore été lu : + +\centrecode*-{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ?- + +Il est clair que la modification de catcode a lieu \emph{à l'intérieur d'un groupe} et que dans ce groupe, \cidx\~ devient un caractère « autre » que l'on peut afficher tel quel. En revanche, une fois sorti du groupe, il reprend son catcode naturel de 13 et redevient donc actif pour afficher une espace insécable. + +Tout est différent dans la deuxième ligne. La macro §\secondoftwo lit ses \emph{deux} arguments et «redonne» le second une fois qu'il a été lu. Voici donc ce qu'il reste après que §\secondoftwo ait été remplacé pare son 2\ieme{} argument : + +\begin{centrage} + \small\ttfamily\fboxsep2pt\fbox{\string\catcode`\string\~12 Le <{}<\string~>{}> est un espace \string?} \Verb|Et ici <<~>> ?| +\end{centrage} + +Le 2\ieme{} argument, ici encadré et qui s'étend jusqu'au premier «\string?» a été lu par §\secondoftwo et donc, le \idx{catcode} de «\string~» est figé à 13. Par conséquent, «\string~» gardera dans tout l'argument encadré le \idx{catcode} 13\idx*{catcode!13 (actif)} qu'il avait lorsque §\secondoftwo a été appelé. L'ordre \idx\catcode\verb|`\~12| rencontré dans l'argument ne pourra agir qu'après cet argument : \cidx\~ prendra donc le \idx{catcode} 12\idx*{catcode!12 (autre)} après la fin de l'argument et ce en-dehors de tout groupe. Il gardera donc ce \idx{catcode} indéfiniment c'est-à-dire jusqu'à ce qu'il soit volontairement modifié. +\grandsaut + +Pour résumer : +\begin{itemize} + \item de «§\gobone\verb|{<a>}{<b>}|», il reste \verb|{<b>}|qui n'est pas encore lu; + \item tandis que §\secondoftwo\verb|{<a>}{<b>}| lit le second argument, le dépouille de ses accolades et le tout devient donc \verb|<b>| où les catcodes sont figés. +\end{itemize} +\end{exercice} + +\begin{exercice} +Le but est d'afficher l'énoncé et la correction d'une courte interrogation de grammaire élémentaire, tous deux stockés dans la macro \verb-\interro- ainsi définie : + +\centrecode-\def\interro{J'ai ét\?{é} invit\?{é} à goût\?{er} chez Max. + On s'est bien amus\?{é} et ensuite, il a fallu + rentr\?{er}.}- + +Écrire une macro \verb-\?- qui admet un argument. Cet argument sera affiché tel quel si la commande \verb-\visible- a été rencontrée auparavant, et si c'est la commande \verb-\cache-, cet argument sera remplacé par «...». Les commandes \verb-\cache- et \verb-\visible- n'admettent pas d'argument. + +On écrira «\verb-\cache\interro-» ou «\verb-\visible\interro-» selon que l'on souhaite afficher l'énoncé ou la correction. +\solution +L'idée est que le texte de remplacement de \verb-\?{<argument>}- doit être + +\centrecode-\choix{<argument>}{...}- + +\noindent où \verb-\choix- sera rendu égal à §\firstoftwo par \verb-\visible- et à §\secondoftwo par \verb-\cache- : + +\showcode/\def\visible{\let\choix=\firstoftwo}¤§*\firstoftwo¤ +\def\cache{\let\choix=\secondoftwo}¤§*\secondoftwo¤ +\def\?#1{\choix{#1}{...}}% +\def\interro{J'ai ét\?{é} invit\?{é} à gout\?{er} chez Max. Après s'être + bien amus\?{é}, nous avons rang\?{é} et il a fallu rentr\?{er}.} +Compléter avec << é >> ou << er >> :\par +\cache\interro\par\medskip¤\idx*\medskip¤ +Correction :\par +\visible\interro/ +\end{exercice} + +\section{Définitions imbriquées} +À l'exercice précédent, on aurait pu procéder de façon plus « économique », sans avoir besoin de définir une macro auxiliaire \verb|\choix|. La macro \verb|\visible| aurait pu être programmée pour définir \verb|\?| \idx\let{}-égale à §\identity (qui affiche son argument tel quel) alors que la macro \verb|\cache| aurait défini la macro \verb|\?| de cette façon : + +\centrecode-\def\?#1{...}- + +\noindent Mais si on écrit + +\centrecode-\def\cache{\def\?#1{...}}- + +\noindent \TeX{} émet un message d'erreur : «\texttt{Illegal parameter number in definition of \string\cache}». L'explication est que l'argument \verb|#1| est compris comme étant celui de la macro \emph{extérieure} \verb|\cache| et non pas comme celui de la macro intérieure \verb|\?|. L'erreur survient, car la macro extérieure \verb|\cache| est définie comme n'ayant \emph{aucun} argument. + +Lorsque des définitions de macros sont imbriquées, la règle veut que pour distinguer les arguments des macros, il faut doubler les tokens de paramètre \verb|#| à chaque niveau d'imbrication supplémentaire. Ainsi, la bonne façon de définir \verb|\cache| est : + +\centrecode-\def\cache{\def\?##1{...}}- + +Voici le code complet : + +\showcode/\def\visible{\let\?=\identity}¤§*\identity¤ +\def\cache{\def\?##1{...}} +\def\interro{J'ai ét\?{é} invit\?{é} à goût\?{er} chez Max. On s'est + bien amus\?{é} et ensuite, il a fallu rentr\?{er}.} +Compléter avec << é >> ou << er >> :\par +\cache\interro\par\medskip¤\idx*\medskip¤ +Correction :\par +\visible\interro/ + +\begin{regle} +Lorsqu'on définit une macro « fille »\idx*{macro fille} dans le \idx{texte de remplacement} d'une macro « mère », les \verb-#- concernant les arguments de la macro fille sont doublés, aussi bien dans le \idx{texte de paramètre} que dans le \idx{texte de remplacement}. + +Ils seront doublés à nouveau à chaque imbrication + supplémentaire pour éviter de confondre les arguments des macros imbriquées. +\end{regle} +\grandsaut + +À la lumière de ce qui vient d'être dit, examinons maintenant le code suivant : + +\centrecodespc-\def\makemacro#1#2{\def#1##1{##1 #2}}- + +On constate que \verb|\makemacro| admet deux arguments et que le premier est nécessairement une \idx{séquence de contrôle} puisque cet argument \verb|#1| se trouve juste après le \verb|\def| dans le texte de remplacement. + +Faisons mentalement fonctionner la macro \verb|\makemacro| lorsqu'on l'appelle de cette façon : \verb|\makemacro\foo{BAR}|. Comme on le sait, le texte de remplacement de ce code va être obtenu en remplaçant chaque \verb|#1| par \verb|\foo| et chaque \verb|#2| par \verb|BAR| ce qui donne : + +\centrecodespc-\def\foo##1{##1 BAR}- + +\noindent Puisqu'on examine le code contenu dans le texte de remplacement, il devient inutile de doubler les tokens de paramètre et donc, tout se passe donc comme si la macro \verb|\foo| était définie par : + +\centrecodespc-\def\foo#1{#1 BAR}- + +Le code obtenu ne présente pas de difficulté particulière, l'argument \verb|#1| de \verb|\foo| est placé devant l'argument \verb|#2| de \verb|\makemacro| qui est \verb|BAR|. Par exemple, si l'on écrivait «\verb-\makemacro\mai{mai 2014}-», alors par la suite, \verb-\mai{10}- aurait comme texte de remplacement «\texttt{10 mai 2014}». + +\showcode/\def\makemacro#1#2{\def#1##1{##1 #2}} +\makemacro\LL{Lamport} +\makemacro\juin{juin 2014} +Paris, le \juin{3}.\medbreak¤\idx*\medbreak¤ +\LL{Cher Monsieur},\smallbreak¤\idx*\smallbreak¤ +Vous êtes convoqué à un examen sur \TeX{} le \juin{21} à +9h00 précise dans le bureau de D. Knuth.\smallbreak¤\idx*\smallbreak¤ +Veuillez croire, \LL{Monsieur}, en mes sentiments \TeX iens./ + +\section{Programmer un caractère actif} +Le niveau de \TeX nicité atteint dans cette section est plutôt relevé. On pourra donc, lors d'une première lecture, se dispenser de se plonger dans cette section, quitte à y revenir plus tard. +\grandsaut + +Avant de rentrer dans le vif du sujet, rappelons ce qu'est réellement un caractère. Aux yeux de \TeX{} lorsqu'il le lit, un caractère (c'est-à-dire un token n'étant pas une \idx{séquence de contrôle}) est constitué de : +\begin{itemize} + \item un code dit «\idx{code de caractère}» qui représente l'ordre du caractère dans la table des caractères. Par exemple, le caractère «\verb|A|» a le code de caractère \number`A{} tandis que «\verb|^|» a le code \number`\^. Il y a 256 codes\footnote{Pour les moteurs 8 bits, il y a effectivement 256 codes possibles. En revanche, pour les moteurs \utf, il y a en a bien plus.} possibles; + \item un code dit «code de catégorie» ou «catcode» qui met le caractère dans une des 16 catégories et lui confère les propriétés communes à tous les caractères de cette catégorie. Ainsi, «\verb|A|» a le code de catégorie \number\catcode`A, et «\verb|^|» le code de catégorie \number\catcode`\^. +\end{itemize} + +Chaque caractère lu possède donc intrinsèquement une \emph{paire de codes}. On peut convenir d'une notation et les mettre entre parenthèses, le premier étant le code de caractère et le second le catcode. Cela donnerait : \showcodes{A} et \showcodes^. +\grandsaut + +L'objet ici est de découvrir comment on peut programmer une macro --~appelons-la §\defactive~-- qui rend actif un caractère choisi par l'utilisateur et qui lui donne un texte de remplacement donné. Ainsi, «\verb-\defactive W{wagons}-» fera qu'après cet appel, le caractère «\verb|W|» sera actif et son texte de remplacement sera «wagons». + +Comme nous l'avons vu, si on modifie un catcode dans le texte de remplacement d'une macro, la modification n'entrera en vigueur qu'après ce texte de remplacement. En effet, la règle vue à la page~\pageref{catcode.inalterable} veut que lorsqu'un argument est lu, les catcodes des tokens qui le composent sont figés. Il est donc exclu de programmer la macro de cette façon + +\centrecode-\def\defactive#1#2{\catcode`\#1=13 \def#1{#2}}-§*\defactive + +\noindent car, malgré l'ordre \verb|\catcode`\#1=13|, le catcode de \verb-#1- ne serait pas modifié à l'intérieur du texte de remplacement de §\defactive. Par exemple, si \verb-#1- est «\verb|W|», son catcode restera 11 (celui que \verb|W| a par défaut) et \TeX{} va échouer lorsqu'il rencontrera \verb|\def|\verb- W{wagons}-, car «\verb|W|» ne sera pas encore actif. + +Évidemment, on l'a vu aussi, on pourrait utiliser un \idx\gdef et agir dans un groupe où l'on aurait déjà modifié le catcode de «\verb|W|» à 13. Cette méthode est possible lorsqu'on connaît \emph{à l'avance} le caractère à rendre actif. Or, ce n'est pas le cas ici puisque l'argument \verb-#1- de la macro est choisi par l'utilisateur. +\grandsaut + +Intéressons-nous par exemple à la primitive \idx\lowercase{} car les fonctionnements de \idx\lowercase{} et \idx\uppercase{} sont symétriques : il suffit de comprendre comment fonctionne l'une pour comprendre comment fonctionne de l'autre. + +Avant d'entrer dans le vif du sujet, il faut mentionner qu'en plus du code de catégorie et du code de caractère, chaque caractère possède deux autres codes : + +\begin{itemize} + \item le \idx{code minuscule} accessible par la primitive \idx\lccode; + \item le \idx{code majuscule} accessible par la primitive \idx\uccode. +\end{itemize} + +Par exemple, le code de caractère de la lettre «A» est {\number`\A} et son code minuscule est {\number`\a} qui est celui de la lettre «a». Il en est ainsi pour chaque lettre majuscule de l'alphabet qui possède un code minuscule correspondant à celui de la lettre minuscule. Grâce à la primitive \idx\the ou \idx\number, on peut afficher les codes de caractère et les codes minuscules des lettres A, B, et même de a, b. On va utiliser ici une macro \verb-\showcodes- pour alléger le code : + +\showcode/\def\showcodes#1{\string#1 : $\number`#1 \rightarrow \number\lccode`#1$}¤\idx*\string\idx*\number\idx*\rightarrow\idx*\the\idx*\lccode¤ +\showcodes A\qquad \showcodes B\qquad¤\idx*[|etc]\qquad\forbidindex\qquad¤ +\showcodes a\qquad \showcodes b\qquad \showcodes ^/ + +On observe que logiquement, le \idx{code minuscule} de «a» est le sien propre. \Qu ant au code minuscule de «\verb|^|», il est égal à 0 ce qui signifie que ce caractère n'a pas de code minuscule prédéfini. + +\begin{regle} +La primitive \idx\lowercase doit être suivie de son argument entre accolades et cet argument, constitué d'un texte dont les accolades sont équilibrées, est lu de la façon suivante : + +\begin{itemize} + \item les séquences de contrôles restent inchangées; + \item pour chaque caractère, le code de caractère est remplacé par le \idx{code minuscule}, étant entendu que le code de catégorie \emph{ne change pas}. + + Si le code minuscule d'un caractère est nul, ce caractère n'est pas modifié par \idx\lowercase. +\end{itemize} +\end{regle} + +Voici la primitive \idx\lowercase en action : + +\showcode/\lowercase{CACAO foobar}\par¤\idx*\lowercase¤ +\lowercase{\def\foo{Bonjour}}% définit une commande \foo +\foo % la commande définie précédemment/ + +On peut détourner la primitive \idx\lowercase de son usage premier qui est de mettre un texte en minuscule. Pour cela, il suffit de modifier le code minuscule d'un caractère. Supposons que nous souhaitions que le \idx{code minuscule} de «A» ne soit plus {\number`\a} qui est celui de la lettre «a», mais {\number`\*} qui est celui de «\verb-*-». Pour cela, il va falloir modifier le \idx\lccode de «A» à l'intérieur d'un groupe semi-simple, puisqu'il est toujours prudent de confiner les modifications lorsqu'on touche aux mécanismes internes de \TeX{} : + +\showcode/\begingroup¤\idx*\begingroup¤ + \lccode`A=`* % change le code minuscule de A¤\idx*\lccode¤ + \lowercase{CACAO ABRACADABRA}\par¤\idx*\lowercase¤ +\endgroup¤\idx*\endgroup¤ +\lowercase{CACAO ABRACADABRA}/ + +Les «*» que l'on obtient ont le catcode de A, c'est-à-dire 11. + +\begin{regle} +Si l'on écrit «\verb|\lccode<entier1>=<entier2>|» alors, dans l'argument de \verb|\lowercase|, tous les caractères de code \verb|<entier1>| seront lus comme des caractères de code \verb|<entier2>| tout en gardant leur catcode originel. + +En pratique, si l'on écrit + +\centrecode-\lccode`\<car1>=`\<car2>- + +puis + +\centrecode-\lowercase{<texte>}- + +alors, dans le \verb|<texte>|, tous les \verb|<car1>| sont lus comme \verb|<car2>|, mais en conservant le catcode de \verb|<car1>|. +\end{regle} + +Nous sommes prêts pour écrire la macro §\defactive et nous allons exploiter le fait que le caractère «\cidx\~» est naturellement actif. Grâce à la primitive \idx\lowercase, on va le transformer dynamiquement pour qu'il devienne le caractère \verb-#1- \emph{actif} de notre macro §\defactive : + +\centrecode|\def\defactive#1#2{%¤§*\defactive¤ + \catcode`#1=13 % #1 sera actif après la fin + % du texte de remplacement + \begingroup + \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) + % en "#1", ceux-ci étant actifs + \lowercase{\endgroup\def~}{#2}% +}| + +\begin{itemize} + \item «\idx\catcode\verb|`#1=13|»\idx*[!13 (actif)]\catcode rend le caractère \verb-#1- actif, mais comme nous l'avons vu, cette modification n'entrera en vigueur qu'après être sorti du texte de remplacement; + \item Le \idx\endgroup n'étant pas modifié par \idx\lowercase, le groupe semi-simple est donc bien refermé \emph{avant} d'effectuer la définition de \verb-#1-; + \item on peut remarquer que l'argument \verb-#2- se trouve \emph{en dehors} de l'argument de \idx\lowercase car sinon, toutes les lettres majuscules de \verb|#2| seraient transformées en minuscules. + + D'ailleurs, comme \verb-{#2}- est à la fin du texte de remplacement de la macro, il devient inutile de le lire puisqu'il sera lu juste après. On peut optimiser la macro de cette façon\label{argument.def} : + + \centrecode-\def\defactive#1{%¤§*\defactive¤ + \catcode`#1=13 + \begingroup + \lccode`~=`#1 + \lowercase{\endgroup\def~}}- +\end{itemize} + +Voyons cette macro à l'\oe uvre dans un exemple : + +\showcode/\def\defactive#1{%¤§*\defactive¤ + \catcode`#1=13 + \begingroup + \lccode`~=`#1 + \lowercase{\endgroup\def~}% +} +\begingroup% les modifications restent locales¤\idx*\begingroup¤ + \defactive W{wagons}% définit le caractère actif "W"¤§*\defactive¤ + Les W constituent les trains. +\endgroup %fermeture du groupe, "W" redevient une lettre¤\idx*\endgroup¤ +\par Les W sont des lettres/ + +\begin{exercice} +Supposons que «\verb-W-» soit rendu actif à l'aide de la macro §\defactive pour lui donner le texte de remplacement «wagons». Imaginons ensuite que l'on rende «\verb|W|» non actif avec \idx\catcode\verb-`\W=11-. + +\Qu estion : si l'on rend à nouveau \verb|W| actif, le texte de remplacement précédemment défini existe-t-il encore ou faut-il redéfinir son texte de remplacement ? +\solution +Il suffit de faire l'expérience : + +\showcode/\defactive W{wagon}¤§*\defactive¤ +1) Les W constituent le train.\par +2) \catcode`W=11 Les W sont des lettres.\par¤\idx*\catcode¤ +3) \catcode`\W=13 Les W constituent-ils le train ?/ + +La réponse est donc « oui ». + +Le \idx[!caractère actif]{texte de remplacement} d'un caractère actif n'est pas perdu si ce caractère change de catcode et redevient actif ensuite. +\end{exercice} + +\begin{exercice} +Décrire ce qui va se passer si on définit le caractère actif «\verb|W|» de la façon suivante : +\centrecode-\defactive W{Wagon}- + +\solution +Tout dépend de la version de §\defactive. La réponse est très différente selon que §\defactive lit 1 ou 2 arguments, c'est-à-dire si elle lit le texte de remplacement du caractère actif ou pas. + +Si elle le lit, le «\verb|W|» de «\verb|Wagon|» aura le catcode en vigueur au moment ou §\defactive lit son argument, c'est-à-dire 11\idx*{catcode!11 (lettre)}. Le texte de remplacement de «\verb|W|» sera «\verb|Wagon|» où toutes les lettres ont le catcode 11. + +\showcode|\def\defactive#1#2{%¤§*\defactive¤ + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs + \lowercase{\endgroup\def~}{#2}% + } +\defactive W{Wagon} +Un W.| + +Si §\defactive ne lit pas le texte de remplacement (version «optimisée»), le caractère «\verb|W|» sera actif à la fin du texte de remplacement et donc, lorsque \verb|\def W| lira le texte de remplacement entre accolades, le «\verb|W|» de «\verb|Wagon|» sera actif. On entrevoit ce qui va se passer lorsque \verb|W| sera exécuté. Dans un premier temps, \verb|W| va être remplacé dans la \idx{pile d'entrée} par + +\centrecode-Wagon- + +puis le premier «\verb|W|», actif, sera exécuté et donc, nous aurons dans la \idx{pile d'entrée} + +\centrecode-Wagonagon- + +Le \verb|W| actif généré sera à nouveau exécuté et ainsi de suite\ldots{} Une \idx{boucle infinie} va s'amorcer au cours de laquelle les caractères «\verb|agon|» seront ajoutés sur la \idx{pile d'entrée} à chaque boucle, provoquant rapidement le dépassement de sa capacité et générant le message d'erreur + +\errcode|\def\defactive#1{%¤§*\defactive¤ + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs + \lowercase{\endgroup\def~}% + } +\defactive W{Wagon} +Un W.|{\ttfamily ! TeX capacity exceeded, sorry [input stack size=5000]} + +La morale que l'on peut retirer de cet exercice est qu'une « optimisation », qui de prime abord peut sembler anodine (ne pas faire lire un argument par une macro), conduit à des limitations et à des erreurs de compilation qui n'existaient pas dans une version « non optimisée ». La programmation en \TeX{} est pavée d'embuches\ldots{} Il est donc nécessaire de parfaitement savoir ce que l'on fait, de connaitre le régime de catcodes en cours lorsqu'une lecture est faite et toujours rester vigilant dès lors qu'il est question de lecture d'argument. +\end{exercice} + +Les man\oe uvres décrites dans cette section reposent sur la supposition que «\cidx\~» est actif lorsque la macro §\defactive est \emph{définie}. Si ce n'est pas la cas et si, pour une raison ou pour une autre, «\cidx\~» a été neutralisé et mis dans la catégorie des caractères « autres » de catcode 12\idx*{catcode!12 (autre)}, la macro §\defactive ne pourra et provoquera une erreur de compilation lors de son exécution. +\grandsaut + +Tout ce qui a été dit avec les primitives \idx\lowercase et \idx\lccode est valable pour leurs symétriques, les primitives \idx\uppercase et \idx\uccode qui agissent sur le « \idx{code majuscule} » d'un caractère. On aurait d'ailleurs tout aussi bien pu procéder avec \idx\uppercase et \idx\uccode. + +\showcode|\def\defactive#1#2{%¤§*\defactive¤ + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \uccode`~=`#1 % dans \uppercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs¤\idx*\uccode¤ + \uppercase{\endgroup\def~}{#2}%¤\idx*\uppercase¤ +} +\defactive W{Wagon} +Un W.| + +Une macro à peu près similaire §\letactive\verb|<token><token>| est facile à programmer : + +\showcode|\def\letactive#1{%¤§*\letactive¤ + \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement + \begingroup + \uccode`~=`#1 % dans \uppercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs¤\idx*\uccode¤ + \uppercase{\endgroup\let~}%¤\idx*\uppercase¤ +} +\def\W{wagon}% +\letactive X\W¤§*\letactive¤ +Un X.| + +\section{Afficher du code tel qu'il a été écrit}\label{litterate}\idx*[|(]{verbatim} +Le but est d'inventer une commande qui désactive toutes les règles de \TeX{} pour que son argument soit affiché tel qu'il a été écrit dans le code source, ce que l'on appelle du «verbatim». Cette commande « §\litterate » va donc rendre inoffensifs tous les tokens de catcode autre que 11 ou 12, en changeant leur catcode pour 12. Ces tokens \emph{spéciaux} sont \boxtoken[]\textvisiblespace, \boxtoken[]\textbackslash, \boxtoken[]\{, \boxtoken[]\}, \boxtoken[]\$, \boxtoken[]\&, \boxtoken[]\#, \boxtoken[]{\char`\$}, \boxtoken[]\_, \boxtoken[]\% et \boxtoken[]{\char`\~}. Plain-\TeX{} définit\footnote{En plain-\TeX, les tokens \texttt{\string^\string^K} et \texttt{\string^\string^A} sont des équivalents pour «\texttt{\string^}» et «\texttt{\string_}».} la macro \idx\dospecials de cette façon : + +\indentcode[7em]-\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~}- + +La macro \idx\do peut être programmée comme on l'entend pour agir sur son argument. Ici, comme le but est de changer le catcode de cet argument pour 12, on va donc la programmer ainsi : + +\centrecode-\def\do#1{\catcode`#1=12 }- + +\noindent et il suffira d'appeler \idx\dospecials pour qu'à partir de ce moment, tous les tokens spéciaux soient inoffensifs et deviennent affichables. + +En ce qui concerne la syntaxe de §\litterate, il serait légitime d'écrire son argument entre accolades, mais cela rendrait la tâche compliquée. En effet, cette macro doit rendre tous les tokens spéciaux inoffensifs \emph{avant} de lire son argument. Par conséquent, les accolades délimitant l'argument ne seraient pas de \emph{vraies} accolades de catcode 1\idx*{catcode!1 (accolade)} et 2, mais des accolades de catcode 12\idx*{catcode!12 (autre)}. Pour savoir où se situe la fin de l'argument, il faudrait parcourir le code qui se trouve après §\litterate token par token, compter les accolades ouvrantes et fermantes jusqu'à obtenir l'égalité entre ces deux quantités qui signifierait la fin de l'argument. C'est faisable, mais inutilement compliqué et implique comme limitation que le code à afficher soit équilibré en accolades ouvrantes et fermantes. Le mieux est de décider à chaque fois d'un token (de catcode 11\idx*{catcode!11 (lettre)} ou 12\idx*{catcode!12 (autre)}) et de placer l'argument entre deux occurrences de ce token\footnote{C'est la syntaxe adoptée par la macro \texttt{\string\verb} de \LaTeX.}. On peut par exemple écrire + +\centrecode-\litterate|\foo # #34{ ~} &_\ |- + +\noindent et obtenir l'affichage «\verb|\foo # #34{ ~} &_\ |». La limitation évidente est que le token choisi comme délimiteur ne doit pas figurer dans l'argument. + +Du côté de la programmation, la macro §\litterate doit ouvrir un groupe semi-simple puis rendre les tokens spéciaux inoffensifs. À l'aide de la macro §\defactive vue à la section précédente, elle doit aussi rendre actif le retour à la ligne «\verb-^^M-\verbidx*[ (retour charriot)]{^^M}» et lui donner comme texte de remplacement \idx\par\idx\leavevmode. Le \idx\leavevmode, en quittant le mode vertical\idx*{mode!vertical} mis en place par \idx\par{}, entre dans le mode horizontal\idx*{mode!horizontal} et commence un nouveau paragraphe. Sans ce \idx\leavevmode, deux retours à la ligne consécutifs seraient équivalents à deux \idx[!consécutifs]\par et le second serait ignoré (voir règle page~\pageref{par.consecutifs}). De plus, le token frontière choisi doit aussi être rendu actif et pour devenir \idx\let-égal à \idx\endgroup; ainsi, lorsque le second token frontière sera rencontré, le groupe semi-simple sera fermé et tous les catcodes seront restaurés comme avant que la macro §\litterate n'entre en action. + +\showcode|\def\litterate#1{% #1=lit le token frontière choisi¤§*\litterate¤ + \begingroup% ouvre un groupe pour y faire les modifications¤\idx*\begingroup¤ + \def\do##1{\catcode`##1=12 }% \do change le catcode de son argument à 12¤\idx*\do\idx*\catcode¤ + \dospecials% rend inoffensifs tous les tokens spéciaux¤\idx*\dospecials¤ + \defactive\^^M{\leavevmode\par}% définit le retour charriot¤§*\defactive\idx*\leavevmode¤ + \letactive#1\endgroup% définit #1 qui sera un \endgroup¤§*\letactive¤ + \tt% passe en fonte à chasse fixe¤\idx*\tt¤ +} +essai 1 : \litterate*\foo # #34{ ¤\%¤} &_\ * +\medbreak¤\idx*\medbreak¤ +essai 2 : \litterate/une première ligne ,, >> + +un saut de ligne et la seconde --/| + +L'ensemble fonctionne bien à part les espaces qui sont rendus avec le mauvais caractère et les certaines \idx{ligature}s qui sont faites à l'essai \no2. + +Pour l'espace tout d'abord, cela s'explique car son catcode a été rendu égal à 12 par \idx\dospecials et le caractère «\verb*| |» se trouve à la position de l'espace dans la \idx{fonte} à chasse fixe. Pour contourner ce problème, il faut rendre l'espace actif après l'appel à \idx\dospecials et lui donner comme texte de remplacement la primitive \idx\ {}. + +Ensuite, pour les \idx{ligature}s, il faut déjà identifier quels sont les caractères doublés qui forment une \idx{ligature} en fonte à chasse fixe. Ces caractères sont : \boxtoken{`}, \boxtoken{<}, \boxtoken{>}, \boxtoken{,}, \boxtoken{'} et \boxtoken{-}. Pour éviter qu'ils ne forment une \idx{ligature}, il va falloir les rendre actifs et les faire suivre soit de «\verb|{}|», soit de \idx\relax soit encore de \idx\kern\verb|0pt|, ou plus généralement de quelque chose ne se traduisant par aucun affichage. Ainsi, ils sembleront se suivre à l'affichage, mais aux yeux de \TeX{}, ils seront séparés par un n\oe ud\footnote{Un n\oe ud est un élément, visible ou pas, qui entre dans la liste horizontale ou verticale que \TeX{} est en train de construire.} qui empêchera la formation de la \idx{ligature}. + +\showcode|\def\litterate#1{% #1=lit le token frontière choisi¤§*\litterate¤ + \begingroup% ouvre un groupe pour y faire les modifications¤\idx*\begingroup¤ + \def\do##1{\catcode`##1=12 }% \do change le catcode de son argument à 12¤\idx*\do\idx*\catcode¤ + \dospecials% rend inoffensifs tous les tokens spéciaux¤\idx*\dospecials¤ + \defactive\^^M{\leavevmode\par}% définit le retour charriot¤\idx*\leavevmode§*\defactive¤ + \defactive\ {\ }% l'espace est actif et devient "\ " + \defactive<{\string<{}}\defactive>{\string>{}}% empêche¤\idx*\string¤ + \defactive-{\string-{}}\defactive`{\string`{}}% les + \defactive,{\string,{}}\defactive'{\string'{}}% ligatures¤\idx*{ligature}\idx*\string§*\defactive¤ + \letactive#1\endgroup% #1 sera un \endgroup¤§*\letactive¤ + \tt% passe en fonte à chasse fixe¤\idx*\tt¤ +} +essai 1 : \litterate*\foo # #34{ %} &_\ * +\medbreak¤\idx*\medbreak¤ +essai 2 : \litterate/une première ligne ,, >> + +un saut de ligne et la seconde --/¤§*\litterate¤|\idx*[|)]{verbatim} + +\chapter{Arguments délimités}\idx*[|(]{argument!délimité} +\section{Théorie}\label{theorie} +Les commandes que l'on définit avec \verb|\def| peuvent également avoir des «arguments délimités». Ces arguments-là ne sont pas lus de la même façon que les arguments «non délimités» que nous avons vus précédemment. On indique à \TeX{} qu'une macro a un ou des arguments délimités lorsque son \idx{texte de paramètre} contient d'autres unités lexicales que \verb-#-$i$ où $i$ est un entier de 1 à 9. Ces unités lexicales sont appelées les délimiteurs. + +\begin{regle} +Un argument \verb-#<-$i$\verb|>| est dit «délimité» si, dans le \idx{texte de paramètre}, il est suivi par autre chose que \hbox{\verb-#<-$i$+1\verb|>|} ou que l'accolade «\cidx\{» qui marque le début du \idx{texte de remplacement}. +\end{regle} + +Voici un exemple : +\centrecode-\def\foo.#1**#2#3/{Bonjour #1, #2 et #3}- + +\noindent Ici, \verb-#1- et \verb-#3- sont les arguments délimités puisqu'ils sont suivis par respectivement «\verb-**-» et «\verb-/-». Par la suite, lorsque \TeX{} va exécuter \verb-\foo-, il s'attendra à ce qu'elle soit immédiatement suivie de «\verb-.-» puis de quelque chose (qui sera l'argument \verb-#1-) qui s'étendra jusqu'à la première occurrence de «\verb-**-», puis autre chose (qui deviendra les arguments \verb-#2#3-) qui s'étendra jusqu'à la première occurrence de «\verb-/-». Ayant défini la commande \verb-\foo- avec le code précédent, supposons que l'on écrive : +\centrecode-\foo.ab{123}c**xyz/- + +\noindent Dans ce cas, \verb-#1- sera «\verb*-ab{123}c-». \Qu ant aux tokens «xyz», ils sont à répartir entre \verb-#2- et \verb-#3- selon la règle suivante : + +\begin{regle} +Lorsqu'une macro est exécutée et qu'elle doit lire une suite de $n$ arguments dont le dernier est délimité, les $n-1$ premiers arguments sont assignés comme des arguments non délimités. Le dernier argument reçoit ce qui reste jusqu'au délimiteur, ceci étant éventuellement vide. +\end{regle} + +D'après cette règle, l'argument \verb-#2- de l'exemple précédent n'est pas délimité et sera «\verb|x|». L'argument \verb-#3- qui est délimité sera donc qu'il reste jusqu'au délimiteur «\verb|/|» c'est-à-dire «\verb|yz|». Le texte de remplacement sera donc : +\centrecode-Bonjour ab123c, x et yz.- + +\begin{regle} +Dans tous les cas, qu'il soit délimité ou pas, si un argument est de la forme \verb-{<tokens>}-, où \verb-<tokens>- est un ensemble d'unités lexicales où les accolades sont équilibrées, alors \TeX{} \emph{supprime les accolades extérieures} et l'argument lu est «\verb-<tokens>-».\idx*{argument!suppression d'accolades} +\end{regle} + +Un cas particulier de cette règle engendre une autre règle : + +\begin{regle} +Un argument délimité est vide si l'ensemble des tokens qui lui correspond est vide ou «\verb-{}-».\idx*{argument!vide}% +\end{regle} + +Ainsi, si on définit la macro \verb|\bar| ainsi : + +\centrecode-\def\bar#1#2#3+{L'argument délimité est : "#3".}- + +\noindent Alors, l'argument \verb|#3| est vide dans ces deux cas : +\begin{itemize} + \item «\verb-\bar{123}x+-» car «\verb|123|» et «\verb|x|» sont les 2 premiers arguments et il ne reste rien pour le troisième; + \item «\verb-\bar{a}{b}{}+-» car \verb|#3| reçoit «\verb|{}|». +\end{itemize} +En revanche, si on avait écrit \verb-\bar{a}{b}{}{}+-, l'argument délimité aurait été «\verb-{}{}-» ce qui n'est pas un argument vide. +\grandsaut + +Plutôt que d'envisager un par un de nombreux cas, voici un tableau qui affiche ce que seraient les arguments \verb-#1-, \verb-#2- et \verb-#3- dans plusieurs cas de figure avec la définition de \verb-\foo- rappelée en haut du tableau : + +\begin{centrage} +\small +\makeatletter +\def\fooa.#1**#2#3/{\textvide#1|} +\def\foob.#1**#2#3/{\textvide#2|} +\def\fooc.#1**#2#3/{\textvide#3|} +\def\textvide#1|{% + \ifx_#1_<vide>% + \else\ttfamily{\everyeof{\relax}\endlinechar\m@ne\catcode`\{=12 \catcode`\}=12 \catcode32=12 \scantokens{#1\relax}}% + \fi} +\makeatother + +\makeatother +\tabcolsep1em +\begin{tabular}{lccc} +\multicolumn4c{\ttfamily\string\def\string\foo.\#1**\#2\#3/\{...\}}\\[1ex]\hline + & \ttfamily\#1 & \ttfamily\#2 & \ttfamily\#3\\\hline +1. \verb*-\foo. abc**123/-& \fooa. abc**123/ & \foob. abc**123/ & \fooc. abc**123/\\ +2. \verb*-\foo.**k/-&\fooa.**k/&\foob.**k/&\fooc.**k/\\ +3. \verb*-\foo.1{**}2**{ab}{cd}/-&\fooa.1{**}2**{ab}{cd}e/&\foob.1{**}2**{ab}{cd}e/&\fooc.1{**}2**{ab}{cd}/\\ +4. \verb*-\foo.{ab}**1{cd} /-&\fooa.{ab}**1{cd} /&\foob.{ab}**1{cd} /&\fooc.{ab}**1{cd} /\\ +5. \verb*-\foo. **xy/-&\fooa. **xy/&\foob. **xy/&\fooc. **xy/\\ +6. \verb*-\foo.xy** z/-&\fooa.xy** z/&\foob.xy** z/&\fooc.xy** z/\\ +7. \verb*-\foo.xy** /-&\multicolumn3c{<Erreur de compilation>}\\ +8. \verb*-\foo**/-&\multicolumn3c{<Erreur de compilation>}\\\hline +\end{tabular} +\end{centrage} +\noindent Voici maintenant quelques observations : +\begin{itemize} + \item la ligne 1 met en évidence que \verb-#1- récolte ce qui est entre «\verb-.-» et «\verb-**-», espaces compris ; + \item on observe à la ligne 2 que le «\verb-.-» est immédiatement suivi du délimiteur «\verb-**-» et donc, l'argument délimité \verb-#1- est vide\idx*{argument!vide}. L'argument \verb-#3-, qui est délimité, est vide puisqu'il ne reste rien après \verb-#2- qui est «k»; + \item la ligne 3 montre que si le délimiteur «\verb-**-» est entre accolades, il n'est pas reconnu comme délimiteur et ne stoppe pas la lecture de l'argument \verb-#1-; + \item à la ligne 4, l'argument délimité \verb-#1- est «\verb-{ab}-» et comme cet argument est un texte entre accolades, les accolades sont supprimées lors de la lecture de cet argument\idx*{argument!suppression d'accolades}. Par contre, l'autre argument délimité \verb-#3- est «\verb*-{cd} -» et lui n'est pas réduit à un texte entre accolades, il y a l'espace en plus. Les accolades ne sont donc pas supprimées ; + \item à la ligne 6, on peut se demander pour quelle raison \verb-#2- n'est pas «\verb*- -» et \verb-#3- n'est pas «\verb-z-». En fait, l'argument non délimité \verb-#2- cherche son assignation dans «\verb*- z-» et comme on l'a vu au chapitre précédent, un espace est ignoré dans une liste d'arguments non délimités\idx*{argument!espace}. Donc \verb-#2- devient «\verb-z-», ce qui explique pourquoi l'argument délimité \verb-#3- est vide puisqu'il ne reste plus rien entre la fin de \verb-#2- et le délimiteur «\verb|/|» + \item l'erreur qui survient à la ligne 7 est due à l'argument non délimité \verb-#2- qui cherche son assignation dans «\verb*- /-». Ignorant l'espace, \verb-#2- devient «\verb|/|» et capture le délimiteur qui ne sera plus vu par \verb-\foo-. \TeX{} va donc lire du code jusqu'à ce qu'il rencontre une autre «\verb|/|», ce qui est peu probable et qui, si cela arrivait, donnerait un argument \verb-#3- beaucoup plus étendu que prévu. Si \TeX{} ne trouve pas d'autre «\verb|/|», la lecture peut se poursuivre jusqu'à ce qu'il arrive sur un \idx\par et comme la macro n'est pas déclarée \idx\long, il va se plaindre d'un «{\ttfamily Paragraph ended before \verb-\foo- was complete}»\footnote{Le paragraphe s'est terminé avant que la lecture de \texttt{\string\foo} n'ait été achevée.}. Si l'on est proche de la fin du fichier, \TeX{} peut même vainement chercher son délimiteur «\verb|/|» jusqu'à la fin du fichier et émettre un «{\ttfamily File ended while scanning use of \verb-\foo-}»\footnote{Fichier terminé pendant la lecture de \texttt{\string\foo}.}. + \item à la ligne 8, ce qui suit la commande \verb-\foo- n'est pas «\verb-.-» donc \TeX{} nous informe que ce qu'il trouve ne correspond pas à ce qu'il attend avec un «\texttt{Use of \string\foo{} doesn't match its definition}»\footnote{L'emploi de {\ttfamily\string\foo} ne correspond pas à sa définition.}. +\end{itemize} + +\begin{exercice} +Voici une commande à arguments délimités \verb-\bar-: +\centrecode-\def\bar#1#2:#3\relax#4{<code>}- + +\begin{enumerate} + \item \Qu els sont les arguments délimités ? + \item \Qu e valent les 4 arguments dans les cas suivants : +\begin{enumerate} + \item \verb-\bar 123:456\relax789- + \item \verb-\bar\def\relax:\relax\relax\relax- + \item \verb-\bar:\relax:{\bar\relax}\bar\relax\bar- + \item \verb-\bar0:a\relax- +\end{enumerate} +\item Donner un cas où l'on est sûr d'obtenir une erreur de compilation. +\item Donner un cas où \verb-#1- vaut «\verb-\bar12:34\relax5-» et \verb-#3- vaut «\verb-\relax-». +\item Donner un cas où \verb-#1- et \verb-#4- sont des arguments vides, mais pas \verb-#2- et \verb-#3-. +\end{enumerate} + +\solution +Rappelons la définition de \verb-\bar- : \verb-\def\bar#1#2:#3\relax#4{<code>}- +\begin{enumerate} + \item Les arguments délimités sont le deuxième et le troisième. Il est important de noter que le 4\ieme{} n'est pas délimité. + \item Voici les résultats présentés dans un tableau : + \begin{centrage}\small + \begin{tabular}[t]{lcccc}\hline + & \verb-#1- & \verb-#2- & \verb-#3- & \verb-#4-\\\hline + a) & \verb-1- &\verb-23-&\verb-456-&\verb-7-\\ + b) &\verb-\def- &\verb-\relax- &<vide> &\verb-\relax-\\ + c) &\verb-:-&\verb-\relax- &\verb-{\bar\relax}\bar- &\verb-\bar-\\ + d) &\verb-0-&<vide>&\verb-a-&<inconnu>\\\hline + \end{tabular} + \end{centrage} + Les chiffres 8 et 9 au premier exemple et le dernier \idx\relax au deuxième exemple ne sont pas pris en compte par la macro \verb-\bar-. + + L'argument \verb-#4- de la dernière ligne est qualifié d'«inconnu», car l'argument qui suit «\verb-\bar0:a\relax-» n'est pas précisé dans l'énoncé. Si cet argument est \idx\par ou deux retours à la ligne consécutifs (ce qui est la même chose), il y aura une erreur de compilation puisque dans ce cas, l'argument \verb-#4- serait ce \idx\par ce qui est interdit puisque la macro \verb-\bar- n'est pas déclarée \idx\long. + \item Ce code «\verb-\bar:a\relax bcd\par-» provoquera une erreur de compilation, car l'argument \verb-#1- qui n'est pas délimité va capturer le «\verb-:-» qui ne sera plus visible par la macro \verb-\bar-. Celle-ci va donc poursuivre sa lecture jusqu'à trouver un autre «\verb-:-» pour affecter à l'argument délimité \verb-#2- ce qu'elle a emmagasiné jusque là. Ce faisant, elle va tomber sur le \verb-\par- et comme cette macro \verb-\bar- n'a pas été définie \idx\long, \TeX{} va émettre une erreur de compilation. + \item Par exemple «\verb-\bar{\bar12:34\relax5}X:{\relax}\relax Y-» où ici les arguments 2 et 4 sont «\verb-X-» et «\verb-Y-». + \item Par exemple «\verb-\bar{}X:Y\relax{}-» où les arguments 2 et 3 sont «\verb-X-» et «\verb-Y-». +\end{enumerate} +\end{exercice} + +\begin{exercice} +\Qu el sera l'affichage produit par le code suivant ? + +\centrecode-\def\bar/#1#2/{*#1*#2*} +\bar////- + +\solution +\TeX{} attend «\verb-/-» juste après \verb-\bar-. Ce caractère est donc absorbé. L'argument \verb-#1- n'est pas délimité, il reçoit donc le second «\verb-/-». Comme \verb-#2- est un argument délimité, il sera vide puisque le second délimiteur arrive immédiatement après \verb-#1-. Il reste le dernier «\verb-/-» qui n'a pas été lu avec la macro \verb-\bar- et ses arguments; il sera donc affiché tel quel juste après la fin du texte de remplacement de \verb-\bar-. + +On obtient donc : {\ttfamily\def\bar/#1#2/{*#1*#2*}\bar////} +\end{exercice} + +\begin{exercice} +Soit une macro \verb-\baz- définie de cette façon : \verb-\def\baz#1\relax{#1}-. Elle ressemble un peu à la commande §\identity que nous avons déjà vue à la page~\pageref{identity} sauf qu'ici, l'argument \verb-#1- est \emph{délimité}. + +Lorsqu'elles sont exécutées, expliquer s'il y a des différences entre ces différentes lignes de code et si oui, lesquelles : + +\centrecode-\baz\let\bar=A\let\bidule=B\relax +\baz{\let\bar=A\let\bidule=B}\relax +\baz{\let\bar=A}{\let\bidule=B}\relax- +\solution +Pour la première ligne, l'argument \verb-#1- est ce qui se trouve entre \verb-\baz- et \verb-\relax-, donc le texte de remplacement de ce code est «\verb-\let\bar=A\let\bidule=B-». \TeX{} va donc définir deux lettres implicites. + +À la deuxième ligne, \TeX{} lit l'argument «\verb-{\let\bar=A\let\bidule=B}-». Comme il est réduit à un texte entre accolades, ces accolades sont supprimées\idx*{argument!suppression d'accolades} et donc cette ligne de code est strictement équivalente à la première. + +Pour la troisième, l'argument est «\verb-{\let\bar=A}{\let\bidule=B}-» qui n'est pas réduit à un texte entre accolades et ces accolades ne seront donc pas supprimées lors de la lecture de l'argument. \TeX{} va donc définir des lettres implicites à l'intérieur de deux groupes. Ces définitions seront perdues à la fin de chaque groupe, c'est-à-dire immédiatement après avoir été définies. Cette troisième ligne de code n'a donc aucun effet qui survit à la fermeture des deux groupes. +\end{exercice} + +Le délimiteur du dernier argument peut également être une accolade ouvrante. Lorsque la macro sera exécutée, cela signifiera que \TeX{} s'attendra à ce que cet argument s'étende jusqu'à la prochaine accolade ouvrante. + +\begin{regle} +Pour spécifier que le dernier argument \verb|<i>| d'une macro doit être délimité par une accolade ouvrante, il faut écrire dans le \idx{texte de paramètre} + +\centrecode-#<i>#{- + +L'accolade qui suit le \cidx\# sert de délimiteur et marque aussi le début du texte de remplacement. + +Lorsque la macro sera exécutée, l'argument \verb|#<i>| s'étendra jusqu'à la prochaine accolade ouvrante\idx*{argument!délimité!par accolade}. +\end{regle} + +Voici une macro \verb-\baz- où son 3\ieme{} argument est délimité par une accolade ouvrante : + +\showcode/\def\baz#1#2#3#{"#1", puis "#2" et enfin "#3".} +\baz{abc}def{gh}ij/ + +D'après les règles vues dans ce chapitre, \verb|#1| est «\verb|abc|», \verb|#2| est «\verb|d|» et l'argument délimité \verb-#3- est bien «\verb-ef-». La suite du code, «\verb-{gh}ij-», ne sera pas lu par la macro \verb-\baz-. C'est pourquoi l'affichage produit par ce code se trouve après le point qui marque la fin de texte de remplacement de la macro \verb|\baz|. +\grandsaut + +Voici enfin deux macros à usage «généraliste» en programmation, toutes les deux à arguments délimités. La première §\firstto@nil lit tout ce qui se trouve jusqu'au prochain «\idx\@nil» et en prend le premier argument non délimité. La seconde, §\remainto@nil prend ce qui reste. Elles\footnote{Ces deux commandes sont \texttt{\string\@car} et \texttt{\string\@cdr} du noyau \LaTeX.} ne sont \emph{pas} à confondre avec §\firstoftwo et §\secondoftwo qui elles, ne sont pas à arguments délimités\label{firstto@nil}. + +On remarque que le caractère «\verb|@|»\idx*{"@ (nom de macros privées)} fait partie du nom de ces deux macros ainsi que de \idx\@nil, ce qui est normalement interdit. Pour que cela soit possible, il faut modifier le code de catégorie de l'arobase qui est naturellement 12 pour le mettre à 11, celui des lettres, afin qu'il puisse servir à constituer un nom de macro. En \TeX{}, on doit écrire «\texttt{\string\catcode`\string\@=11}» pour que \verb|@| devienne une lettre puis «\texttt{\string\catcode`\string\@=12}» pour revenir au régime de catcode de l'arobase par défaut. En \LaTeX{}, les macros \idx\makeatletter et \idx\makeatother effectuent ces actions. + +La zone de ces modifications sera limitée aux définitions des macros de telle sorte qu'il ne sera plus possible de se référer à ces macros par la suite. L'intérêt est qu'elles deviennent « privées », terme qu'il faut comprendre au sens de « envers du décor réservé à un usage de programmation », ou encore « à ne pas mettre entre toutes les mains ». Ainsi, l'utilisateur final ne peut pas y accéder, sauf évidemment s'il effectue la man\oe uvre de modification du catcode de \verb|@|\footnote{\LaTeX3 utilise le caractère «\texttt{\string_}» pour ses macros privées.}. + +Voici les définitions et quelques exemples d'utilisation de §\firstto@nil et §\remainto@nil : + +\showcode/\catcode`\@11 +\long\def\firstto@nil#1#2\@nil{#1}¤§*\firstto@nil¤ +\long\def\remainto@nil#1#2\@nil{#2}¤§*\remainto@nil¤ +a) \firstto@nil foobar\@nil \qquad b) \remainto@nil foobar\@nil \par¤\idx*\qquad¤ +c) \firstto@nil {foo}bar\@nil \qquad d) \remainto@nil {foo}bar\@nil¤§*\firstto@nil§*\remainto@nil¤/ + +On voit souvent cette macro \idx\@nil dans le code de \LaTeX{} et on la verra aussi fréquemment dans les macros de ce livre. On peut se demander ce qu'elle fait vraiment et quelle est sa définition. La réponse est très simple : il est admis que \idx\@nil est une macro \emph{non définie} (et doit le rester) dont le seul usage est de servir de délimiteur pour argument délimité. Ici, une précision s'impose : le fait qu'elle ne soit pas définie \emph{n'implique pas} que, en tant que délimiteur, elle soit interchangeable avec une autre macro non définie. + +\begin{regle} +Lorsqu'un délimiteur d'argument est une \idx{séquence de contrôle}, \TeX{} ne prend pas en compte la signification de cette \idx{séquence de contrôle} : seuls les caractères qui composent son nom sont examinés. +\end{regle} + +Si par exemple, on définit une macro \verb|\foo| ainsi : + +\centrecode-\def\foo#1\truc{<code>}- + +\noindent alors par la suite, \TeX{} s'attendra à ce que \verb|\foo| soit suivie d'un code (qui deviendra l'argument \verb|#1|) dont la fin sera marquée par \verb|\truc| (c'est-à-dire le caractère d'échappement suivi des lettres \verb|t|, \verb|r|, \verb|u| et \verb|c|). Même si une autre macro \verb|\machin| est rendue \idx\let-égale à \verb|\truc|, cette macro \verb|\machin| ne sera pas comprise comme le délimiteur de la macro \verb|\foo|. + +\begin{exercice} +Existe-t-il une différence de comportement entre ces deux lignes de code et si oui, laquelle : + +\centrecode-\firstoftwo{<code1>}{<code2>}¤§*\firstoftwo¤ +\firstto@nil{<code1>}{<code2>}\@nil¤§*\firstto@nil¤- + +\solution +Il n'y a pas de différence de comportement. Ces deux lignes se comportent de façon identique. Dans les deux cas, l'argument \verb-#1- n'est pas délimité et va être dépouillé de ses accolades\idx*{argument!suppression d'accolades}. Leur texte de remplacement est donc pour toutes les deux : \verb-<code1>-. De plus, elles ont toutes les deux lu et absorbé l'argument \verb-<code2>- qui disparait dans leur texte de remplacement. + +Cette équivalence serait également valable entre ces 3 lignes qui toutes exécutent \verb|<code2>|: + +\centrecode-\secondoftwo{<code1>}{<code2>}¤§*\secondoftwo¤ +\remainto@nil{<code1>}{<code2>}\@nil¤§*\remainto@nil¤ +\remainto@nil{<code1>}<code2>\@nil¤§*\remainto@nil¤- +\end{exercice} + +\section{Mise en pratique} +Les arguments délimités ne présentent pas de difficulté théorique spéciale, aussi il est temps de les utiliser sur un exemple. + +\subsection{Afficher ce qui est à droite d'un motif}\label{rightof} +Nous allons nous employer à programmer une macro§\rightof[|(] dont la syntaxe est + +\centrecode-\rightof{<chaine>}{<motif>}- + +\noindent et qui affiche ce qui est à gauche du \verb|<motif>| dans la \verb|<chaine>|, étant entendu que si \verb|<motif>| ne figure pas dans la \verb|<chaine>|, rien ne doit être affiché. + +Du côté de la mécanique interne, cette macro se contentera de lire ses deux arguments, de définir et d'appeler une macro \verb|\right@of|. Cette macro, à arguments délimités, sera chargée d'effectuer le travail. + +L'intérêt du «\verb|@|» dans «\verb|\right@of| est de marquer la frontière entre macro publique et macro privée pour délimiter ce qui a une syntaxe clairement définie et stable dans le temps pour les macros publiques de ce qui est de la cuisine interne pour les macros privées. Ces dernières sont susceptibles d'être modifiées, voire supprimées pourvu que la syntaxe et les fonctionnalités de la macro publique soient conservées. +\grandsaut + +Voici comment doit être définie la macro \verb|\right@of|: + +\centrecode-\def\rightof#1<motif>#2\@nil{#2}- + +\noindent où le délimiteur \verb|<motif>| est l'argument \verb|#2| de la macro principale. + +Voici donc le code de \verb|\rightof| où, conformément à ce qui a été dit, les tokens de paramètre \verb|#| sont doublés pour la macro «fille» \verb|\right@of| : + +\showcode/\catcode`\@=11 % "@" devient une lettre +\def\rightof#1#2{%¤§*\rightof¤ + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\@nil% appelle la macro auxiliaire où #1 est la <chaine> +} +\catcode`\@=12 %"@" redevient un signe¤\idx*\catcode¤ +--\rightof{Programmation}{g}--\par¤\defline\aaa¤ +--\rightof{Programmation}{gram}--\par +--\rightof{Programmation}{on}--/ + +\subsection{Les cas problématiques} +La macro fonctionne bien\ldots{} En apparence seulement, car le cas où \verb|<chaine>| ne contient pas le \verb|<motif>| n'a pas encore été examiné : + +\errcode/\catcode`\@=11 % "@" devient une lettre +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\@nil% appelle la macro auxiliaire +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{Gram}/{! Paragraph ended before \string\right@of was complete. <to be read again>} + +\TeX{} explique qu'un \idx\par a été rencontré avant que l'argument ne soit entièrement lu, ce qui est interdit puisque la macro n'est pas déclarée \idx\long. Le texte de remplacement de \verb-\rightof{Programmation}{z}- (cas de la ligne \no\aaa) permet d'analyser ce qui se passe. Le texte de remplacement est écrit ci-dessous (les doubles \verb-##- ont été remplacés par des simples puisque nous écrivons le texte de remplacement) : + +\centrecode-\def\right@of#1z#2\@nil{#2} +\entre@ Programmation\@nil- + +\noindent La première ligne spécifie donc que la macro \verb-\right@of- doit être suivie de quelque chose (l'argument \verb-#1-) qui s'étend jusqu'à «\verb|z|» puis de quelque chose (l'argument \verb-#2-) qui s'étend jusqu'au \idx\@nil. Or, «\verb|z|» ne figure pas entre \verb|\right@of| et \idx\@nil puisque ce qui s'y trouve est l'argument \verb|#1| de la macro \verb|rightof|, c'est-à-dire « Programmation» ! C'est pourquoi \TeX{} le cherche jusqu'à tomber sur le \idx\par qui stoppe cette vaine recherche puisque la macro n'est pas déclarée \idx\long. + +Tout serait bien plus facile si on disposait d'une macro qui teste si le \verb|<motif>| est contenu dans \verb|<chaine>| : dans l'affirmative, il suffirait d'appeler la macro \verb|\right@of| précédemment définie et sinon, ne rien faire ce qui revient à ne rien afficher. Une telle macro, capable de faire un test, sera écrite plus tard (voir page~\pageref{ifin}). En attendant, il faut trouver une astuce pour se protéger de cet inconvénient. + +En fait aucune erreur ne serait émise si, quelle que soit la \verb|<chaine>|, \verb|\right@of| voyait toujours le \verb|<motif>| avant le \idx\@nil. Afin qu'il en soit ainsi, on va délibérément écrire ce \verb|<motif>| quelque part entre \verb|\right@of| et \idx\@nil lors de l'appel à la macro \verb|\right@of|. L'écrire juste avant le \idx\@nil nous assure que rien ne sera renvoyé dans le cas où \verb|<chaine>| ne contient pas \verb|<motif>| puisque dans ce cas, \verb|##2| est vide : + +\showcode/\catcode`\@=11 % "@" devient une lettre +\def\rightof#1#2{%¤§*\rightof¤ + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire¤\defline\aaa¤ + \right@of#1#2\@nil% appelle la macro auxiliaire avec le motif #2¤\defline\bbb¤ +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{ti}--\par +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{Gram}--/ + +L'inconvénient qui saute aux yeux est que le motif \verb|#2|, ajouté juste avant \idx\@nil, est dirigé vers l'affichage lorsque la \verb|<chaine>| contient le \verb|<motif>| ! Tout simplement parce que l'argument \verb|##2| de \verb|\right@of| qui est affiché est ce qui se trouve entre la \emph{première} occurrence du \verb|<motif>| et \idx\@nil. + +Pour mieux comprendre ce qui se joue en coulisses, examinons ce qui se passe lorsque l'appel «\verb|\rightof{Programmation}{g}|» est exécuté : + +\begin{enumerate} + \item la macro \verb|\right@of| est définie par : \verb|\def\right@if#1g#2\@nil{#2}| à la ligne \no\aaa; + \item écrire «\verb|\right@of Programmationg\@nil|» donne ce qui se trouve entre la première occurrence de «\verb|g|» et \idx\@nil, c'est donc «\verb|rammationg|» qui est affiché. +\end{enumerate} + +Procédons de même avec l'appel «\verb|\rightof{Programmation}{z}|», cas où le \verb|<motif>| n'est pas contenu dans la \verb|<chaine>| : + +\begin{enumerate} + \item la macro \verb|\right@of| est définie par : \verb|\def\right@if#1z#2\@nil{#2}|; + \item écrire «\verb|\right@of Programmationz\@nil|» donne ce qui se trouve entre «\verb|z|» et \idx\@nil, c'est-à-dire rien. +\end{enumerate} + +Comment faire en sorte que le \verb|<motif>| rajouté juste avant le \idx\@nil ne soit pas affiché dans le cas où \verb|<motif>| est contenu dans \verb|<chaine>| ? Réponse : en incluant dans l'argument \verb|##2| de \verb|\right@of| une autre macro à argument délimitée qui fera disparaitre ce qui est indésirable. Cette macro à argument délimité sera §\gobto@@nil et absorbera tout ce qui se trouve entre elle et le prochain \verb|\@@nil| rencontré. On note ici qu'il ne s'agit pas de \idx[|etc]\@nil mais de \verb|\@@nil| afin que le délimiteur de cette macro lui soit spécifique : + +\centrecode-\def\gobto@@nil#1\@@nil{}- + +On va donc ajouter un \verb|\@@nil| lors de l'appel à la macro \verb|\right@of| : + +\centrecode-\right@of#1#2\@@nil\@nil- + +\noindent Et il nous reste à correctement placer la macro §\gobto@@nil. Pour que ce \verb|\@@nil| soit toujours apparié avec une macro §\gobto@@nil, que \verb|<motif>| soit contenu dans \verb|chaine| ou pas, il faut mettre §\gobto@@nil juste avant et juste après \verb|#2| : + +\showcode/\catcode`\@=11 % "@" devient une lettre +\def\gobto@@nil#1\@@nil{}¤§*\gobto@@nil¤ +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil% appelle la macro auxiliaire¤§*\gobto@@nil¤ +} +\catcode`\@=12 %"@" redevient un signe +--\rightof{Programmation}{g}--\par¤§*\rightof¤ +--\rightof{Programmation}{ti}--\par +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{Gram}--/ + +Faisons fonctionner la macro §\rightof dans le cas où \verb|<motif>| est contenu dans \verb|<chaine>|. Comme précédemment, examinons quels sont ses arguments lorsque l'on écrit «\verb|\rightof{Programmation}{g}|»: + +\begin{enumerate} + \item l'appel à la macro \verb|\right@of| se fait ainsi : + + \centrecode-\right@of Programmation\gobto@@nil g\gobto@@nil\@@nil\@nil-§*\gobto@@nil + \item son argument \verb|#2|, qui est entre le premier «\verb|g|» et \verb|\@nil| est : + + \centrecode-rammation\gobto@@nil g\gobto@@nil\@@nil- + \item l'affichage qui en résulte est bien «rammation» car la macro §\gobto@@nil absorbe tout ce qui se trouve jusqu'au \verb|\@@nil|. \TeX{} ne tient aucun compte de ce qu'il absorbe. \Qu e la macro §\gobto@@nil y figure ou pas n'y change rien : la macro à argument délimité effectue « bêtement » son travail sans analyser ce qui se trouve dans son argument. +\end{enumerate} + +Voici maintenant le cas où \verb|<motif>| n'est contenu dans \verb|<chaine>|, comme lorsqu'on écrit «\verb|\rightof{Programmation}{z}|» : + +\begin{enumerate} + \item la macro \verb|\right@of| est appelée par + + \centrecode-\right@of Programmation\gobto@@nil z\gobto@@nil\@@nil\@nil- + \item l'argument \verb|#2| se trouve entre le «\verb|z|» et le \verb|\@nil| : + + \centrecode-\gobto@@nil\@@nil- + \item grâce à la définition de §\gobto@@nil, ce code ne se traduit par aucun affichage. +\end{enumerate} + +Impliquer ainsi plusieurs macros à arguments délimités et faire en sorte que la suivante hérite de l'argument de la précédente pour le traiter à son tour est couramment utilisé en programmation et démontre, comme dans ce cas particulier, la puissance des arguments délimités. En revanche, la gymnastique mentale requise pour mettre en place un tel dispositif et correctement envisager tous les cas est loin d'être naturelle et demande une certaine habitude\ldots{} Le plus difficile est d'analyser un code déjà écrit, voire de comprendre un code que l'on avait écrit soi-même et oublié entre temps. \Qu i peut se vanter de comprendre du premier coup d'\oe il l'appel à la macro auxiliaire mise au point précédemment ? + +\centrecode-\right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil-§*\gobto@@nil + +Le manque de lisibilité est criant et démontre l'importance de commenter son code, autant pour soi-même que pour les autres. Pour augmenter la lisibilité du code, il serait préférable de recourir à une macro \verb|\ifin| (dont l'élaboration sera expliquée page~\pageref{ifin}) qui teste si l'argument \verb|#1| contient l'argument \verb|#2| et ayant pour syntaxe + +\centrecode-\ifin{#1}{#2} + {<code à exécuter si vrai>} + {<code à exécuter si faux>}%- + +\noindent On peut ainsi programmer plus confortablement et utiliser la toute première version de \verb|\rightof| et se dispenser d'envisager le cas problématique où \verb|<chaine>| ne contient pas le \verb|<motif>| : + +\showcode/\catcode`\@=11 % "@" devient une lettre +¤\string\def\string\if{}in\#1\#2\#3\#4\string{<\textit{code vu plus tard}\string}>¤ +\def\rightof#1#2{%¤§*\rightof¤ + \ifin{#1}{#2}% + {% si #1 contient le #2 + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\@nil% appelle la macro auxiliaire + }% + {}% sinon, ne rien faire +} +\catcode`\@=12 %"@" redevient un signe¤\idx*\catcode¤ +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{z}--\par +--\rightof{Programmation}{o}--/ + +\subsection{À gauche toute}\label{leftof} +Ne nous arrêtons pas en si bon chemin et venons-en à la macro symétrique de §\rightof, la macro §\leftof[|(] qui sera chargée d'afficher ce qui est à gauche du \verb|<motif>| dans la \verb|<chaine>|. + +Pour se prémunir de l'erreur de compilation rencontrée dans le cas où le \verb|<motif>| (argument \verb|#2|) n'est pas contenu dans la \verb|<chaine>| (argument \verb|#1|), procédons comme avec §\rightof et ajoutons ce \verb|<motif>| lors de l'appel à la macro auxiliaire : + +\showcode/\catcode`\@=11 +\def\leftof#1#2{%¤§*\leftof¤ + \def\leftof@i##1#2##2\@nil{##1}% renvoie ce qui est à gauche de #2 + \leftof@i #1#2\@nil% ajoute #2 après #1 +} +\catcode`\@=12 +--\leftof{Programmation}{ram}--\par +--\leftof{Programmation}{zut}--¤§*\leftof¤/ + +Cela était attendu : lorsqu'on écrit \verb|\leftof@i #1#2\@nil|, si \verb|#1| ne contient pas \verb|#2|, alors le \verb|#2| ajouté en fin d'argument devient la première occurrence du \verb|<motif>| et donc, \verb|#1| est affiché en entier. + +À nouveau, il va falloir déployer une méthode astucieuse pour que, lorsque \verb|#1| est renvoyé en entier (ce qui correspond au cas problématique), rien ne soit affiché. Pour cela, la macro auxiliaire \verb|\leftof@i| ne va pas afficher, \verb|##1| mais transmettre ce «\verb|##1#1|» à une autre macro auxiliaire \verb|\leftof@ii|. Cette dernière aura comme délimiteur d'argument est \verb|#1| tout entier et se chargera d'afficher ce qui est à gauche de la \verb|<chaine>| \verb|#1| : + +\centrecode-\def\leftof@ii##1#1##2\@nil{##1}- + +Ainsi, la macro \verb|\leftof@i| devient : + +\centrecode-\leftof@i##1#2##2\@nil{\leftof@ii##1#1\@nil}- + +\noindent et on ajoute ici aussi \verb|#1| après \verb|##1| pour que la macro \verb|\leftof@ii| voie toujours la \verb|<chaine>| \verb|#1| : + +\showcode/\catcode`\@=11 +\def\leftof#1#2{%¤§*\leftof¤ + \def\leftof@i##1#2##2\@nil{\leftof@ii##1#1\@nil}% + \def\leftof@ii##1#1##2\@nil{##1}% + \leftof@i #1#2\@nil +} +\catcode`\@=12 ¤\idx*\catcode¤ +--\leftof{Programmation}{ram}--\par +--\leftof{Programmation}{zut}--/ + +Compte tenu de la faible lisibilité de ce code et de la difficulté à suivre comment se transmettent les arguments, voici un tableau montrant comment sont lus et isolés les arguments délimités dans le cas où \verb|<chaine>| contient \verb|<motif>|, c'est-à-dire le premier cas. On a encadré ce qui délimite les arguments des macros : + +\begin{centrage} + \footnotesize + \frboxsep=0.5pt + \def\mystrut{\vphantom{\normalfont Programmation}} + \begin{tabular}{|r|c|}\hline + Appel macro principale & \verb|\leftof{Programmation}{ram}|\\\hline + Appel à \verb|\leftof@i|& \frbox{\ttfamily\mystrut\string\leftof@i}Prog\frbox{\mystrut ram}mationram\verb|\@nil|\\ + Argument \verb|##1| retenu par \verb|\leftof@i| & Prog\\\hline + Appel à \verb|\leftof@ii|& \frbox{\ttfamily\mystrut\string\leftof@ii}Prog\frbox{\mystrut Programmation}\verb|\@nil|\\ + Argument \verb|##1| retenu par \verb|\leftof@ii| &\ttfamily Prog\\\hline + Affichage & Prog\\\hline + \end{tabular} +\end{centrage} + +Voici maintenant le déroulement des opérations lorsque \verb|<chaine>| ne contient pas \verb|<motif>| : + +\begin{centrage} + \footnotesize + \frboxsep=0.5pt + \def\mystrut{\vphantom{\normalfont Programmation}} + \begin{tabular}{|r|c|}\hline + Appel macro principale & \verb|\leftof{Programmation}{zut}|\\\hline + Appel à \verb|\leftof@i|& \frbox{\ttfamily\mystrut\string\leftof@i}Programmation\frbox{\mystrut zut}\verb|\@nil|\\ + Argument \verb|##1| retenu par \verb|\leftof@i| & Programmation\\\hline + Appel à \verb|\leftof@ii|& \frbox{\ttfamily\mystrut\string\leftof@ii}\kern.5pt \frbox{\mystrut Programmation}Programmation\verb|\@nil|\\ + Argument \verb|##1| retenu par \verb|\leftof@ii| &\ttfamily <vide>\\\hline + Affichage & \ttfamily <vide>\\\hline + \end{tabular} +\end{centrage} + +Il est bon de noter que la macro §\leftof, bien que requérant elle aussi deux macros auxiliaires, est programmé différemment de \verb|\rightof|. Dans le cas de §\leftof, la deuxième macro auxiliaire \verb|\leftof@ii| est appelée \emph{dans l'argument} de \verb|\leftof@i|. Dans le cas de \verb|\rightof|, la deuxième macro auxiliaire §\gobto@@nil était écrite dans l'appel à la macro \verb|\right@of|, ce qui est un cas particulier pas toujours faisable. + +\begin{exercice} +Modifier la macro §\rightof pour utiliser la méthode employée avec §\leftof : la première macro auxiliaire \verb|\rightof@i| appellera \emph{dans son argument} une deuxième macro auxiliaire \verb|\rightof@ii|. + +\solution +On utilise une astuce similaire à celle vue avec §\leftof : la deuxième macro auxiliaire \verb|\rightof@ii| va renvoyer ce qui est à \emph{gauche} du \verb|<motif>| puisque celui-ci, indésirable, était rajouté à la toute fin de l'appel à \verb|\rightof@i| : + +\showcode/\catcode`\@=11 +\def\rightof#1#2{%¤§*\rightof¤ + \def\rightof@i##1#2##2\@nil{\rightof@ii##2#2\@nil}% + \def\rightof@ii##1#2##2\@nil{##1}% + \rightof@i#1#2\@nil +} +\catcode`\@=12 ¤\idx*\catcode¤ +--\rightof{Programmation}{g}--\par +--\rightof{Programmation}{z}--/ + +Il est intéressant de noter que lorsque le \verb|<motif>| est «\verb|z|», l'argument \verb|##2| de \verb|\rightof@i| est vide et donc, l'argument \verb|##1| de \verb|\rightof@ii| est vide aussi. +\end{exercice}§*\leftof[|)] + +\subsection{Autre problème : les accolades dans le motif}\label{rightof.et.accolades} +On n'en a pas fini avec les cas problématiques\ldots{} Examinons maintenant pourquoi la macro \verb-\rightof- ne fonctionne pas lorsqu'elle est appelée avec un argument \verb|#2| contenant un (ou des) groupe(s) entre accolades. Voici un exemple simple où une erreur de compilation est produite : + +\errcode/\catcode`\@=11 +\def\gobto@@nil#1\@@nil{}¤§*\gobto@@nil¤ +\def\rightof#1#2{% + \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire + \right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil% appelle la macro auxiliaire¤§*\gobto@@nil¤ +} +\catcode`\@=12 +\rightof{abc{1}def}{c{1}d}% affiche ce qui est à droite de "c{1}d"/{You can't use `macro parameter character \string##' in horizontal mode.} + +\noindent Pour comprendre ce qu'il se passe avec le token «\verb-#-» qui est incriminé dans le message d'erreur, écrivons le texte de remplacement de \verb|\rightof{abc{1}def}{c{1}d}| : + +\centrecode/\def\right@of##1c{1}d##2\@nil{##2} +\right@of abc{1}def\gobto@@nil c{1}d\gobto@@nil\@@nil\@nil¤§*\gobto@@nil¤/ + +Le problème se trouve à la première ligne. \TeX{} va lire «\verb-\def\right@of##1c{1}-» ce qui va définir la macro auxiliaire \verb-\rightof@- : elle sera donc à argument délimité par «\verb|c|» et son texte de remplacement sera «\verb|1|». Par la suite, les tokens qui restent à lire sont «\verb-d##2\@nil{##2}-». Ceux-ci n'étant plus dans le \idx{texte de paramètre} d'une macro, \TeX{} bute «\verb-#-», car ce token est justement attendu dans le texte de paramètre d'une macro\footnote{On le rencontre aussi dans le préambule d'un alignement initié par la primitive \texttt{\string\halign} ou \texttt{\string\valign}.}. Le message d'erreur signale donc que \TeX{} rencontre ce token dans un contexte (ici, le mode horizontal\idx*{mode!horizontal}) dans lequel il est interdit. D'ailleurs, si le mode en cours avait été le mode vertical\idx*{mode!vertical}, le message aurait été le même : «\texttt{You can't use `macro parameter character \#' in vertical mode.}» + +Il est difficile de modifier la macro §\rightof[|)] pour qu'elle puisse aussi trouver ce qui est entre deux arguments qui comportent des groupes entre accolades, car dans ces cas, les arguments délimités sont interdits. Nous emploierons une tout autre méthode, bien plus lente, qui consisterait à s'y prendre token par token (voir page~\pageref{parser.code}). + +\begin{regle} +Les délimiteurs figurant dans le \idx{texte de paramètre} d'une macro ne peuvent pas contenir de tokens de catcode 1\idx*{catcode!1 (accolade)} ou 2 équilibrés (accolade ouvrante et fermante) car celles-ci seraient interprétées non pas comme faisant partie d'un délimiteur, mais comme marquant les frontières du texte de remplacement.\medskip + +La conséquence est donc que lorsqu'une macro définit une \idx{macro fille} à argument délimité où les délimiteurs sont les arguments de la macro mère, ceux-ci ne peuvent pas contenir un groupe entre accolades. +\end{regle}\idx*[|)]{argument!délimité} + +\chapter{Développement}\idx*[|(]{développement} +\TeX{} est un langage particulier et parmi d'autres subtilités, le développement est un de ses mécanismes intimes. Maitriser le développement est nécessaire pour programmer, car cette notion occupe une place centrale dans la programmation \TeX. Certains diraient que la gestion du développement fait partie des choses assez pénibles, mais lorsqu'on programme, il est souvent nécessaire de savoir contrôler finement ce mécanisme fondamental. Ce chapitre, consacré à l'apprentissage des outils permettant de contrôler le développement, nous ouvrira la voie pour appréhender la programmation abordée dans les parties suivantes. + +\section{Développement des commandes} +Intéressons-nous aux commandes (qui ont un texte de remplacement donc), par opposition aux \idx{primitive}s qui sont « codées en dur » dans \TeX{}. Le texte de remplacement est, en quelque sorte, la définition de la commande, c'est-à-dire la représentation interne qu'en a \TeX{}. Reprenons par exemple la commande très simple \verb-\foo- définie par : + +\centrecode-\def\foo{Bonjour}- + +\noindent Son texte de remplacement est « Bonjour », c'est-à-dire que dans le code, lorsque \TeX{} va rencontrer \verb-\foo-, alors il va aller chercher sa signification dans sa mémoire et va remplacer cette \idx{séquence de contrôle} par son texte de remplacement qui est « Bonjour ». Ce remplacement, cette substitution, cette transformation du code s'appelle le « développement », et on dit que la macro \verb-\foo- a été développée par \TeX{}. Pour être tout à fait clair, cette « transformation du code », a lieu dans la mémoire de \TeX{} (nommée « \idx{pile d'entrée} »). Le code source n'est bien évidemment pas modifié. + +\begin{regle}[Règle et définition] +\TeX{} est un langage de macros, c'est-à-dire qu'il agit comme un outil qui \emph{remplace} du code par du code. Ce remplacement, aussi appelé développement, a lieu dans une zone de mémoire nommée \og \idx{pile d'entrée}\fg. + +Ainsi, lorsque \TeX{} exécute une macro, elle (ainsi que ses éventuels arguments) est d'abord remplacée par le code qui lui a été donné lorsqu'on l'a définie avec \verb|\def|. Ce code est le \og \idx{texte de remplacement}\fg{}. Une fois que ce développement a été fait, \TeX{} continue sa route en tenant compte du remplacement effectué. +\end{regle} + +Prenons le temps d'examiner en détail ce qui se passe réellement et définissons les conventions suivantes : le symbole \TeXhead{} représente l'endroit où \TeX{} s'apprête à lire la suite du code. On a vu qu'il ne lisait que ce dont il avait besoin, et on va donc encadrer en pointillés le ou les tokens que \TeX{} va lire à la prochaine étape. Lorsque le développement se fait, la pile est encadrée. Observons donc, étape par étape ce qui se passe lorsqu'on écrit «\verb-\foo le monde-» (l'espace après \verb-\foo- sera ignoré en vertu de la règle concernant les espaces du code vue à la page~\pageref{regle.espace}) :\smallbreak + +\hspace{5em}\TeXhead\verb-\foo le monde-\par\nobreak +\hspace{5em}\TeXhead{\ttfamily\fboxsep1pt \dashbox{\string\foo}le monde}\par\nobreak +\hspace{5em}\TeXhead{\ttfamily\fboxsep1pt \fbox{ Bonjour}le monde}\smallbreak + +\noindent Ayant développé la commande \verb-\foo- dans sa pile, \TeX{} continue à lire le code qu'il a obtenu. Ici, cela se solderait par la lecture des caractères «Bonjourle monde» et par leur affichage. + +Il est important de comprendre que la notion de développement ne s'applique qu'à des séquences de contrôle ou des caractères actifs. On peut donner la définition suivante : + +\begin{regle}[Définition]Une \idx{séquence de contrôle} est développable si \TeX{} peut la remplacer par un code différent.\end{regle} + +Les primitives n'ont pas de \idx{texte de remplacement}. Cependant, certaines d'entre elles sont développables. Nous en avons rencontré une qui est la paire \idx\csname\linebreak[1]\verb-...-\linebreak[1]\idx\endcsname dont l'argument est le code qui se trouve entre elles. En effet, lorsque \TeX{} les exécute, il transforme leur argument en une \idx{séquence de contrôle}. Cette action recoupe la définition donnée juste au-dessus : le code qui est substitué est bien différent de ce qu'il y avait avant le développement. + +La plupart des primitives ne sont pas développables en ce sens que si l'on force leur développement (nous verrons comment), on obtient la primitive elle-même. L'action de ces primitives est de provoquer une action par le processeur lorsqu'elles sont exécutées par \TeX{}. Par exemple, pour la primitive \verb|\def| qui n'est pas développable, son action est de ranger dans la mémoire de \TeX{} le texte de remplacement d'une macro. Les caractères de code de catégorie 11 comme «\verb-A-» ou même ceux dont le code de catégorie est moins inoffensif comme «\verb-$-» ne sont pas développables non plus. Comme les primitives non développables, ils provoquent une action via le programme \texttt{tex} qui est de placer ce caractère dans la page pour les lettres et passer en mode mathématique\idx*{mode!mathématique} pour \verb-$-. Seuls les caractères rendus actifs dont le code de catégorie est 13 peuvent se développer puisque leur comportement est similaire à celui des commandes. + +Prenons maintenant l'exemple suivant où la macro \verb-\foo- admet 2 arguments : +\centrecode-\def\foo#1#2{Bonjour #1 et #2}- + +\noindent Faisons l'hypothèse que l'on stocke deux prénoms «Alice» et «Bob» dans les deux macros suivantes : +\centrecode-\def\AA{Alice } \def\BB{Bob }- + +\noindent Pour bien comprendre le mécanisme du développement, prenons à nouveau le temps d'examiner en détail ce qui va se passer lorsqu'on écrit : + +\centrecode-\csname foo\ensdcsname\AA\BB et les autres-. + +\noindent Dans ce cas, voici les étapes par lesquelles \TeX{} va passer : +\begingroup +\fboxsep1pt +\parskip1.5pt +\def\beforecode{\ttfamily\hspace{5em}} +\begin{enumerate}[parsep=0pt,itemsep=0pt] + \item lecture du code dont il a besoin pour former la commande \boxtoken\foo{} :\par + {\beforecode\TeXhead\dashbox{\string\csname\ foo\string\endcsname}\string\AA\string\BB\ et les autres.} + \item développement de ce qui a été lu, ce qui provoque la construction de la macro \verb-\foo- dans la pile :\par + {\beforecode\TeXhead\fbox{\string\foo}\string\AA\string\BB\ et les autres.} + \item la pile ne contenant pas les deux arguments requis pour la macro \verb-\foo-, ceux-ci sont donc lus à l'extérieur de la pile :\par + {\beforecode\TeXhead\fbox{\rlap{\dashbox{\phantom{\string\foo\string\AA\string\BB}}}\string\foo}\string\AA\string\BB\ et les autres.} + \item développement de \verb-\foo- et ses deux arguments qui provoque sa substitution par son texte de remplacement ;\par + {\beforecode\TeXhead\fbox{Bonjour \string\AA\ et \string\BB} et les autres.} + \item affichage de «Bonjour» et lecture du code jusqu'à \verb-\AA- :\par + {\beforecode Bonjour \TeXhead\fbox{\dashbox{\string\AA} et \string\BB} et les autres.} + \item développement de \verb-\AA- :\par + {\beforecode Bonjour \TeXhead\fbox{Alice et \string\BB} et les autres.} + \item affichage de «Alice et » et lecture du code jusqu'à \verb-\BB- ;\par + {\beforecode Bonjour Alice et \TeXhead\fbox{\dashbox{\string\BB}} et les autres.} + \item développement de \verb-\BB- :\par + {\beforecode Bonjour Alice et \TeXhead\fbox{Bob} et les autres.} + \item lecture du code qui ne contient plus que des caractères inoffensifs jusqu'au «\verb-.-» :\par + {\beforecode Bonjour Alice et Bob et les autres.\TeXhead} +\end{enumerate} +\endgroup +\grandsaut + +Il arrive qu'une \idx{séquence de contrôle} puisse être développée plusieurs fois. Imaginons que l'on définisse deux séquences de contrôle \verb-\A- et \verb-\B- par ce code : + +\centrecode-\def\A{\B} +\def\B{Salut}- + +Si l'on écrit \verb-\A- dans le code, \TeX{} va procéder à son développement et va la remplacer par \verb-\B-. Cette \idx{séquence de contrôle} va être développée à son tour pour être remplacée par « Salut » dans la pile. Dans ce cas, on dit que la macro \verb-\A- a été 2-développée, c'est-à-dire qu'elle a dû subir 2 développements successifs. On pourrait se représenter la situation par des paquets cadeaux : la \idx{séquence de contrôle} est le paquet cadeau (le contenant) et son développement est le contenu. Pour les deux macros \verb-\A- et \verb-\B-, il s'agit d'un système de poupées gigognes où le paquet cadeau \verb-\A- contient le paquet \verb-\B- qui lui-même contient « Salut ». On comprend qu'il faut ouvrir 2 paquets pour prendre connaissance du contenu final : + +\begin{centrage} + \small\boxcs\A{\boxcs\B{Salut}} +\end{centrage} + +Si au lieu de \verb-\A-, on avait écrit «\idx\csname\verb- A-\idx\endcsname», il aurait fallu ajouter un développement de plus au début pour transformer ce code en \verb-\A-. Et donc c'est après 3 développements que «\verb-\csname A\endcsname-» donne «Salut». + +\begin{exercice} +On définit trois macros de cette façon : + +\centrecode-\def\aa{\bb} +\def\bb{\csname cc\endcsname} +\def\cc{Bonjour}- + +Si on écrit dans le code \idx\csname\verb- aa-\idx\endcsname, combien de développements successifs \TeX{} doit-il effectuer pour que le code dans la pile soit «Bonjour» ? +\solution +Il suffit de compter les développements. Dans le schéma suivant, les flèches signifient «se développe en\ldots{}» : + +\begin{center} +\small +\verb-\csname aa\endcsname- $\longrightarrow$ \verb-\aa- $\longrightarrow$ \verb-\bb- $\longrightarrow$ \verb|\csname cc\endcsname| $\longrightarrow$ \verb|\cc| $\longrightarrow$ Bonjour +\end{center} + +Il faut donc 5 développements. +\end{exercice} + +\begin{exercice} +On redéfinit les macros \verb-\aa- et \verb-\bb- de cette façon : + +\centrecode-\def\aa{\bb} +\def\bb{\def\cc{Bonjour}}- + +\Qu el est le 2-développement de \verb-\aa-. Et le 3-développement ? +\solution +Le 2-développement de \verb-\aa- est «\verb-\def\cc{Bonjour}-». + +Pour le 3-développement, la question n'est pas claire. En effet, le 2-développement que l'on voit à la ligne ci-dessus n'est pas un token unique est constitué de 11 tokens. D'ailleurs, le seul qui est susceptible de se développer est \verb|\cc| puisque les autres sont soit une primitive (\verb|\def|), soit des accolades, soit des lettres. On va considérer que le développement ne s'applique qu'au premier token du code obtenu et la réponse va donc être : le 3-développement est identique au 2-développement, car la primitive \verb|\def| se développe en elle-même. +\end{exercice} + +\begin{exercice} +Redéfinissons les macros \verb-\aa-, \verb-\bb- et \verb|\cc| de cette façon : + +\centrecode-\def\aa{X} +\def\bb{Y} +\def\cc{\secondoftwo\aa\bb 123}¤§*\secondoftwo¤- + +\Qu el code est obtenu sur la pile après 1, 2 et 3 développements de la macro \verb-\cc- ? +\solution +État de la pile après : +\begin{itemize} + \item 1 développement : \verb-\secondoftwo\aa\bb 123-§*\secondoftwo + \item 2 développements : \verb-\bb 123- + \item 3 développements : \verb-Y123- +\end{itemize} +\end{exercice} + +\noindent Les règles suivantes découlent de ce qui a été dit : + +\begin{regle}[Règles] +La lecture du code se fait séquentiellement de gauche à droite et \TeX{} ne cherche pas à lire plus de tokens que ce dont il a besoin.\medbreak + +Lorsque \TeX{} rencontre une séquence de contrôle, il lit aussi ses éventuels arguments pour procéder à son développement qui consiste à remplacer le tout par du code. Si la pile ne contient pas assez d'arguments, \TeX{} va chercher ces arguments manquants juste après la pile, c'est-à-dire dans le code source. +\end{regle} + +La première partie de cette règle concernant la lecture séquentielle du code est extrêmement contraignante et rend certaines choses impossibles. Par exemple définir une \idx{séquence de contrôle} formée avec \idx\csname et \idx\endcsname, comme la macro \texttt\textbackslash\boxtoken{ab1c}. En effet, comme on l'a vu à la page~\pageref{construction.nom.etrange}, c'est une erreur que d'écrire : + +\centrecode-\def\csname ab1c\endcsname{<code>}- + +\noindent Nous avons vu également à la page~\pageref{non.dev.csname} que l'on ne peut écrire + +\centrecode-\let\salut\csname foo\endcsname-\idx*\let + +\noindent pour rendre la macro \verb|\salut| égale à \verb|\foo|. + +Concernant le développement, \TeX{} permet d'enfreindre la première partie de la règle en offrant la possibilité, dans une faible mesure, de développer le code \emph{non linéairement} et c'est ce que nous allons découvrir avec la primitive \verb|\expandafter|. + +\section{La primitive \texttt{\textbackslash expandafter}}\idx*[|(]\expandafter +Il n'est pas exagéré de dire qu'aux yeux de débutants en \TeX, \verb|\expandafter| est une des primitives les plus mystérieuses qui soient. Certains lui prêtent des vertus quasi magiques et en viendraient presque à croire que disséminer quelques \verb|\expandafter| dans un code qui ne fonctionne pas le ferait fonctionner ! Ici encore, il ne sera pas question de magie, mais au contraire de démystification puisque cette primitive \verb|\expanfafter| fait une chose extrêmement simple. + +Tout d'abord, elle est développable, c'est-à-dire que lorsque \TeX{} va la développer, il va la remplacer par du code dans la pile. L'action de cette primitive se passe au niveau des tokens et non pas à celui des arguments. Si \verb|<x>| et \verb|<y>| sont deux tokens et si l'on écrit : + +\centrecode-\expandafter<x><y>- + +\noindent cela a pour effet de 1-développer le token \verb-<y>- avant de lire \verb-<x>-. En fait, tout se passe comme si le \verb-\expandafter- permettait de «sauter» \verb-<x>- pour 1-développer \verb-<y>- et revenir ensuite au point de départ. + +\begin{regle} +Si \verb|<x>| et \verb|<y>| sont deux \emph{tokens}, écrire + +\centrecode|\expandafter<x><y>| + +a pour effet, lorsque \TeX{} développe \verb|\expandafter|, de 1-développer \verb|<y>| sans pour autant que la tête de lecture ne bouge de place (et donc reste devant \verb|<x>| qu'elle n'a toujours pas lu). + +Le \verb|\expandafter| disparait après s'être développé et on obtient donc + +\centrecode-<x><*y>- + +où «\verb|*|» indique le 1-développement du token \verb|<y>|. +\end{regle} + +Attention, \verb|<y>| a été 1-développé, mais n'a pas été \emph{lu}. La tête de lecture de \TeX{} n'a pas bougé dans l'opération. On peut se représenter la situation en imaginant que la tête de lecture dispose d'une « tête de développement », capable sur ordre de se désolidariser pour aller développer du code non encore lu. La tête de développement, une fois sa mission accomplie, revient sur la tête de lecture afin que cette dernière poursuive son travail. + +Une application immédiate est que désormais, nous pouvons définir la \idx{séquence de contrôle} \verb|\|\boxtoken{ab1c} avec \idx\csname : il suffit de sauter le \verb|\def| avec un \verb-\expandafter- pour provoquer la formation du nom de la macro avant que \verb-\def- ne la voit : + +\showcode/\expandafter\def\csname ab1c\endcsname{Bonjour} +Texte de remplacement de la macro : \csname ab1c\endcsname/ + +Au lieu d'écrire «\verb|\expandafter\def\csname ab1c\endcsname|», il est préférable de définir une macro §\defname dont l'argument est le nom de la macro. Il est facile de faire de même avec §\letname. + +\showcode/\def\defname#1{\expandafter\def\csname#1\endcsname}¤§*\defname\idx*\csname\idx*\endcsname¤ +\defname{ab1c}{Bonjour}¤§*\defname¤ +Texte de remplacement de \litterate|\abc1| : \csname ab1c\endcsname\par¤§*\litterate¤ +\def\letname#1{\expandafter\let\csname#1\endcsname}¤§*\letname¤ +\def\foo{abc}\letname{foo1}=\foo% rend la macro "\foo1" égale à \foo +Texte de remplacement de \litterate|\foo1| : \csname foo1\endcsname¤\idx*\char\idx*\meaning§*\litterate¤/ + +Afin de comprendre l'utilité du développement, tentons maintenant une expérience en utilisant la macro à arguments délimités §\firstto@nil[|(]. Nous l'avons définie de la façon suivante à la page~\pageref{firstto@nil} : + +\centrecode-\def\firstto@nil#1#2\@nil{#1}- + +\noindent son texte de remplacement est donc le premier argument de ce qui se trouve entre cette macro et \verb-\@nil-. + +Maintenant, si on définit la macro \verb-\foo- par \verb|\def\foo{Bonjour}| et que l'on écrit \verb|\firstto@nil\foo\@nil|, qu'obtient-on ? + +\showcode/ \def\foo{Bonjour}\catcode`@=11 +Résultat : \firstto@nil\foo\@nil¤§*\firstto@nil¤ +\catcode`@=12/ + +Contrairement à ce que l'on pourrait penser, on n'obtient pas la lettre «B» mais le mot entier. C'est que, fort logiquement, \verb-\firstto@nil- a pris comme argument \verb-#1- la macro \verb-\foo- en entier tandis que l'argument délimité \verb|#2| est resté vide. Il \emph{essentiel} de ne pas confondre macro et texte de remplacement : dans un cas, il y a un seul token alors que dans l'autre, il y a tous les tokens du texte de remplacement, c'est-à-dire ici les 7 lettres du mot «Bonjour». Pour que \verb|\first@tonil| agisse sur le texte de remplacement de \verb|\foo| et non pas sur \verb|\foo| elle-même, il faut développer \verb-\foo- \emph{avant} que \TeX{} ne lise la macro \verb-\firstto@nil-. Nous allons utiliser \verb-\expandafter- pour sauter \verb-\firstto@nil- et provoquer ce développement : + +\showcode/\def\foo{Bonjour}\catcode`@=11 +Résultat : \expandafter\firstto@nil\foo\@nil +\catcode`@=12/ + +\noindent Le mécanisme est schématisé ci-dessous : + +\begin{centrage} + \small\jumptok-\firstto@nil\foo-\verb-\@nil- +\end{centrage} + +\noindent La flèche représente le développement du token encadré. Ici donc, le développement de \verb|\expandafter| va provoquer le 1-développement de \verb|\foo| et donne : + +\centrecode-\firsto@nil Bonjour\@nil- + +\noindent Cette expérience faite avec \verb|\firsto@nil| peut être généralisée à n'importe quelle autre macro : + +\begin{regle} +Lorsqu'on stocke des tokens dans une \verb|\<macroA>| (comme cela arrive très souvent), si l'on veut mettre ces tokens dans l'argument d'une autre macro \verb|\<macroB>|, il \emph{faut} développer \verb|\<macroA>| avant que \verb|\<macroB>| n'agisse. +\end{regle} + +\begin{exercice} +Au lieu d'une macro, utiliser un nouveau registre de token \verb|\testtoks| pour stocker les caractères « Bonjour ». Comment faut-il alors utiliser §\firstto@nil pour obtenir la lettre « B »? +\solution +Pour extraire le contenu du registre, il faut développer la primitive \idx\the lorsqu'elle précède le registre. Ici, au lieu de 1-développer la macro, il faut 1-développer \idx\the\idx\toks0 : + +\showcode/\newtoks\testtoks \testtoks={Bonjour}¤\idx*\newtoks¤ +\catcode`@=11 +Résultat : \expandafter\firstto@nil\the\testtoks\@nil¤\idx*\the¤ +\catcode`@=12/ +\end{exercice} +\grandsaut + +Afin de bien maitriser le développement, abandonnons la macro §\firstto@nil[|)] pour construire une macro plus généraliste \verb|\>| qui agit comme un révélateur universel et qui affiche littéralement tout ce qui se trouve entre \verb|\>| et \verb|<| : + +\centrecode|\>ensemble de tokens<| + +On voudrait que \verb|\>12^&$~\foo$<| affiche «\detokenize{12^&$~\foo$}». Pour cela, la primitive \idx\detokenize va bien nous aider. Cette primitive développable admet un argument entre accolades et le transforme en caractères de code de catégorie 12 (et 10 pour l'espace). Par exemple, si on écrit \verb-\detokenize{12^&$~\foo$}-, on obtient « \detokenize{12^&$~\foo$} ». + +Il est utile de faire quelques remarques concernant \idx\detokenize : une espace est \emph{toujours} rajoutée après une \idx{séquence de contrôle}; ici, on peut la voir après \verb-\foo-. Enfin, l'argument doit contenir un texte où les accolades sont équilibrées. Voici le code qui permet de programmer la macro \verb|\>| évoquée plus haut\label{macro.>} : + +\centrecode/\long\def\>#1<{\detokenize{#1}}/ +\long\def\>#1<{\detokenize{#1}} + +Expérimentons cette macro et profitons de \verb-\expandafter- pour « sauter » la macro \verb-\>- afin de développer le premier token qui se trouve juste après : + +\showcode/\def\foo{bonjour} +a) \>\csname foo\endcsname< \par¤\idx*\csname\idx*\endcsname¤ +b) \>\foo< \par +c) \expandafter\>\foo< \par +d) \>\foo\foo< \par +e) \expandafter\>\foo\foo</ + +Chaque fois qu'un \verb-\expandafter- précède \verb-\>- (cas c et e), on constate que le développement du \verb-\foo- a bien lieu tout de suite après la balise. À la dernière ligne, on voit que le premier \verb-\foo- est développé alors que le second, non concerné par le \verb-\expandafter- reste tel quel. + +Maintenant, essayons de 1-développer le \emph{second} \verb-\foo- de la dernière ligne tout en laissant le premier intact. Le principe est que le développement d'un premier \verb|\expandafter| provoque le développement d'un autre situé un token après, et ainsi de suite pour finir par 1-développer le token voulu. Cette capacité que possèdent les \verb|\expandafter| à se transmettre de proche en proche le développement conduit à appeler ce genre de structure un « pont d'\verb|\expandafter|» : + +\begin{centrage} + \small\jumptok-\>\foo\foo-\verb-<- +\end{centrage} + +Une fois que les \verb-\expandafter- se sont passé le relai pour développer le second \verb-\foo-, ceux-ci disparaissent, et le code qui est prêt à être lu est : + +\centrecode-\>\foo Bonjour<- + +\noindent ce que l'on vérifie dans cet exemple : + +\showcode/\def\foo{Bonjour} \expandafter\>\expandafter\foo\foo</ + +La règle fondamentale est la suivante : + +\begin{regle} +Pour 1-développer un token précédé de $n$ tokens, il faut mettre un \verb-\expandafter- devant chacun de ces $n$ tokens. +\end{regle} + +Par exemple, en partant de \verb-\foo-, si l'on veut définir une macro \verb-\bar- dont le texte de remplacement soit «\verb-\foo Bonjour-», alors on doit mettre un \verb-\expandafter-, représenté par un « $\bullet$ » devant les 4 tokens \boxtoken{\def}, \boxtoken{\bar}, \boxtoken[]{\{} et \boxtoken{\foo} pour provoquer le 1-développement du second \verb-\foo- : + +\begin{centrage} + \small\ttfamily$\bullet$\string\def$\bullet$\string\bar$\bullet$\string{$\bullet$\string\foo\string\foo\string} +\end{centrage} + +\showcode|\def\foo{Bonjour} +\expandafter\def\expandafter\bar\expandafter{\expandafter\foo\foo} +\meaning\bar| + +Une application de ceci, très utile en programmation, est l'ajout d'un \verb|<code>| au texte de remplacement d'une macro. Appelons §\addtomacro[|(]\label{addtomacro} la commande qui admet deux arguments dont le premier est une macro et le second est un texte qui sera ajouté à la fin du texte de remplacement de la macro. On doit pouvoir écrire : + +\centrecode-\def\foo{Bonjour} +\addtomacro\foo{ le monde}-§*\addtomacro + +\noindent pour qu'ensuite, la macro \verb-\foo- ait comme texte de remplacement « Bonjour le monde ». La méthode consiste à partir de cette définition : + +\centrecode-\long\def\addtomacro#1#2{\def#1{#1#2}}- + +\noindent Si on écrit «§\addtomacro\verb-\foo{ le monde}-», le texte de remplacement devient + +\centrecode-\def\foo{\foo Bonjour}- + +\noindent On le voit, le texte de remplacement va conduire à une définition \emph{récursive} de \verb-\foo- puisque le texte de remplacement de \verb|\foo| contient \verb|\foo|. Ouvrons une parenthèse et intéressons-nous à ce qui va se passer. +\grandsaut + +L'exécution de \verb|\foo| telle que définie ci-dessus va conduire à une erreur de compilation. En effet, à chaque développement, non seulement \verb|\foo| est créée dans la pile, mais 7 tokens (les 7 lettres du mot « Bonjour ») y sont aussi ajoutés. La pile va donc croitre très rapidement, voici son contenu lors des premiers développements : +\begin{enumerate} + \item \verb|\foo| + \item \verb|\foo Bonjour| + \item \verb|\foo BonjourBonjour| + \item \verb|\foo BonjourBonjourBonjour| + \item etc. +\end{enumerate} + +Cette pile n'étant pas infinie, le maximum va être rapidement atteint : + +\errcode/\def\foo{\foo Bonjour}% définition récursive +\foo% l'exécution provoque un dépassement de la capacité de la pile/% +{! TeX capacity exceeded, sorry [input stack size=5000].} + +Augmenter cette taille ne changerait pas l'issue, même si des développements supplémentaires étaient faits. +\grandsaut + +Fermons la parenthèse et revenons à \verb|\def\foo{\foo Bonjour}|. Afin d'éviter l'écueil du dépassement de la capacité de la pile, il faut 1-développer \verb-\foo- à l'intérieur des accolades pour qu'elle soit remplacée par «Bonjour» et ce, \emph{avant} que \verb|\def| n'entre en action. Comme nous l'avons vu, il suffit de placer un \verb-\expandafter- devant chaque token qui précède ce \verb-\foo-. Voici le code correct qui permet de programmer la macro \verb-\addtomacro- : + +\showcode|\long\def\addtomacro#1#2{%¤§*\addtomacro¤ + \expandafter\def\expandafter#1\expandafter{#1#2}% +} +\def\foo{Bonjour} +\addtomacro\foo{ le monde}¤§*\addtomacro¤ +\meaning\foo¤\idx*\meaning¤| + +\begin{exercice} +En utilisant §\addtomacro, écrire une macro §\eaddtomacro qui agit comme §\addtomacro sauf que le second argument est 1-développé. Ainsi, si on écrit + +\centrecode-\def\foo{Bonjour} \def\world{ le monde} +\eaddtomacro\foo\world- + +alors, le texte de remplacement de la macro \verb-\foo- doit être «Bonjour le monde». +\solution +Si nous partons de ceci : + +\centrecode-\long\def\eaddtomacro#1#2{\addtomacro#1{#2}}- + +le texte de remplacement est de §\eaddtomacro est + +\centrecode-\addtomacro#1{#2}- + +Pour que §\addtomacro accède au texte de remplacement de la macro \verb|#2|, nous savons qu'à l'intérieur des accolades, \verb-#2- doit être 1-développée. Pour ce faire, il faut placer un \verb-\expandafter- devant chacun des 3 tokens qui précèdent \verb-#2- : + +\showcode|\long\def\eaddtomacro#1#2{%¤§*\eaddtomacro¤ + \expandafter\addtomacro\expandafter#1\expandafter{#2}% +} +\def\foo{Bonjour} \def\world{ le monde} +\eaddtomacro\foo\world¤§*\eaddtomacro¤ +\meaning\foo¤\idx*\meaning¤| +\end{exercice}§*\addtomacro[|)] +\grandsaut + +Nous allons maintenant construire les mêmes macros sauf qu'elles agiront sur le contenu de registres de tokens. Mais pour atteindre ce but, une règle va nous être utile\ldots{} + +\begin{regle}\relax\label{primitive.suivie.par.accolade} +Les primitives qui doivent immédiatement être suivies d'une accolade ouvrante ont la particularité suivante : elles développent au maximum\idx*{développement maximal} jusqu'à trouver l'accolade ouvrante. + +Nous connaissons : +\begin{itemize} + \item \idx\lowercase et \idx\uppercase; + \item \idx\detokenize; + \item \idx\toks\verb*-<nombre>= - (ou \verb*-\<nom>= - si on est passé par \idx\newtoks\verb-\<nom>-), sachant que le signe \verb|=| et l'espace qui le suit sont facultatifs. +\end{itemize} + +La conséquence est que l'on peut placer un (ou plusieurs) \verb|\expandafter| entre ces primitives et l'accolade ouvrante pour développer leur argument avant qu'elles n'agissent. +\end{regle} + +La méthode va consister à tirer parti de cette règle. Si \verb|#1| est un registre de tokens, on va partir de + +\centrecode-#1= {\the#1#2}- + +\noindent et placer un \verb|\expandafter| entre \verb*|#1= | et l'accolade ouvrante pour 1-développer \verb|\the#1| afin de délivrer le contenu du registre \verb|#1| : + +\showcode/\long\def\addtotoks#1#2{#1= \expandafter{\the#1#2}}¤§*\addtotoks\idx*\long¤ +\newtoks\bar¤\idx*\newtoks¤ +\bar={Bonjour}% met "Bonjour" dans le registre +\addtotoks\bar{ le monde}% ajoute " le monde"¤§*\addtotoks¤ +\the\bar% affiche le registre¤\idx*\the¤/ + +En copiant la méthode utilisée avec §\eaddtomacro, il est facile de construire la macro §\eaddtotoks qui 1-développe son second argument avant de l'ajouter au contenu du registre à token \verb|#1| : + +\showcode/\long\def\eaddtotoks#1#2{\expandafter\addtotoks\expandafter#1\expandafter{#2}}¤§*\eaddtotoks§*\addtotoks¤ +\bar={Bonjour} +\def\macrofoo{ le monde} +\eaddtotoks\bar\macrofoo¤§*\eaddtotoks¤ +\the\bar¤\idx*\the¤/ + +\section{Développer encore plus avec \texttt{\textbackslash expandafter}} +Jusqu'à présent, nous nous sommes limités à des \verb|\expandafter| provoquant des 1-développements, mais on peut 2-développer, voire plus. Pour voir comment, définissons les macros + +\centrecode-\def\X{Bonjour} \def\Y{\X}- + +\noindent et intéressons-nous au problème suivant : essayons de 2-développer le «\verb-\Y-» qui se trouve entre \verb-\>- et \verb-<- dans «\verb-\>\Y<-» de façon à obtenir l'affichage «Bonjour». + +Ce que nous savons faire, c'est 1-développer \verb-\Y- : + +\centrecode-\expandafter\>\Y<- + +\noindent En l'état, ce code ne fait que 1-développer \verb|\Y|. Pour 2-développer, il faudrait que le \verb|\expandafter| ci-dessus agisse sur le 1-développement de \verb|\Y|. On va donc partir du code ci-dessus et faire en sorte que préalablement, le \verb|\Y| soit 1-développé. Nous savons comment placer les \verb-\expandafter- pour que la macro \verb-\Y- soit 1-développée, il suffit d'en mettre un devant chaque token qui précède \verb-\Y-. Il faut donc placer un \verb-\expandafter- devant le premier \verb-\expandafter- et un autre devant \verb-\>-. On arrive au code suivant : + +\centrecode-\expandafter\expandafter\expandafter\>\Y<- + +\noindent Pour nous assurer que ce code va conduire au résultat escompté, examinons ce qui se passe avec les conventions graphiques vues auparavant. Voici ce qui se passe dans un premier temps : + +\begin{centrage} + \small\jumptok-\expandafter\>\Y-\verb-<- +\end{centrage} + +\noindent La macro \verb-\Y- est développée en \verb-\X- et on obtient le code « \verb-\expandafter\>\X<- » dans la pile. Voici maintenant le deuxième temps : + +\begin{centrage} + \small\jumptok-\>\X-\verb-<- +\end{centrage} + +\noindent \verb-\X- se développe bien en «Bonjour» ce qui donne : + +\centrecode-\>Bonjour<- + +\noindent que nous cherchions à obtenir. Vérifions-le sur cet exemple : + +\showcode/\def\X{Bonjour} \def\Y{\X} +\expandafter\expandafter\expandafter\>\Y</ + +Ce pont d'\verb|\expandatfer| a provoqué \emph{deux} développements pour arriver au code final et on peut parler de « pont à plusieurs niveaux ». +\grandsaut + +Retenons que lorsque deux tokens \verb-<x><y>- sont dans le code, pour 2-développer \verb-<y>- avant de lire \verb-<x>-, nous devons placer trois \verb-\expandafter- devant \verb-<x>- de cette façon : +\centrecode-\expandafter\expandafter\expandafter<x><y>- + +Et si nous voulions 3-développer \verb-<y>-, il faudrait ajouter des \verb|\expandafter| dans le code ci-dessus pour, au préalable, 1-développer \verb-<y>-. Pour cela, il faudrait placer un \verb-\expandafter- devant chaque token qui précède \verb-<y>- et donc en ajouter 4 (un devant chacun des 3 \verb-\expandafter- existants et un devant \verb-<x>-). Nous aurions alors 7 \verb|\expandafter| en tout : + +\centrecode-\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter<x><y>- + +Pour un développement de plus de \verb-<y>- de façon à le 4-développer, il faudrait 1-développer \verb|<y>| dans le code ci-dessus et donc, ajouter 8 \verb|\expandatfer| de plus (un devant les 7 \verb-\expandafter- existants plus un devant \verb-<x>-) ce qui en ferait 15. +\grandsaut + +Essayons d'en tirer une règle générale : soit $u_k$ le nombre d'\verb-\expandafter- placés devant les tokens \verb-<x><y>- qui provoquent le $k$-développement de \verb|<y>|. Si on veut procéder à un développement de plus de \verb-<y>-, il faut ajouter $u_k+1$ \verb-\expandafter- devant \verb-<x><y>- (un devant chacun des $u_k$ \verb-\expandafter- existants et un devant \verb-<x><y>-). Pour traduire ceci par une relation de récurrence mathématique, il suffit de partir de $u_1=1$ puisque un \verb|\expandafter| + placé devant \verb|<x><y>| provoque le 1-développement de \verb|<y>|. On a donc : + +\begin{align*} +u_1&=1&u_{k+1}&=u_k+u_k+1\\ +&&&=2u_k+1 +\end{align*} + +\noindent À l'aide d'une démonstration mathématique facile, on en tirerait que : +\[u_k=2^k-1\] + +Le raisonnement pourrait facilement être généralisé si plusieurs tokens précédaient \verb-<y>-. La règle générale est la suivante et il est bon de s'en souvenir : + +\begin{regle} +Pour provoquer le $k$-développement d'un token \verb|<y>| précédé par $n$ autres tokens, il faut placer $2^k-1$ \verb-\expandafter- devant chacun des $n$ tokens qui le précèdent. +\end{regle} + +\begin{exercice} +Reprenons les macros suivantes : + +\centrecode-\def\X{Bonjour} +\def\Y{\X}- + +Placer les \verb-\expandafter- dans le code ci-dessous pour que le texte de remplacement de la macro \verb-\foo- soit « Bonjour » : + +\centrecode-\def\foo{\Y}- + +\solution +La réponse se trouve dans la règle précédente : il faut 2-développer \verb-\Y- et donc ajouter $2^2-1$, c'est-à-dire 3 \verb-\expandafter- devant chaque token qui précède \verb-\Y-. Ces tokens sont au nombre de 3 («\verb|\def|», «\verb|\foo|» et «\verb|{|») et donc, il faut remplacer chaque « $\bullet$ » par 3 \verb-\expandafter- dans ce code : + +\begin{centrage} + \small$\bullet$\verb-\def-$\bullet$\verb-\foo-$\bullet$\verb-{\Y}- +\end{centrage} + +\noindent Cela donne un code un peu indigeste : + +\centrecode-\expandafter\expandafter\expandafter\def +\expandafter\expandafter\expandafter\foo +\expandafter\expandafter\expandafter{\Y}- +\end{exercice} + +\begin{exercice} +Placer correctement des \verb-\expandafter- dans la dernière ligne du code ci-dessous pour que \verb-\bar- ait comme texte de remplacement « Bonjour le monde » : + +\centrecode-\def\foo{Bonjour} +\def\world{ le monde} +\def\bar{\foo\world}- +\solution +Si on part de + +\centrecode-\def\bar{\foo\world}- + +il faut 1-développer \verb|\foo| et 1-développer \verb|\world| dans le texte de remplacement de \verb|\bar|. + +Nous allons construire un pont d'\verb|\expandafter| en deux passes : la première 1-développera une des deux macros et la deuxième 1-développera l'autre. + +Dans un cas comme celui-ci qui est un cas particulier d'un cas plus général, il faut commencer par placer des \verb|\expandafter| pour 1-développer la macro la plus à gauche. Ceci étant fait, la passe suivante consistera à placer des \verb|\expandatfer| pour développer la macro suivante. Et ainsi de suite pour finir, lors de la dernière passe, par le développement de la macro la plus à droite. On progresse donc de gauche à droite, c'est-à-dire dans l'ordre inverse de celui dans lequel elles seront développées par \TeX{} lorsqu'il lira le code. + +Ici donc, la première passe consiste à placer des \verb|\expandafter| pour 1-développer \verb|\foo|, ce qui revient à en placer un devant chaque token qui précède \verb|\foo| : + +\centrecode-\expandafter\def\expandafter\foo\expandafter{\foo\world}- + +Dans une deuxième passe, nous allons 1-développer \verb-\world- et placer aussi un \verb-\expandafter- devant chaque token qui la précède, en tenant compte de ceux déjà mis en place : + +\centrecode-\expandafter\expandafter\expandafter\def +\expandafter\expandafter\expandafter\foo +\expandafter\expandafter\expandafter +{\expandafter\foo\world}- + +Bien que ne répondant pas aux contraintes de l'énoncé, la macro §\eaddtomacro peut se révéler utile dans un cas comme celui-ci. En effet, elle améliore la lisibilité du code puisqu'elle évite ces nombreux \verb|\expandafter|. Une autre façon de faire aurait été : + +\centrecode-\def\bar{}% +\eaddtomacro\bar\foo +\eaddtomacro\bar\world¤§*\eaddtomacro¤- +\end{exercice} + +La primitive \verb|\expandafter| intervenant au niveau des \emph{tokens}, il est intéressant en programmation de disposer d'une macro, appelons-la §\expsecond[|(], qui intervient au niveau des \emph{arguments}. On voudrait qu'elle soit capable de sauter un argument entier pour 1-développer le premier token de l'argument qui suit. On pourrait donc écrire : + +\centrecode-\expsecond{<arg1>}{<arg2>}- + +\noindent pour que le code finalement lu par \TeX{} soit + +\centrecode-<arg1>{*<arg2>}- + +\noindent où l'étoile indique que le premier token de \verb|<arg2>| est 1-développé. + +\begin{exercice}\label{expsecond} +Construire la macro §\expsecond en utilisant la macro auxiliaire §\swaparg[|(] qui échange 2 arguments en mettant le premier entre accolades : + +\centrecode-\long\def\swaparg#1#2{#2{#1}}- + +\solution +La méthode consiste à partir de + +\centrecode-\long\def\expsecond#1#2{\swaparg{#2}{#1}}- + +qui, après que §\swaparg ait joué son rôle, donnerait «\verb|#1{#2}|». Pour que le premier token de \verb|#2| soit 1-développé, il faut placer des \verb|\expandafter| dans le texte de remplacement de §\expsecond pour provoquer ce développement. Cela donne : + +\centrecode-\long\def\swaparg#1#2{#2{#1}} +\long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}}- +\end{exercice} + +\begin{exercice} +Combien de développements doit subir §\expsecond\verb-{<arg1>}{<arg2>}- pour que le code qui soit effectivement à lire soit \verb-<arg1>{*<arg2>}- ? + +Placer correctement des \verb|\expandafter| dans le 2\ieme{} ligne de ce code + +\centrecode-\def\X{Bonjour} +\>\expsecond{\X}{\X}<- + +pour qu'il affiche « \string\X{} Bonjour ». La macro \verb|\>|, programmée à la page~\pageref{macro.>}, affiche tel quel le code qu'elle rencontre jusqu'au prochain \verb|<|. +\solution +Développons §\expsecond\verb-{<arg1>}{<arg2>}- comme le fait \TeX{} : +\begin{enumerate} + \item \verb-\expandafter\swaparg\expandafter{<arg2>}{<arg1>}- + \item \verb-\swaparg{*<arg2>}{<arg1>}- (les \verb|\expandafter| ont joué leur rôle) + \item \verb-<arg1>{*<arg2>}- +\end{enumerate} +Il faut donc 3 développements. +\medskip + +Dans «\verb-\>\expsecond{\X}{\X}<-», si l'on veut 3-développer §\expsecond, il faudra mettre $2^3-1=7$ \verb-\expandafter- devant \verb-\>- : + +\showcode/\long\def\swaparg#1#2{#2{#1}}¤§*\swaparg\idx*\long¤ +\long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}}¤§*\expsecond\idx*\long¤ +\def\X{Bonjour} +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\>\expsecond{\X}{\X}<¤§*\expsecond¤/ +\end{exercice} + +\begin{exercice} +Dans quels cas §\expsecond ne fonctionne pas comme attendu ? +\solution +Si l'on examine la définition + +\centrecode-\long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}}-§*\expsecond + +on constate que le 1-développement de l'argument \verb|#2| a lieu alors que cet argument est déplacé et n'est plus en dernier, c'est-à-dire qu'il ne précède pas le code qui suit. Dès lors, si cet argument doit agir sur le code qui suit (avec §\gobone, §\identity, §\firstoftwo ou §\secondoftwo), il n'est plus en mesure de le faire. Dans l'exemple ci-dessous, on souhaite 1-développer §\gobone pour donner à \verb|\foo| le texte de remplacement «\verb|B|» : + +\errcode/\expsecond{\def\foo}\secondoftwo{A}{B}¤§*\expsecond§*\secondoftwo¤/{Argument of \texttt{\string\secondoftwo} has an extra \char`\}} + +Le 2\ieme{} argument de §\expsecond est §\secondoftwo donc, après avoir 1-développé §\expsecond dans «\verb|\expsecond{\def\foo}\secondoftwo{A}{B}|», voici ce que l'on obtient : + +\centrecode-\expandafter\swaparg\expandafter{\secondoftwo}{\def\foo}- + +Lorsque §\secondoftwo est 1-développé, en cherchant son premier argument, il voit «\verb|}|» et c'est là que \TeX{} proteste car une accolade fermante ne peut pas être le début d'un argument. + +Pour que «B» soit le texte de remplacement de \verb|\foo|, il aurait fallu que «\verb|\secondoftwo{A}|\linebreak[1]\verb|{B}|» soit le 2\ieme{} argument de §\expsecond : + +\showcode/\expsecond{\def\foo}{\secondoftwo{A}{B}}¤§*\expsecond§*\secondoftwo¤ +\meaning\foo¤\idx\meaning¤/ +\end{exercice}§*\expsecond[|)]§*\swaparg[|)] + +La macro §\expsecond peut être utilisée si son premier argument est une macro : + +\centrecode-\expsecond\<macro>{<argument>}- + +\noindent Dans ce cas, §\expsecond 1-développe l'argument de la macro avant d'exécuter la macro. C'est pourquoi la macro §\expsecond a un alias «§\exparg» défini par \idx\let dont le nom suggère davantage que l'argument d'une macro a été développé. + +\begin{exercice} +En reprenant le même principe que §\exparg, définir une macro §*\exptwoargs + +\centrecode-\exptwoargs\<macro>{argument 1}{argument 2}- + +\noindent qui 1-développe les 2 premiers tokens des deux arguments avant de lancer la \verb|\<macro>| +\solution +Deux appels imbriqués à §\exparg conduisent au résultat : + +\centrecode-\def\exptwoargs#1#2#3{\expsecond{\expsecond{#1}{#2}}{#3}}- + +\noindent Comme l'argument \verb|#3| se trouve une seule fois dans le texte de remplacement de la macro et à la fin de celui-ci, il est possible de ne pas le faire lire par §\exptwoargs : + +\showcode/\def\exptwoargs#1#2{\expsecond{\expsecond{#1}{#2}}}¤§*\exptwoargs§*\expsecond¤ +\def\foo{Bonjour}\def\bar{ le monde} +\def\displaytwoargs#1#2{\detokenize{#1#2}}% affiche tels quels les arguments +\exptwoargs\displaytwoargs\foo\bar¤§*\exptwoargs¤/ +\end{exercice} + +Une macro plus générale, capable de développer plusieurs arguments sera programmée plus loin (voir page~\pageref{eargs}). + +\section{La primitive \texttt{\textbackslash edef}}\idx*[|(]\edef +On a vu que \verb-\expandafter- permettait de contrôler finement la profondeur de développement moyennant, c'est vrai, de longs et laborieux ponts d'\idx[|)]\expandafter. Mais il existe des endroits où \TeX{} développe au maximum\idx*[|(]{développement maximal} ce qu'il rencontre. Attention, il s'agit bien d'un développement et non pas d'une exécution. Le plus célèbre de ces endroits est le texte de remplacement des macros définies avec la primitive \verb|\edef|. Cette primitive fonctionne comme sa s\oe ur \verb-\def- sauf que tout ce qui se trouve à entre les accolades est développé au maximum et ce qui en résulte est le \idx{texte de remplacement} de la macro définie par \verb|\edef|. Essayons : + +\showcode/\def\aa{Bonjour}\def\bb{\aa} +\def\cc{ le monde} +\edef\foo{\bb\cc} +\meaning\foo¤\idx*\meaning¤/ + +On constate que le texte de remplacement de \verb-\foo- contient le 2-développement de \verb|\bb| et le 1-développement de \verb|\cc|. Ces développements sont les plus profonds que l'on puisse faire puisqu'ensuite on ne trouve plus que des lettres ou des espaces. Le texte de remplacement de \verb-\truc- est bien « Bonjour le monde ». + +Comme pour \verb-\def-, on peut aussi spécifier qu'une macro définie avec \verb|\edef| admet des arguments, et on utilise pour cela la même syntaxe \verb|#<chiffre>|, aussi bien pour le \idx{texte de paramètre} que pour le texte de remplacement. Le développement maximal fait lors de la définition concerne tout ce qui se trouve dans le \idx{texte de remplacement} \emph{à l'exception} des emplacements des arguments qui ont la syntaxe \verb|#<chiffre>|. Les arguments, qui seront lus plus tard lorsque la macro sera appelée, seront insérés tels quels à la place de \verb|#<chiffre>| : ils ne sont donc pas soumis au développement maximal. + +On peut le constater ici où la macro \verb-\ee- est définie avec un \verb-\edef- et admet un argument. À l'aide d'\verb-\expandafter-, on provoque le 1-développement de la macro \verb-\ee- dont l'argument est la macro \verb-\dd-. On peut constater que cet argument «\verb-\dd-» reste non développé bien qu'il soit l'argument d'une macro définie avec \verb-\edef- : + +\showcode/\def\aa{Bonjour } \def\bb{\aa} \def\cc{le } \def\dd{ monde} +\edef\ee#1{\bb\cc#1 !!!} +\meaning\ee\par¤\idx*\meaning¤ +\expandafter\>\ee{\dd}</ + +\begin{regle} +La primitive \verb-\edef- a la même syntaxe et les mêmes actions que \verb-\def-, c'est-à-dire qu'elle stocke un \idx[|etc]{texte de remplacement}\forbidindex{texte de remplacement} associé à une \idx{séquence de contrôle}. La différence réside dans le fait que la totalité du texte de remplacement est développée \emph{au maximum} avant que l'assignation soit faite. + +Le développement s'applique au texte de remplacement \emph{hors arguments} symbolisés par \verb|#<chiffre>|. Ceux-ci, qu'ils soient délimités ou pas, seront insérés tels qu'ils seront lus dans le texte de remplacement (qui lui aura été préalablement développé). + +La primitive \idx\xdef effectue les mêmes actions que \verb|\edef| sauf que la définition faite est globale. +\end{regle} + +\subsection{\texttt{\char`\\noexpand} pour contrer \texttt{\char`\\edef}}\idx*[|(]\noexpand +Cette primitive \verb-\edef- se révèle fort utile, et rend de grands services. Mais dans son argument où tout est développé au maximum, on voudrait pouvoir bloquer le développement d'une \idx{séquence de contrôle}. Par exemple, si l'on écrit + +\centrecode-\edef\foo{\aa\bb\cc}- + +\noindent comment laisser \verb-\aa- et \verb-\cc- se développer au maximum tout en bloquant le développement de \verb-\bb- ? C'est là qu'une nouvelle primitive entre en jeu, il s'agit de \verb-\noexpand-. + +\begin{regle} +La primitive développable \verb|\noexpand| bloque le développement du token $x$ qui la suit. Elle a pour effet, lorsqu'elle se 1-développe, d'être le token $x$ lui-même. Si $x$ est une séquence de contrôle qui aurait été développée, $x$ est rendu temporairement égal à \verb|\relax|. +\end{regle} + +\showcode|\def\foo{123xyz} +a) signification du 1-développement d'un \litterate-\noexpand- :¤\idx*\string\idx*\noexpand§*\litterate¤ + \expandafter\meaning\noexpand\foo¤\idx*\meaning¤ + +b) exécution d'un \litterate-\noexpand- : \noexpand\foo% identique à \relax + +c) 1-développement de \litterate-\noexpand- dans le texte de remplacement de + \litterate-\bar- :¤§*\litterate¤ + \expandafter\def\expandafter\bar\expandafter{\noexpand\foo} + \meaning\bar¤\idx*\meaning¤| + +Le dernier cas montre qu'entre le moment du 1-développement de \verb|\noexpand|\linebreak[1]\verb|\foo| et le moment où \TeX{} lit ce 1-développement, plusieurs choses sont faites (lecture et exécution du \verb|\def| suivie de la lecture \verb|\bar| et de l'accolade ouvrante). Entre temps, le développement initié par le pont d'\verb|\expandafter| s'est arrêté, signant la mort du \verb|\relax| temporaire qui est donc redevenu \verb|\foo|. +\grandsaut + +Dans le code « \verb-\edef\foo{\aa\bb\cc}-», tentons maintenant de bloquer le développement de \verb|\bb| : + +\showcode/\def\aa{Bonjour}\def\bb{\aa}\def\cc{\bb} +a) \edef\foo{\aa\noexpand\bb\cc}\meaning\foo \qquad b) exécution : \foo\par¤\idx*\meaning¤ +c) \edef\foo{\aa\noexpand{\aa\cc}}\meaning\foo\qquad d) exécution : \foo/ + +Tout se passe comme prévu au a) pour la première définition de \verb-\foo- avec \verb-\edef- : le \verb-\noexpand- qui précède \verb-\bb- empêche bien son développement. + +À la deuxième ligne (cas c), on a tenté bien maladroitement de bloquer le développement de ce qui se trouve entre accolades. Ceci échoue puisque \verb-\noexpand- bloque le développement du \emph{token} qui suit et qui est dans ce cas «\cidx\{». Cette accolade ouvrante n'a d'ailleurs nul besoin d'être protégée d'un développement puisqu'elle n'est pas développable (et elle se développe donc en elle-même). Pour bloquer le développement de plusieurs séquences de contrôle, il \emph{faut} mettre un \verb-\noexpand- devant chacune d'elles ! + +\subsection{\texttt{\char`\\unexpanded} pour contrer \texttt{\char`\\edef}} +Multiplier les \verb-\noexpand- et en écrire autant qu'il y a de tokens à protéger peut être lourd, voire impossible si on ne connait pas le nombre de tokens lorsqu'on manipule l'argument d'une macro par exemple. Pour contourner cette difficulté, on peut utiliser la primitive \idx\unexpanded de \eTeX{} dont le développement est de bloquer le développement du texte entre accolades qui suit la primitive. + +Voici deux façons de faire équivalentes, la première en plaçant un \verb|\noexpand| devant \verb|\bb| et \verb|\aa| la seconde en mettant ces deux tokens dans l'argument de la primitive \verb|\unexpanded| : + +\showcode/\def\aa{Bonjour}\def\bb{Au revoir} +a) \edef\truc{\aa\noexpand\bb\noexpand\aa\bb}\meaning\truc \par¤\idx*\meaning¤ +b) \edef\truc{\aa\unexpanded{\bb\aa}\bb}\meaning\truc¤\idx*\meaning¤/\idx*[|)]\noexpand + +La primitive \idx\unexpanded, tout comme \idx\detokenize et d'autres, fait partie des primitives qui doivent être suivies d'une accolade ouvrante. Ces primitives initient un développement maximal jusqu'à ce qu'elles rencontrent une accolade ouvrante (voir règle page~\pageref{primitive.suivie.par.accolade}). On peut donc intercaler un ou plusieurs \idx\expandafter entre ces primitives et l'accolade ouvrante de façon à procéder au développement du (ou des) premiers tokens du texte entre accolades. + +\subsection{\texttt{\char`\\the\char`\\toks\codeelement{nombre}} pour contrer \texttt{\char`\\edef}}\idx*\toks +Il existe un autre moyen qui ne fait pas appel à \eTeX{} et qui permet de bloquer le développement dans un \verb|\edef|. Il s'agit du contenu d'un registre de tokens : dans le texte de remplacement d'un \verb|\edef|, le contenu d'un registre de tokens obtenu par le développement de \idx\the\linebreak[1]\idx\toks\linebreak[1]\verb|<nombre>| (ou par «\verb|\the|\linebreak[1]\verb|<nom d'un registre>|») n'est pas développé. + +Voici l'exemple précédent traité avec un registre à token (ici, nous prenons le registre \no0) : + +\showcode|\def\aa{Bonjour}\def\bb{Au revoir} +\toks0={\aa\bb}¤\idx*\toks¤ +\edef\foo{\aa\the\toks0 \bb}¤\idx*\toks¤ +\meaning\foo¤\idx*\meaning¤| + +\subsection{Protéger une macro avec \texttt{\char`\\protected}} +Tout comme on peut déclarer une macro «\idx\long» ou «\idx\global» lors de sa définition, on peut aussi déclarer qu'on souhaite définir une macro « protégée ». On entend « protégée contre le développement maximal dans l'argument d'une primitive ». Pour ce faire, il faut utiliser la primitive de \eTeX {} «\idx\protected» et la placer avant le \verb|\def| (ou \verb|\edef|) qui effectue la définition. La macro ainsi définie ne se développera pas dans un \verb|\edef| : + +\showcode/\def\aa{Bonjour} +\protected\def\bb{ le}% \ bb est protégée !¤\idx*\protected¤ +\def\cc{ monde} + +\edef\foo{\aa\bb\cc} +Signification de \string\foo~: \meaning\foo \par¤\idx*\meaning¤ +Exécution de \string\foo~: \foo¤\idx*\string¤/ + +Il faut bien comprendre que la macro est protégée \verb|\bb|d'un développement est maximal, mais pas de l'action de \verb|\expandafter|. Voici comment on peut 1-développer \verb|\bb| dans le texte de remplacement de \verb|\edef| avec un \verb|\expandafter| : + +\showcode/\def\aa{Bonjour} +\protected\def\bb{ le}¤\idx*\protected¤ +\def\cc{ monde} + +\edef\foo{\expandafter\aa\bb\cc}% le \expandafter 1-développe \bb +\meaning\foo/ + +\subsection{Les moyens de bloquer le développement maximal}\label{bloquer.developpement.maximum} +Il faut savoir est que certaines primitives (\idx\edef, \idx\xdef, \idx\csname\linebreak[1]\verb|...|\linebreak[1]\idx\endcsname que nous connaissons mais aussi \idx\message, \idx\write et \idx\errmessage que nous verrons plus tard) développent au maximum ce qui est dans leur argument\footnote{Il y a aussi \texttt{\char`\\special}, \texttt{\char`\\mark} et \texttt{\char`\\marks} qui ne seront pas étudiées dans ce livre. Pour être complet, il faut signaler que le développement maximal est aussi en marche dans un alignement lorsque \TeX{} recherche un \texttt{\char`\\omit} ou un \texttt{\char`\\noalign}.}. Les méthodes pour contrer ce développement maximal valent aussi bien pour \verb|\edef| que pour n'importe quelle autre de ces primitives. Voici le résumé des méthodes que nous avons vues : + +\begin{regle} +Il existe 4 moyens de bloquer le développement maximal dans les arguments de certaines primitives : +\begin{enumerate} + \item mettre un \idx\noexpand devant chaque token dont on veut bloquer le développement; + \item placer les tokens dont on veut bloquer le développement dans l'argument de la primitive \idx\unexpanded de \eTeX{}; + \item stocker au préalable tous les tokens dont on veut bloquer le développement dans un \verb|<registre de token>|\idx*{registre!token} et écrire dans l'argument de la primitive \idx\the\verb|<registre de token>|; + \item lorsqu'on définit une macro, il est possible de faire précéder le \verb|\def| de la primitive \idx\protected de \eTeX{} pour empêcher le développement maximal de cette macro. +\end{enumerate} +\end{regle}\idx*[|)]\edef{}\idx*[|)]{développement maximal} + +\section{Code purement développable}\idx*[|(]{code purement développable} +On pourrait penser que la primitive \verb|\edef| peut simuler une « exécution » pour mettre dans la \idx{séquence de contrôle} qu'elle définit le « résultat » de l'exécution du \idx{texte de remplacement}\footnote{On peut stocker l'affichage résultant d'un code arbitraire à l'aide des registres de boites que nous verrons plus tard.}. Il s'agit généralement d'une erreur, et pour comprendre pourquoi, il faut parler de ce qu'est du « code purement développable ». + +Afin de mieux comprendre de quoi il s'agit, prenons un exemple simple, n'ayant aucun intérêt si ce n'est pédagogique. Supposons que la macro \verb|\foo| a comme texte de remplacement «abc», ce qui suppose qu'un \verb|\def\foo{abc}| a été rencontré auparavant. Voici un code élémentaire qui définit une macro \verb|\foo| et qui l'exécute : + +\showcode/\def\foo{Bonjour}% définition +\foo% exécution/ + +Ce code de deux lignes n'est pas purement développable. Essayons de comprendre pourquoi en le mettant dans un \verb|\edef| pour définir la macro \verb|\bar| : + +\centrecode-\edef\bar{\def\foo{Bonjour}\foo}- + +Voici ce que va faire \TeX{} lorsqu'il va développer au maximum le code dans le texte de remplacement de \verb|\edef| : + +\begin{enumerate} + \item la primitive non développable \verb|\def| se développe en elle-même et reste donc inchangée ; + \item les deux occurrences de \verb|\foo| vont être développées et donc remplacées par « abc ». +\end{enumerate} + +Le texte de remplacement de \verb|\bar| sera donc : + +\centrecode-\def abc{Bonjour}abc- + +Vérifions-le : + +\showcode/\def\foo{abc} +\edef\bar{\def\foo{Bonjour}\foo} +\meaning\bar¤\idx*\meaning¤/ + +Ce texte de remplacement comporte une erreur, car après un \verb|\def|, on \emph{doit} trouver une \idx{séquence de contrôle} (et non pas des caractères normaux comme a, b et c). Mais cette erreur n'apparait pas lors de la définition de\verb|\bar| car \TeX{} effectue les assignations sans procéder à l'analyse du texte de remplacement. Ce n'est que si l'on cherche à exécuter la macro \verb|\bar|, et donc à exécuter son texte de remplacement que \TeX {} émettra le message d'erreur « \texttt{Missing control sequence} ». + +\begin{exercice} +Décrire ce qui se passerait si l'on écrivait le code ci-dessous alors que la macro \verb|\foo| n'est pas définie. + +\centrecode-\edef\bar{\def\foo{Bonjour}\foo}- + +\solution + +Lors du développement maximal de \verb|\foo|, celle-ci n'étant pas définie, \TeX{} émettrait le message d'erreur «\texttt{Undefined + control sequence}». +\end{exercice} + +La conclusion est donc sans appel : que \verb|\foo| soit définie ou pas, le code n'est pas purement développable. On pourrait rétorquer que pour ce code soit purement développable, il suffirait de bloquer le développement de \verb|\foo| en se servant de la primitive \idx\noexpand : + +\showcode/\def\foo{abc} +\edef\bar{\def\noexpand\foo{Bonjour}\noexpand\foo}¤\idx*\noexpand¤ +Signification : \meaning\bar\par¤\idx*\meaning¤ +Exécution : \bar/ + +Peut-on dire que le code, enrichi de \idx\noexpand, est purement développable ? En fait, tout dépend de ce que l'on attendait ! Si l'on voulait simplement construire un code exécutable stocké dans une macro, alors oui. Mais dans la grande majorité des cas, on attend qu'un code purement développable se réduise, lors de son développement maximal, aux caractères affichés si ce code était exécuté. + +\begin{regle} +Un code est purement développable si son développement maximal se réduit aux caractères qui auraient été affichés s'il avait été exécuté par \TeX. + +Dans la majorité des cas, un tel code ne doit être constitué que caractères (ceux que l'on souhaite dans l'affichage final) ainsi que de séquences de contrôle ou caractères actifs \emph{développables}. Citons : +\begin{itemize} + \item du côté des primitives : \idx\expandafter, \idx\number, \idx\the, \idx\string ainsi que les primitives de \eTeX{} \idx\detokenize et \idx\unexpanded. Tous les tests de \TeX{} que nous verrons plus tard sont développables. Il y a aussi deux primitives de \eTeX{}, \idx\numexpr et \idx\dimexpr qui seront étudiées dans la partie suivante; + \item les macros définies par l'utilisateur, qu'elles soient à arguments délimités ou pas, pourvu que leur développement maximal soit constitué des caractères que l'on souhaite dans l'affichage final. +\end{itemize} +\end{regle}\idx*[|)]{code purement développable} + +\section{Propager le développement} +\subsection{Lier des zones de développement maximal} +Dans certaines zones bien spécifiques, le développement est maximal\idx*{développement maximal} et donc, dans ces zones, le code doit être purement développable. + +L'expression « propager le développement » signifie mettre en \oe uvre des méthodes pour que ces zones de développement maximal se transmettent de proche en proche le développement. Le but est de développer des territoires très éloignés de l'endroit où le premier développement a eu lieu. + +Parfois, du code \TeX{} est constitué de zones de développement maximal entrecoupées de zones « normales » où ce développement maximal n'existe pas. Le schéma ci-dessous représente une de ces situations où les zones de développement maximal sont encadrées et les tokens des zones normales sont représentés par «$\ast$». La tête de lecture de \TeX{} est représentée par \TeXhead{} : + +\begin{centrage} +\small\fboxsep0pt +\TeXhead$\ast$\fbox{\strut\kern1.5cm }$\ast$\fbox{\strut\kern1.5cm }$\ast\ast$\fbox{\strut\kern1.5cm }$\ast x$ +\end{centrage} + +Il est possible, sans déplacer la tête de lecture, de 1-développer le token $x$. Pour cela, on va amorcer le développement de la première zone de développement maximal en mettant un \idx\expandafter juste avant le premier token «$\ast$». Par la suite, il faudra exporter le développement hors des zones encadrées à l'aide d'un \verb|\expandafter| placé en dernière position. Un pont d'\verb|\expandafter| prend ensuite le relai dans la zone normale pour communiquer le développement à la zone encadrée suivante. À la fin de la dernière zone, un pont d'\verb|\expandafter| permettra d'atteindre le token $x$ pour le 1-développer. En notant «$\bullet$» la primitive \verb|\expandafter|, voici ce que devient le schéma : + +\begin{centrage} +\small\fboxsep0pt +\TeXhead$\bullet\ast$\fbox{\strut\kern1.5cm $\bullet$}$\bullet\ast$\fbox{\strut\kern1.5cm $\bullet$}$\bullet\ast${}$\bullet\ast$\fbox{\strut\kern1.5cm $\bullet$}$\bullet\ast${}$x$ +\end{centrage} + +\begin{exercice} +\Qu e faudrait-il modifier dans ce schéma pour provoquer le 2-développement du token $x$ ? + +\solution +Tout restant égal par ailleurs, il faut initier un pont d'\verb|\expandafter| à deux passes à la fin de la dernière zone de développement maximal, c'est-à-dire mettre 3 \idx\expandafter au lieu d'un seul : + +\begin{centrage} +\small\fboxsep0pt +\TeXhead$\bullet\ast$\fbox{\strut\kern1.5cm $\bullet$}$\bullet\ast$\fbox{\strut\kern1.5cm $\bullet$}$\bullet\ast${}$\bullet\ast$\fbox{\strut\kern1.5cm $\bullet${}$\bullet${}$\bullet$}$\bullet${}$\bullet${}$\bullet\ast${}$x$ +\end{centrage} +\end{exercice} + +\subsection{Étude d'un cas} +Pour illustrer ce propos, passons à un cas plus concret, quoiqu'encore très théorique. Supposons qu'avant de lire les macros \verb-\foo- et \verb-\bar-, nous devions provoquer le 2-développement de la macro \verb-\wee- et le 1-développement de \verb-\fin- dans le code suivant : + +\centrecode-\foo\bar\wee\fin- + +\noindent Le nombre d'\idx\expandafter nécessaires est assez important. Essayons de les compter ! Plaçons mentalement les \verb|\expandafter| pour provoquer tour à tour le développement des macros prises \emph{de gauche à droite}, c'est-à-dire que nous allons d'abord chercher à développer \verb|\wee| qui est la plus à gauche des deux macros. Ensuite, nous partirons du code obtenu et chercherons à développer \verb|\fin|. + +Pour 2-développer \verb|\wee|, il faut placer $2^2-1=3$ \idx\expandafter devant chaque token qui la précède ce qui fait $3\times2=6$ \idx\expandafter, notés $\bullet$: + +\begin{centrage} +\small$\bullet\bullet\bullet$\litterate-\foo-$\bullet\bullet\bullet$\litterate-\bar\wee\fin- +\end{centrage} + +\noindent Dans ce code, le nombre de tokens qui précèdent \verb-\fin- devient égal à 9 : nous avons les 6 \idx\expandafter que nous venons de placer et les 3 séquences de contrôle \verb|\foo|, \verb|\bar| et \verb|\wee|. Pour 1-développer \verb|\fin|, il faut mettre un \verb|\expandafter| devant chaque token qui précède \verb|\fin| ce qui fait 9 \verb|\expandatfer| de plus et porte donc leur nombre total à $6+9=15$. Voici le code obtenu : + +\begin{centrage} +\small$\bullet\bullet\bullet\bullet\bullet\bullet\bullet$\litterate-\foo-$\bullet\bullet\bullet\bullet\bullet\bullet\bullet$\litterate-\bar-$\bullet$\litterate-\wee\fin- +\end{centrage} + +Venons-en maintenant aux zones de développement maximal et au lieu d'écrire directement le nom des 4 macros, aidons-nous de la paire \verb-\csname- et \verb-\endcsname- : + +\centrecode-\csname foo\expandafter\endcsname +\csname bar\expandafter\endcsname +\csname baz\expandafter\endcsname +\csname fin\endcsname- + +Le rôle des \idx\expandafter mis à chaque fin de zone de développement maximal (sauf la dernière) est de transmettre le développement en dehors de la zone de développement maximal pour toucher le \idx\csname suivant qui commande à son tour le développement maximal jusqu'au prochain \idx\endcsname. L'\verb|\expandafter| se trouvant en fin de zone transmet à son tour le développement au \verb|\csname| suivant, et ainsi de suite jusqu'au dernier \verb-\endcsname- qui n'est pas précédé d'un \idx\expandafter et stoppe donc cet enchainement. La structure est clairement celle que l'on voyait dans le schéma de la section précédente : externaliser le développement pour déclencher le développement d'une nouvelle zone de développement maximal pour, en réitérant la man\oe uvre, transmettre très loin la flamme du développement. Voici le schéma qui traduirait cette situation (ici, aucun token ne se trouve entre les zones de développement maximal) : + +\begin{centrage} +\small\fboxsep0pt +\TeXhead\fbox{\strut\kern1.5cm $\bullet$}\thinspace\fbox{\strut\kern1.5cm $\bullet$}\thinspace\fbox{\strut\kern1.5cm $\bullet$}\thinspace\fbox{\strut\kern1.5cm } +\end{centrage} + +Le code donné ci-dessus se 1-développe en « \verb-\foo\bar\wee\fin- ». En voici la preuve : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\expandafter\>% provoque le 1-développement de : +\csname foo\expandafter\endcsname¤\idx*\csname\idx*\endcsname¤ +\csname bar\expandafter\endcsname +\csname wee\expandafter\endcsname +\csname fin\endcsname</ + +Dans le code ci-dessus, aucune \idx{séquence de contrôle} n'est développée. Pour provoquer le 1-développement de \verb-\fin-, il suffit de mettre 3 \idx\expandafter après «wee» au lieu d'un seul. En suivant le même raisonnement, pour 2-développer \verb-\wee-, il faut 3-développer \verb-\csname-\linebreak[1] \verb-baz-\linebreak[1]\verb-\endcsname- et donc placer 7 \idx\expandafter après «bar». Cela donne le code : + +\centrecode-\csname foo\expandafter\endcsname +\csname bar\expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\endcsname +\csname wee\expandafter\expandafter\expandafter\endcsname +\csname fin\endcsname- + +\noindent Pour nous assurer que ce code fonctionne correctement, définissons \verb-\wee- pour que son 2-développement soit «XX» et \verb-\fin- pour que son 1-développement soit «YY» : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\def\fin{YY} \def\wee{\weeA} \def\weeA{XX} +\expandafter\>% +\csname foo\expandafter\endcsname¤\idx*\csname\idx*\endcsname¤ +\csname bar\expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\endcsname +\csname wee\expandafter\expandafter\expandafter\endcsname +\csname fin\endcsname</ + +Le premier \idx\expandafter qui précède \verb-\>- suffit à atteindre, de zone de développement en zone de développement, la toute fin de ce code avant même que le premier nom de \idx{séquence de contrôle} \verb-\foo- ne soit formé. + +\begin{exercice} +Où faut-il placer des \idx\expandafter dans « \verb-\>\foo\bar\wee\fin<- » pour obtenir la même chose que dans l'exemple précédent ? +\solution +Il faut les 15 \idx\expandafter déjà vus sauf qu'il faut sauter le token \verb|\>| au départ. On a déjà calculé qu'il faut 7 \idx\expandafter avant \verb-\foo-, il en faut également 7 pour sauter \verb-\>- qui vient en première position. Cela nous fait $15+7=22$ \idx\expandafter (au lieu de 12 avec \idx\csname et \idx\endcsname) : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\def\fin{YYY} \def\wee{\weeA} \def\weeA{XXX} +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\>% +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\foo +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\bar +\expandafter\wee\fin</ +\end{exercice} + +\subsection{\Qu 'est-ce qu'un nombre ?} +La notion de nombre (compris comme nombre \emph{entier}) étant étroitement liée à celle de développement, il est temps d'aborder ce sujet.\grandsaut + +Tout d'abord, il est faux de croire que lorsque \TeX{} exécute le code + +\centrecode-le nombre 123 est impair- + +\noindent il \emph{lit} le nombre 123. Il ne fait qu'afficher séquentiellement les caractères «1» puis «2» et «3» sans pour autant que ces caractères ne soient isolés et analysés comme un tout formant un nombre entier. Pour que \TeX{} déclenche la lecture d'un nombre, il faut qu'il se trouve à un endroit où il s'attend à en lire un. + +Nous avons déjà rencontré plusieurs cas où \TeX{} doit lire un nombre. C'est le cas pour les primitives \verb|\catcode|, \idx\lccode et \idx\uccode dont la syntaxe complète est + +\begin{centrage} + \small$\left.\vcenter{\hbox{\verb|\catcode|}\par\hbox{\verb|\lccode|}\par\hbox{\verb|\uccode|}}\right\}$\verb-<nombre>= <nombre>- +\end{centrage} + +\noindent où le signe \verb|=| et l'espace qui le suit sont facultatifs. + +Nous avons vu qu'après la primitive \idx\toks, \TeX{} attend le \verb|<nombre>| correspondant au registre de tokens auquel on veut accéder. Nous savons également qu'après la primitive \verb|\number|, \TeX{} s'attend à lire un nombre pour le convertir en chiffres arabes et en base 10. Un \verb|<nombre>| est également attendu après la primitive \idx\char et le tout se traduit par l'affichage du caractère dont le code de caractère est \verb|<nombre>|. + +Pour être complet concernant les caractères affichés via \idx\char, il est possible d'utiliser la primitive \idx\chardef dont la syntaxe est + +\centrecode-\chardef\<macro>= <nombre>- + +\noindent Comme pour \verb|\catcode|, \idx\lccode et \idx\uccode, le signe «=» et l'espace qui le suit sont facultatifs. L'action qu'effectue ce code est de rendre la \verb|\<macro>| équivalente à \verb|\char<nombre>|. L'équivalence dont il est question ici ressemble à celle effectuée avec \verb|\let|. Ainsi, si on écrit : + +\centrecode-\chardef\foo= 65- + +\noindent alors, la macro \verb|\foo| sera équivalente à \idx\char\verb|65| et produira un «A» à l'affichage. + +Une \verb|\<macro>| définie avec \idx\chardef revêt une dualité intéressante : lorsque \TeX{} s'attend à lire un nombre, cette \verb|\<macro>| est alors comprise comme un nombre. Avec la macro \verb|\foo| ci-dessus, le nombre lu serait 65. + +\showcode/\chardef\foo65 ¤\idx*\chardef¤ +a) |\foo|\qquad % employée seule, \foo affiche un A¤\idx*\qquad¤ +b) |\number\foo|\qquad % si TeX lit un nombre, \foo est le nombre 65¤\idx*\number¤/ + +Comme il n'y a que 256 caractères possibles\footnote{Pour les moteurs 8 bits, car les moteurs \utf en ont beaucoup plus.}, les primitives \idx\char et \idx\chardef ne peuvent prendre en compte que des nombres compris entre 0 et 255. Le mode mathématique\idx*{mode!mathématique} requiert d'afficher beaucoup plus de caractères que ces 256, aussi il existe les primitives \idx\mathchar et \idx\mathchardef qui sont au mode mathématique\idx*{mode!mathématique} ce que sont \idx\char et \idx\chardef au mode texte. Le caractère dual d'une macro définie par \idx\mathchardef existe sauf que le nombre qu'elle peut représenter est compris entre 0 et \numprint{32767}\footnote{Voir le \TeX book, page 181.} et lorsqu'elle est utilisée pour afficher un caractère, le mode mathématique doit être en vigueur. + +\showcode/\mathchardef\foo4944 ¤\idx*\mathchardef¤ +a) |$\foo$|\qquad % en mode math, affiche le signe "somme" (uniquement en mode maths)¤\idx*\qquad¤ +b) |\number\foo|\qquad % si TeX lit un nombre, \foo est 4944¤\idx*\number¤/ + +Il est temps de donner la définition d'un nombre et montrer en quoi elle est liée au développement. + +\label{definition.entier}\begin{regle} +\relax\idx*{nombre!définition}Un «nombre» est un entier relatif compris entre $-2^{31}+1$ et $2^{31}-1$, c'est-à-dire entre $-\numprint{2147483647}$ et $\numprint{2147483647}$. Tout nombre hors de cet intervalle provoque une erreur de compilation. + +Lorsque \TeX{} s'attend à lire un nombre, il entre dans une phase de développement maximal. Dans les résidus de ce développement, il cherche tout d'abord une série de signes optionnels de catcode 12 : «\verb|+|» et «\verb|-|». Le signe du nombre final dépendra de la parité du nombre de signes «\verb|-|» lus au total. Ensuite, plusieurs cas peuvent se présenter : +\begin{enumerate} + \item le nombre est explicitement écrit en + + \begin{itemize}[topsep=0pt] + \item base 8 sous la forme \verb|'<signes>|; + \item base 10 sous la forme \verb|<signes>|; + \item base 16 sous la forme \verb|"<signes>|, + \end{itemize} + + où les \verb|<signes>| sont une suite de chiffres de catcode 12 allant de \verb|0| à \verb|7| pour la base 8, de \verb|0| à \verb|9| pour la base 10 et pour la base 16, de \verb|0| à \verb|9| auxquels s'ajoutent les lettres \emph{majuscules} de catcode 11 ou 12 allant de \verb|A| à \verb|F|. + + \TeX{} stoppe la lecture du nombre au premier token qui n'est pas un \verb|<signe>| valide. Si ce token est un espace, il sera absorbé. Le nombre lu comprend donc la plus longue série de \verb|<signes>| valides possible; + \item un « compteur » se présente (nous verrons les compteurs plus loin). Le nombre qu'il contient est lu et la lecture du nombre est stoppée; + \item un « registre de dimension » se présente. Dans ce cas, la dimension est convertie en entier (nous verrons comment plus loin) et la lecture du nombre s'achève; + \item une \idx{séquence de contrôle} définie avec \idx\chardef pour les nombres compris entre 0 à 255 ou avec \idx\mathchardef pour ceux compris entre 0 à 32767 se présente, le nombre correspondant est lu et la lecture du nombre est stoppée; + \item un nombre écrit sous la forme \verb|`<car>| ou \verb|`\<car>| se présente, où «\verb|`|» est l'apostrophe inverse. Le nombre lu est le code de caractère de \verb|<car>|. Le \emph{développement maximal se poursuit} jusqu'au premier token non développable qui signe la fin de la lecture du nombre. \emph{Si ce token est un espace, il est absorbé}. +\end{enumerate} +\end{regle} + +Plain-\TeX{} définit la \idx{séquence de contrôle} \idx\z@ comme étant un registre de dimension égal à la dimension nulle \verb|0pt|. Cette \idx{séquence de contrôle} est souvent employée en programmation, car elle présente 3 avantages : + +\begin{enumerate} + \item elle est convertie en l'entier 0 lorsque \TeX{} lit un nombre entier; + \item elle est la dimension \verb|0pt| lorsque \TeX{} lit une dimension; + \item dans les deux cas précédents, elle stoppe la lecture et le développement maximal. +\end{enumerate} + +La primitive \idx\number est développable et son 1-développement provoque un travail assez conséquent : lecture du nombre selon la règle vue ci-dessus et conversion de ce nombre en base 10 sous la forme la plus simple en tokens de catcode 12. Par «forme la plus simple», on entend que le signe du nombre n'est donné que s'il est négatif et que les 0 inutiles de gauche sont supprimés si le nombre était explicitement écrit. + +Lorsque \idx\the est immédiatement suivi d'un compteur, d'un registre de dimension ou d'une séquence de contrôle définie avec \idx\chardef ou \idx\mathchardef, elle se comporte comme \idx\number. + +Les compteurs, les registres de dimensions et les séquences de contrôle définies avec \idx\chardef ou \idx\mathchardef sont des représentations \emph{internes} d'entiers qui nécessitent une primitive développable (\idx\the ou \idx\number) pour obtenir les chiffres formant ce nombre en base 10. C'est donc une erreur que de les en dépourvoir pour les afficher. + +\subsection{Nombres romains} +Tout comme \idx\number et \idx\the sont capable de produire des nombres arabes, la primitive \idx\romannumeral produit des nombres romains. + +\begin{regle} +La primitive \idx\romannumeral doit être suivie d'un \verb|<nombre>| valide. + +Son 1-développement est +\begin{itemize} + \item l'écriture en chiffres romains de catcode 12 si le \verb|<nombre>| est strictement positif; + \item vide si le nombre est négatif ou nul. +\end{itemize} + +Si le \verb|<nombre>| est supérieur à 1000, la lettre «\verb|m|» de catcode 12 sera produite autant de fois qu'il y a de milliers dans le nombre. +\end{regle} + +Nous faisons fonctionner ici la primitive \idx\romannumeral en l'encadrant des caractères «\verb|"|» pour mettre en évidence que les espaces qui suivent les nombres disparaissent bien. Vérifions également que le développement maximal a bien lieu avec les macros \verb-\foo- et \verb-\bar- : + +\showcode|a) "\romannumeral 27 "\quad¤\idx*\romannumeral\idx*\quad¤ +b) "\romannumeral 4687 "\quad +c) "\romannumeral 0 "\quad% 0 donc développement vide +d) "\romannumeral -2014 "\quad% négatif donc développement vide +e) \def\foo{7}\def\bar{\foo}% + "\romannumeral \foo\foo\bar"\quad% 777 +f) "\romannumeral 1\bar8\foo"\quad% 1787 +g) "\romannumeral 1\bar8 \foo"% 178 (stoppé par l'espace) puis 7¤\idx*\quad\idx*\romannumeral¤| + +Remarquons la différence entre les cas f et g où le simple espace en plus après «8» au cas g stoppe la lecture du nombre et le développement maximal. On constate que cet espace est absorbé, car aucun espace n'existe à l'affichage entre «\verb|clxxviii|» et «7». + +\subsection{Le développement de \texttt{\char`\\romannumeral}}\idx*[!développement|(]\romannumeral +La primitive \idx\romannumeral est très intéressante, car son 1-développement déclenche un développement maximal dont on peut contrôler la portée. Surtout, ce développement maximal peut ne laisser aucune trace si le nombre que lit finalement \idx\romannumeral est négatif ou nul. + +Si dans ce schéma + +\begin{centrage} + \small\fboxsep1pt + \fboxrule0.4pt + \verb-\romannumeral-\fbox{\fboxsep0pt \fcolorbox{gray!60}{gray!60}{\strut\kern2.5cm }$x$\textvisiblespace} +\end{centrage} + +\begin{enumerate} + \item la zone grise ne contient que des tokens purement développables dont le développement est vide; + \item $x$ est un nombre négatif ou nul +\end{enumerate} + +\noindent alors, 1-développer \idx\romannumeral revient à développer au maximum tous les tokens se trouvant dans la zone grise et à supprimer le nombre $x$ et l'espace qui le suit. + +La zone encadrée devient une zone de développement maximal dont la portée est contrôlée par le placement du nombre négatif ou nul $x$. + +Certes, cela implique une contrainte forte : tous les tokens dans la zone grise doivent être purement développables et une fois ceci fait, ils ne doivent rien laisser. En pratique, cette zone grise contient des appels à des macros et des tests. +\grandsaut + +Pour mettre en évidence l'utilité d'une telle structure, reprenons l'exercice de la page~\pageref{expsecond} où l'on a vu que §\expsecond\verb-{<arg1>}{<arg2>}- devait se développer 3 fois pour que le code effectivement dans la pile soit \verb-<arg1>{*arg2}- (l'étoile indique que le premier token de l'argument 2 est 1-développé). Nous avions calculé qu'il fallait placer 7 \idx\expandafter devant \verb-\>- pour parvenir à nos fins : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\def\swaparg#1#2{#2{#1}}¤§*\swaparg¤ +\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}}¤§*\expsecond§*\swaparg¤ +\def\X{Bonjour} +\expandafter\expandafter\expandafter\expandafter +\expandafter\expandafter\expandafter +\>\expsecond\X\X<¤§*\expsecond¤/ + +Avec \verb-\romannumeral-, un seul \verb|\expandafter| sera requis pour sauter \verb|\>| et provoquer le 1-développement de \verb-\romannumeral- qui va tout développer au maximum et s'arrêter au bon moment. Ce \emph{bon moment} sera juste avant le \verb|\X| observé à l'affichage. Comme ce \verb|\X| est l'argument \verb|#2| de §\swaparg, un simple \verb*|0 |, placé avant cet argument \verb|#2| stoppe le développement maximal initié par \verb|\romannumeral| : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\def\swaparg#1#2{0 #2{#1}}% le "0 " stoppe ici le développement maximal¤§*\swaparg¤ +\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}}¤§*\expsecond§*\swaparg¤ +\def\X{Bonjour} +\expandafter\>\romannumeral\expsecond\X\X<¤\idx*\romannumeral§*\expsecond¤/ + +\begin{exercice} +À quel autre endroit aurait-on pu placer «\verb*-0 -» pour que le développement se passe comme on le souhaite ? +\solution +On veut stopper la lecture du nombre juste avant l'argument \verb-#2- de la commande §\swaparg. Cet argument est l'argument \verb-#1- de la commande §\expsecond et il suffit donc de mettre «\verb*-0 -» au début du premier argument de §\expsecond lorsqu'elle est appelée. + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\def\swaparg#1#2{#2{#1}}¤§*\swaparg¤ +\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}}¤§*\expsecond§*\swaparg¤ +\def\X{Bonjour} +\expandafter\>\romannumeral\expsecond{0 \X}\X<¤\idx*\romannumeral§*\expsecond¤/ + +Contrairement à celle de l'exemple précédent, cette méthode présente l'avantage de ne pas avoir à modifier les macros §\swaparg et §\expsecond. +\medbreak + +On aurait donc remplacer \verb*|0 | par \idx\z@ avec l'avantage pour ce dernier de stopper la lecture du nombre sans avoir à insérer un espace : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\def\X{Bonjour} +\catcode`\@=11 +\expandafter\>\romannumeral\expsecond{\z@\X}{\X}<¤\idx*\romannumeral\idx\z@§*\expsecond¤ +\catcode`\@=12 / +\end{exercice}\idx*{nombre}\idx*[!développement|)]\romannumeral + +\section{Programmation d'une variable unidimensionnelle} +Finissons ce chapitre en programmant une « variable », constituée de cellules qui auront chacune un nom et qui contiendront un code choisi par l'utilisateur. Il s'agit ici de copier les variables de type tableau (ou array) à une dimension qu'offrent la plupart des langages de programmation. + +Supposons que l'on veuille définir la variable unidimensionnelle \verb-\foobar-. Pour définir la variable et remplir les cases dont les noms sont «\texttt 0», «\texttt 1», «\texttt 3» et «\texttt{toto}», on doit pouvoir écrire : + +\centrecode-\newunivar\foobar¤§*\newunivar¤ +\defunivar\foobar[0]{abcd} +\defunivar\foobar[1]{1 23} +\defunivar\foobar[3]{XY Z} +\defunivar\foobar[toto]{Bonjour}¤§*\defunivar¤- + +\noindent Et par la suite, il suffira que l'on écrive \verb-\foobar[0]- pour que le code «\verb|abcd|», contenu dans la cellule «\verb|0|» soit restitué et exécuté. +\grandsaut + +Passons maintenant à la mécanique interne. Si la variable est «\verb|\foobar|», décidons que le contenu des cellules sera stocké dans des séquences de contrôle auxiliaires. Par exemple, le code de la cellule \verb|0| sera contenu dans la macro \verb-\-\boxtoken{\foobar[0]}. Autrement dit, le \emph{nom} de la macro doit être constitué des 10 caractères (inoffensifs) \boxtoken{ \unskip\textbackslash}, \boxtoken f, \boxtoken o, \boxtoken o, \boxtoken b, \boxtoken a, \boxtoken r, \boxtoken{[}, \boxtoken 0 et \boxtoken]. Pour former un tel nom, nous ferons appel à la paire \idx\csname\linebreak[1]\verb-...-\linebreak[1]\idx\endcsname. + +L'ordre «\verb-\newunivar\foobar-» doit donc définir la macro \verb-\foobar- de cette façon : + +\centrecode-\def\foobar[#1]{\csname\string\foobar[#1]\endcsname}-\idx*\string + +Si on place ce texte de remplacement dans la définition de §\newunivar, il suffit de remplacer \verb|\foobar| par l'argument \verb|#1| de §\newunivar et de doubler les «\verb|#|». La macro §\newunivar sera donc définie ainsi : + +\centrecode-\def\newunivar#1{\def#1[##1]{\csname\string#1[##1]\endcsname}}-\idx*\string§*\newunivar + +\noindent On pourra ainsi appeler par la suite \verb-\foobar[0]- qui se 2-développera en la macro \verb-\-\boxtoken{\foobar[0]} tout en offrant la protection, au cas où la cellule n'a pas été définie, que la paire \verb-\csname-\linebreak[1]\verb-...-\linebreak[1]\verb-\endcsname- génère l'inoffensif \idx\relax. Aucune erreur de compilation ne sera donc provoquée en cas d'appel à une cellule non définie. + +Pour la macro §\defunivar\verb-\<macro>[<nom>]-, un \idx\expandafter forcera la formation de la \idx{séquence de contrôle} avant que \verb-\def-, qui procède l'assignation, ne voie cette \idx{séquence de contrôle}. Par conséquent, §\defunivar\verb-\foo[0]- doit avoir le texte de remplacement suivant : + +\centrecode-\expandafter\def\csname\string\foo[0]\endcsname-\idx*\string§*\defunivar + +\noindent Et donc, §\defunivar doit être définie comme suit : + +\centrecode-\def\defunivar#1[#2]{\expandafter\def\csname\string#1[#2]\endcsname}-\idx*\string§*\defunivar + +\noindent ou encore, en utilisant §\defname + +\centrecode-\def\defunivar#1[#2]{\defname{\string#1[#2]}}- + +\noindent Il est inutile de lire l'argument du \verb|\def| qui est le contenu de la cellule. En effet, comme on l'a déjà vu à la page~\pageref{argument.def}, lire cet argument serait redondant puisqu'il faudrait le placer entre accolades à la fin du texte de remplacement de §\defunivar. Voici donc comment procéder : + +\showcode/\def\newunivar#1{\def#1[##1]{\csname\string#1[##1]\endcsname}}¤§*\newunivar\idx*\string¤ +\def\defunivar#1[#2]{\defname{\string#1[#2]}}¤§*\defunivar\idx*\string§*\defname¤ +\newunivar\foobar¤§*\newunivar¤ +\defunivar\foobar[0]{abcd} +\defunivar\foobar[1]{1 23} +\defunivar\foobar[3]{XY Z}¤§*\defunivar¤ +Cellule 0 : \foobar[0]\par +Cellule 1 : \foobar[1]\par +Cellule 2 : \foobar[2]\par% cellule non définie : \foobar[2] donne \relax +Cellule 3 : \foobar[3]\bigbreak + +\newunivar\client¤§*\newunivar¤ +\defunivar\client[nom]{M. Raymond {\sc Tartempion}}¤\idx*\sc¤ +\defunivar\client[adr]{5 rue de la paix} +\defunivar\client[cod_post]{75000} +\defunivar\client[ville]{Paris}¤§*\defunivar¤ +% fin des définitions, affichage de l'adresse : +\client[nom]\par +\client[adr]\par +\client[cod_post] \client[ville]/ + +On remarque que la programmation de cette variable unidimensionnelle est ici, grâce à la richesse et la puissance des instructions de \TeX{}, relativement facile et extrêmement concise puisqu'elle tient en deux lignes ! + +\begin{exercice} +Comment modifier les macros §\newunivar et §\defunivar vues précédemment pour que les cellules ne soient pas stockées dans la macro \verb-\-\boxtoken{\foobar[0]} mais dans la macro \verb-\-\boxtoken{foobar@0} ? +\solution +Faisons la supposition fondée que \idx\escapechar définit «\verb|\|» comme \idx{caractère d'échappement}. Lorsque \idx\string est développée à l'intérieur de \verb-\csname-\linebreak[1]\verb-...-\linebreak[1]\verb-\endcsname-, il con\-vertit le token \boxtoken{\foobar} en 7 tokens de catcode 12\idx*{catcode!12 (autre)} qui sont \boxtoken{ \unskip\textbackslash} \boxtoken{f} \boxtoken{o} \boxtoken{o} \boxtoken{b} \boxtoken{a} \boxtoken{r}. Il suffit de manger le premier token \boxtoken{ \unskip\textbackslash} avec un §\gobone en ayant pris soin de 1-développer \idx\string avant que §\gobone n'entre en action : + +\centrecode-\def\newunivar#1{%¤§*\newunivar¤ + \def#1[##1]{\csname\expandafter\gobone\string#1@##1\endcsname}}¤\idx*\string§*\gobone¤ +\def\defunivar#1[#2]{%¤§*\defunivar¤ + \defname{\expandafter\gobone\string#1@#2}}¤\idx*\string§*\gobone§*\defname¤- +\end{exercice} + +\begin{center} +$\star$\par +$\star\quad\star$ +\end{center} + +Voici la fin de cette longue et difficile partie où, en nous intéressant aux commandes, nous avons découvert comment \TeX{} fonctionne. Pour résumer à l'extrême, \TeX{} est un langage de macros et donc, se comporte comme une machine qui remplace du code par du code jusqu'à arriver à des tokens non développables qui seront «exécutés» (des primitives, des caractères ordinaires de catcode 10, 11 ou 12 qui seront affichés ou des caractères revêtant des propriétés liées à leur catcode, comme \verb|$|). + +Si l'on entre un peu plus dans les détails et en résumant ce qui a été dit, voici comment on peut se représenter le fonctionnement de \TeX{} : comme nous l'avons vu à la première partie, le code \TeX{} est constitué de cases chacune formée d'un \idx{octet} pour les \idx*{moteur!8 bits}moteurs 8 bits ou par une séquence d'octets codant un caractère \utf pour les moteurs \utf.\idx*{moteur!utf8} Lorsqu'ils sont transformés en tokens lorsque \TeX{} les lit, chacun de ces caractères obéit à des règles particulières propres à leurs catégories. Voici les cas particuliers qui sont transformés lors de la lecture : + +\begin{itemize} + \item lorsque des lettres sont précédées du \idx{caractère d'échappement} «\verb-\-» de catcode 0\idx*{catcode!0 (caractère d'échappement)}, le tout ne forme qu'un seul token, une \idx{séquence de contrôle}; + \item lorsqu'un \idx{espace} (catcode \number\catcode`\ )\idx*{catcode!10 (espace)} dans le code est suivi d'autres espaces\idx*{espace!consécutifs}, les autres espaces sont ignorés; + \item un seul retour charriot \verbidx[ (retour charriot)]{^^M} (catcode 5) est compris comme étant un espace; + \item deux retours charriots consécutifs sont équivalents au token \idx\par; + \item deux caractères identiques de catcode \number\catcode`\^\idx*{catcode!7 (exposant)} consécutifs (comme «\verbidx{^^}») sont convertis en un caractère selon les règles exposées à la 1\iere{} partie; + \item le caractère «\cidx\%» (catcode \number\catcode`\%) ainsi que tous les caractères jusqu'au retour charriot suivant sont ignorés. +\end{itemize} + +Ainsi, on peut considérer le code \TeX{} comme étant des caractères écrits les uns à la suite des autres comme s'ils étaient sur un ruban et, après leur lecture, ces caractères sont transformés en de la matière assimilable par \TeX{}, les tokens. \TeX{} fonctionne à la manière d'une tête de lecture qui se déplace toujours linéairement de gauche à droite sur le ruban où est écrit le code. Voici, comment on peut se représenter schématiquement le fonctionnement de \TeX{} : + +\begin{enumerate} + \item lire un token : utiliser en priorité les tokens stockés dans la pile d'entrée si elle n'est pas vide et dans le cas contraire, construire le token à partir des caractères du code source en appliquant les règles de lecture du code en vigueur à ce moment-là. + + Si ce token lu requiert la lecture d'autres tokens (lecture d'un nombre, cas d'une macro à plusieurs arguments, cas de \verb|\def| qui doit être suivi d'une macro, d'un texte de paramètre et d'un texte de remplacement, etc.), recommencer en 1 jusqu'à ce que les tokens requis soient lus; + \item figer les catcodes de tous les tokens lus et les stocker en mémoire; + \begin{enumerate} + \item si ce qui vient d'être lu n'est pas développable, exécuter l'action que commande le tout; + \item sinon, 1-développer le premier token et insérer devant la tête de lecture (dans la \idx{pile d'entrée}) ce qui en résulte; + \end{enumerate} + \item retourner en 1. +\end{enumerate} + +Lorsque \TeX{} est en phase de développement et si \verb|\expandfter| est la primitive à développer, convenons que 0 est la position de cet \verb|\expandafter| dans la liste des tokens à lire. Lorsque \verb|\expandafter| est développé, la tête de lecture de \TeX{} a la capacité, sans bouger de place, de «détacher» une «tête de développement». Celle-ci, en se désolidarisant de la tête de lecture, ira 1-développer le token se trouvant plus loin dans le code à la position 2 et suivra éventuellement les ordres de développement que son 1-développement implique. Une fois qu'il n'y a plus d'ordre de développement à suivre, la tête de développement revient sur la tête de lecture qui reprend son travail où elle l'avait laissé et tiendra compte, dans le futur, des développements effectués par sa tête de développement. +\grandsaut + +On peut donc énoncer que le fonctionnement de \TeX{} s'apparente à celui d'une «\idx*{Turing (machine)}machine de Turing».\idx*[|)]{développement} +%| | +%| Fin partie 2 | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Partie 3 | +%| | +\defpartcomment{\lettrine[lines=3,slope=4pt,nindent=0pt]{\libertineInitialGlyph{A}}{\kern -5pt près} avoir abordé la plus grosse partie des spécificités de \TeX{}, nous pouvons entrer dans le vif du sujet avec de la programmation non linéaire. Aucun algorithme complexe ne sera abordé dans cette partie, bien au contraire, nous nous en tiendrons aux plus élémentaires et aux plus passepartouts. Malgré leur simplicité, ils sont néanmoins difficiles à cause des spécificités du langage \TeX{}.} + +\part{Structures de contrôle et récursivité} +\chapter{Les outils de programmation de \TeX} +Le langage \TeX{} a été doté des structures de contrôles nécessaires pour élaborer n'importe quel algorithme, aussi compliqué soit-il. À ce titre, c'est donc un langage \emph{complet}\idx*{langage complet}. Bien sûr, il ne viendrait à l'idée de personne de coder en \TeX{} un décompresseur zip ou un algorithme de jeu d'échecs\footnote{Il faut tout de même signaler une récente extension appelée « reverxii » pour \TeX{} qui joue au jeu d'Othello et qui tient en moins de 1000 octets ! Cette très faible taille tient au fait que l'auteur a utilisé toutes les astuces \TeX{}iennes (programmation de caractères actifs notamment) pour que la taille soit la plus petite possible, au prix d'une lisibilité nulle à un tel point qu'il est presque impossible de dire s'il s'agit de code \TeX{} ou pas !\par Le programme comporte une intelligence artificielle qui, bien que loin d'être redoutable, peut tenir tête à un joueur débutant.} ! Dans le monde de \TeX{}, le mot « compilation » signifie « traduction de la totalité code source en document affichable ». En \TeX{}, lorsqu'on parle de programmation, ce mot devient ambigu, car dans le monde de la programmation, « compilation» signifie « transformation du code source en code exécutable », le fichier binaire exécutable obtenu étant d'une grande rapidité. Ce qu'il faut comprendre est que le langage \TeX{} est \emph{interprété}, c'est-à-dire « exécuté au fur et à mesure » lors de la compilation ce qui le rend lent ! +\grandsaut + +Les principales structures de contrôle dont \TeX{} est doté sont les suivantes : + +\begin{itemize} + \item l'assignation, c'est-à-dire la possibilité de stocker des informations dans des « registres » (le mot « variables » est plus couramment utilisé dans d'autres langages), qui sont en quelque sorte des endroits de la mémoire accessibles et lisibles facilement. En \TeX{}, les structures les plus couramment utilisées capables de stocker de l'information sont les suivants : + \begin{itemize} + \item les séquences de contrôle; + \item les registres de tokens; + \item les registres d'entiers ou « compteurs » qui accueillent les entiers et sont dotés des 4 opérations où la division n'est possible que par un entier, étant entendu que cette division donne la partie entière; + \item les registres de dimensions stockent des longueurs et qui permettent, sous certaines réserves que nous verrons, de manipuler des nombres décimaux. Les 4 opérations sont disponibles, mais la division n'est possible que par un \emph{entier}; + \item les registres de boites qui stockent un contenu arbitraire sous forme affichable, mais il n'est plus possible d'accéder au code qui a généré ce contenu. + \end{itemize} + \item les tests ; + \item la \idx{récursivité}, c'est-à-dire la possibilité pour une macro de s'appeler elle-même, que ce soit directement ou par l'intermédiaire d'une ou plusieurs autres macros ; + \item la localité, c'est-à-dire la possibilité de définir une zone où les assignations resteront valables dans cette zone pour être détruites une fois sorti de cette zone, tout en permettant de procéder à des assignations \emph{globales} qui survivent une fois que la zone est fermée ; + \item la possibilité d'écrire dans un fichier, c'est-à-dire d'y stocker des \emph{caractères}; + \item la possibilité de lire les caractères stockés dans un fichier. +\end{itemize} + +Et c'est à peu près tout ! Cette liste est \emph{minimaliste} et n'a rien à voir le pléthore de moyens que propose un langage de programmation actuel. Pour faire une comparaison dans les moyens simples et basiques, il n'y a par exemple, aucune structure de boucle (for, loop, while, etc.). Ni de « variables » de type évolué comme les tableaux à plusieurs dimensions par exemple. Aucune trace non plus d'opérations scientifiques. Mais qu'à cela ne tienne, les structures énoncées ci-dessus sont bien suffisantes pour en programmer soi-même. Il faut s'y habituer, en \TeX{}, on ne dispose que du strict minimum et bien souvent, on doit construire soi-même des structures de contrôle plus évoluées comme les boucles. La plupart du temps avec \TeX, tout est question d'assignations, de lecture raisonnée d'arguments, de comparaison, de développement et de récursivité. Pour autant que ça soit pénible au début, en contrepartie, c'est extrêmement souple puisqu'on peut bâtir des boucles sur mesure. C'est ce que nous allons apprendre à faire dans cette partie en étudiant plus spécialement les tests et la récursivité. + +Mais avant de les aborder, il nous faut revenir aux nombres entiers et comprendre comment \TeX{} calcule. + +\chapter{\TeX{} et les entiers}\idx*[|(]{nombre}% +S'il y a bien un domaine où \TeX{} n'excelle pas, c'est celui du calcul. On ne pourrait lui en vouloir puisque, conçu pour composer du texte, il n'était pas nécessaire de le doter de puissants outils de calcul. Mais depuis que \TeX{} a été écrit, beaucoup de temps s'est écoulé à l'échelle informatique et désormais, tout langage embarque pléthore d'outils de calcul. Par conséquent, un programmeur habitué à ces outils ressent à un moment ou à un autre une légitime frustration envers \TeX{} qui ne les propose pas. Voyons tout de même ce que \TeX{} offre en matière de calculs arithmétiques. + +\section{L'arithmétique \TeX{}ienne} +\subsection{Les entiers} +\idx*[|(]{compteur}\TeX{} manipule des entiers signés sur $32$ bits. Ils sont donc compris entre $-2^{31}+1$ et $2^{31}-1$ c'est-à-dire entre $-\numprint{2147483647}$ et $\numprint{2147483647}$. Ces entiers sont stockés dans des registres spéciaux appelés « compteurs ». 256 de ces registres sont disponibles avec \TeX{} et $\numprint{65536}$ avec $\varepsilon$\TeX{}. Ainsi, \verb|\count42| fait référence au compteur \no42. Contrairement aux registres de tokens avec \idx\toks\verb|0|, aucune convention n'existe quant à un compteur portant un numéro spécial qui serait non utilisé et qui servirait de « compteur brouillon ». + +\TeX{} met aussi à disposition la primitive \idx\countdef, qui ressemble à \idx\chardef ou \idx\toksdef. Par exemple, écrire + +\centrecode-\countdef\foo=42-\idx*\countdef + +\noindent rend \verb|\foo| équivalent à \verb|\count42| de telle sorte que le registre \no42 peut être désigné par la \idx{séquence de contrôle} \verb|\foo|. L'équivalence dont il est question ici ressemble à celle de \idx\let en ce sens que \verb|\foo| n'est pas développable et n'a pas de texte de remplacement mais \emph{sera} \verb|\count42|. + +Comme pour les registres de tokens avec \idx\toksdef, plain-\TeX{} fournit une macro \idx\newcount qui, suivie d'une \verb-\<macro>-, permet d'allouer un compteur pour y faire ensuite référence avec une macro plutôt que par un numéro. La macro \idx\newcount se charge de trouver le numéro du prochain registre de compteur libre. Si par exemple, on écrit \verb-\newcount\foo- et si 89 est le numéro du prochain registre libre, alors la macro \idx\newcount procèderait à l'assignation suivante : + +\centrecode-\global\countdef\foo=89-\idx*\global\idx*\countdef + +\noindent La primitive \idx\global, placée devant l'assignation, rend celle-ci globale : on ne peut donc pas allouer localement un registre de compteur qui serait ensuite libéré après la sortie d'un groupe\footnote{Bien évidemment, mobiliser un compteur localement est possible, mais il faudrait écrire une nouvelle macro le permettant. Ceci est fait par le \idx[!etex]{package} \texttt{etex.sty} pour \LaTeX{} qui, entre autres fonctionnalités, fournit une macro \texttt{\char`\\loccount\char`\\\codeelement{macro}} par laquelle le compteur est défini de façon \emph{locale}.}. + +\begin{regle} +Un compteur a vocation à contenir un nombre entier de $-2^{31}+1$ à $2^{31}-1$. On y fait référence par + +\centrecode-\count<nombre>- + +Il est aussi possible d'utiliser la commande + +\centrecode-\newcount\<macro>- + +qui fait le nécessaire pour lier la \verb|\<macro>| au prochain numéro de compteur non utilisé. Par la suite, la \verb|\<macro>| se comporte exactement comme \idx\count\verb|<nombre>|. Comme les registres de tokens, les \verb|<compteurs>| peuvent donc indifféremment être désignés selon ces deux possibilités. +\grandsaut + +Pour assigner un \verb|<entier>| (voir la définition d'un entier à la page~\pageref{definition.entier}) à un compteur, il faut écrire\idx*{assignation!compteur} + +\centrecode-<compteur>= <entier>- + +\noindent où le signe \verb-=- ainsi que l'espace qui le suit sont facultatifs. +\end{regle} + +Rappelons que pour \emph{afficher} la valeur du compteur, nous ne pouvons pas écrire \verb|<compteur>| dans le code sans risquer de provoquer une erreur de compilation. En effet, \TeX{} attendrait une assignation qui commence de cette façon et si ce \verb|<compteur>| n'était pas suivi d'un \verb|<nombre>|, \TeX{} se plaindrait d'un «\texttt{Missing number, treated as zero} ». Un compteur étant une représentation interne d'un entier, pour \emph{afficher} sa valeur, il faut traduire cette représentation interne en caractères affichables, et faire précéder le \verb|<compteur>| de \idx\the, \idx\number ou \idx\romannumeral : + +\showcode/\newcount\foo \newcount\bar¤\idx*\newcount¤ +\foo=42 \bar=\foo \string\bar\ vaut : \the\bar \par¤\idx*\string\idx*\the¤ +\bar=\foo57 \string\bar\ vaut : \number\bar\par¤\defline\aaa\idx*\number¤ +\bar=2014 \string\bar\ vaut : \romannumeral\bar¤\idx*\romannumeral\idx*\string¤/ + +\noindent La ligne \no\aaa{} montre que les caractères «\verb|57|» ne sont pas pris en compte pour l'affectation. Comme cela est dit à la page~\pageref{definition.entier}, si le \verb|<nombre>| que \TeX{} lit est un registre de compteur, le nombre qu'il contient est lu, et la lecture du nombre s'arrête (laissant ici «\verb|57|» hors du nombre pris en compte). + +\begin{exercice} +\Qu e fait le code suivant ? + +\centrecode-\newcount\foo +\newcount\bar +\foo=57 +\bar=9\foo8\relax- + +\solution +Tout d'abord, deux compteurs sont créés. Le nombre \verb|57| est affecté au compteur \verb|\foo|. Puis, à la dernière ligne, à droite de \verb|\bar=|, l'entier lu est \verb|9| car la lecture du nombre s'arrête sur \verb|\foo| qui n'est pas développable et n'est pas un chiffre. + +Le compteur \verb|\bar| vaut donc 9 et ce qui est à la suite «\verb|\foo8\relax|» est une affectation qui rendrait le compteur \verb|\foo| égal à 8, écrasant la valeur \verb|57| qu'il avait auparavant. Le code proposé fait 3 assignations et ne produit aucun affichage. + +À la dernière ligne, si nous avions voulu assigner 9578 au compteur \verb|\bar| (où 57 est la valeur du compteur \verb|\foo|), il aurait fallu écrire : + +\centrecode-\bar=9\number\foo8\relax- + +de telle sorte que le développement mis en place pour la lecture du nombre convertisse \idx\number\verb|\foo| en caractères valides pour un \verb|<nombre>|. +\end{exercice} + +\subsection{Les opérations}\idx*[|(]{opérations arithmétiques} +Venons-en aux opérations arithmétiques à notre disposition. Celles-ci opèrent sur des compteurs, c'est-à-dire qu'il n'est pas possible de faire des opérations sur des nombres explicitement écrits en chiffres. + +\begin{regle} +Dans les lignes ci-dessous, \verb|<n>| est un nombre entier au sens de \TeX{} : + +\begin{itemize} + \item \idx\advance\verb-<compteur> by <n>- : ajoute \verb-<n>- au \verb-<compteur>-; + \item \idx\multiply\verb-<compteur> by <n>- : multiplie le \verb-<compteur>- par \verb-<n>-; + \item \idx\divide\verb-<compteur> by <n>- : divise le \verb-<compteur>- par \verb-<n>- en lui assignant la troncature à l'unité si le résultat de la division n'est pas entier. +\end{itemize} + +Dans tous les cas, le mot «\verb-by-» et l'espace qui le suit sont facultatifs. +\end{regle} + +Avec ces trois opérations, il est possible de programmer une macro \verb-\fonction- dont l'argument est un entier $n$ et qui calcule et affiche la valeur de $(3n-4)^2$. Le principe est d'assigner l'entier $n$ à un compteur et effectuer les opérations sur ce compteur (le multiplier par 3, lui ajouter $-4$, puis le multiplier par lui-même) pour l'afficher ensuite : + +\showcode/\newcount\foo¤\idx*\newcount¤ +\def\fonction#1{% + \foo=#1 % assigne l'entier #1 au compteur puis + \multiply\foo3 % multiplie par 3¤\idx*\multiply¤ + \advance\foo-4 % soustrait 4¤\idx*\advance¤ + \multiply\foo\foo% élève au carré (lmultiplie \foo par lui même)¤\idx*\multiply¤ + \number\foo% enfin, afficher le résultat¤\idx*\number¤ +} +a) \fonction5\qquad b) \fonction{-13}/ + +La macro donne bien les résultats attendus, mais l'inconvénient est qu'elle nécessite un compteur et surtout qu'elle n'est pas purement développable. En effet, si nous écrivons \verb-\edef\bar{\fonction5}-, la macro \verb-\bar- n'aura pas 121 comme texte de remplacement. La raison est que le texte de remplacement de \verb-\fonction- contient des primitives qui ne se développent pas (\idx\multiply, \idx\advance) et la séquence de contrôle \verb|\foo| qui n'est pas développable non plus. Il s'agit ici d'un cas qui illustre la fondamentale différence entre « affichage produit par une macro après exécution » et « texte de remplacement de cette macro ». Ce n'est pas parce que «\verb|\fonction5|» affiche \verb|121| que tout se passe comme si «\verb|\fonction5|» était équivalent (même après développement) aux caractères \verb|121|. Nous pouvons le constater sur cet exemple : + +\showcode/\newcount\foo¤\idx*\newcount¤ +\def\fonction#1{% + \foo=#1 \multiply\foo3 \advance\foo-4 \multiply\foo\foo¤\defline\aaa\idx*\multiply\idx*\advance¤ + \number\foo}¤\idx*\number¤ +\edef\bar{\fonction{5}}% +a) Signification : \meaning\bar\par¤\idx*\meaning¤ +b) Exécution : \bar¤\defline\bbb¤/ + +Pourquoi obtenons-nous \verb|0| lorsque \verb|\bar| est exécutée ? Parce que le code contenu dans \verb|\bar| (et affiché au cas a) effectue dans un premier temps les calculs sur le compteur \verb|\foo|, mais au lieu d'afficher le ce que contient ce compteur, elle affiche 0, qui est le résidu du développement de \idx\number\verb|\foo|. +\grandsaut + +Non contente de créer une macro non purement développable, la méthode précédente peut rapidement rendre l'enchainement des opérations lourd, fastidieux et peu lisible, pour peu que les expressions à calculer soient plus complexes. En l'état de \TeX{} tel qu'il a été écrit par \idx*{Knuth Donald}D.~\textsc{Knuth}, il n'est pas possible de s'y prendre autrement. Heureusement, avec le moteur \idx\eTeX\idx*{moteur!etex}, une autre alternative est possible. + +\subsection{La primitive \texttt{\char`\\numexpr}}\idx*[|(]{\numexpr} +Pour les entiers, la primitive \idx\numexpr\label{numexpr} permet d'effectuer un enchainement d'opérations arithmétiques via une écriture mathématique en notation infixée où les priorités arithmétiques habituelles sont respectées. Dans ces expressions, les espaces sont ignorés, les signes d'opérations sont \verb|+|, \verb|-|, \verb|*| et \verb|/| et des entiers explicitement écrits en chiffres peuvent y figurer. Pour la division, la seule différence avec la division \TeX ienne réside dans le fait qu'elle donne \emph{l'entier le plus proche} au lieu de \emph{la troncature}\idx*{opérations arithmétiques!troncature (division)}. Les expressions aussi contenir des parenthèses. + +\begin{regle} +La primitive \idx\numexpr permet de calculer des enchainements d'opérations sur les entiers, pris au sens de la définition de la page~\pageref{definition.entier}. Elle doit être suivie d'une expression arithmétique où les espaces sont ignorés, les signes opératoires sont \verb|+|, \verb|-|, \verb|*|, \verb|/| et où les parenthèses sont admises . + +Elle cherche à évaluer ce qui la suit en amorçant un développement maximal jusqu'à rencontrer un token qui ne peut figurer dans une expression arithmétique. Si l'expression arithmétique est suivie d'un \idx\relax, alors ce \idx\relax est mangé par \idx\numexpr. Ceci constitue une spécificité de la primitive \texttt{\string\numexpr} puisqu'avec \TeX, seuls les \emph{espaces} sont mangés après les nombres. C'est donc une excellente habitude de faire suivre une expression arithmétique évaluée par \idx\numexpr d'un \idx\relax. + +La structure + +\centrecode-\numexpr<expression arithmétique>\relax>- + +est un entier au sens de \TeX{}, mais un peu comme l'est un compteur, c'est une représentation \emph{interne} d'un entier et donc, pour l'afficher, il faut recourir à une primitive qui transforme la représentation interne en caractères affichables telle que \idx\the, \idx\number ou \idx\romannumeral. +\end{regle} + +L'exemple suivant montre comment, avec \idx\number, on peut afficher une expression évaluée par \idx\numexpr : + +\showcode|a) \number\numexpr2+3*4\relax\qquad¤\idx*\number\idx*\numexpr¤ +b) \romannumeral\numexpr2*3\relax\qquad¤\idx*\romannumeral¤ +c) \number\numexpr(6-2*4)*(1-(2-6))\relax\qquad +d) \number\numexpr7/4\relax\qquad %7/4 vaut 1,75 +e) \edef\foo{\number\numexpr2+3*4\relax}\meaning\foo¤\idx*\meaning¤| + +Une expression évaluée par \idx\numexpr peut contenir une sous-expression, elle-même évaluée par un autre \idx\numexpr. Voici comment calculer 3*(1+2*5)+2, en évaluant l'expression entre parenthèses avec un \idx\numexpr imbriqué dans l'expression à calculer et en prenant soin de stopper sa portée par un \idx\relax : + +\showcode/\number\numexpr3*\numexpr1+2*5\relax+2\relax/ + +L'emploi de \idx\numexpr peut rendre la macro \verb-\fonction- vue précédemment \emph{purement développable} : + +\showcode/\def\fonction#1{\number\numexpr(3*#1-4)*(3*#1-4)\relax}¤\idx*\number\idx*\numexpr¤ +a) \fonction5\qquad +b) \fonction{-13}\qquad +c) \edef\bar{\fonction5}\meaning\bar¤\idx*\meaning¤/ + +\begin{exercice} +Dans le code ci-dessus, l'expression «\verb|3*#1-4|» est évaluée \emph{deux} fois, ce qui est inutile et consommateur de temps. Comment modifier le code pour qu'elle ne soit évaluée qu'une seule fois ? +\solution +L'idée est de provoquer le calcul de «\verb|3*#1-4|» et passer le résultat à une macro auxiliaire \verb|\carre| qui affichera le carré de son argument: + +\showcode/\def\fonction#1{\exparg\carre{\number\numexpr3*#1-4\relax}}¤\idx*\numexpr§*\exparg¤ +\def\carre#1{\number\numexpr#1*#1\relax}¤\idx*\number\idx*\numexpr¤ +a) \fonction{5}\qquad +b) \fonction{-13}\qquad +c) \edef\bar{\fonction5}\meaning\bar¤\idx*\meaning¤/ + +Ici, la macro §\exparg 1-développe et force le calcul de \verb|\number\numexpr3*#1-4\relax| en un nombre signé explicitement écrit, qui est transmis à la macro \verb|\carre| qui se charge de le multiplier par lui-même. +\end{exercice} + +Il est parfois gênant que la primitive de \TeX{} \idx\divide sur les compteurs et la division «\verb|/|» de \idx\numexpr ne donnent pas les mêmes résultats. En effet, cela rompt la cohérence qui existe entre les trois autres opérations. Comment réconcilier les deux et faire en sorte d'inventer une division pour \idx\eTeX{} qui donne aussi la troncature ? Pour cela, il existe une formule générale. Si $x$ et $y$ sont deux entiers \emph{positifs}, si «\verb|/|» représente la division donnant l'entier le plus proche (la division de \idx\numexpr donc), la troncature du quotient de $x$ par $y$ est + +\[(x-(y-1)/2)/y\] + +À l'aide de \idx\numexpr, il est facile de programmer une macro \verb|\truncdiv{|$x$\verb|}{|$y$\verb|}| qui calcule la troncature du quotient de $x$ par $y$. Le résultat est donné sous la forme d'un entier \emph{non explicite}, c'est-à-dire qu'il faut \idx\number pour transformer cet entier en nombre affichable : + +\showcode|\def\truncdiv#1#2{\numexpr(#1-(#2-1)/2)/#2\relax}¤\idx*\numexpr§*\truncdiv\idx*\number¤ +8/3 : +a) \number\truncdiv83\qquad % doit donner 2 +b) \number\truncdiv{-8}3\qquad % doit donner -2 +c) \number\truncdiv8{-3}\qquad % doit donner -2 +d) \number\truncdiv{-8}{-3}\par % doit donner 2 +4/3 : +e) \number\truncdiv43\qquad % doit donner 1 +f) \number\truncdiv{-4}3\qquad % doit donner -1 +g) \number\truncdiv4{-3}\qquad % doit donner -1 +h) \number\truncdiv{-4}{-3} % doit donner 1¤§*\truncdiv¤| + +La macro donne bien des résultats erronés lorsque les nombres sont de signes contraires. Nous verrons au prochain chapitre comment la modifier pour que le résultat soit correct dans tous les cas. + +Bien entendu, §\truncdiv peut être écrite dans une expression numérique évaluée par \idx\numexpr puisque les \idx\numexpr peuvent être imbriqués sous réserve que les \idx\numexpr internes aient leur portée limitée par \idx\relax, ce qui est le cas ici.\idx*[|)]{opérations arithmétiques}\idx*[|)]{\numexpr} + +\section{Le test \texttt{\textbackslash{}ifnum}}\label{ifnum}\tidx*[|(]{ifnum}% +Le test \tidx{ifnum} est le premier test abordé puisqu'il a un étroit rapport avec le chapitre en cours sur les nombres entiers. Il est aussi l'un des plus fréquents. Il n'est cependant qu'un test parmi beaucoup d'autres et avant de s'intéresser plus en détail à \tidx{ifnum}, un peu de théorie sur les tests de \TeX{} doit être abordée. + +\subsection{Structure des tests} + +\begin{regle} +Les tests de \TeX{} sont exécutés par des primitives qui obéissent aux contraintes suivantes : + +\begin{enumerate} + \item le nom des primitives exécutant un test commence par les lettres «\verb|if|» suivies d'autres lettres notées \verb|<test>| déterminant de quel test il s'agit; + \item si \verb|\if<test>| est une primitive exécutant un test sur ses \verb|<arguments>|, sa structure est la suivante : + +\centrecode-\if<test><arguments> + <code exécuté si le test est vrai> +\else + <code exécuté si le test est faux> +\fi- + + La branche entre \tidx{else} et \tidx{fi} est facultative et donc, un test peut également avoir la structure suivante : + +\centrecode-\if<test><arguments> + <code exécuté si le test est vrai> +\fi- +\end{enumerate} + +Au lieu de \tidx{else} et \tidx{fi}, on peut mettre toute séquence de contrôle rendue \idx\let-égale à \tidx{else} ou \tidx{fi}. + +Les tests et les primitives \tidx{else} et \tidx{fi} sont développables. +\end{regle} + +Les codes exécutés selon l'issue du test peuvent aussi contenir des tests sans que \TeX{} ne s'y perde dans les appariements entre les primitives \verb|\if<test>| et leur \tidx{else} ou \tidx{fi}. En effet, un peu comme le compteur d'accolades, \TeX{} est doté d'un compteur interne d'imbrication de tests qui assure que chaque primitive de test est correctement appariée avec le bon \tidx{else} ou \tidx{fi}. + +Il est en outre primordial de comprendre qu'à l'occasion d'un test, la tête de lecture de \TeX{} \emph{ne lit pas} tout le code dépendant du test (jusqu'au \tidx{fi}) pour stocker en mémoire les codes à exécuter selon l'issue du test afin de mettre l'un ou l'autre sur la pile juste après avoir effectué le test. Non, après avoir exécuté le test elle continue sa lecture linéaire, mais elle \emph{sait} qu'elle se trouve dans la portée d'un test et qu'à un moment ou à un autre, elle aura à lire \tidx{fi}, éventuellement précédé d'un \tidx{else}. + +Pour que ce point soit clair, mettons-nous à la place de la tête de lecture de \TeX{} qui doit exécuter le test suivant (cas \no1) : + +\centrecode-\if<test><arguments><code vrai>\else <code faux>\fi- + +\noindent ou (cas \no2) : + +\centrecode-\if<test><arguments><code vrai>\fi- + +\subsubsection{Test vrai} +Supposons pour l'instant que le test est vrai. Voici l'enchainement des opérations qui vont avoir lieu : + +\begin{enumerate} + \item tout va commencer par le 1-développement de «\verb|\if<test><arguments>|» qui va disparaitre à l'occasion de ce développement; + \item ce qui est à suivre, le \verb|<code vrai>| est exécuté normalement; + \item le 1-développement de qui suit est vide. Il s'agit de + \begin{centrage}\small + \verb|\else <code faux>\fi|\quad(cas \no1)\kern1cm ou\kern1cm \verb|\fi|\quad(cas \no2) + \end{centrage} +\end{enumerate} + +\subsubsection{Test faux} +Supposons à présent que le test est faux. Voici ce qui va se passer : + +\begin{itemize} + \item cas \no1 : + \begin{enumerate} + \item le 1-développement de «\verb-\if<test><arguments><code vrai>\else-»\linebreak[4] est vide (où le \tidx{else} est apparié avec le test initial); + \item ce qui suit, c'est-à-dire \verb|<code faux>| est exécuté normalement; + \item le \tidx{fi} final qui reste est 1-développé et disparait. + \end{enumerate} + \item cas \no 2 : le 1-développement de «\verb-\if<test><arguments><code vrai>\fi-» est vide et la totalité de ce code disparait donc en 1-développement. +\end{itemize} + +\begin{regle} +Lorsqu'un test est exécuté par une primitive de test \verb|\if<test>|, les primitives \tidx{else} et \tidx{fi} ne sont \emph{pas} supprimées lorsque le test est fait. Elles restent en place et lorsqu'elles seront rencontrées plus tard, elles s'autodétruiront par un simple développement. +\end{regle} + +\subsection{Comparer des entiers} +\subsubsection{Le test \texttt{\char`\\ifnum}} +\begin{regle} +La primitive \tidx{ifnum} effectue une comparaison entre deux entiers. La syntaxe est de la forme + +\centrecode-\ifnum<entier1><signe><entier2> + <code exécuté si le test est vrai> +\else + <code exécuté si le test est faux> +\fi- + +\noindent où le \verb-<signe>-, de catcode 12\idx*{catcode!12 (autre)}, est soit «\verb-=-» si l'on veut tester l'égalité entre les deux entiers, soit «\verb-<-» ou «\verb->-» s'il l'on cherche à tester une inégalité stricte. +\end{regle} + +\showcode/a) \ifnum 15=14 vrai\else faux\fi\qquad% test faux¤\tidx*{ifnum}¤ +b) \ifnum4<200 vrai\else faux\fi\qquad% test vrai +c) \newcount\foo \foo=9 + \ifnum\foo>9 nombre\else chiffre\fi% test faux¤\idx*\newcount¤/ + +Vérifions maintenant ce qui a été exposé en théorie sur le 1-développement d'un test. Pour ce faire, mettons à contribution la macro \verb|\>...<| vue à la partie précédente : + +\showcode/\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +\expandafter\>\ifnum5=5 vrai\else faux\fi<\par% test vrai +\expandafter\>\ifnum5=6 vrai\else faux\fi<% test faux +\fi\fi% rétablir l'équilibre \ifnum et \fi¤\defline\aaa¤/ + +Les trois premières lignes de ce code permettent de visualiser les choses, mais rompent l'équilibre entre les \tidx{ifnum} et les \tidx{fi}. En effet, les \tidx{ifnum} sont exécutés par \verb|\expandafter|, mais les \tidx{fi}, détokénisés par \verb|\>|, ne seront jamais exécutés par \TeX. Pour rétablir l'équilibre et éviter que ce bug ne se propage jusqu'à la fin du code source de ce livre, deux \tidx{fi} ont été rajoutés à la ligne \no\aaa. + +\begin{exercice} +Vérifier que les appariements des tests avec leur \tidx{else} et \tidx{fi} sont corrects en programmant une macro \verb-\numtest- qui admet un argument de type entier et qui affiche «chiffre» si son argument est compris entre $-9$ et $9$ inclus. Dans les autres cas, la macro doit afficher «nombre positif» ou «nombre négatif». +\solution +Voici une façon de programmer la macro \verb|\numtest| qui, compte tenu de ce qui a été dit sur le développement des tests, est purement développable : + +\showcode/\def\numtest#1{% + \ifnum#1>-10 ¤\tidx*{ifnum}¤ + \ifnum#1<10 + chiffre% + \else + nombre positif% + \fi + \else + nombre négatif% + \fi +} +a) \numtest{3}\qquad +b) \numtest{-67}\qquad +c) \numtest{-8}\qquad +d) \numtest{21}\qquad +e) \edef\foo{\numtest{2014}}\meaning\foo¤\idx*\meaning¤/ +\end{exercice} +\begin{exercice} +Écrire une macro \verb-\normalise- qui admet un argument de type entier positif. Si l'entier est compris entre 0 et 999 compris, cette macro devra afficher son argument avec 3 chiffres, quitte à rajouter des 0 inutiles à gauche pour satisfaire cette exigence. Dans les autres cas, aucun affichage ne sera fait. + +Par exemple, l'argument «17» donnera un affichage de «017» et «8» sera affiché «008». +\solution +Il suffit d'imbriquer des tests comme à l'exemple précédent et afficher 00 ou 0 juste avant l'argument \verb-#1- selon le cas. Ici par contre, la branche \tidx{else} n'est pas nécessaire : + +\showcode/\def\normalise#1{% + \ifnum#1>-1 % ne faire quelque chose que si #1 est positif ou nul¤\tidx*{ifnum}¤ + \ifnum#1<100 % si <100 + 0% afficher un 0 + \ifnum#1<10 % si <10 + 0%afficher un autre 0 + \fi + \fi + \number#1 %afficher le nombre¤\idx*\number¤ + \fi}% affiche le nombre +a) \normalise{749}\qquad +b) \normalise{0017}\qquad +c) \normalise8\qquad +d) \normalise{1789}\qquad +e) \normalise{-18}\qquad +f) \normalise{0}/ + +Le \idx\number qui se trouve devant l'entier \verb-#1- a deux fonctions : +\begin{itemize} + \item celle d'afficher \verb-#1- sans provoquer d'erreur de compilation si \verb-#1- est un compteur; + \item purger les éventuels 0 inutiles qui se trouvent à gauche de \verb-#1- si celui-ci est écrit explicitement. Ainsi, écrire \verb-\normalise{0017}- mène au résultat correct. +\end{itemize} + +Il reste à remarquer que la macro \verb|\normalise|, puisque son texte de remplacement n'est constitué que de tests et de caractères affichables, est purement développable. +\end{exercice} + +\subsubsection{Le test \texttt{\char`\\ifcase}}\tidx*[|(]{ifcase}% +Pour seconder le test \tidx{ifnum}, \TeX{} met à disposition le test \tidx{ifcase} qui opère également sur des entiers. Ce test fait un peu chambre à part dans le monde des tests, car sa syntaxe échappe à la syntaxe générale des tests et c'est pourquoi elle est expliquée en détail ici. + +\tidx*[|(]{or}\begin{regle} +Le test \tidx{ifcase} teste si un entier est successivement égal à 0, 1, 2, ... selon la syntaxe suivante : + +\tidx{or}\centrecode-\ifcase<nombre> + <code exécuté> si <nombre> = 0 +\or + <code exécuté> si <nombre> = 1 +\or + <code exécuté> si <nombre> = 2 +etc... +\else + <code alternatif>% exécuté si aucune égalité précédente n'est vérifiée +\fi- + +Les entiers auxquels le \verb|<nombre>| est comparé commencent nécessairement à 0, sont consécutifs et vont aussi loin qu'il y a de branches \tidx{or}. Il est donc nécessaire d'écrire autant de \tidx{or} que l'on veut envisager de cas. + +La branche \verb|\else<code alternatif>| est facultative. +\end{regle} + +Voici comment nous pourrions programmer une macro \verb|\mois{<entier>}| qui affiche en toutes lettres le nom du mois dont le numéro est passé en argument. Il est même possible de donner le mois en cours comme argument avec la primitive \idx\month, se comportant comme un compteur, qui contient le numéro du mois en cours : + +\showcode/\def\mois#1{% + \ifcase#1\relax¤\tidx*{ifcase}¤ + \char`\#\char`\#% afficher "##" si argument = 0 + \or janvier\or f\'evrier\or mars\or avril%¤\tidx{or}¤ + \or mai\or juin\or juillet\or aout% + \or septembre\or octobre\or novembre\or d\'ecembre% + \else + \char`\#\char`\#% afficher "##" sinon + \fi +} +a) \mois{-4}\qquad +b) \mois{3}\qquad +c) \mois{11}\qquad +d) \mois{20}\qquad +e) \edef\foo{\mois{\month}}\meaning\foo% mois en cours¤\idx*\month¤/\tidx*[|)]{ifcase}\tidx*[|)]{or}% + +\subsection{Programmer un test : syntaxe} +Les primitives de \TeX{} ne suffisent pas --~et de loin~-- à couvrir tous les tests qu'il est possible de faire. On est donc parfois amené à programmer des \emph{macros} qui effectuent des tests plus complexes à l'aide notamment d'imbrications de tests primitifs. Décidons dès à présent que les noms de ces macros commencent aussi par les lettres «\verb|if|» suivies d'autres lettres notées \verb|<test>|. C'est un choix \emph{arbitraire} et tout autre choix aurait également été légitime : certains font commencer les \emph{macros} de test par les lettres \emph{majuscules} «\verb|IF|» pour ne pas qu'elles soient confondues avec les \emph{primitive}s qui commencent avec des lettres \emph{minuscules}. + +La syntaxe de ces macros, différente de celle des primitives de test, fait davantage consensus. Si \verb|\if<test>| est une macro effectuant un test sur ses \verb|<arguments>| (pris comme argument d'une macro), il est extrêmement courant d'adopter la syntaxe suivante, très facile d'utilisation : + +\centrecode-\if<test><arguments> + {<code exécuté si le test est vrai>} + {<code exécuté si le test est faux>}- + +Basons-nous sur cette syntaxe pour programmer une macro §\ifletter[|(] qui teste si son argument, que l'on suppose constitué d'un seul caractère, noté \verb|<car>|, est une lettre \emph{minuscule}. Par souci de concision, notons \verb|<code vrai>| et \verb|<code faux>| les codes exécutés selon les issues des tests : + +\centrecode-\ifletter{<car>} + {<code vrai>} + {<code faux>}- + +Pour effectuer le test avec \tidx{ifnum}, seule primitive de test que nous connaissons pour l'instant, il va falloir exploiter une façon d'écrire un entier qui est \verb|`<car>| où \verb|<car>| est un caractère. L'entier que cela représente est le code du caractère \verb|<car>| (voir page~\pageref{definition.entier}). Nous allons également tirer parti du fait que les codes des caractères de «\verb|a|» à «\verb|z|» sont des entiers consécutifs. Par conséquent, \verb|<car>| est une lettre minuscule si l'entier \verb|`<car>| est compris entre les entiers \verb|`a| et \verb|`z| (bornes comprises). + +\subsection{Programmer un test : méthodes} +Compte tenu du fait qu'il existe plusieurs méthodes pour programmer la \emph{structure} d'une telle macro, nous allons en envisager plusieurs et elles seront numérotées pour pouvoir facilement y faire référence plus tard. + +\subsubsection{Méthode \no1 : intuitive} +Une façon intuitive de le faire est de faire lire les trois arguments par la macro : + +\begin{enumerate}[label={}] + \item \verb|#1| : le caractère \verb|<car>| à tester; + \item \verb|#2| : le \verb|<code vrai>|, exécuté si le test est vrai; + \item \verb|#3| : le \verb|<code faux>|, exécuté si le test est faux. +\end{enumerate} + +Ensuite, il suffit d'imbriquer 2 tests comme nous l'avons déjà fait et selon l'issue de ces tests, écrire à l'intérieur des branches des tests les arguments \verb-#2- ou \verb-#3- : + +\showcode/\def\ifletter#1#2#3{% + \ifnum`#1<`a% si le caractère est avant "a"¤\tidx*{ifnum}¤ + #3% exécuter "<code faux>" + \else% sinon + \ifnum`#1>`z% si le caractère est après "z" + #3% exécuter "<code faux>" + \else% dans tous les autres cas + #2% "#1" est une lettre : exécuter "<code vrai>" + \fi + \fi} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}/ + +Il y a une certaine inutilité dans ce code qui est de lire \verb-#2- et \verb-#3- par la macro \verb|\ifletter| pour les réécrire plus loin dans le code. L'idéal serait de ne lire que l'argument \verb-#1-, de faire le test et selon l'issue, de lire l'un des deux arguments qui suivent (et qui n'ont pas encore été lus). + +\subsubsection{Méthode \no2 : \texttt{\char`\\firstoftwo} et {\char`\\secondoftwo}} +Les macros §\firstoftwo et §\secondoftwo que nous avons écrites à la page~\pageref{firstoftwo} permettent de sélectionner un argument parmi deux. C'est d'ailleurs parce qu'elles sont utilisées à l'issue d'un test qu'elles sont si fréquemment rencontrées en programmation. La façon naïve de les placer dans les branches des tests serait d'écrire : + +\centrecode*-\def\ifletter#1{% + \ifnum`#1<`a + \secondoftwo + \else + \ifnum`#1>`z + \secondoftwo + \else + \firstoftwo + \fi + \fi}- + +\noindent Mais agir de cette façon provoquerait des résultats inattendus et des erreurs de compilation. Il ne faut surtout pas oublier que les \tidx{else} et les \tidx{fi} ne sont pas résorbés lorsque le test est fait, mais restent en place pour être développés (et éliminés) plus tard ! Il faut donc faire disparaitre les \tidx{else} et les \tidx{fi} \emph{avant} que §\firstoftwo ou §\secondoftwo n'entrent en jeu et ne les prennent à tort comme leurs arguments ! Pour cela, nous savons qu'il faut les 1-développer. Le cas du premier §\secondoftwo est le plus simple : nous mettrons donc un \idx\expandafter avant lui pour faire disparaitre les 7 dernières lignes. + +Pour le test imbriqué, c'est un peu plus compliqué. Mettre un \idx\expandafter devant §\firstoftwo et §\secondoftwo fait disparaitre que le \tidx{else} ou le \tidx{fi} intérieur. Il restera encore le \tidx{fi} de la dernière ligne à 1-développer. Nous devons donc créer un pont d'\idx\expandafter à deux passes pour 1-développer d'abord le \tidx{else} ou le \tidx{fi} intérieur puis le \tidx{fi} final. + +Voici donc comment coder la macro \verb|\ifletter| : + +\showcode/\def\ifletter#1{%¤\tidx*{ifnum}¤ + \ifnum`#1<`a + \expandafter\secondoftwo% \expandafter 1-développe "\else...\fi"¤§*\secondoftwo¤ + \else + \ifnum`#1>`z + \expandafter\expandafter\expandafter% 1-développe "\else...\fi" puis "\fi" + \secondoftwo¤§*\secondoftwo¤ + \else + \expandafter\expandafter\expandafter%1-développe "\fi" puis "\fi" + \firstoftwo¤§*\firstoftwo¤ + \fi + \fi} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}/ + +La macro \verb|\ifetter| est purement développable (à condition bien sûr que les instructions contenues dans \verb|<code vrai>| et \verb|<code faux>| le soient). + +\subsubsection{Méthode \no3 : \texttt{\char`\\romannumeral}} +La méthode précédente produit une macro purement développable, mais le nombre de développements à faire pour arriver au \verb|<code vrai>| ou \verb|<code faux>| dépend du nombre de tests faits et donc, dépend de l'argument. Ne pas savoir à l'avance combien de développements sont nécessaires pour arriver au résultat escompté peut se révéler gênant dans certains cas particuliers. + +\Qu el que soit son argument, faisons en sorte que la macro \verb-\ifletter- se développe en \verb-<code vrai>- ou \verb-<code faux>- en deux développements. L'astuce, nous l'avons déjà vu, consiste à initier une zone de développement maximal avec la primitive \idx\romannumeral et stopper ce développement maximal au bon moment en ajoutant \verb*|0 | ou \idx\z@ (ou tout autre nombre négatif). Ce moment est ici le début de \verb-<code vrai>- ou \verb-<code faux>- et donc, pour insérer cet ajout, il faut lire les deux derniers arguments de la macro \verb|\ifletter| : + +\showcode/\def\ifletter#1#2#3{% + \romannumeral % <- initie le développement maximal¤\idx*\romannumeral¤ + \ifnum`#1<`a + \expandafter\secondoftwo¤§*\secondoftwo¤ + \else + \ifnum`#1>`z + \expandafter\expandafter\expandafter\secondoftwo¤§*\secondoftwo¤ + \else + \expandafter\expandafter\expandafter\firstoftwo¤§*\firstoftwo¤ + \fi + \fi{0 #2}{0 #3}% <- "0 " stoppe le développement maximal +} +\long\def\>#1<{\detokenize{#1}}¤\idx*\detokenize\idx*\long¤ +a) \expandafter\expandafter\expandafter\>\ifletter{0}{vrai}{faux}<\qquad +b) \expandafter\expandafter\expandafter\>\ifletter{x}{vrai}{faux}</ + +\subsubsection{Méthode \no4 : macro auxiliaire} +Pour s'épargner tous ces \idx\expandafter et si nous n'avons pas besoin d'une macro purement développable, il est judicieux de s'aider d'une macro auxiliaire --~appelons-la \verb|\donext|~-- que l'on rend \idx\let-égale à §\firstoftwo ou §\secondoftwo dans les branches des tests. Cette macro sera appelée à la fin, après être sorti des branches des tests : + +\showcode/\def\ifletter#1{%¤\tidx*{ifnum}¤ + \ifnum`#1<`a + \let\donext=\secondoftwo¤§*\secondoftwo¤ + \else + \ifnum`#1>`z + \let\donext=\secondoftwo¤§*\secondoftwo¤ + \else + \let\donext=\firstoftwo¤§*\firstoftwo¤ + \fi + \fi + \donext% exécuter l'action décidée ci-dessus +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}/ + +\subsubsection{Méthode \no5 : \texttt{\char`\\csname} et \texttt{\char`\\endcsname}} +La méthode de loin la plus élégante, mais hélas la plus lente est d'écrire les tests \emph{à l'intérieur} de la paire \idx\csname\linebreak[1]\verb-...-\linebreak[1]\idx\endcsname. Une fois que les tests seront développés, que les branches inutiles auront disparu suite au développement maximal initié par \idx\csname, il devra rester entre \idx\csname et \idx\endcsname soit «\texttt{firstoftwo}», soit «\texttt{secondoftwo}». La fin du mot «\verb|oftwo|» étant commune à toutes les issues possibles, il est possible de \emph{factoriser} le code en mettant cette partie de mot à la toute fin, juste avant le \idx\endcsname. Cette méthode présente l'avantage de donner \verb|<code vrai>| ou \verb|<code faux>| en trois développements, quel que soit l'argument donné à \verb|\ifletter| : + +\showcode/\def\ifletter#1{%¤\idx\csname\tidx{ifnum}¤ + \csname + \ifnum`#1<`a + second% <- commenter la fin de ligne pour éviter un + \else% espace parasite dans le nom de la macro créée + \ifnum`#1>`z + second% + \else + first% + \fi + \fi + oftwo% + \endcsname +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}/ + +\begin{exercice} +Comment faudrait-il modifier ce dernier code pour que \verb|\ifletter| teste si son argument est une lettre de l'alphabet, aussi bien minuscule que majuscule ? +\solution +La primitive \idx\lowercase est utile dans ce cas puisqu'elle change toutes les lettres en lettres minuscules et laisse inchangées les séquences de contrôle : + +\showcode/\def\ifletter#1{%¤\idx*\lowercase\idx*\csname\tidx*{ifnum}¤ + \lowercase{\csname + \ifnum`#1<`a second% + \else + \ifnum`#1>`z second\else first\fi + \fi + oftwo\endcsname¤\idx*\endcsname¤ + }% +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}/ + +Hélas, la primitive \idx\lowercase n'est \emph{pas} développable ! Autrement dit, elle n'a pas vocation à être \emph{développée,} mais \emph{exécutée} c'est-à-dire que le remplacement des lettres par des lettres minuscules n'est pas fait sur la pile de \TeX{} mais plus tard, en interne, lors de l'exécution. Le code suivant montre qu'un \idx\edef ne provoque aucun développement sur cette primitive : + +\showcode/\edef\foo{\lowercase{AbCd}}\meaning\foo/ + +Pour en revenir à la macro \verb|\ifletter|, si nous souhaitons qu'elle reste purement développable, il faut lui faire exécuter deux tests de plus (ce qui suppose autant d'imbrications supplémentaires) pour tester la place qu'occupe l'entier \verb|`<car>| par rapport aux entiers \verb|`A| et \verb|`Z| : + +\showcode/\def\ifletter#1{%¤\idx*\csname¤ + \csname + \ifnum`#1<`A second% + \else + \ifnum`#1<`Z first%¤\tidx*{ifnum}¤ + \else + \ifnum`#1>`a + \ifnum`#1<`z + first% + \else + second% + \fi + \else + second% + \fi + \fi + \fi + oftwo% + \endcsname +} +a) \ifletter{4}{vrai}{faux}\qquad +b) \ifletter{t}{vrai}{faux}\qquad +c) \ifletter{D}{vrai}{faux}\qquad +d) \edef\foo{\ifletter{x}{vrai}{faux}}\meaning\foo\qquad¤\idx*\meaning¤ +e) \edef\foo{\ifletter{=}{vrai}{faux}}\meaning\foo/ + +Ici encore, 3 développements sont nécessaires pour que \verb|\ifletter{<car>}{<vrai>}{<faux>}| se développe en \verb|<vrai>| ou \verb|<faux>| comme le montre ce schéma où une flèche représente un développement : + +\begin{centrage} +\small +\verb|\ifletter{<car>}|% +$\longrightarrow$% +\verb|\csname...\endcsname|% +$\longrightarrow +\vcenter{\hbox{§\firstoftwo}\hbox{ou}\hbox{§\secondoftwo}} +\longrightarrow +\vcenter{\hbox{\verb|<vrai>|}\hbox{ou}\hbox{\verb|<faux>|}}$ +\end{centrage} +\end{exercice} + +\subsubsection{Retour sur la méthode \no1} +Bien que la méthode \no1 (dont le code est rappelé ci-dessous) semble identique aux autres méthodes, il n'en est rien ! + +\centrecode*-\def\ifletter#1#2#3{% + \ifnum`#1<`a + #3% + \else + \ifnum`#1>`z + #3% + \else + #2% + \fi + \fi}- + +Le fait que les arguments \verb|#2| et \verb|#3| soient insérés \emph{dans} les branches du test (alors qu'avec §\firstoftwo et §\secondoftwo, ils restent au dehors pour les autres méthodes) signifie que ces arguments peuvent agir sur ce qui se trouve après eux dans ces mêmes branches\ldots{} Et éventuellement tout casser ! Imaginons par exemple que l'argument \verb|#3| soit la macro §\gobtwo et que le premier test soit vrai. Lorsque \verb|#3|, c'est-à-dire la macro §\gobtwo va être exécutée, elle va capturer les deux arguments qui la suivent pour les faire disparaitre. Tout va se passer comme si un \tidx{else} et un \tidx{ifnum} étaient retirés du texte de remplacement. Ces deux primitives n'existant plus, l'équilibre entre les \tidx{fi} et les primitives de test est rompu et \TeX{}, lorsqu'il rencontrera un \tidx{fi} excédentaire, émettra l'erreur de compilation «\texttt{Extra \char`\\fi}». + +Il est donc particulièrement important qu'une macro effectuant un test avec la syntaxe + +\centrecode-\if<test><arguments>{<code vrai>}{<code faux>}<code qui suit>- + +\noindent rejette \emph{au-dehors} des branches des tests les arguments \verb|<code vrai>| et \verb|<code faux>| de telle sorte que le code qu'ils contiennent puisse éventuellement agir sur le \verb|<code qui suit>|. La méthode \no1, puisque contrevenant à ce principe, est une méthode à déconseiller.§*\ifletter[|)]% + +\subsection{Exercices sur les entiers} +\begin{exercice} +Programmer une macro §\ifinside[|(] dont voici la syntaxe : + +\begin{centrage} + \small§\ifinside $n$\verb|[|$a$\verb|,|$b$\verb|]{<code vrai>}{<code faux>}| +\end{centrage} + +où $n$, $a$ et $b$ sont 3 entiers avec $a\leqslant b$. La macro §\ifinside teste si $n$ appartient à l'intervalle fermé $[a\;\string;\;b]$. +\solution + +La première idée est d'imbriquer des tests sur les inégalités. Il faut exécuter \verb|<code faux>| si $n<a$ ou si $n>b$, et exécuter \verb|<code vrai>| sinon : + +\showcode/\def\ifinside#1[#2,#3]{% + \csname + \ifnum#1<#2 second% si n<a, <code faux> + \else + \ifnum#1>#3 second% si n>b, <code faux> + \else first% sinon, <code vrai> + \fi + \fi + oftwo% + \endcsname +} +a) \ifinside 3[1,8]{oui}{non}\qquad +b) \ifinside -7[-20,-11]{oui}{non}\qquad +c) \ifinside 9[0,6]{oui}{non}\qquad +d) \ifinside -1[-5,1]{oui}{non}/ + +Une autre approche serait de considérer l'entier + +\[(n-a)(n-b)\] + +Si cet entier est négatif ou nul, $n$ est dans l'intervalle. Autrement dit, §\ifinside doit exécuter \verb|<code faux>| lorsque cet entier est strictement positif. En s'aidant de \idx\numexpr, un seul test \tidx{ifnum} devient alors nécessaire pour décider si $n$ est dans l'intervalle ou pas\label{ifinside} : + +\showcode/\def\ifinside#1[#2,#3]{% + \ifnum\numexpr(#1-#2)*(#1-#3)\relax>0 + \expandafter\secondoftwo + \else + \expandafter\firstoftwo + \fi +} +a) \ifinside 3[1,8]{oui}{non}\qquad +b) \ifinside -7[-20,-11]{oui}{non}\qquad +c) \ifinside 9[0,6]{oui}{non}\qquad +d) \ifinside -1[-5,1]{oui}{non}/§*\ifinside[|)] +\end{exercice} + +\begin{exercice} +Programmer une macro §\absval\verb-{entier}-, purement développable et qui se développe en la valeur absolue de l'entier. +\solution +La première idée est de tester si l'entier est négatif auquel cas, nous prendrons l'opposé de ce nombre en mettant un signe «\verb+-+» devant : + +\showcode/\def\absval#1{%¤\idx*\number\tidx*{ifnum}§*\absval¤ + \ifnum#1<0 \number-#1 + \else \number#1 + \fi} +a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0}¤§*\absval¤/ + +Mais la meilleure solution serait de se servir du fait que la primitive \idx\number développe au maximum tout ce qu'elle trouve jusqu'à trouver un caractère qui ne peut pas appartenir à un nombre. Nous pouvons donc mettre le test \emph{dans} le nombre évalué par \idx\number, c'est-à-dire après cette primitive. Le développement maximal de ce test laissera «\verb+-+» si le nombre est négatif et rien sinon : + +\showcode/\def\absval#1{\number\ifnum#1<0 -\fi#1 }¤§*\absval¤ +a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0}¤§*\absval¤/ + +Un fonctionnalité intéressante est d'autoriser des expressions arithmétiques dans l'argument de §\absval. Pour ce faire, nous pouvons évaluer cet argument avec \idx\numexpr : + +\showcode/\def\absval#1{\number\ifnum\numexpr#1\relax<0 -\fi\numexpr#1\relax}¤\idx*\numexpr\idx*\relax§*\absval¤ +a) \absval{-87}\qquad +b) \absval{75}\qquad +c) \absval{0}\qquad +d) \absval{-20+13}\qquad% -7 affiché 7 +e) \absval{5-3*(-4)+10-50}% -23 affiché 23¤§*\absval¤/ + +Le seul inconvénient est que nous évaluons \emph{deux} fois l'expression arithmétique \verb|#1| ce qui est redondant et source de ralentissement. Un programmeur rigoureux décomposerait le travail et écrirait deux macros. La première serait chargée d'évaluer l'expression arithmétique et de passer comme argument à la seconde macro le \emph{nombre explicitement écrit} qui a été obtenu. Celle-ci serait chargée de donner la valeur absolue de son argument : + +\showcode/\catcode`\@11 +\def\absval#1{\exparg\absval@i{\number\numexpr#1\relax}}¤\idx*\numexpr\idx*\relax§*\absval§*\exparg¤ +\def\absval@i#1{\number\ifnum#1<\z@-\fi#1 }¤\idx*\number\idx*\z@¤ +\catcode`\@12 +a) \absval{-87}\qquad +b) \absval{75}\qquad +c) \absval{0}\qquad +d) \absval{-20+13}\qquad% résultat -7 qui est affiché 7 +e) \absval{5-3*(-4)+10-50}% résultat -23 qui est affiché 23¤§*\absval¤/ +\end{exercice} + +\label{intdiv}\begin{exercice} +Nous avions vu au chapitre précédent la macro §\truncdiv qui appliquait la formule suivante pour calculer la troncature du quotient de $x$ par $y$ en utilisant la division de \idx\numexpr : + +\[\frac{x-\frac{y-1}2}y\] + +Voici le code de la macro tel que nous l'avions élaboré: + +\centrecode|\def\truncdiv#1#2{\numexpr(#1-(#2-1)/2)/#2\relax}|§*\truncdiv + +La formule n'étant valable que pour $x$ et $y$ positifs, cette macro donnait des résultats erronés pour des arguments négatifs. Modifier la macro §\truncdiv pour qu'elle donne le bon résultat, quels que soient les signes de ses deux arguments. +\solution +Déjà, la formule donnée est équivalente à +\[\frac{2x-y+1}{2y}\] +qui sera sans doute un peu plus rapide puisqu'il n'y a qu'une seule division. + +Puisqu'elle n'est valable que pour les entiers positifs, nous allons reprendre le code de la macro §\truncdiv donné dans l'énoncé en remplaçant les arguments par leur valeur absolue. Pour que le quotient final tienne compte des signes des arguments, nous allons multiplier le quotient par 1, précédé du signe du premier argument et du signe du second. De cette façon, s'ils sont tous les deux positifs, nous aurons \verb|--1| et la primitive \idx\number sait que ce nombre est 1. Si l'on note $\sigma_x$ le signe de $x$, cela donne + +\[\sigma_x\sigma_y1\times\frac{2|x|-|y|+1}{2|y|}\] + +Pour cela, reprenons la macro §\absval et créons une macro §\sgn qui se développe en «\verb|-|» si son argument est strictement négatif : + +\showcode|\def\sgn#1{\ifnum#1<0 -\fi}¤§*\sgn¤ +\def\truncdiv#1#2{%¤§*\truncdiv§*\absval§*\absval§*\truncdiv¤ + \numexpr + \sgn{#1}\sgn{#2}1*% multiplie le quotient ci-dessous par +1 ou -1¤§*\sgn¤ + (2*\absval{#1}-\absval{#2}+1)/(2*\absval{#2})¤§*\absval¤ + \relax +} +a) \number\truncdiv{-8}3\qquad +b) \number\truncdiv{-8}{-3}\qquad +c) \number\truncdiv8{-3}\qquad +d) \number\truncdiv{0}{-5}¤§*\truncdiv¤| + +Une optimisation possible de la formule est d'écrire au dénominateur $2y$ (qui contient le signe de $y$) et supprimer $\sigma_y$ au début. Il est aussi possible de déplacer $\sigma_x$ au dénominateur : + +\[\frac{2|x|-|y|+1}{\sigma_x2y}\] + +Si $y$ est une expression arithmétique, il est plus prudent de mettre \verb|#2| (qui représente $y$) entre parenthèses au dénominateur. Cela nous donnerait la macro + +\centrecode|\def\truncdiv#1#2{%¤§*\truncdiv¤ + \numexpr(2*\absval{#1}-\absval{#2}+1)/(\sgn{#1}2*(#2))\relax¤§*\absval§*\sgn¤ +}| + +En l'état, elle n'est pas satisfaisante, car si \verb|#1| est une expression arithmétique, §\sgn\verb|{#1}| va provoquer une erreur puisque rien n'est prévu pour évaluer l'argument de §\sgn. De plus, l'argument \verb|#2| est évalué \emph{deux} fois : une fois par §\absval et une autre dans la parenthèse au dénominateur. + +Il faut mettre un peu d'ordre et décider que les deux arguments de §\truncdiv doivent tout d'abord être calculés par une première macro puis être transmis en tant que nombres explicitement écrits à une seconde macro qui fera le calcul. Comme les arguments sont déjà calculés, il ne faut pas que \verb|absval| ne les calcule à nouveau. Abandonnons donc cette version de §\absval pour écrire «§\sgn\verb|{<entier>}<entier>|» ce qui arithmétiquement, est la valeur absolue de l'\verb|<entier>| : + +\showcode|\catcode`\@11 +\def\sgn#1{\ifnum#1<\z@-\fi}¤\idx*\z@§*\sgn¤ +\def\truncdiv#1#2{% + \exptwoargs\truncdiv@i{\number\numexpr#1\relax}{\number\numexpr#2\relax}%¤§*\truncdiv§*\exptwoargs\idx*\number§*\truncdiv¤ +} +\def\truncdiv@i#1#2{%¤§*\truncdiv¤ + \numexpr(2*\sgn{#1}#1-\sgn{#2}#2+1)/(\sgn{#1}2*#2)\relax¤§*\sgn¤ +} +\catcode`\@12 +a) \number\truncdiv{-8}3\qquad +b) \number\truncdiv{-8}{-3}\qquad +c) \number\truncdiv8{-3}\qquad +d) \number\truncdiv{0}{-5}\qquad +e) \number\truncdiv{20+2*3}{4-5*2}% 26/(-6) doit donner -4¤§*\truncdiv¤| +\end{exercice} + +\begin{exercice} +\def\siecle#1{% + \ifnum#1=0 \else + \uppercase\expandafter{\romannumeral\ifnum#1<0-\fi#1 }% + \raise1ex \hbox{e\ifnum#1=1 r\fi} + siècle \ifnum#1<0 avant J.C.\fi + \fi +} +Élaborer une macro §\siecle\verb|{<nombre>}| qui, selon le nombre contenu dans son argument, adapte l'écriture des siècles :\medbreak +\begin{tabular}{>{\ttfamily\hfill}m{0.5\linewidth}<{\kern1em \normalfont affiche\kern1em}@{}l} +\string\siecle\{19\}&\siecle{19}\\ +\string\siecle\{-3\}&\siecle{-3}\\ +\string\siecle\{1\}&\siecle{1}\\ +\string\siecle\{0\}&\siecle{0}<vide> +\end{tabular} + +Pour surélever un texte enfermé dans une boite (ici une \idx\hbox), on aura recours à la primitive \idx\raise et on écrira : + +\centrecode-\raise1ex \hbox{<exposant>}- + +pour élever l'\verb|<exposant>| de \verb|1ex|. +\solution +Il s'agit de faire quelque chose sauf si l'argument est nul. Nous pouvons donc écrire + +\centrecode-\ifnum#1=0 \else<action à faire>\fi- + +\noindent mais le moteur \idx\eTeX{}\idx*{moteur!etex} dispose de la primitive \tidx{unless} qui, placée devant un test, échange les branches correspondant aux issues du test : ce qui est entre le test et \tidx{else} est échangé avec ce qui est entre \tidx{else} et \tidx{fi}. Nous écrirons donc ici : + +\centrecode-\unless\ifnum#1=0 <action à faire>\fi- + +Pour obtenir des chiffres romains en majuscule, nous allons utiliser la primitive \idx\uppercase en ayant pris soin de développer au préalable son argument traduit en chiffres romains par \idx\romannumeral. Pour être surs que le nombre évalué est toujours positif, nous écrirons \idx\romannumeral§\absval\verb|{#1}|. + +Pour l'« exposant », nous allons donc utiliser la primitive \idx\raise qui a la faculté d'élever --~ici d'\verb|1ex|~-- la boite horizontale qui la suit. Cette boite contiendra «\verb|e|» suivi, si la valeur absolue de l'argument est 1, d'un «\verb|r|». Il faudra ensuite insérer «av. J.-C.» si l'argument \verb|#1| est strictement négatif. Le code est finalement assez facilement lisible : + +\showcode/\def\siecle#1{%¤§*\siecle¤ + \unless\ifnum#1=0 % ne faire quelque chose que si #1 différent de 0¤\tidx*{unless}¤ + \uppercase\expandafter{\romannumeral\absval{#1}}% chiffres romains majuscules¤\defline\aaa\idx*\uppercase\idx*\romannumeral§*\absval¤ + \raise 1ex \hbox{e\ifnum\absval{#1}=1 r\fi} siècle% affiche l'exposant puis "siècle" + \ifnum#1<0 {} av. J.-C.\fi% affiche si besoin "av. J.-C."¤\idx*\raise\idx*\hbox§*\absval\tidx*{ifnum}¤ + \fi +} +a) \siecle{19}\qquad +b) \siecle{-3}\qquad +c) \siecle{1}\qquad +d) \siecle{0}\qquad +e) \siecle{-1}¤§*\siecle¤/ + +Le \idx\expandafter de la ligne \no\aaa{} est automatiquement développé puisque \idx\uppercase fait partie de ces macros qui doivent être suivie d'une accolade ouvrante et qui donc, développe au maximum ce qui est entre elle et cette accolade. Le développement de cet \idx\expandafter provoque celui de \idx\romannumeral qui convertit le nombre en chiffres romains avant que \idx\uppercase ne joue son rôle et écrive les chiffres romains en majuscule. +\end{exercice} + +\begin{exercice} +Programmer une macro §\hdif[|(]\verb|{h:m:s}{h':m':s'}| qui calcule le temps qui s'est écoulé entre l'heure \verb|h:m:s| et l'heure \verb|h':m':s'| qui lui est antérieure. L'affichage se fera en heures, minutes et secondes :\medbreak + +\begin{tabular}{>{\ttfamily\hfill}m{0.6\linewidth}<{\kern1em \normalfont affiche\kern1em}@{}l} +\verb|\hdif{18:51:20}{9:20:10}|&\hdif{10:51:20}{9:20:10}\\ +\verb|\hdif{14:20:0}{13:50:0}|&\hdif{14:20:0}{13:50:0}\\ +\verb|\hdif{7:50:20}{5:50:20}|&\hdif{7:50:20}{5:50:20}\\ +\verb|\hdif{7:50:10}{6:50:20}|&\hdif{7:50:10}{6:50:20}\\ +\verb|\hdif{20:00:00}{19:59:15}|&\hdif{20:00:00}{19:59:15}\\ +\verb|\hdif{17:13:15}{14:12:45}|&\hdif{17:13:15}{14:12:45} +\end{tabular} +\solution +La soustraction de durées exprimées en heures, minutes et secondes se fait comme au collège : +\begin{enumerate} + \item effectuer les soustractions indépendamment entre les heures, les minutes et les secondes ; + \item si le nombre de secondes est strictement négatif, l'augmenter de 60 et soustraire 1 au nombre de minutes ; + \item puis, si le nombre de minutes est strictement négatif, l'augmenter de 60 et soustraire 1 au nombre d'heures ; +\end{enumerate} + +L'algorithme s'annonce finalement assez simple. + +Comme pour nous en France, le caractère «\verb|:|» est parfois rendu actif\footnote{Afin qu'il insère une espace insécable du type \texttt{\string\kern\codeelement{dimension}} juste avant «\texttt{\string\string\string\:}».}, il faut ouvrir un groupe semi-simple et changer son code de catégorie \emph{avant} de lire l'argument de \verb|\hdif|. Le groupe semi-simple sera fermé à la fin, après l'affichage. + +La principale méthode de programmation ici consiste à passer les arguments à une macro à arguments délimités \verb|\hdiff@ii| qui isolera les heures, minutes et secondes, effectuera les soustractions entre nombres exprimés dans la même unité et ajustera les résultats s'ils sont négatifs. Pour ce qui est de l'affichage du résultat, les nombres d'heures, minutes et secondes ne seront affichés que s'ils ne sont pas nuls. Enfin, du côté de la typographie, une macro temporaire \verb|\inter@space|, vide au début, sera rendue \idx\let-égale à un espace dès qu'un nombre (d'heures ou de minutes) sera affiché. Elle sera insérée avant le nombre suivant et ainsi créera une espace avant minutes ou avant les secondes si celles-ci ont été précédées par l'affichage d'un nombre. + +Voici le code commenté : + +\showcode|\catcode`\@11 +\edef\saved@catcode{\number\catcode`\:}% sauvegarde du catcode de ":" +\catcode`\:12 % ":" est désormais un caractère non actif normal +\def\hdif{% + \begingroup% ouvrir un groupe semi-simple¤\idx*\begingroup¤ + \catcode`\:12 % modifier le catcode de ":" + \hdif@i% aller lire les deux arguments +} + +\def\hdif@i#1#2{% lit les 2 arguments + \hdif@ii#1-#2\@nil% et les transmet à la macro à argument délimités +} + +\def\hdif@ii#1:#2:#3-#4:#5:#6\@nil{% + %%%%%% calcul des nombres à afficher %%%%%% + \edef\nb@hrs{\number\numexpr#1-#4\relax}% différence des heures¤\idx*\number\idx*\numexpr¤ + \edef\nb@min{\number\numexpr#2-#5\relax}% différence des minutes + \edef\nb@sec{\number\numexpr#3-#6\relax}% différence des secondes + \ifnum\nb@sec<\z@ % si la différence des secondes est <0¤\tidx*{ifnum}\idx*\z@¤ + \edef\nb@sec{\number\numexpr\nb@sec+60\relax}% ajouter 60 sec + \edef\nb@min{\number\numexpr\nb@min-1\relax}% enlever 1 aux minutes + \fi + \ifnum\nb@min<\z@ % si les minutes sont <0 + \edef\nb@min{\number\numexpr\nb@min+60\relax}% ajouter 60 min + \edef\nb@hrs{\number\numexpr\nb@hrs-1\relax}% enlever 1 aux heures + \fi + %%%%%% affichage du résultat %%%%%% + \let\inter@space\empty% pas d'espace avant un nombre pour l'instant + \ifnum\nb@hrs>\z@ % si les heures sont >0 + \nb@hrs h% afficher les heures et "h" + \let\inter@space\space% espace pour plus tard + \fi + \ifnum\nb@min>\z@ % si les minutes sont >0 + \inter@space% afficher une espace éventuelle + \nb@min min% afficher les minutes et "min" + \let\inter@space\space¤\idx*\space¤ + \fi + \ifnum\nb@sec>\z@ % si les secondes sont >0¤\idx*\z@¤ + \inter@space% afficher une espace éventuelle + \nb@sec s% afficher les secondes et "s" + \fi + \endgroup% fermer le groupe ouvert au début¤\idx*\endgroup¤ +} +\catcode`\:=\saved@catcode\relax% restaure le code de catégorie de ":" +\catcode`\@12 +a) \hdif{10:51:20}{9:20:10}\par +b) \hdif{14:20:0}{13:50:0}\par +c) \hdif{7:50:20}{5:50:20}\par +d) \hdif{7:50:10}{6:50:20}\par +e) \hdif{20:00:00}{19:59:15}\par +f) \hdif{17:13:15}{14:12:45}| +\end{exercice}\idx*[|)]{nombre}§*\hdif[|)]\tidx*[|)]{ifnum}% + +\chapter{Une première récursivité}\idx*[|(]{récursivité} +Le moment est venu de se jeter dans le grand bain de la récursivité ! En effet, nous en savons assez pour sortir de la programmation linéaire et passer au stade suivant. + +La définition la plus communément admise, même si elle est un peu laconique tient en ces mots : un algorithme est récursif\idx*{récursivité} s'il s'appelle lui-même. Dans le cas d'une macro, cela signifie qu'une macro est récursive si elle s'appelle elle-même, que ce soit directement ou par le truchement d'autres macros intermédiaires. On comprend bien que cela implique qu'une \emph{boucle} se met en place, c'est-à-dire qu'une portion de code sera lue plusieurs fois. + +Pour prendre une analogie, nous mettrions en place une \idx{récursivité} si, pour parcourir un trajet rectiligne de A vers B, nous nous fixions comme règle de reculer de 10 mètres à chaque fois que nous passons sur un point précis C du parcours. Le simple bon sens montre que nous n'arriverons jamais à la fin du parcours. En effet, nous serions prisonniers d'une \idx{boucle infinie}, condamnés à parcourir éternellement la même portion de 10 mètres ! Pour éviter cet écueil, il faudrait une règle supplémentaire, appelée «\idx{point d'arrêt}» qui permet de sortir de cette boucle. Par exemple + +\begin{itemize} + \item ne rebrousser chemin si le nombre de fois que l'on a vu le point C est inférieur à 20; + \item au point C, continuer sans rebrousser chemin si l'on marché plus d'une heure; + \item ne rebrousser chemin au point C que si l'on a parcouru moins de \numprint[km]{1}. +\end{itemize} + +Il en va de même en informatique. Une récursivité \emph{doit} comporter un \idx{point d'arrêt} sans quoi elle devient une \idx{boucle infinie}. Un point \idx{point d'arrêt} est un test sur un paramètre qui évolue avec les itérations de telle sorte qu'à un moment, ce test change d'état et valide la sortie de boucle. +\grandsaut + +Ne nous lançons pas dans une macro de grande ampleur et dans un premier temps, restons tout de même modestes. Fixons-nous comme objectif de programmer une commande §\ncar[|(] qui admet 2 arguments, le premier est un nombre positif $n$ et le second un caractère. Le but de cette macro sera d'afficher le caractère $n$ fois. + +Cette macro, effectuant un travail très simple, sera un prétexte pour envisager plusieurs méthodes de programmation. + +\subsubsection{Une première approche} +Il suffit d'un peu de réflexion pour bâtir un algorithme effectuant l'action voulue. L'approche la plus naturelle fait appel à une variable qui, dans le monde de \TeX{} sera un compteur : + +\begin{algo} + \item assigner 0 au \idx{compteur}; + \item si le compteur est strictement inférieur à $n$ + \begin{algo} + \item afficher le caractère; + \item incrémenter le compteur de 1; + \item retourner en 2; + \end{algo} + \item fin de l'algorithme +\end{algo} + +Comment s'y prendre pour coder un tel algorithme en \TeX{}? Tout d'abord, nous voyons que le code correspondant au point 2 est parcouru plusieurs fois avec des valeurs différentes du compteur. Cette portion de code sera donc nécessairement \emph{récursive}. Par conséquent, le point 1 sera dévolu à une macro qui initialisera le compteur à 0 et passera la main à une deuxième macro, amenée à s'appeler elle-même. Une macro qui procède à des initialisations et passe la main à la \emph{vraie} macro principale s'appelle une macro «chapeau»\idx*{macro chapeau}. Décidons de nommer la macro chapeau \verb|\ncar| et la macro récursive \verb|\ncar@i|. Voici l'algorithme précédent présenté sous une forme d'un \emph{\idx{pseudocode}}, plus proche de la syntaxe avec laquelle on rédige un programme sans toutefois s'encombrer des subtilités syntaxiques : + +\begingroup +\numalgofalse +\def\algoleftskip{.1\hsize} +\def\algorightskip{.1\hsize}\label{algo1} +\algorithm[\#]{Afficher $n$ fois un caractère}/ +macro ~ncar~#1#2 + ii:=0% initialise le compteur à 0 + val@max:=#1% stocke la valeur maximale + car@save:=#2% stocke le caractère à afficher + ~ncar@i~% appeler la macro récursive +~fin~\medskip +macro ~ncar@i~ + ~si~ ii < val@max% si la valeur maxi n'est pas atteinte + afficher car@save + ii:=ii + 1% incrémenter le compteur + ncar@i% recommencer + ~finsi~ +~fin~/ +\endgroup + +Il est utile de remarquer que la boucle mise en place dans la macro \verb|ncar@i| est de type « tant que». En effet, dans un pseudocode adapté à un langage de plus haut niveau que ne l'est \TeX\footnote{Le format \LaTeX{} fournit une macro \texttt{\string\@whilenum} qui est un « tant que » où le test fait est une comparaison sur des entiers.}, on aurait pu écrire : + +\begingroup +\numalgofalse +\def\algoleftskip{.1\hsize} +\def\algorightskip{.1\hsize} +\algorithm[\#]{Macro ncar@i}/ +macro ~ncar@i~ + ~tant que~ ii < val@max% tant que la valeur maxi n'est pas atteinte + afficher car@save + ii:=ii + 1% incrémenter le compteur + ~fin tant que~ +~fin~/ +\endgroup + +Il suffit maintenant de traduire en \TeX{} le premier \idx{pseudocode}, plus proche du niveau de \TeX{} que ne l'est le second : + +\showcode/\newcount\ii¤\idx*\newcount¤ +\catcode`\@=11 +\def\ncar#1#2{% + \ii=0 % initialise le compteur à 0 + \def\val@max{#1}% stocke la valeur maximale + \def\car@save{#2}% stocke le caractère à afficher + \ncar@i% appelle la macro récursive +} +\def\ncar@i{%¤\tidx*{ifnum}¤ + \ifnum\ii<\val@max% si la valeur maxi n'est pas atteinte + \car@save% afficher le caractère + \advance\ii 1 % incrémenter le compteur¤\idx*\advance¤ + \ncar@i% recommencer¤\defline\aaa¤ + \fi +} +\catcode`\@=12 +\ncar{7}{*}\par +\ncar{13}W\par +\ncar{10}{foobar }/\idx*[|)]{compteur} + +Cela fonctionne comme attendu, et même au-delà puisque le code à reproduire est stocké dans une macro et donc, ce code peut être constitué de plusieurs caractères et pourrait aussi bien contenir des séquences de contrôle. Ce code comporte néanmoins un défaut : la récursivité mise en place n'est pas terminale! + +\begin{regle} +Une \idx[!terminale]{récursivité} est terminale lorsque rien n'est laissé sur la pile lors de l'appel récursif. + +Pratiquement, la récursivité est terminale si l'appel récursif intervient \emph{en dernier} dans le code de la macro qui s'appelle elle-même ou intervient après avoir supprimé par développement tout ce qui se trouve après cet appel. +\end{regle} + +Ici ce n'est pas le cas puisque lorsque la macro \verb-\ncar@i- s'appelle elle même à la ligne \no\aaa{}, il reste encore le \tidx{fi} avant la fin du code de la macro. Ce \tidx{fi}, résidu non encore résorbé du test \tidx{ifnum}, va rester sur la pile pour être lu plus tard. Par conséquent, dans les exemples ci-dessus, il y aura 7, 13 ou 10 \tidx{fi} qui vont s'accumuler sur la pile pour qu'après le dernier test (qui sera négatif), ils soient tous lus ce qui provoquera leur disparition par développement. Pour ne pas surcharger inutilement la pile de \TeX{}, il faut supprimer le \tidx{fi} \emph{avant} d'effectuer l'appel récursif. Il suffit ici de placer un \idx\expandafter avant l'appel récursif \verb-\ncar@i- pour 1-développer le \tidx{fi}. + +Mais il y a plus grave\ldots{} En plus de la récursivité non terminale\idx*{récursivité!terminale}, le code donné ci-dessus comporte une faute de programmation. Comme nous l'avons vu, lorsque \TeX{} lit le nombre qui suit le signe de comparaison \verb-<-, il développe au maximum\idx*{développement maximal} tout ce qu'il trouve jusqu'à trouver quelque chose qui ne peut entrer dans la composition d'un nombre. Il va donc développer \verb-\val@max- (dont le développement est constitué de chiffres) et va poursuivre en développant \emph{aussi} \verb-\car@save-. Si jamais le développement de \verb-\car@save- est un chiffre ou commence par un ou plusieurs chiffres, ceux-ci vont entrer dans la composition du nombre et, étant absorbés lors de cette lecture, ils ne seront plus disponibles pour l'affichage. Non seulement le test se fera sur une valeur fausse, mais un ou plusieurs caractères prévus pour être affichés seront capturés par le test et rendus indisponibles pour l'affichage. + +Nous pouvons en faire l'expérience avec ce code : + +\showcode/\newcount\ii¤\idx*\newcount¤ +\catcode`\@=11 +\def\ncar#1#2{% + \ii=0 % initialise le compteur à 0 + \def\val@max{#1}% stocke la valeur maximale + \def\car@save{#2}% stocke le caractère à afficher + \ncar@i% appelle la macro récursive +} +\def\ncar@i{% + \ifnum\ii<\val@max% si la valeur maxi n'est pas atteinte + \car@save% afficher le caractère + \advance\ii 1 % incrémenter le compteur¤\idx*\advance¤ + \ncar@i% recommencer + \fi +} +\catcode`\@=12 +\ncar{2}{3a}/ + +Ici, le premier test qui est fait, alors que \verb|\ii| vaut 0, est : + +\centrecode-\ifnum\ii<23- + +\noindent où le 2 est le développement de \verb|\val@max| et le 3 est le début de celui de \verb|\car@save|. Ce test est vrai et le restera jusqu'à ce que le compteur vaille 23. La macro \verb|\ncar@i| va donc s'appeler 23 fois, et le «\verb|a|», qui stoppe la lecture du nombre, sera donc affiché 23 fois. + +Pour éviter que \TeX{} ne développe trop loin, il \emph{faut} mettre un \idx\relax\footnote{On pourrait aussi mettre une macro qui se développe en un espace puisqu'un espace stoppe la lecture du nombre et, contrairement à \idx\relax, est absorbé par cette lecture. On peut donc remplacer \idx\relax par \texttt{\string\space}.} après \verb-\val@max- pour stopper la lecture du nombre. La bonne façon de coder la macro \verb-\ncar@i- est donc : + +\centrecode-\def\ncar@i{% + \ifnum\ii<\val@max\relax + \car@save + \advance\ii 1 + \expandafter\ncar@i + \fi}- +\grandsaut + +Voici maintenant deux autres variantes pour coder cette macro : + +\begin{enumerate} + \item on peut utiliser une macro auxiliaire \verb-\donext- qui sera égale, selon l'issue du test, aux instructions à effectuer s'il est positif et à \idx\relax sinon. La macro sera appelée après être sorti des branches du test : + +\centrecode-\def\ncar@i{% + \ifnum\ii<\val@max\relax + \def\donext{\car@save \advance\ii 1 \ncar@i}% + \else + \let\donext\relax + \fi + \donext +}- + \item une autre méthode, plus élégante et plus économe, car elle se passe d'une macro temporaire consisterait, selon l'issue du test, à lire l'argument entre accolades se trouvant juste après le \tidx{fi} ou à manger cet argument. Pour ces deux actions, on utilise les macros déjà définies §\identity et §\gobone (voir page~\pageref{identity}) : + +\centrecode-\def\ncar@i{% + \ifnum\ii<\val@max\relax + \expandafter\identity% exécute...¤§*\identity¤ + \else + \expandafter\gobone% ou mange... + \fi + {\car@save \advance\ii 1 \ncar@i}%... ce code +}- +\end{enumerate} + +\begin{exercice} +Trouver un moyen pour programmer la macro \verb-\ncar{n}{<car>}- sans programmer aucune boucle ni aucune récursivité. + +L'exercice est vraiment difficile et tient davantage lieu de curiosité ou d'acrobatie \TeX nique que de vraie méthode. +\solution +Nous allons mettre à contribution la primitive \idx\romannumeral et utiliser une de ses propriétés : si un nombre est supérieur à 1000, alors cette macro produit un nombre en chiffres romains qui commence avec autant de fois le caractère «\verb|m|» (de catcode 12\idx*{catcode!12 (autre)}) qu'il y a de milliers dans le nombre passé en argument : + +\showcode/a) \romannumeral 9600 \qquad b) \romannumeral 12000/ + +Lorsque nous allons appeler la macro \verb-\ncar{#1}{#2}- il va suffire d'ajouter «000» après le nombre \verb-#1- pour obtenir \verb-#1- fois le caractère «\verb|m|» via la primitive \idx\romannumeral. Il ne nous restera plus qu'à « transformer » le caractère «\verb|m|» vers le caractère que nous souhaitons afficher. Nous avons déjà vu comment le faire à l'aide de \idx\lccode et la primitive \idx\lowercase : + +\showcode/\def\ncar#1#2{% + \begingroup¤\idx*\begingroup\idx*\lccode¤ + \lccode`\m=`#2 % dans \lowercase, les "m" deviennent des "#2" + \lowercase\expandafter{\expandafter\endgroup\romannumeral#1000 }%¤\idx*\lowercase\idx*\endgroup\idx*\romannumeral¤ +} +a) \ncar{7}{*}\qquad +b) \ncar{15}{\%}\qquad +c) \ncar{10}{4}/ + +Comme toutes les primitives devant être suivies d'une accolade ouvrante, \idx\lowercase développe au maximum\idx*{développement maximal} tout ce qui se trouve entre elle et cette accolade. Le développement d'un pont d'\idx[|etc]\expandafter\forbidindex\expandafter{} exploite ici cette propriété pour propager le développement et 1-développer \verb*-\romannumeral#1000 -. Ce développement est \emph{nécessaire} car sans lui, l'argument de \idx\lowercase serait : + +\centrecode-\endgroup\romannumeral#1000 - + +\noindent et comme cet ensemble de tokens ne contient pas le token «\verb|m|», \idx\lowercase n'aurait aucune action et ce code serait lu tel quel. + +Nous répondons bien à l'énoncé de l'exercice, aucune récursivité ou boucle \emph{explicite} n'a été utilisée. Il est évident qu'en coulisses, \TeX{} utilise une boucle lorsqu'il exécute la primitive \idx\romannumeral. +\end{exercice} + +\Qu elle que soit la méthode employée pour parvenir à nos fins, aucune d'entre elles n'est \emph{purement développable}. Si nous écrivions + +\centrecode-\edef\foo{\ncar{3}{*}}- + +\noindent le texte de remplacement de \verb|\foo| ne serait pas «\verb|***|».. La raison tient au fait que les textes de remplacements de \verb|\ncar| ou \verb|\ncar@i| contiennent des séquences de contrôle non développables : + +\begin{itemize} + \item un compteur \verb|\ii|; + \item la primitive \idx\def; + \item la primitive \idx\advance. +\end{itemize} + +\subsubsection{Une approche purement développable} +Pour construire une macro purement développable, il faut proscrire toutes les primitives non développables contenues dans le code actuel : \idx\advance, \idx\relax, \idx\def et les appels aux compteurs. La question qui se pose naturellement est : « sans compteur, comment compter » ? + +Le réponse tient dans la primitive de \idx\eTeX : \idx\numexpr. L'idée est de programmer \verb-\ncar- pour que cette macro s'appelle elle-même en décrémentant son premier argument avec \idx\numexpr; cet argument ira donc \emph{en décroissant} de la valeur maxi à 0, alors que c'était l'inverse pour les versions vues jusqu'alors. Le fait que \verb|\ncar| s'appelle directement elle-même rend inutile toute macro auxiliaire. Voici ce que devient l'algorithme, toujours du type « tant que », qui tend vers une extrême simplicité : + +\begingroup +\numalgofalse +\def\algoleftskip{.1\hsize} +\def\algorightskip{.1\hsize} +\algorithm[\#,\{,\}]{Décrémenter le 1\ier{} argument}/macro ~ncar~#1#2 + ~si~ #1 > 0 + afficher #2 + appeler ~ncar~{#1-1}{#2} + ~fin si~ +~fin~/ +\endgroup + +Du côté de \TeX{}, l'appel récursif prendra la forme suivante : + +\centrecode/\exparg\ncar@i{\number\numexpr#1-1}{#2}/ + +\noindent où §\exparg 1-développe \idx\number qui lance \idx\numexpr qui à son tour, effectue le calcul \verb/#1-1/. Tout cela donnera un argument de «5» si \verb-#1- vaut «6». Regardons le code fonctionner sur un exemple : + +\showcode/\def\ncar#1#2{% + \ifnum#1>0 % <- espace après le "0" + #2% affiche le caractère + \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif¤\idx*\number\idx*\numexpr§*\exparg¤ + \fi +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}{7}}\meaning\foo/ + +La première chose importante à noter que le 0 est suivi d'un espace ce qui stoppe la lecture du nombre et qui met à l'écart \verb-#2-, même s'il commence par des chiffres. La seconde remarque est que cette récursivité n'est pas terminale\idx*{récursivité!terminale} puisque le \tidx{fi} n'est pas mangé avant l'appel récursif. Comme cela a été vu précédemment, les macros §\identity et §\gobone règlent ce problème : + +\showcode/\def\ncar#1#2{% + \ifnum#1>0 \expandafter\identity% si #1>0 exécuter...¤§*\identity¤ + \else \expandafter\gobone% ...sinon manger¤§*\gobone¤ + \fi% ce qui est entre ces accolades : + {#2% afficher le caractère + \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif¤\idx*\number\idx*\numexpr§*\exparg¤ + }% +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo/ + +\subsubsection{Jouer avec le \texttt{\char`\\fi}} +Une autre ruse de \TeX pert consiste à écrire une macro à argument délimité qui \emph{inverse} l'ordre d'un \verb-<code>- quelconque et du prochain \tidx{fi}, sous réserve que le \verb|<code>| ne contienne pas un \tidx{fi} : + +\centrecode-\long\def\antefi#1\fi{\fi#1}-§*\antefi + +\noindent La macro §\antefi lit tout le code \verb-#1- jusqu'au prochain \tidx{fi} qui sert de délimiteur. Ce \tidx{fi}, partie intégrante de la macro, n'est pas exécuté et n'est donc pas pas pris en compte par le compteur interne de \tidx{fi}. Ce n'est que lorsque §\antefi se développera que le \tidx{fi}, token rendu en premier, marquera pour \TeX{} la fin de la portée du test. + +\showcode/\long\def\antefi#1\fi{\fi#1}¤§*\antefi\idx*\long¤ +\def\ncar#1#2{% + \ifnum#1>0 + \antefi% comme si le \fi était "déplacé" ici¤§*\antefi¤ + #2% affiche le caractère + \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif¤\idx*\number\idx*\numexpr¤ + \fi +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo/ + +La présence d'un \tidx{else} provoquerait une erreur si le §\antefi se trouvait avant le \tidx{else}. Dans ce cas, le §\antefi se développerait en \tidx{fi} suivi d'un code \verb|#1| contenant un \tidx{else} qui du coup, se retrouverait orphelin, en dehors du territoire du test. En revanche, §\antefi fonctionne bien dans la branche \tidx{else}\linebreak[1]\verb-...-\tidx{fi} d'un test. + +\begin{exercice} +Comment peut-on modifier la définition de §\antefi pour que l'astuce fonctionne aussi bien avec la présence d'un \tidx{else} que sans ? +\solution +Nous pouvons définir comme argument \emph{non délimité} le code à mettre après le \tidx{fi}. Appelons §\afterfi cette macro qui aura pour mission de placer après le \tidx{fi} son argument : + +\centrecode/\long\def\afterfi#1#2\fi{#2\fi#1}/ + +\noindent De cette façon, si l'argument délimité \verb-#2- contient un \tidx{else}, ce \tidx{else} est recopié tel quel et se trouve devant le \tidx{fi} et donc n'occasionnera aucune erreur de compilation. + +\showcode/\long\def\afterfi#1#2\fi{#2\fi#1}¤§*\afterfi\idx*\long¤ +\def\ncar#1#2{% + \ifnum#1>0 + \afterfi{\exparg\ncar{\number\numexpr#1-1}{#2}}% <- argument renvoyé après le \fi¤\idx*\number\idx*\numexpr§*\afterfi¤ + #2% <- ceci reste avant le \fi + \fi +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo/ +\end{exercice} + +\subsubsection{Une solution avec \texttt{\char`\\expandafter}} +Si nous étions était \emph{certains} que l'argument \verb|#2| était composé d'un unique token, nous pourrions nous passer de §\antefi ou §\afterfi et sauter ce token pour aller développer le \tidx{fi} et rendre la récursivité terminale\idx*{récursivité!terminale}. Il suffirait de propager le développement avec des \idx\expandafter : + +\centrecode/\def\ncar#1#2{% + \ifnum#1>0% + #2% + \expandafter\ncar\expandafter{\number\numexpr#1-1\expandafter}% + \expandafter{\expandafter#2\expandafter}% + \fi +}/ + +Mais l'appel \verb|\ncar{5}{foobar}| ne serait pas une récursivité terminale\idx*{récursivité!terminale} puisque dans + +\centrecode/{\expandafter foobar\expandafter}/ + +\noindent le pont d'\idx\expandafter est très insuffisant pour sauter tous les tokens de « foobar ». Si nous voulons nous passer de §\antefi, ce qu'il y a de mieux à faire est de mettre l'argument \verb|{#2}| après le \tidx{fi}. Si le test est faux, il faudra manger cet argument avec §\gobone et s'il est vrai, \verb|{#2}| reste en place et devient le 2\ieme{} argument de §\ncar. Pour cela, l'astuce consiste à exploiter le fait que la primitive \idx\numexpr engage un développement maximal. Il suffit donc de placer un \idx\expandafter à la fin de cette zone pour développer (et donc faire disparaitre) la branche \verb|\else...\fi| : + +\showcode/\def\ncar#1#2{% + \ifnum#1>0 + #2% afficher le caractère + \expandafter\ncar\expandafter% le pont d'\expandafter lance \number et \numexpr + {\number\numexpr#1-1\expandafter}% le dernier \expandafter mange "\else...\fi"¤\idx*\number\idx*\numexpr¤ + \else + \expandafter\gobone% si le test est faux, manger {#2} ci-dessous¤§*\gobone¤ + \fi{#2}% +} +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \edef\foo{\ncar{5}X}\meaning\foo/ + +\subsubsection{Définir une sous-macro} +Examinons une dernière méthode pour construire une récursivité terminale\idx*{récursivité!terminale}. À l'intérieur de la macro \verb-\ncar-, nous allons définir une « sous-macro » nommée \verb-\ncar@i- qui elle, n'admettra qu'un seul argument, le nombre noté \verb|i| qui compte les itérations. Grâce à l'imbrication des macros, les arguments de la macro mère \verb-\ncar- seront accessibles dans le texte de remplacement de \idx{macro fille} \verb-\ncar@i-. Rappelons-nous que le token \verb-#- doit être doublé à chaque niveau d'imbrication de \verb|\def| pour distinguer les arguments de la macro mère de ceux de la macro fille. + +Nous compterons dans \emph{l'ordre croissant}, de 0 la valeur maxi. + +\showcode/\catcode`\@11 +\def\ncar#1#2{% + \def\ncar@i##1{% + \ifnum##1<#1 % si i < valeur maxi + #2% afficher le caractère + \exparg\ncar@i{\number\numexpr##1+1\expandafter}% puis recommencer avec i+1¤\idx*\number\idx*\numexpr¤ + \fi% \fi mangé par le \expandafter en fin de zone de \numexpr + }% + \ncar@i{0}% commence avec la valeur "compteur" 0 +} +\catcode`\@12 +a) \ncar{7}{*}\qquad +b) \ncar{13}{W}\qquad +c) \ncar{5}{X}/ + +L'inconvénient de cette méthode est que la macro \verb-\ncar- n'est pas purement développable puisque son texte de remplacement contient la primitive \idx\def§\ncar[|)]. + +\begin{exercice}\label{compte}§*\compte[|(]% +Écrire une macro \verb-\compte{<nombre>}- qui affiche les entiers de 1 à \verb-<nombre>-, séparés les uns des autres par une virgule et une espace. Si le \verb-<nombre>- est négatif ou nul, aucun affichage ne sera produit. + +On écrira un code non purement développable utilisant un compteur et un code purement développable. Dans tous les cas, la récursivité devra être terminale. +\solution +Voici un code qui n'est pas purement développable. Comme au premier exemple vu avec \verb-\ncar-, nous utilisons un compteur et nous stockons la valeur maximale dans la séquence de contrôle \verb-\val@max-. + +\showcode/\catcode`\@11 \newcount\compte@cnt¤\idx*\newcount¤ +\def\compte#1{% + \def\val@max{#1}\compte@cnt=0 % effectue les initialisations + \compte@i% appelle la macro principale +} +\def\compte@i{% + \ifnum\compte@cnt<\val@max% si compteur < valeur maxi + \advance\compte@cnt1 % incrémenter le compteur¤\idx*\advance¤ + \number\compte@cnt, % afficher le nombre, la virgule et l'espace¤\idx*\number¤ + \expandafter\compte@i %recommencer + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \compte{2}/ + +Le problème saute aux yeux et révèle une erreur de programmation : la virgule et l'espace sont aussi affichés après le dernier nombre, ce qui ne devrait pas être le cas. + +La méthode la plus immédiate est de tester à l'intérieur du test principal si, après incrémentation, \verb-\compte@cnt- est inférieur à \verb-\val@max- et si tel est le cas, afficher «\verb*-, -». Voici donc le code corrigé : + +\showcode/\catcode`\@11 \newcount\compte@cnt¤\idx*\newcount¤ +\def\compte#1{\def\val@max{#1}\compte@cnt=0 \compte@i} +\def\compte@i{% + \ifnum\compte@cnt<\val@max\relax + \advance\compte@cnt1 ¤\idx*\advance¤ + \number\compte@cnt + \ifnum\compte@cnt<\val@max , \fi% afficher ", " si le maxi n'est pas atteint¤\defline\aaa¤ + \expandafter\compte@i + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b)\compte{-4}\qquad +c) \compte{2}/ + +Bien qu'il fonctionne parfaitement, ce code n'est pas satisfaisant parce que le test de la ligne \no\aaa{} est fait à chaque itération. Certes, le ralentissement qu'il occasionne à chaque fois est insignifiant, mais il est réel et il se cumule au fur et à mesure des appels. Un programmeur rigoureux optimiserait ce code. + +Essayons de supprimer ce test. Nous allons tout d'abord rajouter un test dans la macro \verb-\compte- pour tester si son argument est strictement positif, seul cas où quelque chose sera affiché. Ce test ne sera donc fait qu'une fois. Dans le cas où il est positif, la macro récursive \verb|\coompte@i| sera appelée et elle produira l'affichage en deux étapes : + +\begin{itemize} + \item la valeur du compteur sera affichée; + \item le test décidant si la macro s'appelle elle-même sera fait et s'il est positif, cela signifie qu'il reste encore des valeurs à afficher. La virgule et l'espace seront donc affichés avant que le compteur ne soit incrémenté et l'appel récursif ne soit exécuté. +\end{itemize} + +\showcode/\catcode`\@11 \newcount\compte@cnt¤\idx*\newcount¤ +\def\compte#1{% + \ifnum#1>0 % ne faire quelque chose que si l'argument est positif + \def\val@max{#1}\compte@cnt=1 % faire les initialisations + \expandafter\compte@i% appeller la macro récrusive + \fi +} +\def\compte@i{% + \number\compte@cnt\relax % afficher le nombre + \ifnum\compte@cnt<\val@max\relax % si valeur maxi n'est pas atteinte + , % afficher la virgule et l'espace + \advance\compte@cnt1 % incrémenter le compteur¤\idx*\advance¤ + \expandafter\compte@i % recommencer + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \compte{2}/ + +Passons maintenant à une solution purement développable dans laquelle, pour compter les itérations, nous allons utiliser la primitive \idx\numexpr. Nous allons traduire l'algorithme vu ci-dessus de façon à nous passer de compteur. Il suffit de passer à la macro \verb-\compte@i- deux arguments, l'un qui contient la valeur à afficher (et qui compte les itérations à partir de 1) et l'autre la valeur maximale qui est l'argument \verb-#1- de \verb-\compte-. Comme vu dans un exemple précédent, nous rendons cette fois-ci la récursivité terminale\idx*{récursivité!terminale} en mettant le code \emph{entier} contenant l'appel récursif dans un argument après le \tidx{fi} : cet argument sera lu tel quel avec §\identity si le test est positif et sera mangé avec §\gobone dans le cas contraire. + +\showcode/\catcode`\@11 +\def\compte#1{% + \ifnum#1>\z@% ne faire quelque chose que si #1 > 0¤\idx*\z@¤ + \antefi\compte@i{1}{#1}%¤§*\antefi¤ + \fi +} + +\def\compte@i#1#2{% + #1% afficher le nombre + \ifnum#1<#2 % si le maxi n'est pas atteint + \expandafter\identity% exécuter...¤§*\identity¤ + \else + \expandafter\gobone% sinon, manger...¤§*\gobone¤ + \fi% le code entre accolades ci-dessous + {, % afficher la virgule et l'espace + \exparg\compte@i{\number\numexpr#1+1}{#2}% appel récursif¤\idx*\number\idx*\numexpr§*\exparg¤ + }% +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \edef\foo{\compte{2}}\meaning\foo/ + +Une variante plus concise aurait été possible en utilisant §\antefi dans la macro auxiliaire : + +\showcode/\catcode`\@11 +\def\compte#1{% + \ifnum#1>\z@% ne faire quelque chose que si #1 > 0¤\idx*\z@¤ + \antefi\compte@i{1}{#1}%¤§*\antefi¤ + \fi +} + +\def\compte@i#1#2{% + #1% afficher le nombre + \ifnum#1<#2 % si le maxi n'est pas atteint + \antefi% déplace le \fi ici¤§*\antefi¤ + , % affiche la virgule et l'espace + \exparg\compte@i{\number\numexpr#1+1}{#2}% appel récursif¤\idx*\number\idx*\numexpr§*\exparg¤ + \fi +} +\catcode`\@12 +a) \compte{9}\qquad +b) \compte{-4}\qquad +c) \edef\foo{\compte{2}}\meaning\foo/ +§*\compte[|)]\end{exercice} + +Il faut tirer une leçon de ce chapitre. La programmation de macros récursives purement développables, disqualifiant de fait tout stockage d'information via un compteur, \idx\def ou \idx\toks, oblige à transmettre ces informations sous forme d'arguments. Il devient donc nécessaire de fournir à chaque itération tous les arguments nécessaires au fonctionnement de la macro même si parfois, des arguments invariables (comme la valeur maximale \verb|#2| ci-dessus) doivent être lus à chaque fois. + +Rendre une macro récursive purement développable est l'assurance qu'elle va fonctionner comme on l'attend dans tous les cas et à ce titre, tend à devenir le Graal que certains recherchent à tout prix. Il est cependant bon de garder à l'esprit que la facilité et la sécurité que l'on gagne d'un côté cachent une certaine lourdeur dans les mécanismes mis en jeu dans sa programmation. Les contorsions que l'on doit faire notamment au niveau des arguments pour rendre une macro purement développable impliquent généralement une programmation beaucoup plus délicate que si elle ne n'était pas.\idx*[|)]{récursivité} + +\chapter{Une boucle « for »}§*\for[|(] +\label{for}Nous savons à présent suffisamment de choses pour programmer une structure de contrôle de type « \verb|for| » où une « variable » --~une macro en réalité~-- va prendre les valeurs entières successives entre deux entiers $n_1$ et $n_2$. Fixons-nous par exemple la syntaxe sera la suivante : + +\begin{centrage} + \small\verb|\for\<macro>=|$n_1$\verb| to |$n_2$\verb|\do{<code>| +\end{centrage} + +\noindent où le \verb-<code>- pourra contenir la \verb-\<macro- qui tient lieu de variable et qui prend successivement toutes les valeurs de $n_1$ à $n_2$. Compte tenu de sa syntaxe, il est bien évident que la macro \verb-\for- doit avoir un texte de paramètre contenant des arguments délimités par «=», «\verb|to|» et par «\verb-\do-». + +\subsubsection{Une première version} +Voici un premier jet, assez intuitif : + +\showcode/\catcode`\@11 +\def\for#1=#2to#3\do#4{% #1=\<macro> #2=n1 #3=n2 #4=<code> + \def#1{#2}% initialise la \<macro> à l'entier n1¤\defline\aaa¤ + \def\val@max{#3}% stocke n2 + \def\loop@code{#4}% stocke le <code> + \for@i#1% appelle la macro récursive et passe la \<macro> en argument +} +\def\for@i#1{% + \unless\ifnum#1>\val@max\relax% tant que la \<macro> est <= n2¤\tidx*{unless}¤ + \loop@code% effectue le code précédemment sauvegardé + \edef#1{\number\numexpr#1+1}% incrémenter la \<macro>¤\idx*\number\idx*\numexpr¤ + \expandafter\for@i\expandafter#1% et recommencer après avoir mangé le \fi + \fi +} +\catcode`\@=12 +\for\ii=1to5\do{Maintenant \string\ii\ vaut : \ii.\endgraf}\medbreak¤\idx*\medbreak\idx*\string¤ +"\for\ii=1to0\do{A}"\medbreak% boucle parcourue 0 fois +"\for\ii=1to1\do{A}"\medbreak% boucle parcourue 1 fois¤\idx*\medbreak¤ +\for\ii = 0 to 10 \do {[\ii]}.¤\defline\bbb¤/ + +Remarquons tout d'abord que la séquence de contrôle \idx\do n'est jamais définie et peu importe qu'elle le soit\footnote{Comme nous l'avons vu, elle est bel et bien définie dans plain-\TeX{} notamment pour seconder la macro \idx\dospecials.} : elle ne sert que de délimiteur à l'argument \verb-#3- de la macro \verb-\for-. + +À la ligne \no\aaa, l'argument \verb|#2|, qui est l'entier $n_1$, est assigné à \verb-#1- qui est la \verb|\<macro>|. C'est une mauvaise idée si on laisse des espaces --~toujours utiles pour la lisibilité~-- comme c'est le cas à l'appel de la ligne \no\bbb. Dans ce cas, lors de la première itération, \verb-#1- a comme texte de remplacement «\verb*- 0 -». Pour se prémunir de cet inconvénient, il aurait fallu écrire à la ligne \no\aaa{} + +\centrecode-\edef#1{\number\numexpr#2\relax}- + +\noindent et ainsi tirer parti du fait que non seulement \idx\numexpr évalue l'éventuelle expression arithmétique \verb|#1|, mais le fait en \emph{ignorant les espaces}. La même méthode doit être utilisée pour stocker l'entier $n_2$ dans \verb|\val@max|. + +Plutôt que de définir une macro auxiliaire récursive \verb-\for@i- \emph{à l'extérieur} de la macro principale comme c'est le cas ici, nous avons tout à gagner à la définir à l'intérieur en tant que « \idx{macro fille}». De cette façon, tous les arguments de la macro chapeau sont accessibles. Nous économisons donc la macro \verb-\loop@code-. + +Remarquons enfin que la macro \verb-\for- n'était pas déclarée \idx\long, interdisant donc la primitive \idx\par dans ses arguments. Ceci explique pourquoi il faut utiliser \idx\endgraf (une macro \idx\let-égale à \idx\par) pour provoquer la fin du paragraphe. + +\showcode/\catcode`\@11 +\long\def\for#1=#2to#3\do#4{%¤§*\for\idx*\long¤ + \def\for@i{% + \unless\ifnum#1>\val@max\relax% tant que la \<macro> <= n2¤\tidx*{unless}¤ + #4% code à exécuter + \edef#1{\number\numexpr#1+1}% incrémenter \<macro>¤\idx*\number\idx*\numexpr¤ + \expandafter\for@i% et recommencer après avoir mangé le \fi + \fi + }% + \edef#1{\number\numexpr#2\relax}% initialise la variable à n1 + \edef\val@max{\number\numexpr#3\relax}% stocke n2 + \for@i% appelle la sous-macro récursive +} +\catcode`\@=12 +\for\ii = 1 to 5 \do {Maintenant \string\ii\ vaut : \ii.\par}\medbreak¤\idx*\medbreak\idx*\string¤ +"\for\ii=1to0\do{A}"\medbreak% boucle parcourue 0 fois +"\for\ii=1to1\do{A}"\medbreak% boucle parcourue 1 fois¤\idx*\medbreak¤ +\for\ii = 0 to 10 \do {[\ii]}./ + +\subsubsection{Mise en place d'un incrément} +En l'état actuel, la macro \verb-\for- incrémente sa « variable » de 1 à chaque itération. Comment s'y prendre pour que cet incrément puisse être spécifié par l'utilisateur ce qui constituerait en quelque sorte un argument « optionnel »? Il est naturel de penser à cette syntaxe : + +\begin{centrage} + \small\verb|\for\<macro>=|$n_1$\verb| to |$n_2$ \verb|\do| $i$ \verb|{<code>}| +\end{centrage} + +Nous allons modifier la macro \verb-\for- pour que l'incrément $i$, qui est un entier signé, soit pris en compte. Si celui-ci n'est pas spécifié, il sera pris égal à $1$ si $n_1<n_2$ et à $-1$ sinon. L'exercice est difficile, d'autant que nous ne savons pas encore tester si un argument est vide, il faudra donc trouver une autre méthode ! + +Il faut déjà modifier le texte de paramètre de la macro \verb-\for- pour qu'elle prenne un 4\ieme{} argument après le \verb-\do-, suivi ensuite du 5\ieme{} qui sera le code à exécuter à chaque itération. Ce serait une erreur que de la définir ainsi : + +\centrecode-\def\for#1=#2to#3\do#4#5{...}- + +\noindent Le problème avec cette définition est que \TeX{} va toujours lire \emph{deux arguments} après le \verb-\do- ! Et si nous omettons l'argument optionnel, \verb-#4- va devenir le \verb-<code>- et \verb-#5- sera l'argument suivant, non destiné à être lu par la macro ! Pour éviter un tel mélange d'arguments, l'argument optionnel recevant $i$ \emph{doit} être délimité par l'accolade ouvrante\idx*{argument!délimité!par accolade} qui marque le début du \verb-<code>-. Nous allons donc écrire : + +\centrecode-\def\for#1=#2to#3\do#4#{...}- + +\noindent Ce faisant, \verb-#4- sera donc tout ce qui se trouve entre \verb-\do- et la prochaine accolade ouvrante, cet ensemble étant éventuellement vide. Mais en procédant de cette façon, l'argument \verb-{<code>}- ne sera pas encore lu. Il faudra appeler une macro auxiliaire chargée de lire le code devant être exécuté à chaque fois. + +L'autre problème qui surgit est que l'argument \verb-#4- peut être vide, ou égal à +4, -2 ou n'importe quel entier signé. Comment s'y prendre pour transformer cet argument en un nombre, même s'il est vide ? L'astuce consiste à mettre un 0 devant cet argument et évaluer le tout avec \idx\numexpr. Nous aurions donc : + +\centrecode-\edef\for@increment{\number\numexpr0#4\relax}- + +\noindent Par acquit de conscience, assurons-nous que cette astuce fonctionne. Voici les cas qui peuvent se présenter : + +\begin{itemize} + \item si \verb-#4- est vide ou réduit à des espaces, alors le «0» ajouté sera évalué par \idx\numexpr et donc, \verb-\for@increment- vaudra 0 ; + \item si \verb-#4- vaut «5» alors, «05» sera évalué et nous obtiendrons 5; + \item si \verb-#4- vaut «+4» alors, «0+4» sera évalué et nous obtiendrons 4 ; + \item enfin, si \verb-#4- vaut «-10» alors, «0-10» sera évalué et vaudra -10. +\end{itemize} + +Une fois ceci fait, si \verb-\for@increment- est nul, cela traduit une absence d'argument optionnel ou pire, un incrément explicitement donné égal à 0 par l'utilisateur joueur ou inconscient! Dans ces cas, il suffit de le redéfinir à 1 si $n1<n_2$ (soit si \verb-#2-${}<{}$\verb-#3-) et à $-1$ sinon. + +Passons à l'étape suivante : il faut vérifier que le signe de l'incrément (lorsqu'il est explicitement écrit) est « compatible » avec les valeurs de \verb-#2- et \verb-#3-. En effet, si \verb-#2- vaut 4, \verb-#3- vaut 10 et l'incrément vaut $-2$, on comprend bien qu'additionner l'incrément $-2$ à 4 est une tâche vaine. On n'arrivera jamais à 10 et, après un \emph{très grand} nombre d'itérations, on va dépasser la borne inférieure $-2^{31}+1$ permise pour un nombre, provoquant à ce moment (ou même avant) une erreur de compilation. + +Un peu d'analyse montre que l'incrément est « incompatible » avec les bornes \verb-#2- et \verb-#3- si les entiers $\#3-\#2$ et \verb-\for@increment- sont de signes contraires, c'est-à-dire lorsque le signe de l'expression arithmétique suivante est strictement négatif : + +\centrecode/\for@increment*(#3-#2)/ + +Voici le code : + +\showcode/\catcode`\@11 +\def\for#1=#2to#3\do#4#{% + \edef\for@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel¤\defline\aaa¤ + \ifnum\for@increment=\z@% s'il est nul,¤\idx*\z@¤ + \edef\for@increment{% le redéfinir : + \ifnum\numexpr#3\relax<\numexpr#2\relax¤\defline\bbb\idx*\numexpr¤ + -% ajoute un signe - si #3<#2 + \fi + 1% devant "1" + }% \for@increment vaut donc 1 ou -1 + \fi + \ifnum\numexpr\for@increment*(#3-#2)\relax<\z@% si l'argument est incompatible¤\defline\ccc\idx*\z@¤ + \expandafter\firstoftwo% exécuter le 1er argument qui suit¤§*\firstoftwo¤ + \else + \expandafter\secondoftwo% sinon le second¤§*\secondoftwo¤ + \fi + {Incrément incompatible !\gobone %\gobone mangera le <code> qui à lire¤\defline\ddd¤ + }% + {\edef#1{\number\numexpr#2\relax}% initialise la \<macro> + \edef\cmp@sgn{\ifnum\for@increment<\z@<\else>\fi}% stocke "<" ou ">" pour plus tard¤\idx*\z@¤ + \expandafter\for@i\expandafter#1\expandafter% appelle la macro récursive + {\number\numexpr#3\relax}% et lui lui passe la \<macro> (#1) et n2 (#3) + }% +} + +% #1 = \<macro> #2 = n2 +\long\def\for@i#1#2#3{% l'argument #3 (le <code>) est lu à ce moment-là¤\idx*\long¤ + \def\for@ii{% + \unless\ifnum#1\cmp@sgn#2\relax% tant que la \<macro> n'a pas dépassé n2¤\defline\fff\tidx*{unless}¤ + #3% exécute le <code>¤\defline\eee¤ + \edef#1{\number\numexpr#1+\for@increment}% incrémente la \<macro>¤\idx*\number\idx*\numexpr¤ + \expandafter\for@ii% recommence après avoir mangé le \fi + \fi + }% + \for@ii% appelle la sous-macro récursive¤\defline\ggg¤ +}% +\catcode`\@=12 +a) \for\ii = -4 to 7 \do{(\ii)}\par +b) \for\jj = 20 to -50\do-10 {(\jj)}\par +c) \for\xx = 8 to 185\do 20 {[\xx]}\par +d) \for\yy = 0 to 10\do -2{(\yy)}\par +e) "\for\ii = 1 to 0 \do1{XX}"\par +f) "\for\ii = 1 to 1 \do{A}"\par% boucle parcourue 1 fois +g) \for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}% imbrication de boucles/ + +Il est utile de reprendre ce code en détail, car il comporte beaucoup d'astuces\ldots{} Laissons pour l'instant le cas g où le problème de l'imbrication de boucles sera résolu un peu plus loin. + +À la ligne \no\aaa{}, si le nombre \verb|\for@increment|, résultat de l'évaluation de \verb|0#4| est nul, cela signifie que l'argument optionnel \verb|#4| est absent ou égal à \verb|0|. Dans ce cas, la macro \verb|\for@increment| est redéfinie par un \idx\edef et un signe «\verb|-|» est ajouté si \verb|#3| est inférieur à \verb|#2| (ligne \no\bbb) de telle sorte que \verb|\for@increment| contienne \verb|1| ou \verb|-1|. + +La ligne \no\ccc{} teste si \verb|\for@increment| est « compatible » avec les valeurs \verb|#2| et \verb|#3|. À l'aide des macros §\firstoftwo et §\secondoftwo, un des 2 arguments qui suit est exécuté : + +\begin{itemize} + \item le premier argument se contente d'afficher « Incrément incompatible » et appelle la macro §\gobone qui mange le \verb-<code>- qui n'a pas encore été lu ce qui termine tout le processus. Bien évidemment, il est possible de ne rien afficher et se contenter de mettre §\gobone dans le deuxième argument; + \item le deuxième argument définit la séquence de contrôle \verb-\cmp@sgn- pour que son texte de remplacement soit «\verb-<-» si l'incrément est négatif et «\verb->-» sinon. Ce signe sera utilisé dans un test plus tard. Il ne reste plus ensuite qu'à appeler la macro auxiliaire \verb-\for@i- en lui passant la \verb|\<macro>| (argument \verb-#1-) et l'entier $n_2$ (argument \verb-#3-) préalablement évalué par \idx\numexpr. +\end{itemize} + +La macro \verb|\for@i| lira \emph{trois} arguments, les deux que lui passe la macro \verb-\for- et le \verb-<code>- qui doit être exécuté à chaque itération puisque celui-ci n'a pas été lu par la macro chapeau \verb-\for-. Comme la macro \verb-\for@i- est appelée à être récursive, il est très peu commode --~et maladroit~-- d'installer une récursivité avec trois arguments à lire à chaque appel. Pour éviter cette lourdeur, la macro \verb-\for@i- ne sert que de macro chapeau pour la vraie macro récursive \verb-\for@ii- qui n'admet aucun argument puisque, étant \emph{dans} la macro \verb-\for@i-, elle peut accéder à ses arguments. + +Le code de la macro \verb-\for@ii- est assez simple, le \verb|<code>| est exécuter à la ligne \no\eee{} puis la \verb|\<macro>| est incrémentée de la valeur signée \verb-\for@increment-. La ligne \no\fff{} nécessite un petit commentaire : le test \tidx{ifnum} s'attend à lire un nombre et développe tout au maximum jusqu'à trouver un token qui ne puisse pas constituer un entier : la macro \verb|#1| va être développée et la lecture du nombre ne va pas s'arrêter. Le développement va continuer et la macro \verb-\cmp@sgn- va être développée à son tour. Comme son texte de remplacement est «\verb|<|» ou «\verb|>|», la lecture du nombre va être stoppée, mais le but recherché, c'est-à-dire le développement de \verb|\cmp@sgn| est effectué. Enfin, le test est construit avec \tidx{unless} ce qui signifie que ce qui se trouve jusqu'au \tidx{fi} est exécuté si le test \emph{contraire} est vérifié. Ce test contraire correspond à une inégalité \emph{large} ($\leqslant$ ou $\geqslant$) ce qui implique qu'une itération sera faite lorsque la \verb|\<macro>| est égale à $n_2$. + +\subsubsection{Imbrication de boucles : analyse du problème} +Comme le cas g l'a mis en évidence dans le code précédent, lorsque deux boucles \verb-\for- sont imbriquées le fonctionnement attendu n'est pas obtenu et il semble qu'une seule itération de la boucle la plus extérieure est faite entièrement : + +\centrecode-\for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}- + +\noindent se traduit par «(1,0)(1,1)(1,2)». + +Il faut inventer un remède qui permet d'imbriquer autant de boucles \verb-\for- que l'on veut. Mais tout d'abord, il faut analyser le problème. Par chance, c'est assez simple ici. Dans un premier temps, \verb-\for@i- est appelée par \verb-\for- ce qui a pour effet de définir la macro \verb-\for@ii- avec le texte de remplacement (auquel nous donnons le numéro~1) suivant : + +\centrecode-\unless\ifnum\ii\cmp@sgn4\relax + \for\jj=0to2\do{(\ii,\jj)}\par + \edef\ii{\number\numexpr\ii+\for@increment}% + \expandafter\for@ii +\fi- + +Lorsque la macro \verb|\for@ii| est exécutée à la ligne \no\ggg, elle est développée et donc, tout ce code est placé sur la pile pour y être exécuté. Lorsque la 2\ieme{} ligne de la pile est exécutée, une nouvelle boucle §\for démarre: + +\centrecode-\for\jj=0to2\do{(\ii,\jj)}- + +Comme \verb|\ii| vaut 1, cette boucle va afficher «(1,0)(1,1)(1,2)» mais en coulisse, l'irréparable s'est déjà produit ! Ce nouvel appel à §\for a redéfini la macro \verb|\for@ii| avec ce texte de remplacement (appelé \no2) : + +\centrecode-\unless\ifnum\jj\cmp@sgn2\relax + (\ii,\jj)% + \edef\jj{\number\numexpr\jj+\for@increment}% + \expandafter\for@ii +\fi- + +\Qu e va-t-il se passer quand la boucle interne (dont la variable est \verb|\jj|) sera terminée et que «(1,0)(1,1)(1,2)» sera affiché? La macro \verb|\jj| vaudra 3 et \TeX{} va continuer à lire le code sur sa pile qui est le texte de remplacement \no1 amputé de ses deux premières lignes : + +\centrecode-\edef\ii{\number\numexpr\ii+\for@increment}% + \expandafter\for@ii +\fi- + +\noindent Par conséquent, \verb|\ii| va être incrémenté (et vaudra 2). La macro \verb|\for@ii| (dont le texte de remplacement est le \no2) va être appelée et le test + +\centrecode-\unless\ifnum\jj\cmp@sgn2\relax- + +\noindent sera faux car \verb|\jj| (qui vaut 3) a dépassé $n_2$ qui est 2. Par conséquent, aucun code ni aucune récursivité ne seront exécutés. Ce test faux signe donc la fin de la boucle extérieure. + +\subsubsection{Imbrication de boucles : résolution du problème} +Tous les problèmes viennent donc de la redéfinition de \verb-\for@ii- par la boucle intérieure et du fait que cette redéfinition survive à la fin de la boucle intérieure. Certes, exécuter chaque boucle dans un groupe règlerait le problème, mais cette solution de facilité empêcherait toute globalité au code qui est exécuté à chaque itération et donc diminuerait les fonctionnalités de la boucle. + +Le remède serait donc de définir une macro \verb-\for@ii- spécifique à chaque « variable ». Pour cela, la macro chapeau \verb|\for| va appeler une macro \verb|\for@i| chargée de définir la macro récursive \verb|\|\boxtoken{for@ii@\string\<macro>} où \verb|\<macro>| est le développement de \verb|\string#1|. Par exemple, si l'on exécute + +\centrecode-\for\xx=1to5\do{X}- + +\noindent la macro \verb|\for| va appeler \verb|\for@i| qui va se charger de définir et lancer la macro récursive \verb-\-\boxtoken{for@ii@\string\xx}. Plus aucun risque de redéfinition n'existe, sauf si l'utilisateur fait preuve d'inconscience et utilise le même nom de variable pour deux boucles imbriquées. + +Du côté de la méthode à utiliser, nous allons faire en sorte que \verb|\for| passe à la macro \verb|\for@i| les arguments suivants : + +\begin{enumerate}[label={{\ttfamily\char`\#\arabic*} :}] + \item la macro récursive \verb-\-\boxtoken{for@ii@\string\<macro>}; + \item le signe de comparaison explicitement écrit "\verb|<|" ou "\verb|>|"; + \item l'incrément sous forme d'un entier explicitement écrit; + \item la \verb|\<macro>| qui est l'argument \verb|#1| de \verb|\for|; + \item l'entier $n_2$ qui est l'argument \verb|#3| de \verb|\for|. +\end{enumerate} + +Par exemple, si nous écrivons + +\centrecode-\for\xx=1to20\do4{X}- + +\noindent la macro \verb|\for@i| doit être appelée ainsi : + +\begin{centrage} + \small\verb-\for@i\-\boxtoken{for@ii@\string\xx}\verb-<{4}\xx{20}- +\end{centrage} + +Le fait que les arguments \verb|#2|, \verb|#3| et \verb|#5| soient transmis explicitement sous forme de tokens de catcode 12 évite de créer une macro pour les stocker et nous met donc à l'abri de toute redéfinition de cette macro par une boucle imbriquée. + +Prenons le parti de stocker ces 5 arguments dans une macro \verb|\macro@args| définie avec \verb|\edef|. Rappelons-nous que dans le texte de remplacement d'un \verb|\edef|, un \idx\noexpand est nécessaire pour bloquer le développement d'une séquence de contrôle : les arguments \verb|#1| et \verb|#4| sont ici concernés. Une fois la macro \verb|\macro@args| définie, l'appel à \verb|\for@i| se fera donc par + +\centrecode|\expandafter\for@i\macro@args| + +\showcode/\catcode`\@11 +\def\for#1=#2to#3\do#4#{% + \edef\for@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel + \ifnum\for@increment=\z@% s'il est nul,¤\idx*\z@¤ + \edef\for@increment{% le redéfinir à -1 (si #3<#2) et 1 sinon + \ifnum\numexpr#3-#2\relax<\z@ -1\else 1\fi¤\idx*\numexpr\idx*\z@¤ + }% \for@increment vaut donc 1 ou -1 + \fi + \ifnum\numexpr\for@increment*(#3-#2)\relax<\z@ + \expandafter\gobone% si argument optionnel incompatible, manger le {<code>}¤§*\gobone[|etc]\forbidindex\gobone¤ + \else + \edef#1{\number\numexpr#2}% initialise la \<macro> + \edef\macro@args{% définit et développe les arguments à passer à \for@i + %#1=nom de la macro récursive : + \expandafter\noexpand\csname for@ii@\string#1\endcsname¤\idx*\csname\idx*\endcsname\idx*\string¤ + \ifnum\for@increment<\z@ <\else >\fi% #2=signe de comparaison¤\idx*\z@¤ + {\for@increment}% #3=incrément + \noexpand#1% #4=\<macro>¤\idx*\noexpand¤ + {\number\numexpr#3\relax}% #5=entier n2¤\idx*\number\idx*\numexpr¤ + }% + \antefi% externalise la ligne ci-dessous de la portée du test¤§*\antefi¤ + \expandafter\for@i\macro@args% appelle \for@i avec les arguments définis ci-dessus + \fi +} + +% #1=nom de la macro récursive de type "\for@ii@\<macro>" +% #2=signe de comparaison % #3=incrément +% #4=\<macro> % #5=entier n2 % #6=<code> à exécuter +\long\def\for@i#1#2#3#4#5#6{%¤\idx*\long¤ + \def#1{% définit la sous macro récursive + \unless\ifnum#4#2#5\relax% tant que la \<macro> variable n'a pas dépassé n2¤\tidx*{unless}¤ + \afterfi{% rendre la récursivité terminale¤§*\afterfi\idx*{récursivité!terminale}¤ + #6% exécute le code¤\defline\bbb¤ + \edef#4{\number\numexpr#4+#3\relax}% incrémente la \<macro> + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive¤\defline\ccc¤ +}% +\catcode`\@=12 +\for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}\medbreak¤\idx*\medbreak¤ + +\for\carA= `\A to `\E \do{\for\carB= `w to `z \do{\char\carA\char\carB}\quad}¤\idx*[|etc]\quad\forbidindex\quad¤/ + +\begin{exercice} +En l'état, la boucle est parcourue autant de fois que l'imposent les bornes et l'incrément. Or, dans certains cas, une condition programmée par l'utilisateur devient vraie au cours des itérations et à partir de ce moment, exécuter les itérations suivantes devient indésirable. Comment programmer une macro §\exitfor qui, insérée dans le \verb|<code>| à exécuter, permette de sortir prématurément de la boucle ? +\solution +La macro §\exitfor est susceptible de se trouver dans le \verb|<code>| à la ligne \no\bbb. Cette macro doit donc annuler l'appel récursif de la ligne \no\ccc. Il y a plusieurs moyens de le faire, le plus simple est de charger §\exitfor de redéfinir la macro récursive \verb|#1| comme ayant un texte de remplacement vide : + +\showcode/\catcode`\@11 +\long\def\for@i#1#2#3#4#5#6{%¤\idx*\long¤ + \def\exitfor{\def#1{}}%¤§*\exitfor¤ + \def#1{% définit la sous macro récursive + \unless\ifnum#4#2#5\relax% tant que la variable n'a pas dépassé l'entier max¤\tidx*{unless}¤ + \afterfi{% rendre la récursivité terminale¤§*\afterfi\idx*{récursivité!terminale}¤ + #6% exécute le code¤\defline\bbb¤ + \edef#4{\number\numexpr#4+#3\relax}% incrémente la variable #1¤\idx*\number\idx*\numexpr¤ + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive +}% +\catcode`\@=12 +\for\ii= 1 to 9 \do{\string\ii{} vaut \ii.\ifnum\ii=5 \exitfor\fi\par}¤\idx*\string§*\exitfor¤/ + +Cela fonctionne correctement car il n'y a aucune boucle \verb|for| imbriquée. + +Dans le cas de boucles imbriquées, la macro §\exitfor va être définie plusieurs fois (une fois par imbrication). Par conséquent, la dernière définition, celle de la boucle la plus intérieure, écrase les autres. On peut donc sortir de la boucle la plus intérieure, mais plus des boucles de niveau supérieur. + +Le remède le plus simple est de donner comme argument à §\exitfor la \verb|\<macro>| correspondant à la boucle de laquelle on souhaite sortir. Ainsi, §\exitfor\verb|#1| redéfinirait la macro \verb-\-\boxtoken{for@ii@\string#1} comme ayant un texte de remplacement vide. Pour être complets sur le sujet, nous créerons aussi la macro §\ifexitfor de syntaxe + +\centrecode-\ifexitfor\<macro>{<code vrai>}{<code faux>}- + +\noindent Ainsi, §\ifexitfor\verb|\<macro>| permet de savoir, en dehors de la boucle de variable \verb|\<macro>|, si on en est prématurément sorti ou pas. Définir §\ifexitfor fait appel au test \verb|\ifx| et à la macro \idx\empty et devient donc, en anticipant un peu, une bonne transition vers le chapitre suivant qui aborde d'autres tests que propose \TeX. + +Le code ci-dessous permet de voir plusieurs façons, plus ou moins rapides, de sortir de 2 boucles imbriquées : + +\showcode/\def\exitfor#1{% #1=\<macro> correspondant à la boucle de laquelle on veut sortir¤§*\exitfor¤ + \defname{for@ii@\string#1}{}¤\idx*\csname\idx*\endcsname\idx*\string§*\defname¤ +} + +\def\ifexitfor#1{% envoie vrai si on est prématurément sorti de la boucle de \<macro> #1¤§*\ifexitfor¤ + % si la macro récursive est égale à la macro "\empty" + \expandafter\ifx\csname for@ii@\string#1\endcsname\empty¤\idx*\string\idx*\empty¤ + \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai"¤§*\firstoftwo¤ + \else + \expandafter\secondoftwo% sinon, renvoyer "faux"¤§*\secondoftwo¤ + \fi +} + +% on ne sort QUE de la boucle intérieure quand \ii=3 donc +% les boucles sont normalement exécutées pour \ii=4 et \ii=5 +a. \for\ii=1 to 5\do1{% + \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\jj\fi}%¤§*\exitfor¤ + \qquad + } + +% on sort de la boucle extérieure lorsque \ii=3 donc la boucle +% intérieure est faite entièrement même lorsque \ii=3 +b. \for\ii=1 to 5\do1{% + \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\ii\fi}%¤§*\exitfor¤ + \qquad + } + +% on sort de la boucle intérieure lorsque \ii=3 +% et aussi de la boucle extérieure à l'aide de \ifexitfor +c. \for\ii=1 to 5\do1{% + \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\jj\fi}%¤§*\exitfor¤ + \ifexitfor\jj{\exitfor\ii}{}% si on est sorti de \jj, sortir aussi de \ii¤§*\exitfor§*\ifexitfor¤ + \qquad + }/ +\end{exercice}§*\for[|)]{} + +\chapter[\Qu elques autres tests]{Quelques autres tests} +Après avoir touché du doigt la syntaxe générale des tests et leurs particularités concernant la façon dont ils se développent, après avoir examiné plus particulièrement le test \tidx{ifnum}, nous allons dans ce chapitre découvrir quelques autres tests de \TeX{}. Le but n'est pas de les passer tous en revue, mais d'examiner ceux qui sont le plus utiles et les utiliser pour en créer d'autres. + +\section{Le test \texttt{\textbackslash{}ifx}}\tidx*[|(]{ifx} +\subsection{\Qu e teste \texttt{\char`\\ifx} ?} +Un des tests les plus utilisés est sans doute le test \tidx{ifx}. + +\begin{regle} +Le test \tidx{ifx} compare les significations des deux \emph{tokens} qui le suivent. Il a la syntaxe suivante : + +\centrecode-\ifx<token1><token2> + <code vrai> +\else + <code faux> +\fi- + +Il est vrai si la signification des deux tokens est la même. Parmi toutes les possibilités qui se présentent selon la nature des deux tokens, les trois cas suivants sont les plus courants : + +\begin{enumerate} + \item si les deux tokens sont des caractères, alors le test est positif si les caractères sont identiques, c'est-à-dire si leur \idx{code de caractère} \emph{et} leur catcode sont égaux; + \item si les deux tokens sont des \idx{primitive}s, le test est positif si les primitives sont les mêmes; + \item si les deux tokens sont des macros ou des caractères actifs\idx*{caractère actif}, le test \verb-\ifx- compare leurs textes de remplacement : le test est positif si les textes de remplacement sont identiques, c'est-à-dire constitués des mêmes tokens avec les mêmes catcodes. La comparaison tient également compte des propriétés de ces macros, notamment si elles sont \idx\long ou \idx\outer. + + Deux macros non définies sont égales pour \verb-\ifx-, quels que soient leurs noms. +\end{enumerate} +\end{regle} + +Contrairement à \tidx{ifnum}, le test \tidx{ifx} ne déclenche pas un développement maximal et donc, les deux tokens sont comparés tels quels. + +Si deux tokens appartiennent à deux catégories différentes parmi les trois listées dans la règle ci-dessus, le test sera faux. Par exemple, si une macro \verb-\foo- a le texte de remplacement «\verb|a|», alors le test \verb-\ifx a\foo- est faux : + +\showcode/\def\foo{a}\ifx a\foo vrai\else faux\fi/ + +En effet, le premier token «\verb|a|» est un caractère, une sorte de primitive pour \TeX{} alors que le second \verb-\foo- est une macro. Notons que la primitive \idx\meaning donne une bonne idée de ce qu'est la \emph{signification} d'un token, même si elle ne dit rien sur les catcodes des tokens composant le texte de remplacement d'une macro (voir les cas \nos6 et 7 ci-dessous) : + +\showcode/1) \meaning9\par% un caractère de catcode 12¤\idx*\meaning\idx*{catcode!12\space(autre)}¤ +2) \meaning a\par% une lettre +3) \def\foo{a}\meaning\foo\par% une macro +4) \long\def\foo{a}\meaning\foo\par% une macro déclarée \long¤\idx*\long¤ +5) \meaning\sdlkfj\par% une macro indéfinie +6) \edef\foo{\string a}\meaning\foo\par%\foo contient un "a" de catcode 12¤\idx*\string¤ +7) \def\foo{a}\meaning\foo\par%\foo contient un "a" de catcode 11/ + +Pour fixer les idées, effectuons quelques autres tests qui montrent comment réagit \verb|\ifx| : + +\showcode/a) \ifx abvrai\else faux\fi\quad% a est n'est pas égal à b +b) \ifx++vrai\else faux\fi\quad% le caractère "+" est égal à "+" +c) \ifx\relax\par vrai\else faux\fi\quad% \relax n'est pas la même primitive que \par +d) \ifx\par\par vrai\else faux\fi\quad% \par et \par sont les mêmes primitives +e) \ifx\sdfk\qlms vrai\else faux\fi\quad% 2 macros non définies sont égales +f) \def\foo{abcd}\def\bar{abc}% \foo et \bar ont des textes de remplacement différents + \ifx\foo\bar vrai\else faux\fi\quad +g) \def\foo{abcd}\def\bar{abcd }% \foo et \bar ont des textes de remplacement différents + \ifx\foo\bar vrai\else faux\fi\quad +h) \def\foo{abcd}\def\bar{abcd} + \ifx\foo\bar vrai\else faux\fi\quad% \foo et \bar ont les mêmes textes de remplacement +i) \long\def\foo{a}\def\bar{a}¤\idx*[|etc]\long\forbidindex\long¤ + \ifx\foo\bar vrai\else faux\fi\quad% \foo est \long, \bar ne l'est pas +j) \edef\foo{\string a}% \foo contient un "a" de catcode 12¤\idx*{catcode!11\space(lettre)}\idx*{catcode!12\space(autre)}\idx*\string¤ + \def\bar{a}% \bar contient un "a" de catcode 11 + \ifx\foo\bar vrai\else faux\fi/ + +\begin{exercice} +Il a été dit que le test \verb-\ifx- était sensible aux codes de catégorie. Écrire une macro§*\cmpmacro + +\centrecode-\cmpmacro<\macroA><\macroB>{<code vrai>}{<code faux>}- + +\noindent qui comparera les macros contenues dans ses deux premiers arguments sans tenir compte des codes de catégorie des tokens qui composent leurs textes de remplacement. +\solution +L'idée et de détokéniser le texte de remplacement des deux macros, d'assigner ces deux textes détokénisés à des macros auxiliaires et enfin, de comparer ces deux macros avec \verb-\ifx-. Pour ne pas laisser ces deux macros auxiliaires derrière nous, créons-les dans un groupe semi-simple que nous refermerons après la comparaison. Remarquons à cette occasion que la paire \idx\begingroup et \idx\endgroup peut être « imbriquée » avec les \verb-\ifx-, \tidx{else} et \tidx{fi} d'un test. + +\showcode/\def\cmpmacro#1#2{%¤§*\cmpmacro¤ + \begingroup¤\idx*\begingroup¤ + \edef\tempa{\detokenize\expandafter{#1}}\edef\tempb{\detokenize\expandafter{#2}}%¤\idx*\detokenize¤ + \ifx\tempa\tempb% si égalité + \endgroup\expandafter\firstoftwo% ferme le groupe et lit 1er argument¤\idx*\endgroup§*\firstoftwo¤ + \else + \endgroup\expandafter\secondoftwo% sinon, ferme le groupe et lit le 2e argument¤§*\secondoftwo¤ + \fi +} +a) \edef\foo{\string a}\def\bar{a}¤\idx*\string¤ + \cmpmacro\foo\bar{vrai}{faux}\qquad +b) \edef\foo{\detokenize{$i^2=-1$\relax}}\def\bar{$i^2=-1$\relax} + \cmpmacro\foo\bar{vrai}{faux}¤§*\cmpmacro¤/ +\end{exercice} + +\subsection{Le test \texttt{\char`\\ifx}, la \texttt{\char`\\let}-égalité et les quarks} +Voici une règle particulière qu'il est utile de connaitre : + +\begin{regle} +Le test \tidx{ifx} ne distingue pas deux tokens \idx\let-égaux. +\end{regle} + +Autrement dit, si un token de type \verb|\<macro>| est rendu égal au token $x$ avec \idx\let, alors, \verb-\ifx- verra \verb|\<macro>| et $x$ égaux. + +Nous pouvons l'observer ci-dessous où des tokens, à chaque fois \idx\let-égaux, rendent tous les tests vrais : + +\showcode/a) \let\rien\relax \ifx\rien\relax vrai\else faux\fi\qquad +b) \let\AA=a \ifx a\AA vrai\else faux\fi\qquad +c) \let\foo=_\let\bar=_ \ifx\foo\bar vrai\else faux\fi/ + +Pour exploiter la règle précédente, il est parfois commode de créer des macros spéciales dont le développement est elles-mêmes, autrement dit \emph{invariantes} par développement ! Elles portent le nom de «\idx[|(]{quark}s». Voici comment nous pourrions définir un quark nommé §\quark : + +\centrecode-\def\quark{\quark}- + +\noindent Bien évidemment, il ne faut jamais qu'un quark ne soit exécuté puisqu'en se développant, il ne change pas : cette propriété mènerait à une \idx{boucle infinie} dont on ne peut sortir que par interruption manuelle. À vrai dire, les quarks ne sont utiles que pour les comparaisons via \tidx{ifx}. Essayons de comprendre pourquoi en prenant par exemple une macro \verb-\test- définie comme suit : + +\centrecode-\def\test{\quark}- + +\noindent Dans ce cas, le test «\verb|\ifx\test\quark|» est vrai parce que §\quark est un quark. Il serait faux s'il ne l'était pas. En effet, \tidx{ifx} compare le texte de replacement de \verb|\test| et celui de §\quark : ce texte de remplacement est bien §\quark dans les deux cas. + +Le test est également positif si \verb|\test| est défini par + +\centrecode-\let\test=\quark- + +\noindent puisque dans ce cas, deux macros \idx\let-égales sont comparées. + +\showcode/\def\quark{\quark}% définit un quark¤§*\quark¤ +1) \def\test{\quark} \ifx\quark\test vrai\else faux\fi +\qquad +2) \let\test=\quark \ifx\quark\test vrai\else faux\fi¤§*\quark¤/ + +Le fait que \tidx{ifx} donne un test vrai aussi bien pour une macro définie par \idx\def ou par \idx\let se révèle bien pratique et constitue une particularité des quarks. + +\begin{regle}[Définition et propriétés] +Un quark est une macro dont le texte de remplacement est constitué de la macro elle-même. + +Un quark ne doit \emph{jamais} être exécuté, car étant invariant par développement, son traitement par \TeX{} engendrerait une \idx{boucle infinie}.\medbreak + +Si \verb|\<quark>| est un quark et si \verb|\<macro>| est définie par + +\begin{centrage} +\small\verb|\def\<macro>{\<quark>}|\quad {\normalsize ou par}\quad\verb|\let\<macro> = \<quark>| +\end{centrage} + +\noindent alors, le test \verb|\ifx\<macro>\<quark>| sera vrai dans les deux cas. +\end{regle}\idx*[|)]{quark} + +\section{Applications du test \texttt{\textbackslash ifx}} +\subsection{Tester si un argument est vide}\idx*[|(]{argument!vide} +Il arrive souvent qu'il faille tester si un argument est vide (c'est-à-dire constitué de 0 token). La méthode naïve, mais aussi la plus efficace est la suivante : on assigne avec \verb|\def| cet argument à une \verb|\<macro>| temporaire. Ensuite, on compare avec \verb-\ifx- cette \verb|\<macro>| et la macro nommée \idx\empty, définie dans plain-\TeX{}\footnote{La macro équivalente \texttt{\string\@empty} est définie dans le format \LaTeX.}, dont le texte de remplacement est vide:§*\ifempty[|(] + +\centrecode-\def\empty{}- + +Cela donne le code suivant : + +\showcode/\def\ifempty#1{%¤§*\ifempty¤ + \def\tempa{#1}% stocke l'argument #1 dans \tempa¤\defline\aaa¤ + \ifx\tempa\empty% si \tempa = \empty + \expandafter\firstoftwo% 1er argument¤§*\firstoftwo¤ + \else + \expandafter\secondoftwo% sinon, second¤§*\secondoftwo¤ + \fi +} +a) \ifempty{abc}{vide}{pas vide}\qquad +b) \ifempty{}{vide}{pas vide}\qquad +c) \ifempty{ }{vide}{pas vide}\qquad +d) \ifempty{\empty}{vide}{pas vide}¤\defline\bbb¤/ + +\begin{exercice}\label{argument.vide} +Expliquer pourquoi le test est négatif à la ligne \no\bbb{} du code ci-dessous +\solution +La ligne \no\aaa{} effectue l'action suivante : + +\centrecode-\def\tempa{\empty}- + +Le test \verb|\ifx\tempa\empty| compare donc les deux textes de remplacement de ces macros qui sont \idx\empty pour la première et un texte vide pour la seconde. Ces deux textes de remplacement ne sont pas égaux ce qui explique que le test est négatif. +\end{exercice} + +La macro §\ifempty fonctionne parfaitement, mais elle n'est \emph{pas} purement développable à cause de la présence du \idx\def dans son texte de remplacement. Pour rendre la macro développable, l'astuce consiste à utiliser le fait que lorsqu'un argument \verb|#1| d'une macro est vide, tout se passe comme s'il n'existait pas dans son texte de remplacement. Dans ce cas, les tokens qui sont juste avant et juste après \verb|#1| deviennent consécutifs. Il suffit que ces deux tokens soient les mêmes (par exemple «\verb|_|») pour que le test «\verb|\ifx_#1_|» devienne positif lorsque \verb|#1| est vide : + +\showcode/\def\empty{} +\long\def\ifempty#1{%¤§*\firstoftwo§*\secondoftwo§*\ifempty¤ + \ifx_#1_\expandafter\firstoftwo + \else\expandafter\secondoftwo + \fi} +a) \ifempty{abc}{vide}{pas vide}\qquad +b) \ifempty{}{vide}{pas vide}\qquad +c) \ifempty{ }{vide}{pas vide}\qquad +d) \ifempty{\empty}{vide}{pas vide}\qquad +e) \edef\foo{\ifempty{abc}{vide}{pas vide}}\meaning\foo/ + +Bien évidemment, au lieu de «\verb-_-», nous aurions aussi pu utiliser «\verb-|-», \idx\empty ou tout autre token. La macro fonctionne bien, mais il va se trouver des cas particuliers où elle va donner des faux positifs ! En effet, le test \verb-\ifx<token>#1<token>- est vrai si \verb|#1| est vide, mais il sera \emph{aussi} vrai si \verb-#1- commence par le \verb|<token>|. Nous pouvons l'observer ici où «\verb|W|» est pris comme token de comparaison qui, contrairement à «\verb|_|» peut être \emph{affiché} par \TeX{}. Le cas \no4 met en évidence le problème : + +\showcode/\long\def\ifempty#1{%¤§*\firstoftwo§*\secondoftwo§*\ifempty¤ + \ifx W#1W\expandafter\firstoftwo + \else \expandafter\secondoftwo + \fi} +1) \ifempty{foobar}{vide}{pas vide}\qquad +2) \ifempty{}{vide}{pas vide}\qquad +2) \ifempty{\empty}{vide}{pas vide}\qquad +4) \ifempty{Wagons}{vide}{pas vide}/ + +On peut observer que le test \verb|\ifx| a comparé le «\verb|W|» qui le suit et le «\verb|W|» de « \verb|Wagons|». Comme il est vrai, tout ce qui est jusqu'au \verb|\else| a été affiché. + +Pour se prémunir de cet inconvénient, la méthode la plus communément utilisée consiste à \emph{détokéniser} l'argument \verb|#1| et l'entourer de deux tokens qui ne sont pas de catcode 12 (\idx\relax est habituellement utilisé dans ce cas). Pour que cela fonctionne, «\verb|\detokenize{#1}|» doit être développé avant que le test n'entre en jeu et nécessite donc un pont d'\idx\expandafter : + +\centrecode-\expandafter\ifx\expandafter\relax\detokenize{#1}\relax- + +Avec cette méthode, le test n'est positif \emph{que} lorsque \verb|#1| est vide. En effet, + +\begin{enumerate} + \item si \verb|#1| commence par un espace, \verb|\detokenize{#1}| se développe en un espace (de catcode 10) suivi d'autres tokens. Le test se fera donc entre \idx\relax et \verb*| | et sera faux; + \item si \verb|#1| commence par un autre token, le développement de \idx\detokenize\verb|{#1}| commence par un caractère de catcode 12. La comparaison entre \idx\relax et ce token est fausse ; + \item enfin, si \verb|#1| est vide, \verb|\detokenize{}| se développe en 0 token et donc, les primitives \idx\relax deviennent consécutives et sont vues égales par le test \tidx{ifx} qui est alors positif. +\end{enumerate} + +\showcode/\long\def\ifempty#1{%¤§*\firstoftwo§*\secondoftwo§*\ifempty¤ + \expandafter\ifx\expandafter\relax\detokenize{#1}\relax¤\idx*\detokenize¤ + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi +} +a) \ifempty{abc}{vide}{pas vide}\qquad +b) \ifempty{}{vide}{pas vide}\qquad +c) \ifempty{ }{vide}{pas vide}\qquad +d) \ifempty{\relax}{vide}{pas vide}\qquad +e) \edef\foo{\ifempty{abc}{vide}{pas vide}}\meaning\foo/§*\ifempty[|)]% + +Utiliser \idx\detokenize est la seule méthode développable qui soit vraiment infaillible, mais elle a le désavantage d'être un peu lourde à écrire, c'est pourquoi on lui préfère souvent + +\centrecode-\ifx\empty#1\empty- + +\noindent tout en restant conscient des faux positifs qu'elle peut engendrer dans les cas --~le plus souvent improbables~-- où \verb-#1- commence par la macro \idx\empty ou toute autre macro \idx\let-égale à \idx\empty. +\grandsaut + +\begin {exercice} +\label{reverse}Programmer une macro §\reverse, admettant comme argument un \verb|<mot>| et dont le développement maximal est le \verb|<mot>| écrit dans l'ordre inverse. + +Par exemple, §\reverse\verb|{foobar}| donne «\verb|raboof|». +\solution +Pour y parvenir, il faut imaginer deux réservoirs, A et B recevant des caractères. L'algorithme est le suivant : +\begin{algo} + \item mettre le \verb|<mot>| dans le réservoir A et initialiser le réservoir B à \verb|<vide>| ; + \item prendre le premier caractère du réservoir A et le mettre en \emph{première} position dans le réservoir B ; + \item si le réservoir A n'est pas vide, retourner au point 2. +\end{algo} + +Avec des arguments délimités, il est très facile de construire de tels réservoirs tout en gardant la macro purement développable. Décidons pour l'instant que ces délimiteurs sont des points. La structure des arguments sera donc : + +\centrecode-<réservoir A>.<réservoir B>.- + +\noindent Voici ce que sont les étapes pour inverser le texte « abcd » : +\begin{center} + \verb|abcd..|${}\longrightarrow{}$\verb|bcd.a.|${}\longrightarrow{}$\verb|cd.ba.|${}\longrightarrow{}$\verb|d.cba.|${}\longrightarrow{}$\verb|.dcba.| +\end{center} + +Le choix du point interdit que le \verb|<mot>| en contienne, ce qui n'est pas absurde en soi. Cependant, pour plus de sécurité, nous prendrons \verb|\@nil| comme délimiteur. Le code de la macro §\reverse ne présente aucune difficulté particulière : + +\showcode/\catcode`\@11 +\def\reverse#1{%¤§*\reverse¤ + \ifempty{#1} + {}% s'il n'y a rien à inverse, ne rien faire + {\reverse@i#1\@nil\@nil}% initialisation des réservoirs et appeler \reverse@i +} +\def\reverse@i#1#2\@nil#3\@nil{% #1 est le 1er caractère du réservoir A + \ifempty{#2}% si ce qui reste après ce 1er caractère est vide¤§*\ifempty¤ + {#1#3}% renvoyer #1#3 qui est le résultat final + {\reverse@i#2\@nil#1#3\@nil}% sinon, recommencer en déplaçant #1 + % en 1re position du réservoir B +} +\catcode`\@12 +a) \reverse{foobar}\qquad +b) \edef\foo{\reverse{Bonjour}}\meaning\foo\qquad +c) \reverse{Bonjour le monde}\qquad +d) \reverse{Bonjour{ }le{ }monde}¤§*\reverse¤/ + +Selon la règle de la page~\pageref{argument.espace}, les espaces qui ne sont pas entre accolades sont ignorés lorsque lus comme argument par une macro. Il est donc normal que « Bonjour le monde » donne un résultat inversé sans espace. +\end {exercice} + +Le simple fait pour pouvoir tester de façon fiable si un argument est vide ouvre déjà la voie à d'autres tests\ldots{}\idx*[|)]{argument!vide} + +\subsection{Tester si un code contient un motif}\label{test.code.contient.motif} +Le but est d'élaborer un test qui vérifie si un \verb-<code>- contient un autre code que l'on appelle \verb-<motif>-. Nous allons tenter d'écrire une macro §\ifin[|(] dont voici la syntaxe :\label{ifin} + +\centrecode-\ifin{<code>}{<motif>}{<vrai>}{<faux>}- + +\noindent Le code \verb-<vrai>- sera exécuté si \verb-<code>- contient \verb-<motif>- et le code \verb|<faux>| le sera dans le cas contraire. + +Comme nous l'avions fait avec les macros §\rightof et §\leftof du chapitre \ref{rightof} et \ref{leftof} de la partie 2, nous allons utiliser une macro auxiliaire à arguments délimités. Cette macro \verb-\ifin@i- sera située à l'intérieur du texte de remplacement de §\ifin de telle sorte qu'elle puisse accéder aux arguments de §\ifin. Elle aura le \idx{texte de paramètre} suivant : + +\centrecode-\def\ifin@i##1#2##2\@nil{code à définir}- + +\noindent Ainsi, les arguments délimités \verb|##1| et \verb|##2| collectent ce qui se trouve respectivement avant et après le \verb|<motif>| qui est l'argument \verb|#2| de la macro chapeau. + +L'idée est que la macro §\ifin appelle la macro \verb-\ifin@i- de cette façon : + +\centrecode-\ifin@i#1#2\@nil- + +\noindent où le \verb|<motif>| (argument \verb-#2-) est ajouté après le \verb|<code>| (argument \verb|#1|) afin de satisfaire la macro à argument délimité \verb|\ifin@i| même dans le cas où \verb|#1| ne contient pas \verb|#2|. Avec cet appel, de deux choses l'une : + +\begin{itemize} + \item soit \verb-#1- contient déjà \verb-#2- et donc la première occurrence de \verb-#2- est dans \verb-#1- et nous pouvons donc affirmer que ce qui se trouve après cette première occurrence (qui est l'argument \verb|##2| de \verb|\ifin@i|) n'est pas vide puisqu'au moins constitué de \verb-#2- que nous avons rajouté; + \item soit \verb-#1- ne contient pas \verb-#2-, ce qui fait que la première occurrence de \verb-#2- est le \verb-#2- que nous avons ajouté juste devant le \verb|\@nil|. Par conséquent, ce qui se trouve après cette occurrence est vide. +\end{itemize} + +Il suffit donc de tester si ce qui est après la première occurrence de \verb-#2- est vide ou pas, ce que fait la ligne \no4 du code ci-dessous : + +\showcode/\catcode`\@11 +\def\ifin#1#2{%¤§*\ifin¤ + \def\ifin@i##1#2##2\@nil{% définit la macro auxiliaire + \ifempty{##2}% si ce qu'il y a derrière le motif est vide¤§*\ifempty¤ + \secondoftwo% aller à "faux"¤§*\secondoftwo¤ + \firstoftwo% sinon à "vrai"¤§*\firstoftwo¤ + }% + \ifin@i#1#2\@nil% appel de la macro auxiliaire +} +\catcode`\@12 +a) \ifin{abc\relax1}{bc}{vrai}{faux}\qquad +b) \ifin{abc \relax1}{c\relax}{vrai}{faux}\qquad +c) \ifin{abc \relax1}{ }{vrai}{faux}\qquad +d) \ifin{abc \relax1}{}{vrai}{faux}\qquad +e) \ifin{}{a}{vrai}{faux}/ + +Le gros inconvénient des arguments délimités est le défaut que nous avions signalé avec la macro §\rightof à la page~\pageref{rightof.et.accolades}. Le \verb|<motif>| ne peut pas contenir d'accolades ouvrantes puisqu'elles se retrouveraient dans le texte de paramètre de \verb|\ifin@i| et en fausseraient la définition. Nous allons donc nous limiter à un code et un motif sans accolades\ldots{} Avant de trouver mieux !§*\ifin[|)] + +\begin{exercice}\label{ifstart} +Écrire une macro §\ifstart[|(] de syntaxe + +\centrecode-\ifstart{<code>}{<motif>}{<vrai>}{<faux>}- + +\noindent qui teste si le \verb|<code>| commence par le \verb|<motif>| et exécute \verb|<vrai>| si c'est le cas et \verb|<faux>| sinon. +\solution +La méthode est très similaire à celle déployée dans §\ifin. Dans l'homologue de \verb-\ifin@i- qui est \verb|\ifstart@i|, il suffira de tester si ce qui est \emph{avant} la première occurrence du motif (c'est-à-dire \verb-##1-) est vide ou pas. Ce qui donne ce code : + +\showcode/\catcode`\@11 +\def\ifstart#1#2{% + \def\ifstart@i##1#2##2\@nil{\ifempty{##1}}%¤§*\ifempty¤ + \ifstart@i#1#2\@nil +} +\catcode`\@12 +a) \ifstart{abc}{bc}{vrai}{faux}\qquad +b) \ifstart{abc}{ab}{vrai}{faux}\qquad +c) \ifstart{ abc}{ }{vrai}{faux}\qquad +d) \ifstart{abc}{}{vrai}{faux}\qquad +e) \ifstart{}{a}{vrai}{faux}/ + +Les deux derniers cas sont problématiques. Il serait logique que l'avant-dernier soit vrai puisque «\verb|abc|» commence bien par un argument vide. Pour le dernier au contraire, il faudrait qu'il soit faux, car un argument vide ne contient pas le motif «\verb|a|». + +Pour faire en sorte qu'un \verb|<motif>| vide donne toujours le code \verb|<vrai>|, il suffit à la ligne \no4 de tester si \verb|#2| est vide et exécuter §\firstoftwo si c'est le cas et appeler \verb-\ifstart@i- sinon : + +\centrecode-\ifempty{#2}\firstoftwo{\ifstart@i#1#2\@nil}- + +Si le \verb|<code>| est vide, l'appel à \verb-\ifstart@i- est \verb-\ifstart@i#2\@nil- et donc, l'argument délimité \verb-##1- de \verb-\ifstart@i- est logiquement vide. On peut artificiellement le remplir en mettant un \idx\relax juste avant le \verb-#2- de la ligne 4. Cela donne le code : + +\showcode/\catcode`\@11 +\def\ifstart#1#2{% + \def\ifstart@i##1#2##2\@nil{\ifempty{##1}}%¤§*\ifempty¤ + \ifempty{#2}% si motif vide¤§*\ifempty¤ + \firstoftwo% exécuter code "vrai"¤§*\firstoftwo¤ + {\ifstart@i#1\relax#2\@nil}% sinon, aller à la macro auxiliaire +} +\catcode`\@12 +a) \ifstart{abc}{bc}{vrai}{faux}\qquad +b) \ifstart{abc}{ab}{vrai}{faux}\qquad +c) \ifstart{ abc}{ }{vrai}{faux}\qquad +d) \ifstart{abc}{}{vrai}{faux}\qquad +e) \ifstart{}{a}{vrai}{faux}/ +§*\ifstart[|)]\end{exercice} + +\subsection{Tester si un code se termine par un motif} +Il est bien plus difficile de tester si un \verb|<code>| se termine par un \verb|<motif>|. Cela est dû à la façon qu'ont de fonctionner les arguments délimités qui ne s'intéressent qu'à la \emph{première} occurrence d'un délimiteur et non pas à la dernière. Si nous souhaitons écrire une macro §\ifend[|(] dont la syntaxe est + +\centrecode-\ifend{<code>}{<motif>}{<vrai>}{<faux>}- + +\noindent nous allons devoir recourir à un algorithme récursif. + +\begin{algo} + \item ajouter le \verb|<motif>| au début du \verb|<code>| et dans le résultat obtenu, + \item appeler \verb|<code>| ce qui est après la première occurrence du \verb|<motif>|; + \item si \verb|<code>| est vide exécuter \verb|vrai|; + \item sinon + \begin{algo} + \item si \verb|<code>| contient le \verb|<motif>|, retourner en 2; + \item sinon, exécuter \verb|faux|. + \end{algo} +\end{algo} + +Le point \no1 sera l'appel initial à une macro auxiliaire récursive \verb|\ifend@i| à argument délimité. Cette macro occupera les points suivants (de 2 à 4). + +Traduisons cet algorithme en code \TeX{} et vérifions qu'il fonctionne sur quelques exemples. Le point 1 sera codé de cette façon : + +\centrecode-\ifend@i#2#1\@nil- + +La macro auxiliaire \verb-\ifend@i- va se charger, grâce à son argument délimité, d'isoler ce qui se trouve après la première occurrence de \verb|<motif>|. Voici ce que sera son \idx{texte de paramètre} : + +\centrecode-\def\if@end##1#2##2\@nil{...}- + +\noindent À l'intérieur de la macro \verb|\if@end|, il suffira de tester si \verb-##2- est vide auquel cas, §\firstoftwo sera exécuté. Dans le cas contraire, il faudra tester avec §\ifin si le \verb|##2| contient le \verb|<motif>| qui est \verb|#2| et appeler récursivement \verb|\ifend@i| dans l'affirmative et exécuter §\secondoftwo sinon : + +\showcode/\catcode`\@11 +\def\ifend#1#2{% + \def\ifend@i##1#2##2\@nil{% ##2 = ce qui reste après le motif + \ifempty{##2}% si ##2 est vide¤§*\ifempty¤ + \firstoftwo% exécuter l'argument "vrai"¤\defline\bbb§*\firstoftwo¤ + {\ifin{##2}{#2}% sinon, si ##2 contient le <motif>¤§*\ifin¤ + {\ifend@i##2\@nil}% appeler \ifend@i avec ce qui reste + \secondoftwo% sinon, exécuter l'argument "faux"¤§*\secondoftwo¤ + }% + }% + \ifend@i#2#1\@nil% appelle la macro récursive¤\defline\aaa¤ +} +\catcode`\@12 +1) \ifend{abc de}{de}{vrai}{faux}\qquad +2) \ifend{abd de }{de}{vrai}{faux}\qquad +3) \ifend{abc de }{ }{vrai}{faux}\qquad +4) \ifend{}{a}{vrai}{faux}\qquad +5) \ifend{abc de}{}{vrai}{faux}/ + +\begin{exercice} +Expliquer pourquoi le code ci-dessus renvoie le contraire de ce que l'on attend aux cas \nos4 et 5 et proposer des solutions pour y remédier. +\solution +Dans le cas où le \verb|<code>| est vide (cas \no4), l'appel de la ligne \no\aaa{} devient + +\centrecode-\ifend@i a\@nil- + +\noindent et l'argument \verb|##2| de \verb|\ifend@i| qui se trouve après la première occurrence de «\verb|a|» est vide. Pour s'en prémunir, nous pouvons artificiellement meubler avec \idx\relax ce qui se trouve après cette première occurrence. L'appel à la ligne \no\aaa{} deviendrait donc : + +\centrecode-\ifend@i#2\relax#1\@nil- + +Pour le cas \no5, le \verb|<motif>| est vide, l'appel de la ligne \no\aaa{} est + +\centrecode-\ifend@i abc de\@nil- + +\noindent et le texte de paramètre de \verb|\ifend@i| est «\verb-\def\ifend@i##1##2\@nil{...}-» ce qui signifie que l'argument \verb|##1| n'est plus un argument délimité : il devient le premier argument (ici «\verb|a|») et \verb|##2| est le reste jusqu'au \verb|\@nil| (c'est-à-dire «\verb|bc de|»). Continuons à faire fonctionner la macro mentalement : le test §\ifempty\verb|{##2}| est faux, le test suivant §\ifin\verb|{##2}{#2}| est faux aussi puisque \verb|##2| est vide : la macro §\secondoftwo va donc exécuter le code \verb|faux|. + +Le meilleur remède est sans doute de tester dès le premier appel (ligne \no\aaa) si \verb|#2| est vide, cas où §\firstoftwo doit être exécuté. + +\showcode/\catcode`\@11 +\def\ifend#1#2{% + \def\ifend@i##1#2##2\@nil{% ##2 = ce qui reste après le motif + \ifempty{##2}% si ##2 est vide¤§*\ifempty¤ + \firstoftwo% exécuter l'argument "vrai"¤§*\firstoftwo¤ + {\ifin{##2}{#2}% sinon, si ##2 contient le <motif>¤§*\ifin¤ + {\ifend@i##2\@nil}% appeler \ifend@i avec ce qui reste + \secondoftwo% sinon, exécuter l'argument "faux"¤§*\secondoftwo¤ + }% + }% + \ifempty{#2}% si le motif est vide¤§*\ifempty¤ + \firstoftwo% exécuter "vrai"¤§*\firstoftwo¤ + {\ifend@i#2\relax#1\@nil}% sinon, appelle la macro récursive +} +\catcode`\@12 +1) \ifend{abc de}{de}{vrai}{faux}\qquad +2) \ifend{abd de }{de}{vrai}{faux}\qquad +3) \ifend{abc de }{ }{vrai}{faux}\qquad +4) \ifend{}{a}{vrai}{faux}\qquad +5) \ifend{abc de}{}{vrai}{faux}/ +§*\ifend[|)]\end{exercice} + +\subsection{Remplacer un motif par un autre}\substin +Nous allons maintenant construire une macro §\substin[|(], capable de remplacer un \verb|<motif1>| par un \verb|<motif2>| dans un \verb|<code>| : + +\centrecode-\substin{<code>}{<motif1>}{<motif2>}- + +La méthode va consister à parcourir le \verb|<code>| un peu de la même façon que nous le faisions avec la macro §\ifend. Voici l'algorithme : + +\begin{algo} + \item si le \verb|<code>| est vide, ne rien faire et fin du processus; + \item sinon + \begin{algo} + \item si le \verb|<code>| contient le \verb|<motif1>|, aller au point \no3; + \item sinon afficher le \verb|<code>| et fin du processus; + \end{algo} + \item afficher la partie du \verb|<code>| qui se trouve avant la 1\iere{} occurrence de \verb|<motif1>|; + \item afficher \verb|<motif2>|; + \item retourner au point \no1 en appelant \verb|<code>| ce qui se trouve après \verb|<motif1>|. +\end{algo} + +Les points \nos1 et 2 vont être effectués par une macro auxiliaire dont le seul argument non délimité sera le +\verb|<code>| en cours d'examen. Les points \nos3 à 5 seront dévolus à une autre macro auxiliaire qui elle, sera à argument délimité puisqu'il faut séparer ce qui se trouve avant et après le \verb|<motif1>|. Voici ce que cela donne en \idx{pseudocode} : + +\begingroup +\numalgofalse +\def\algoleftskip{.1\hsize} +\def\algorightskip{.1\hsize}\label{algo2} +\algorithm[\#]{Remplacer toutes les occurrences d'un motif}| +macro ~substin~#1#2#3% #1=<code>, #2=<motif1>, #3=<motif2> + macro ~substin@i~##1 + ~si~ ##1 est vide + relax% ne rien faire + ~sinon~ + ~si~ ##1 contient #3 + ~substin@ii~##1@nil% appeler la macro à argument délimités + ~sinon~ + ##1% afficher le <code> + ~finsi~ + ~finsi~ + ~fin~\medbreak + macro ~substin@ii~##1#2##2@nil% ##1 et ##2=ce qui est avant/après <motif1> + afficher "##1#3" + appeler ~substin@i~(##2)% puis recommencer avec le code qui reste + ~fin~\medbreak + appeler ~substin@i~(#1)% engage le processus +~fin~| +\endgroup + +La traduction de ce \idx{pseudocode} en \TeX{} ne pose pas de problème particulier : + +\showcode/\catcode`\@11 +\def\substin#1#2#3{% + \def\substin@i##1{% + \ifempty{##1}% si le <code> est vide¤§*\ifempty¤ + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#2}% sinon, si le <code> contient <motif1>¤§*\ifin¤ + {\substin@ii##1\@nil}% appeler la macro à argument délimités + {##1}% sinon, afficher le <code> + }% + }% + \def\substin@ii##1#2##2\@nil{% + ##1#3% afficher ##1 et #3 (qui est <motif2>) + \substin@i{##2}% et recommencer avec ce qui reste + }% + \substin@i{#1}% +} +\catcode`\@12 +a) \substin{abracadabra}{a}{A}\qquad +b) \substin{abracadabra}{x}{W}\qquad +c) \substin{abracadabra}{br}{}\qquad +d) \substin{1\relax3}\relax{222}\qquad/§*\substin[|)] + +\begin{exercice} +Modifier cette macro en une macro §\substtocs[|(] pour qu'elle n'affiche pas le résultat, mais le mette dans le texte de remplacement d'une \verb|\<macro>| dont l'utilisateur pourra choisir le nom : + +\centrecode-\substtocs\<macro>{<code>}{motif1}{motif2}- + +\solution +Au lieu d'afficher des morceaux de code, il faudra les ajouter au texte de remplacement d'une macro chargée de collecter le code résultant de ces substitutions. Pour ajouter du code au texte de remplacement d'une macro, §\addtomacro, programmée à la page~\pageref{addtomacro} sera utile. + +\showcode/\catcode`\@11 +\def\substtocs#1#2#3#4{% + \def\substtocs@i##1{% + \ifempty{##1}% si le <code> est vide¤§*\ifempty¤ + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#3}% sinon, si le <code> contient <motif1>¤§*\ifin¤ + {\substtocs@ii##1\@nil}% appeler la macro à argument délimités + {\addtomacro#1{##1}}% sinon, ajouter le <code>¤§*\addtomacro¤ + }% + }% + \def\substtocs@ii##1#3##2\@nil{% + \addtomacro#1{##1#4}% ajouter ##1#4¤§*\addtomacro¤ + \substtocs@i{##2}% et recommencer avec ce qui reste + }% + \let#1=\empty% initialise la macro à vide¤\idx*\empty¤ + \substtocs@i{#2}% +} +\catcode`\@12 +\substtocs\foo{abracadabra}{a}{A}\meaning\foo\par +\substtocs\foo{abracadabra}{x}{W}\meaning\foo\par +\substtocs\foo{abracadabra}{br}{}\meaning\foo\par +\substtocs\foo{1\relax3}\relax{222}\meaning\foo\par +\substtocs\foo{\ifnum1=2 test vrai\fi}{2}{1}\meaning\foo/ +\end{exercice}§*\substtocs[|)] + +\begin{exercice}\label{exo.majuscules} +Écrire une macro §\majmot[|(]\verb|{<phrase>}| qui met en majuscule tous les mots de la \verb|phrase|. +\solution +Pour que §\majmot effectue son travail, dans la \verb|<phrase>|, chaque espace doit être remplacé par «\verb*| \majuscule|», où la macro §\majuscule lit un argument (qui sera la lettre du mot suivant) et la met en majuscule. En coulisses, nous utiliserons la primitive \idx\uppercase. + +Nous allons mettre §\substin à contribution pour effectuer ces remplacements, en ayant pris soin de mettre §\majuscule tout au début de la phrase afin de traiter la première lettre du premier mot. + +\showcode/\def\majuscule#1{\uppercase{#1}}¤\idx*\uppercase§*\majuscule¤ +\def\majmot#1{\substin{\majuscule#1}{ }{ \majuscule}}¤§*\substin§*\majuscule¤ +\majmot{un petit texte}\par +\majmot{Un grand texte sans importance}/ + +Conclusion : ça ne fonctionne pas du tout ! + +Une petite analyse montre que tout vient de la façon dont est faite la substitution et il faut revenir au code de §\substin : + +\begingroup +\renewcommand*\indentcodeactivechars[1]{% + \defactive\^^M{\par\hspace{#1}}% + \defactive\^^I{{ }{ }}% tab = 4 espaces + \catcode`\¤=13 + \begingroup + \lccode`\~`\¤\relax + \lowercase{\endgroup\long\def~##1~}{% + \begingroup + \catcode`\{=1 \catcode`\}=2 \catcode`\#=6 \catcode`\\=0 \catcode`\$=3 + \catcode`\f=11 + \endlinechar=-1 + \scantokens{##1}% + \endgroup + }% échappement ¤...¤ + \footnotesize +}\indencodenumtrue +\indentcode/\def\substin#1#2#3{%¤§*\substin¤ + \def\substin@i##1{% + \ifempty{##1}% si le code est vide¤§*\ifempty¤ + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#2}% sinon, si le code contient motif1¤§*\ifin¤ + {\substin@ii##1\@nil}% appeler la macro à argument délimités + {##1}% sinon, afficher le code + }% + }% + \def\substin@ii##1#2##2\@nil{% + ##1#3% afficher ##1 et #3 (qui est <motif2>) + \substin@i{##2}% et recommencer avec ce qui reste + }% + \substin@i{#1}% +}/\endgroup + +Constatons à ligne \no11 que le code \verb|#2| est substitué par le motif \verb|#3| donc, \verb|##1#3| est exécuté par \TeX{} dans la foulée. C'est de cette exécution \emph{au fur et à mesure} que vient le problème. Ici en effet, le motif de substitution \verb|#3| est «\verb*| \majsucule|». Notre processus échoue parce que lors de l'exécution au fur et à mesure, la macro §\majuscule va lire son argument qui est \verb|\substin@i| de la ligne \no12. Cet argument sera traité par \idx\uppercase qui le laisse intact puisque \idx\uppercase n'a aucun effet sur les séquences de contrôle. Le code ci-dessus ne produit donc aucun effet. + +Nous allons nous en sortir en utilisant la macro §\substtocs de façon à stocker le code obtenu dans une macro temporaire \verb|\temp| que nous afficherons à la toute fin du processus, avant de sortir du groupe semi-simple dans lequel elle a été créée : + +\showcode/\def\majuscule#1{\uppercase{#1}}¤\idx*\uppercase§*\majuscule¤ +\def\majmot#1{% + \begingroup¤\idx*\begingroup¤ + \substtocs\temp{\majuscule#1}{ }{ \majuscule}%¤§*\substtocs§*\majuscule¤ + \temp% exécute la macro temporaire + \endgroup¤\idx*\endgroup¤ +} +\majmot{un petit texte}\par +\majmot{Un grand texte Sans importance}/ + +La leçon à retenir ici est que faire exécuter par \TeX{} de morceaux de code dans une macro récursive n'est pas équivalent à les mettre tous dans une séquence de contrôle que l'on exécute en une fois à la fin.§*\majmot[|)] +\end{exercice} + +\begin{exercice} +Écrire une macro §\cnttimes[|(]\label{cnttimes}\verb|{<code>}{<motif>}| qui affiche combien de fois le \verb|<motif>| est contenu dans le \verb|<code>|. +\solution +Nous allons procéder comme dans §\substtocs. La macro chapeau initialisera un compteur à 0 et passera la main à une macro auxiliaire, chargée de l'incrémenter à chaque fois que le motif sera rencontré : + +\showcode/\catcode`\@11 +\newcount\cnt@occ¤\idx*\newcount¤ +\def\cnttimes#1#2{%¤§*\cnttimes¤ + \def\cnttimes@i##1{% + \ifempty{##1}% si le <code> est vide¤§*\ifempty¤ + {\number\cnt@occ}% afficher le nombre d'occurrences¤\idx*\number¤ + {\ifin{##1}{#2}% sinon, si le <code> contient <motif1>¤§*\ifin¤ + {\cnttimes@ii##1\@nil}% appeler la macro à argument délimités + {\number\cnt@occ}% sinon, afficher le nombre d'occurrences + }% + }% + \def\cnttimes@ii##1#2##2\@nil{%¤\idx*\advance¤ + \advance\cnt@occ1 + \cnttimes@i{##2}% et recommencer avec ce qui reste + }% + \cnt@occ=0 % initialise le compteur + \cnttimes@i{#1}% +} +\catcode`\@12 +a) \cnttimes{abracadabra}{a}\qquad +b) \cnttimes{abracadabra}{ra}\qquad +c) \cnttimes{abracadabra}{w}\qquad +d) \cnttimes{abc def ghijk l }{ }\qquad +e) \cnttimes{\ifnum1=2 vrai\else faux\fi}{\ifnum}¤§*\cnttimes¤/§*\cnttimes[|)] + +Juste pour le plaisir de se compliquer la vie, essayons de modifier la macro en §\cnttimestocs[|(] pour qu'elle réponde aux contraintes suivantes : + +\begin{enumerate} + \item ne pas utiliser de compteur; + \item n'écrire qu'une seule macro intérieure au lieu de deux; + \item stocker le résultat dans une macro spécifiée par l'utilisateur. +\end{enumerate} + +Si nous nous interdisons l'utilisation d'un compteur, nous devrons recourir à la primitive \idx\numexpr pour remplacer \idx\advance afin d'incrémenter le nombre d'itérations. Il est évidemment possible de stocker le nombre d'itérations dans une séquence de contrôle, mais dans ce cas, se priver d'un compteur pour basculer vers une macro n'a pas vraiment de sens, autant conserver un compteur. Il est plus original de stocker le nombre d'itérations dans un argument qui s'ajoutera à ceux de la macro récursive \verb|\cnttimes@i| vue dans le code précédent. Pour pouvoir facilement incrémenter cet argument (qui implique le 1-développer) avant de lancer l'appel récursif, il est judicieux de le placer comme premier argument. L'idée la plus pratique est donc d'ajouter cet argument (et de le délimiter par un \verb|\@nil|) avant ceux de \verb-\cnttimes@ii-. La macro récursive \verb|\cnttimestocs@i| aura donc le texte de paramètre suivant : + +\centrecode-\def\cnttimestocs@i##1\@nil##2#2##3\@nil{...}- + +\noindent Il faudra spécifier «\verb|0|» comme argument \verb|##1| lors du premier appel. + +Ensuite, comme cette macro récursive à est à argument délimité par le \verb|<motif>|, elle \emph{doit} recevoir un \verb|<code>| qui contient ce \verb|<motif>|. Il est donc nécessaire de tester la présence de ce \verb|<motif>| lors du premier appel et également à l'intérieur de la macro récursive avant qu'elle ne s'appelle elle-même avec le \verb|<code>| restant. + +\showcode|\catcode`\@11 +\long\def\cnttimestocs#1#2#3{% #3=macro recevant le résultat¤§*\cnttimestocs¤ + \long\def\cnttimestocs@i##1\@nil##2#2##3\@nil{% + % ##1=nb d'occurrences rencontrées jusqu'alors + % ##2 et ##3=ce qui est avant/après le <motif> + \ifin{##3}{#2}% si le <motif> est dans ce qui reste¤§*\ifin¤ + {\expandafter\cnttimestocs@i% appeler la macro récursive + \number\numexpr##1+1\relax\@nil% avec une occurrence de plus¤\idx*\number\idx*\numexpr¤ + ##3\@nil% et ce qui reste + }% + {\edef#3{\number\numexpr##1+1\relax}}% sinon, stocker 1 occurrence de plus dans #3 + }% + \ifin{#1}{#2}% si le <motif> est dans le <code>¤§*\ifin¤ + {\cnttimestocs@i 0\@nil#1\@nil}% appeler la macro récursive avec 0 occurrence + {\def#3{0}}% sinon, mettre 0 dans #3 +} +\catcode`\@12 +a) \cnttimestocs{abracadabra}{a}\foo \meaning\foo\qquad +b) \cnttimestocs{abracadabra}{ra}\foo \meaning\foo\qquad +c) \cnttimestocs{abracadabra}{w}\foo \meaning\foo\qquad +d) \cnttimestocs{abc def ghijk l }{ }\foo \meaning\foo\qquad +e) \cnttimestocs{\ifnum1=2 vrai\else faux\fi}{\ifnum}\foo \meaning\foo| +§*\cnttimestocs[|)]\end{exercice} + +\subsection{Substitutions successives} +Nous allons maintenant écrire une macro §\multisubst[|(], généralisation de la macro §\substin où nous pourrons spécifier plusieurs substitutions successives : + +\centrecode-\multisubst{<code>}{{<motif1>}{<motif2>}{<motif3>}{<motif4>}{<motif5>}...}- + +\noindent Cette macro substituera toutes les occurrences de \verb|<motif1>| par \verb|<motif2>| puis toutes celles de \verb|<motif3>| par \verb|<motif4>| et ainsi de suite jusqu'à ce que la liste des substitutions soit vide dans le deuxième argument. Les motifs utilisés dans la substitution seront donc lus par \emph{paires}. L'idée est de se servir de la macro §\substin précédemment écrite en lui envoyant comme second et troisième argument les motifs de substitution pris deux par deux dans la liste. Comme un espace non entouré d'accolades est ignoré en tant qu'argument, les motifs peuvent être groupés par paires \emph{séparées par un espace} dans la liste des motifs afin d'en améliorer la lisibilité. + +\showcode/\catcode`\@11 +\def\multisubst#1#2{% + \def\subst@code{#1}% stocke le <code> + \multisubst@i#2\@nil% appelle la macro récursive avec la liste de motifs +} + +\def\multisubst@i#1#2#3\@nil{% #1 et #2=paire de motifs #3=motifs restants + \expandafter\substtocs\expandafter\subst@code\expandafter% 1-développe¤§*\substtocs¤ + {\subst@code}{#1}{#2}% le <code> et effectue la substitution en cours + \ifempty{#3}% si la liste des motifs est vide¤§*\ifempty¤ + \subst@code% exécuter le <code> obtenu + {\multisubst@i#3\@nil}% recommencer avec les motifs qui restent +} +\catcode`\@12 +1) \multisubst{abracadabra}{aA rR}\par +2) \multisubst{Ce texte devenu \`a peine reconnaissable montre que le¤\defline\aaa¤ + r\'esultat contient des sonorit\'es catalanes, corses ou grecques + assez inattendues.}{a{AA} ya uy ou io ei {AA}e}/ + +La macro chapeau \verb|\multisubst| de la ligne \no2 se contente de lire le \verb|<code>| et l'assigne à la macro \verb|\subst@code| avant de passer la main à la véritable macro récursive \verb|\multisubt@i|, à argument délimité. La liste des motifs de substitutions est passée comme argument pour s'étendre jusqu'au \verb|\@nil|. Les arguments non délimités \verb|#1| et \verb|#2| deviennent les deux motifs de substitution de l'itération en cours tandis que \verb|#3| est la liste des paires restantes. + +Le cas \no2 des lignes \nos\aaa{} à \number\numexpr\aaa+2\relax{} traite d'une façon différente le problème de la permutation circulaire des voyelles déjà résolu à la page~\pageref{permutation.voyelles}. On ne touche plus à aucun code de catégorie, mais on effectue tour à tour les substitutions désirées. La méthode est cependant \emph{lente}, car l'argument est ici un texte assez long et celui-ci sera parcouru par §\multisubst autant de fois qu'il y a de substitutions à faire, c'est-à-dire 7 fois. La « propreté » que l'on gagne à ne pas modifier les codes de catégorie se paie en lenteur. +\grandsaut + +Au-delà ce ce défaut lié à la méthode utilisée, le code présente une autre redondance : chaque paire est lue \emph{plusieurs fois}. Elle est lue une première fois par la macro chapeau \verb|\multisubst| puisqu'elle lit toutes les paires. Par la suite, à chaque itération, la macro récursive \verb|\multisubst@i| lit (et mange) la première paire de son argument, mais lit \emph{aussi que la totalité des paires restantes}. La totalité des paires est donc lue deux fois : une fois par la macro chapeau et une fois par la macro récursive lors de la première itération (qui ensuite supprime la paire \no1). À la 2\ieme{} itération, la macro récursive lit la totalité des paires restantes et supprime la paire \no2, etc. Ainsi, s'il y a $n$ paires, la première est lue 2 fois, la 2\ieme{} est lue 3 fois, et ainsi de suite jusqu'à la $n$\ieme{} qui est lue $n+1$ fois. + +Il s'agit donc de changer de méthode afin de limiter le nombre de lectures des paires. Envisageons le scénario suivant : + +\begin{itemize} + \item lire la liste de toutes les paires une fois afin de les transmettre à la macro récursive en les faisant suivre d'une paire spéciale, constituée de deux arguments spéciaux; + \item cette macro récursive, au lieu de lire tous les arguments, n'en lit que deux : pour savoir si elle doit les prendre comme une paire de motifs ou comme condition d'arrêt, il faut qu'elle les teste et les compare aux arguments spéciaux; + \item si le test est positif, cela signifie que la fin de la liste des couples de motifs a été complètement lue et que la fin du processus doit être exécutée; + \item dans le cas d'un test négatif, la substitution est exécutée et la macro s'appelle elle même pour une nouvelle itération. +\end{itemize} + +Cette façon de procéder implique que chaque paire est lu exactement deux fois; une fois par la macro chapeau et une fois par la macro récursive. + +Un dernier détail doit être décidé : que sont des arguments \emph{spéciaux}? Ce sont des arguments qui ne sont pas susceptibles d'être un des deux motifs. Tout est imaginable, mais il semble assez sûr de prendre un \idx{quark} tant ce genre de macro est improbable dans une liste de motifs de substitution. Voici comment modifier le code : + +\showcode/\catcode`\@11 +\def\quark{\quark}¤§*\quark¤ + +\def\multisubst#1#2{% #1 = <code> #2 = <liste des paires> + \def\subst@code{#1}% stocke le <code> + \multisubst@i#2\quark\quark% appelle la macro récursive avec ¤§*\quark¤ + % 2 arguments spéciaux en fin de liste +} + +\def\multisubst@i#1#2{% #1#2 = paire de motifs en cours + \def\arg@a{#1}% stocke le <motif1> dans une macro + \ifx\arg@a\quark% si le motif spécial est atteint¤§*\quark¤ + \expandafter\subst@code% exécuter le code obtenu + \else + \expandafter\substtocs\expandafter\subst@code\expandafter% 1-développe¤§*\substtocs¤ + {\subst@code}{#1}{#2}% le <code> et effectue la substitution en cours + \expandafter\multisubst@i% puis lis la paire de motifs suivants + \fi +} +\catcode`\@12 +\multisubst{abracadabra}{aA rR}\par +\multisubst{Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des + sonorit\'es catalanes, corses ou grecques assez inattendues.}{a{AA} ya uy ou io ei {AA}e}/§*\multisubst[|)] + +\label{lire.arguments}\begin{regle}[Méthode de programmation] + Lorsqu'une liste d'arguments consécutifs doit être lue au fur et à mesure par une macro récursive, deux méthodes de lecture sont envisageables. Elles ont en commun une première lecture de tous les arguments afin de les passer à la macro récursive et c'est cette dernière qui a deux façons de fonctionner : + + \begin{enumerate} + \item lire la totalité des arguments restants à chaque itération, utiliser ceux qui sont utiles à l'itération en cours et recommence1r avec tous les arguments restants. S'arrêter lorsque la liste d'arguments restants est vide; + \item ne lire \emph{que} les arguments utiles à l'itération en cours, les utiliser et recommencer. S'arrêter lorsque l'un des arguments lus est égal à un argument spécifique préalablement placé en fin de liste par la macro chapeau. + \end{enumerate} + \end{regle} + +Il est inutile de dire qu'un programmeur rigoureux choisit de préférence la deuxième méthode même si souvent, le nombre d'arguments à lire est peu élevé auquel cas la première méthode est parfaitement acceptable.\tidx*[|)]{ifx} + +\section{Autres tests} +\subsection{Pseudotests et variables booléennes}\idx*[|(]{pseudotest} +\TeX{} dispose des pseudotests \tidx{iftrue} et \tidx{iffalse}, le premier est toujours vrai et le second toujours faux. Bien qu'elles en partagent la syntaxe et les subtilités de développement, ces primitives n'effectuent pas de tests. Cependant, nous allons le voir un peu plus bas, elles n'en sont pas moins utiles. + +\showcode/\iftrue foobar\else ceci n'est jamais lu par \TeX\fi\par +\iffalse ceci n'est jamais lu par \TeX\else foobar\fi/ + +On peut bien sûr rendre une macro \idx\let-égale à \tidx{iftrue} ou \tidx{iffalse} pour, au choix, disposer d'une macro simulant un test toujours vrai ou toujours faux. C'est d'ailleurs exactement ce que fait la macro de plain-\TeX{} \idx\newif\verb|<macro>| qui construit 3 macros qui ont un comportement identique à celui de variables « booléennes » que l'on trouve dans d'autres langages. + +Ainsi, si nous écrivons \idx\newif\verb|\ifbar| alors, 3 séquences de contrôle seront créées : +\begin{itemize} + \item \verb|\bartrue| qui rend \verb|\ifbar| \idx\let-égale à \tidx{iftrue} ; + \item \verb|\barfalse| qui rend \verb|\ifbar| \idx\let-égale à \tidx{iffalse} ; + \item \verb|\ifbar| qui tient lieu de \tidx{iftrue} ou \tidx{iffalse} selon la « valeur booléenne » qui lui aura été donnée par l'une des deux premières macros. +\end{itemize} + +\showcode/\newif\ifhomme¤\idx*\newif¤ +\def\debutlettre{Ch\ifhomme er Monsieur\else ère Madame\fi}% + +\hommetrue +\debutlettre +\medbreak¤\idx*\medbreak¤ + +\hommefalse +\debutlettre/ + +Le \idx[!plain-\TeX]{format} plain-\TeX{} fait en sorte que lorsqu'on écrit \idx\newif\verb|\<nom>|, le \verb|<nom>| commencer \emph{obligatoirement} par «\verb|if|». Il est intéressant de noter que \LaTeX{} ré-implémente la macro \idx\newif en autorisant le \verb|<nom>| à commencer par deux caractères quelconques qui seront enlevés lorsque \verb|\<nomtrue>| ou \verb|\<nomfalse>| sont crées. + +\begin{regle} +Les \idx{pseudotest}s \tidx{iftrue} et \tidx{iffalse} n'effectuent aucun test et donc n'admettent aucun argument. Ils sont respectivement toujours vrai et toujours faux. En dépit de ce caractère invariable, ils se comportent comme n'importe quel test, notamment en ce qui concerne l'éventuelle présence du \tidx{else} et celle, obligatoire, du \tidx{fi}. + +La macro \idx\newif, suivie d'une macro \verb|\if<nom>| crée 3 nouvelles macros dont le comportement est identique à celui des variables booléennes. Le test \verb|\if<nom>| sera vrai si la macro \verb|\<nom>true| a été exécutée auparavant et sera faux si c'est la macro \verb|\<nom>false|. +\end{regle}\idx*[|)]{pseudotest} + +\subsection{Le test \texttt{\textbackslash ifcat}}\tidx*[|(]{ifcat} +Le test \verb|\ifcat<token1><token2>| compare les codes de catégorie des deux tokens qui le suivent immédiatement. Ce test se comporte un peu comme \tidx{ifnum} en ce sens qu'il lance un développement maximal \emph{avant} de faire le test. Sans action visant à empêcher le développement (utilisation de \idx\noexpand ou \idx\unexpanded), le test compare donc les deux premiers tokens non développables résultant du développement maximal. + +Il faut savoir que pour \verb|\ifcat|, un token de type séquence de contrôle est vu comme ayant un code de catégorie égal à 16. Cette 17\ieme{} catégorie, spécifique au test \verb|\ifcat|, vient s'ajouter aux 16 autres catégories déjà existantes pour la primitive \idx\catcode. En revanche, si une séquence de contrôle ou un caractère actif a été préalablement rendu \idx\let-égal à un \verb|<token>|, \verb|\ifcat| prend en compte le code de catégorie du \verb|<token>|. + +\showcode/\catcode`\*=13 \catcode`\+=13 % "*" et "+" sont actifs +1) \def*{xyz}\def+{abc}% définit les caractères actifs + \ifcat *+vrai\else faux\fi\qquad ¤\tidx*{ifcat}¤ +2) \ifcat \noexpand *\noexpand +vrai\else faux\fi\qquad¤\idx*\noexpand¤ +3) \def\foo{foobar}% + \ifcat \noexpand\foo\relax vrai\else faux\fi\qquad% \foo et \relax sont vus égaux ¤\idx*\noexpand¤ +4) \ifcat = vrai\else faux\fi\qquad% "=" et " " n'ont pas le même catcode +5) \ifcat _^vrai\else faux\fi\qquad% "_" et "^" n'ont pas le même catcode +6) \let\foo=& + \ifcat &\foo vrai\else faux\fi% "&" et \foo (let-égal à "&") sont vus égaux¤\tidx*{ifcat}¤/ + +Ce qui arrive lors du test \no1 est facilement compréhensible : le caractère actif «\verb-*-» est développé au maximum pour donner «\verb-xyz-». Le test \verb|\ifcat| comparera donc les deux premiers tokens issus de ce développement, «\verb-x-» et «\verb-y-», et comme ils ont le même catcode, le test sera vrai et tout ce qui est avant le \tidx{else} sera exécuté. Le «\verb|z|» restant va être dirigé vers l'affichage. Ensuite, le caractère actif «\verb|+|» va se développer et afficher «\verb|abc|». Enfin, «\verb|vrai|» sera affiché. On obtient bien l'affichage «\verb|zabcvrai|». + +Le \idx\noexpand du test \no3 bloque le développement de la macro \verb|\foo| et la comparaison qui est faite est bien entre les catcodes de «\verb-\foo-» et «\verb-\relax-». + +\begin{regle} +Le test \verb|\ifcat| lance le développement maximal et dès qu'il obtient deux tokens non développables (ou dont le développement est bloqué par \idx\noexpand ou \idx\unexpanded), il effectue la comparaison des codes de catégorie. + +Si un token testé par \verb|\ifcat| est une séquence de contrôle ou un caractère actif rendu \idx\let-égal à un \verb|<token>|, \verb|\ifcat| prend en compte le code de catégorie du \verb|<token>|. + +Si un token testé par \verb|\ifcat| est une séquence de contrôle, son code de catégorie est vu égal à 16. +\end{regle} + +\begin{exercice}\label{ifcs} +Écrire une macro §\ifcs[|(], de syntaxe + +\centrecode|\ifcs<token>{<vrai>}{<faux>}| + +qui teste si le \verb|<token>| est une séquence de contrôle au sens de \verb|\ifcat|. Selon l'issue du test, \verb|<vrai>| ou \verb|<faux>| seront exécutés. +\solution +La comparaison sera faite entre le \verb|<token>| dont on aura bloqué le développement et \idx\relax. Voici le code : + +\showcode/\catcode`\@11 +\def\ifcs#1{%¤§*\firstoftwo§*\secondoftwo\idx*\noexpand¤ + \ifcat\noexpand#1\relax\expandafter\firstoftwo + \else \expandafter\secondoftwo + \fi +} +\catcode`\@12 +1) \def\foo{bar}\ifcs\foo{vrai}{faux}\qquad +2) \ifcs\def{vrai}{faux}\qquad +3) \ifcs A{vrai}{faux}\qquad +\catcode`\*=13 +4) \let*=W \ifcs*{vrai}{faux}\qquad +5) \let*=\relax \ifcs*{vrai}{faux}\qquad +6) \let\foo=A \ifcs\foo{vrai}{faux}\qquad +7) \ifcs\bgroup{vrai}{faux}/ + +Remarquons aux cas \nos5, 6 et 7 que si l'on s'en tient à la vraie définition d'une séquence de contrôle (suite de caractères qui commence par le caractère d'échappement), le test §\ifcs que nous avons écrit n'est pas satisfaisant. +\end{exercice}§*\ifcs[|)]\tidx*[|)]{ifcat} + +\subsection{Le test \texttt{\textbackslash if}}\tidx*[|(]{if} +Le test \verb|\if| est en tous points semblable au test \verb|\ifcat| sauf que ce sont les codes de \emph{caractère} des deux tokens qui sont comparés. + +\begin{regle} +Le test \verb|\if| instaure un développement maximal et dès qu'il obtient deux tokens non développables (ou dont le développement est bloqué par \idx\noexpand ou \idx\unexpanded), il effectue la comparaison de leurs codes de caractère. + +Si un token testé par \verb|\if| est une séquence de contrôle ou un caractère actif rendu \idx\let-égal à un \verb|<token>|, \verb|\if| prend en compte le code de caractère du \verb|<token>|. + +Si un token testé par \verb|\if| est une séquence de contrôle, son code de caractère est vu égal à 256. +\end{regle} + +\showcode/1) \def\foo{xxy}\def\bar{aab} + \if\foo\bar vrai\else faux\fi\qquad +2) \if aA vrai\else faux\fi\qquad% "a" et "A" n'ont pas les mêmes charcode +3) \if\relax\noexpand\foo vrai\else faux\fi\qquad% \relax et \foo ont un charcode=256¤\idx*\noexpand¤ +4) \let\foo=&\if\foo &vrai\else faux\fi\qquad% \foo est vue comme "&" +5) \if\string~\noexpand~vrai\else faux\fi¤\idx*\string\cidx*\~¤/ + +\begin{exercice}\label{ifcs.la.vraie} +Utiliser le test \verb|\if| pour que la macro §\ifcs[|(]\verb|<token>{<vrai>}{<faux>}| vue précédemment ne renvoie \verb|<vrai>| que si le \verb|<token>| est une séquence de contrôle, c'est-à-dire lorsqu'il commence par le caractère d'échappement. +\solution +Il est inévitable que nous allons devoir tester le caractère d'échappement. Pour cela, nous allons ouvrir un groupe semi-simple et imposer un caractère d'échappement avec \idx\escapechar. Nous le prendrons égal à «\verb|@|» mais tout autre choix conviendrait; l'essentiel est de se prémunir d'un régime spécial où l'\idx\escapechar serait négatif ou nul. Il suffit ensuite de tester si les deux caractères qui se trouvent au début de «\idx\string\verb|#1a|» (le «\verb|a|» protège d'un argument vide) et «\idx\string\idx\relax» sont les mêmes. Après avoir fermé le groupe, les classiques §\firstoftwo ou §\secondoftwo renvoient \verb|<vrai>| ou \verb|<faux>|. Pour isoler le premier caractère d'une chaine arbitraire, la macro §\firstto@nil sera utilisée. + +\showcode/\catcode`\@11 +\def\ifcs#1{%¤§*\ifcs¤ + \begingroup¤\idx*\begingroup¤ + \escapechar=`\@ % prend "@" comme caractère d'échappement¤\idx*\escapechar¤ + \if% les premiers caractères de + \expandafter\firstto@nil\string#1a\@nil% "#1a"¤§*\firstto@nil\idx*\string¤ + \expandafter\firstto@nil\string\relax\@nil% et "\relax" sont-ils égaux ?¤\idx*\string§*\firstto@nil¤ + \endgroup\expandafter\firstoftwo% si oui, fermer le groupe et renvoyer "vrai"¤§*\firstoftwo\idx*\endgroup¤ + \else% sinon, fermer le groupe et renvoyer "faux" + \endgroup\expandafter\secondoftwo¤§*\secondoftwo¤ + \fi +} +\catcode`\@12 +1) \def\foo{bar}\ifcs\foo{vrai}{faux}\qquad +2) \ifcs\def{vrai}{faux}\qquad +3) \ifcs A{vrai}{faux}\qquad +\catcode`\*=13 +4) \let*=W \ifcs*{vrai}{faux}\qquad +5) \let*=\relax \ifcs*{vrai}{faux}\qquad +6) \let\foo=A \ifcs\foo{vrai}{faux}\qquad +7) \ifcs\bgroup{vrai}{faux}¤\idx*\bgroup¤/ +§*\ifcs[|)]\end{exercice}\tidx*[|)]{if} + +\subsection{Test incomplets}\idx*[|(]{test incomplet} +Entrons à présent au plus profond de la mécanique \TeX ienne des tests et examinons les tests incomplets. Un test est dit incomplet lorsque les choses à comparer sont en nombre insuffisant et font intervenir les primitives du test lui-même, à savoir \tidx[|etc]{else} et \tidx[|etc]{fi}. +\grandsaut + +Demandons-nous quelle est la comparaison faite dans ce test : + +\centrecode-\ifx a\fi- + +\noindent Si l'on s'en tient à la définition de \tidx{ifx}, le «\verb|a|» est comparé au \verb|\fi| mais ce même \verb|\fi|, capturé par la comparaison et dès lors indisponible, ne casse-t-il pas l'équilibrage entre le \tidx{ifx} et le \verb|\fi| ? + +Dans le même ordre d'idée, que se passe-t-il lors de ce test ? + +\centrecode-\ifnum1=2\else3\fi 4- + +\noindent Lorsque le \verb|2| est lu, \TeX{} est en train de lire un nombre et se trouve donc en phase de \idx{développement maximal}. Va-t-il développer le \verb|\else|, puis lire \verb|3| puis poursuivre son développement et manger le \verb|\fi|? Ensuite, continue-t-il son développement plus loin avec le 4 et avec ce qui se trouve après ? Le \verb|\else| et le \verb|\fi|, mangés par la lecture du nombre, rompent-ils l'équilibrage entre le \tidx{ifnum} et le \verb|\fi| ? + +\idx*[!spécial|(]{\relax}\begin{regle} +\relax Lorsqu'un \verb|\else| ou un \verb|\fi| apparié avec un test est rencontré lors de l'évaluation de ce test, \TeX{} insère un \verb|\relax| spécial afin que le \verb|\else| ou le \verb|\fi| demeurent hors de l'évaluation faite par le test. +\end{regle} + +Ainsi, le test «\verb|\ifx a\fi|» devient «\verb|\ifx a|\boxtoken\relax\verb|\fi|» où le \boxtoken\relax est un \idx\relax spécial inséré par \TeX{} et qui sera comparé avec «\verb|a|». De même, le test «\verb|\ifnum1=2\else3\fi|» devient «\verb|\ifnum1=2|\boxtoken\relax\verb|\else3\fi|» où le \idx\relax spécial interrompt la lecture du nombre 2. +\grandsaut + +Voici comment définir une macro §\specialrelax dont le texte de remplacement est un \idx\relax spécial : + +\centrecode-\edef\specialrelax{\ifnum1=1\fi}- + +\noindent Ici, un \boxtoken\relax sera inséré avant le \verb|\fi| et comme le test est vrai, ce \boxtoken\relax sera tout ce qui reste dans la branche du test. + +\showcode/\edef\specialrelax{\ifnum1=1\fi}¤§*\specialrelax¤ +\meaning\specialrelax¤\idx*\meaning¤/ + +Pourquoi dit-on \idx\relax \emph{spécial} et non pas \idx\relax tout court ? Les \idx\relax spéciaux insérés par \TeX{} sont différents des \idx\relax ordinaires lus dans le code source aux yeux de \TeX{} lui-même. En voici la preuve : + +\showcode/\edef\specialrelax{\ifnum1=1\fi}% texte de remplacement = \relax spécial¤§*\specialrelax¤ +\edef\normalrelax{\relax}% texte de remplacement = \relax normal +\meaning\specialrelax\par¤\idx*\meaning¤ +\meaning\normalrelax\par +Les 2 \string\relax{} sont \ifx\specialrelax\normalrelax identiques\else différents\fi.¤\idx*\string§*\specialrelax¤/ + + Ils sont différents car \boxtoken\relax n'est \emph{pas} une primitive. C'est un token interne défini dans le programme \texttt{tex} lui-même qui se donne l'apparence de \idx\relax mais qui est codé en dur et qui n'est pas modifiable par l'utilisateur. Autant on pourrait vivre dangereusement et redéfinir la primitive \idx\relax, autant il est impossible de redéfinir \boxtoken\relax : + +\errcode/\edef\specialrelax{\ifnum1=1\fi}¤§*\specialrelax¤ +\expandafter\def\specialrelax{foobar}% redéfinition un \relax spécial¤§*\specialrelax¤/{Missing control sequence inserted}% +\idx*[!spécial|)]\relax\idx*[|)]{test incomplet} + +\section{Tests successifs} +\subsection{Programmation du test \texttt{\textbackslash ifnumcase}}§*\ifnumcase[|(]% +Nous avons vu le test \tidx{ifcase}\verb|<nombre>|, cousin de \tidx{ifnum}, qui permettait de décider que faire selon des valeurs entières du \verb|<nombre>|. L'inconvénient est que ces valeurs doivent commencer à 0 et se suivre de 1 en 1. Cette contrainte rend ce test inutilisable si l'on souhaite tester si le \verb|<nombre>| est successivement égal à des valeurs non consécutives et distantes les unes des autres. L'idée est donc de créer un test qui décide que faire selon des valeurs entières précisées par l'utilisateur (par exemple $-5$, 14, $-16$, 20) et offrir une branche facultative §\elseif où un code alternatif sera exécuté si aucune égalité n'a été rencontrée. + +Nous allons nous fixer la syntaxe suivante : + +\centrecode/\ifnumcase{<nombre>}% + {<valeur 1>}{<code 1>} + {<valeur 2>}{<code 2>} + ... + {<valeur n>}{<code n>} +\elseif + <code alternatif> +\endif¤§*\endif¤/ + +Le §\elseif et le \verb|<code alternatif>| qui le suit sont facultatifs. + +De plus, nous allons nous imposer une contrainte : lorsqu'un \verb|<code>| est exécuté, il doit pouvoir accéder aux tokens qui suivent le §\endif. Autrement dit, ce \verb|<code>| doit être exécuté à la toute fin du traitement, juste avant que la macro ne rende la main. + +L'idée directrice est de lire tout d'abord le \verb|<nombre>| puis de passer la main à une macro récursive qui va lire ensuite \emph{un seul} argument. Si cet argument est : + +\begin{itemize} + \item une \verb|<valeur k>|, lire le \verb|<code k>| et l'exécuter si le test est vrai après avoir tout mangé jusqu'au §\endif. S'il est faux, recommencer; + \item §\elseif, aucun test n'a été vrai et il faut exécuter le \verb|<code alternatif>| après avoir tout mangé jusqu'au §\endif; + \item §\endif, cela signifie la fin du processus et correspond au cas où §\elseif n'est pas présent et aucun test n'a été vrai. +\end{itemize} + +Comme nous ne savons pas encore tester si un argument est une \verb|<valeur>| (c'est-à-dire un entier), nous allons d'abord tester si l'argument est §\elseif puis s'il est §\endif et si les deux tests précédents sont négatifs, nous considèrerons que c'est un entier. + +Afin d'être propre dans la programmation, l'ensemble du processus se déroulera dans un groupe semi-simple. Il conviendra de le fermer au bon moment. Pour faciliter la comparaison avec \tidx{ifx}, les macros §\elseif et §\endif seront des \idx[|(]{quark}s locaux à ce groupe. + +\begin{algo} + \item ouvrir un groupe, lire le \verb|<nombre>|, définir les quarks \verb|\elseif| et \verb|\endif|; + \item lire le premier argument $x$ qui suit ; + \begin{algo} + \item si $x={}$§\elseif, fermer le groupe et exécuter tout ce qui reste jusqu'à §\endif ; + \item sinon + \begin{itemize} + \item si $x={}$§\endif fermer le groupe; + \item sinon, cela signifie que $x$ est une \verb|<valeur>| + \begin{algo}%[label=(\roman*)] + \item si $x={}$\verb-<nombre>-, fermer le groupe, exécuter l'argument suivant après voir tout supprimé tout jusqu'à §\endif ; + \item si $x\neq{}$\verb-<nombre>-, manger l'argument suivant et retourner en 2. + \end{algo} + \end{itemize} + \end{algo} +\end{algo} + +Nous aurons besoin d'une macro auxiliaire récursive pour le point 2 ainsi que d'une macro auxiliaire qui s'occupe du point 2a que nous appellerons \verb|\idto@endif| : + +\centrecode-\def\idto@endif#1\endif{#1}-§*\endif + +\noindent et d'une autre pour le point 2bi) que nous appellerons \verb|\firstto@endif|. Elle sera chargée de lire l'argument suivant (argument non délimité \verb|#1|) et de lire tout ce qui se trouve jusqu'au §\endif (qui sera un argument délimité). Son texte de remplacement sera le premier argument : + +\centrecode-\def\firstto@endif#1#2\endif{#1}-§*\endif + +Voici le code : + +\showcode/\catcode`\@11 +\def\ifnumcase#1{%¤§*\ifnumcase¤ + \begingroup¤\idx*\begingroup¤ + \def\elseif{\elseif}\def\endif{\endif}% définir deux "quarks" locaux¤§*\elseif§*\endif¤ + \def\nombre@{#1}% stocker le nombre à comparer dans \nombre@ + \ifnumcase@i% appeller la macro récursive +} + +\def\ifnumcase@i#1{% + \def\nxt@arg{#1}% stocker l'argument suivant + \ifx\nxt@arg\elseif% si c'est \elseif¤§*\elseif¤ + \def\next@todo{\endgroup\idto@endif}% fermer le groupe et aller à \idto@endif + \else + \ifx\nxt@arg\endif% si c'est \endif¤§*\endif¤ + \let\next@todo\endgroup% fermer le groupe + \else + \ifnum\nombre@=\nxt@arg% s'il y a égalité + \def\next@todo{\endgroup\firstto@endif}% fermer le groupe puis \firstto@endif + \else% sinon + \let\next@todo=\ifnumcase@ii% aller à \ifnumcase@ii + \fi + \fi + \fi + \next@todo% exécuter l'action décidée ci-dessus +} + +\def\ifnumcase@ii#1{\ifnumcase@i} +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\catcode`\@12 +\def\swaptwo#1#2{#2#1}¤§*\swaptwo¤ +\def\testenombre#1{% + \ifnumcase{#1} + {1}{C'est "un" et je prends le premier argument: \firstoftwo}¤§*\firstoftwo¤ + {3}{J'ai vu un "trois" et je mange les deux arguments : \gobtwo}¤§*\gobtwo¤ + {15}{L'argument est "15" et je prends le second argument : \secondoftwo}¤§*\secondoftwo¤ + \elseif¤§*\elseif¤ + ni 1, ni 3, ni 5 et j'inverse les deux arguments : \swaptwo¤§*\swaptwo¤ + \endif¤§*\endif¤ +} +\testenombre{3}{foo}{bar}\par +\testenombre{16}{foo}{bar}\par +\testenombre{15}{foo}{bar}\par +\testenombre{1}{foo}{bar}/ + +Pour mettre en évidence que chaque \verb|<code>| a accès à ce qui se trouve immédiatement après le §\endif, les macros §\firstoftwo, §\secondoftwo sont appelées à la fin des \verb|<code>| pour qu'elles agissent sur les arguments qui suivent le §\endif. Une nouvelle macro §\swaptwo qui inverse l'ordre des deux arguments suivants est également utilisée. + +\begin{exercice} +En l'état, la macro §\ifnumcase n'est pas purement développable, c'est-à-dire qu'on ne peut pas la mettre dans un \idx\edef. Comment pourrait-on la modifier pour qu'elle le devienne (à condition bien sûr que les \verb|<code>| le soient) ? +\solution +Les primitives non développables doivent être supprimées : \idx\begingroup, \idx\endgroup, \idx\def et \idx\let. Il n'y aura plus de groupe semi-simple et les \idx{quark}s seront donc définis de façon globale, à l'extérieur de la macro §\ifnumcase. + +Dans les branches des tests de \verb|\ifnumcase@i|, nous pouvons facilement nous passer de la macro \verb|\next@todo| à condition de recourir aux macros §\firstoftwo et §\secondoftwo qui rejettent hors des branches les codes exécutés selon l'issue du test. En revanche, il est plus difficile de se passer de \verb|\nombre@| qui a stocké le \verb|<nombre>| dans la macro chapeau. La seule alternative est de passer le \verb|<nombre>| comme argument supplémentaire à la macro récursive. + +Le code tend à se simplifier : l'essentiel est désormais fait par la macro récursive §\ifnumcase qui à chaque fois, va lire le même premier argument (le \verb|<nombre>|) ainsi qu'un second argument qui devra être testé pour décider que faire. Dans le cas où ce second argument est une \verb|<valeur>|, il faudra aller lire le \verb|<code>| correspondant, soit pour l'exécuter, soit pour le manger avant de repartir pour un tour. + +\showcode/\catcode`\@11 +\def\endif{\endif}\def\elseif{\elseif}% définit les quarks¤§*\elseif§*\endif¤ + +\def\ifnumcase#1#2{% #1=<nombre> #2=prochain argument + \ifx\elseif#2% si #2 est \elseif¤§*\elseif¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤§*\firstoftwo§*\secondoftwo¤ + \idto@endif% aller à \idto@endif, sinon : + {\ifx\endif#2% si #2 est \endif¤§*\endif¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤§*\secondoftwo¤ + {}% ne rien faire. Sinon #2 est une <valeur i>: + {\ifnum#1=#2 % s'il y a égalité entre <nombre> et <valeur i> + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤§*\secondoftwo¤ + \firstto@endif% aller à \firstto@endif + {\ifnumcase@i{#1}}% sinon aller à \ifnumcase@i + }% + }% +} + +\def\ifnumcase@i#1#2{% #1=<nombre> #2=<code i> à suivre qui est mangé + \ifnumcase{#1}% retourner à \ifnumcase en transmettant #1 +} + +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\catcode`\@12 +\def\swaptwo#1#2{#2#1}¤§*\swaptwo¤ +\def\testenombre#1{% + \ifnumcase{#1} + {1}{C'est "un" et je prends le premier argument: \firstoftwo}¤§*\firstoftwo¤ + {3}{J'ai vu un "trois" et je mange les deux arguments : \gobtwo}¤§*\gobtwo¤ + {15}{L'argument est "15" et je prends le second argument : \secondoftwo}¤§*\secondoftwo¤ + \elseif¤§*\elseif¤ + ni 1, ni 3, ni 5 et j'inverse les deux arguments : \swaptwo¤§*\swaptwo¤ + \endif¤§*\endif¤ +} +\testenombre{3}{foo}{bar}\par +\testenombre{16}{foo}{bar}\par +\testenombre{15}{foo}{bar}\par +\edef\macro{\testenombre{1}{foo}{bar}}\meaning\macro/ +\idx*[|)]{quark}§*\ifnumcase[|)]\end{exercice} + +\subsection{Programmation du test \texttt{\textbackslash ifxcase}}§*\ifxcase[|(]% +Il ne nous reste plus qu'à faire la même chose avec le test \tidx{ifx} en programmant une macro \verb|\ifxcase| dont voici la syntaxe : + +\centrecode/\ifxcase <token> + <token 1>{<code 1>} + <token 2>{<code 2>} + ... + <token n>{<code n>} +\elseif%¤§*\elseif¤ + <code alternatif> +\endif¤§*\endif¤/ + +\noindent qui compare successivement \verb|<token>| avec \verb|<token 1>|, \verb|<token 2>|, etc, où les comparaisons sont faites au sens de \tidx{ifx}. Si un des tests est positif, le \verb|<code i>| correspondant sera exécuté et sinon, ce qui se trouve après la macro facultative §\elseif le sera. + +Il n'y a rien de bien difficile, il suffit de recopier le code de §\ifnumcase en remplaçant le test \tidx{ifnum} par \tidx{ifx}: \label{ifxcase} + +\showcode/\catcode`\@11 +\def\endif{\endif}\def\elseif{\elseif}%¤§*\elseif§*\endif¤ +\def\ifxcase#1#2{% #1=<token> #2=prochain argument + \ifx\elseif#2% si #2 est \elseif¤§*\elseif¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤§*\firstoftwo§*\secondoftwo¤ + \idto@endif% aller à \idto@endif, sinon : + {\ifx\endif#2% si #2 est \endif¤§*\endif¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤§*\secondoftwo¤ + {}% ne rien faire. Sinon #2 est un <token i>: + {\ifx#1#2% s'il y a égalité entre <token> et <token i> + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤§*\secondoftwo¤ + \firstto@endif% aller à \firstto@endif + {\ifxcase@i{#1}}% sinon aller à \ifnumcase@i + }% + }% +} +\def\ifxcase@i#1#2{% #1=<token> #2=<code i> à suivre (qui est mangé) + \ifxcase{#1}% retourner à \ifxcase en transmettant #1 +} +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\catcode`\@12 +\def\testtoken#1{%¤§*\testtoken¤ + \ifxcase{#1} + a{Le token est "a"} + +{J'ai vu un "+"} + \relax{L'argument est "\string\relax"}¤\idx*\string¤ + \elseif¤§*\elseif¤ + Tous les tests sont négatifs% + \endif¤§*\endif¤ +} +1) \testtoken a\par +2) \let\foo=a\testtoken\foo\par +3) \testtoken+\par +4) \testtoken\relax\par +5) \edef\foo{\testtoken\relax}\meaning\foo\par¤\idx*[|etc]\meaning\forbidindex\meaning¤ +6) \edef\foo{\testtoken W}\meaning\foo¤§*\testtoken¤/ + +On constate aux cas \nos1 et 2 que le test est positif aussi bien lorsque le \verb|<token>| est un «\verb|a|» explicite que lorsqu'il est une macro \idx\let-égale au caractère «\verb|a|».§*\ifxcase[|)]% + +\chapter{Une boucle « loop repeat »} +\section{Implémentation de plain-\TeX} +Plain-\TeX{} définit lui-même sa boucle « \idx\loop\ldots\idx\repeat » dont voici l'implémentation telle qu'elle a été écrite par \idx*{Knuth Donald}D.~\textsc{Knuth} : + +\centrecode-\def\loop#1\repeat{\def\body{#1}\iterate}¤\idx*\iterate\idx*\loop¤ +\def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next} +\let\repeat=\fi- + +Il est clair que la macro \idx\loop est à argument délimité (le délimiteur est \idx\repeat) et stocke dans la macro \idx\body tout ce qui se trouve entre \idx\loop et \idx\repeat (que l'on appelle « corps de la boucle») avant d'appeler la macro \idx\iterate. On remarque également qu'aucune macro n'est déclarée \idx\long et donc il faudra proscrire la primitive \idx[|etc]\par\forbidindex\par{} entre \idx\loop et \idx\repeat. Ensuite, l'idée est que ce qui se trouve entre \idx\loop et \idx\repeat, stocké dans \idx\body, \emph{doit} contenir un test. Ce test, s'il est positif, autorisera la boucle exécuter une itération de plus tandis que s'il est négatif, il constitue la \emph{condition d'arrêt} de la boucle qui lui commande de s'arrêter. Cela suppose que le corps de la boucle modifie un paramètre pris en compte par le test afin que ce dernier devienne à un moment négatif pour permettre la sortie de la boucle et éviter ainsi une boucle infinie. + +Le mécanisme est assez facile à saisir : ce test sera exécuté lorsque \idx\iterate puis \idx\body seront développés. Si ce test est positif, \verb|\next| est rendue \idx\let-égale à \idx\iterate et sinon, elle est rendue \idx\let-égale à \idx[|etc]\relax\forbidindex\relax. Cette macro \verb|\next| est ensuite appelée après être sorti du test, ce qui rend la récursivité terminale\idx*{récursivité!terminale}. + +La structure de la boucle est donc la suivante : + +\centrecode-\loop + <code a> + <test> + <code b> +\repeat- + +\noindent où les \verb|<code a>| et \verb|<code b>| sont facultatifs, c'est-à-dire que l'un ou l'autre peuvent être réduit à 0 token. Il est en revanche important de comprendre que \verb|<code a>| est toujours exécuté au moins une fois puisqu'il vient avant le test. + +De par sa construction, il est également important de remarquer que ce qui se trouve entre \idx\loop et \idx\repeat ne doit pas contenir un test accompagné d'un \verb|\else| puisqu'un \verb|\else| est déjà écrit en dur dans la macro \idx\iterate. Si l'utilisateur écrivait un \verb|\else| par mégarde, cela provoquerait une erreur de type «\verb|extra \else|» lorsque \TeX{} atteindrait le second \verb|\else|. Malgré cette petite contrainte, cette structure est très souple du fait que le test peut se trouver où l'on veut entre \idx\loop et \idx\repeat. + +Enfin, on constate à la dernière ligne que \idx\repeat est rendu \idx\let-égal à \verb|\fi| ce qui a pour conséquence que \idx\repeat sera vu par \TeX{} comme un \verb|\fi| et donc pris en compte lors du comptage interne de \TeX{} des tests, \verb|\else| et des \verb|\fi|. En d'autres termes, le \idx\repeat sera bien apparié avec le \verb|<test>| se trouvant entre \idx\loop et \idx\repeat et par conséquent, la boucle peut être mise dans les branches d'un test sans provoquer de mauvais appariements entre les tests et les \verb|\fi|. Une structure de ce type est donc tout à fait envisageable : + +\centrecode-<test> + <code 1> + \loop + ... + \repeat + <code 2> +\else + <code 3> + \loop + ... + \repeat + <code 4> +\fi- + +Voici à titre d'exemple comment on peut programmer la macro §\compte vue dans un exercice du chapitre~\ref{compte} : + +\showcode/\newcount\xx % définit un compteur utilisé dans la boucle¤\idx*\newcount¤ +\def\compte#1{%¤§*\compte¤ + \xx=0 % initialise le compteur + \loop% début de la boucle¤\idx*\loop¤ + \advance\xx1 % incrémente le compteur¤\idx*\advance¤ + \ifnum\xx<#1 % s'il est inférieur à la borne + \number\xx , % affiche le nombre et la virgule¤\idx*[|etc]\number\forbidindex\number¤ + \repeat% et recommence¤\idx*\repeat¤ + \ifnum#1>0 \number\xx\relax\fi% si le nombre #1 est >0, afficher le dernier nombre + } +a) \compte{1}.\qquad b) \compte{-3}.\qquad c) \compte{7}.¤§*\compte¤/ + +\section{Implémentation de \LaTeX} +Le \idx[!\LaTeX]{format}\LaTeX{} redéfinit la boucle \idx\loop\ldots\idx\repeat de cette façon : + +\centrecode-\long\def\loop#1\repeat{%¤\idx*\long\idx*\loop\idx*\repeat¤ + \def\iterate{#1\relax\expandafter\iterate\fi}%¤\idx*\iterate¤ + \iterate + \let\iterate\relax}¤\idx*\iterate¤ +\let\repeat=\fi- + +La macro \idx\loop est déclarée \idx\long et l'on fait l'économie de la macro \idx\body en déclarant \idx\iterate comme une « sous-macro » de \idx\loop de telle sorte que l'argument \verb|#1| lui soit accessible. En considérant que \verb|#1| contient un test, on voit clairement que ce test peut être accompagné d'un \verb|\else| puisque le code de la macro \idx\iterate ne comporte pas cette primitive mais seulement un \verb|\fi|. Le plus simple est de faire fonctionner la boucle dans les cas où son corps contient un \verb|\else| ou pas :\medbreak + +\noindent +\begin{minipage}[t]{.5\linewidth} +\footnotesize +\hfill Sans \verb|\else|\hfill\null +\centrecode-\loop + <code a> + <test> + <code b> +\repeat- +\end{minipage}% +\begin{minipage}[t]{.5\linewidth} +\footnotesize +\hfill Avec \verb|\else|\hfill\null +\centrecode-\loop + <code a> + <test> + <code b> + \else + <code c> +\repeat- +\end{minipage}\medbreak + +Voilà ce qui est stocké dans la macro \idx\iterate dans chaque cas :\medbreak + +\noindent +\begin{minipage}[t]{.5\linewidth} +\footnotesize +\hfill Sans \verb|\else|\hfill\null +\centrecode-<code a> +<test> + <code b>\relax + \expandafter\iterate +\fi- +\end{minipage}% +\begin{minipage}[t]{.5\linewidth} +\footnotesize +\hfill Avec \verb|\else|\hfill\null +\centrecode-<code a> +<test> + <code b> +\else + <code c>\relax + \expandafter\iterate +\fi- +\end{minipage}\medbreak + +Il est maintenant facile de constater que si le corps de la boucle contient un \verb|\else|, c'est la branche du test négatif (après le \verb|\else|) qui maintiendra la récursivité. En revanche, s'il n'y a pas de \verb|\else|, c'est la branche du test positif qui la maintient. Autrement dit, la condition d'arrêt est un test \emph{négatif} s'il n'y a pas de \verb|\else| alors que c'est un test \emph{positif} s'il y en a. + +Malgré ce petit manque de cohérence, on peut dire que l'implémentation \LaTeX{} est supérieure à celle de plain-\TeX{} puisque la souplesse de pouvoir mettre de \verb|\else| dans la boucle rend parfois service, sans compter que l'on peut également faire appel à \tidx{unless} pour échanger les branches du test. + +\section{Imbrications de boucles} +Malgré leur côté pratique, ni l'implémentation \TeX{} ni celle de \LaTeX{} ne permettent l'imbrication de boucles \idx\loop\ldots\idx\repeat, principalement à cause de deux choses : + +\begin{itemize} + \item si deux boucles étaient imbriquées, le \idx\repeat de la boucle intérieure serait pris comme fin de la boucle extérieure puisqu'avec les arguments délimités, la première occurrence du délimiteur est prise en compte ; + \item la macro \idx\iterate serait redéfinie par la boucle intérieure. +\end{itemize} + +Avec la syntaxe actuelle, aucune implémentation facile à programmer et n'utilisant pas de groupe permet d'imbriquer des boucles \idx\loop \idx\repeat n'est faisable. La difficulté est de trouver ce qui se trouve entre un \idx\loop et son \idx\repeat apparié ! Nous allons donc tenter de bâtir une boucle §\xloop\verb|...|§\xrepeat, fonctionnant de la même façon que \verb|\loop...\repeat| sauf que l'imbrication des boucles sera possible. + +Pour commencer, nous allons nous attacher à collecter le code compris entre un §\xloop et son §\xrepeat apparié. Ce code sera stocké dans la macro \verb|\xiterate| et simplement \emph{affiché} : + +\begin{algo} + \item définir un compteur \verb|\cnt@repeat| qui comptabilisera les occurrences de §\xrepeat rencontrées, et l'initialiser à 0. Initialiser \verb|\xiterate| à vide; + \item ajouter à \verb|\xiterate| tout le code qui se trouve jusqu'au prochain §\xrepeat; + \item stocker dans \verb|\cnt@loop| le nombre de fois que §\xloop est contenu dans le texte de remplacement de \verb|\xiterate|; + \item tester si \verb|\cnt@loop|${}={}$\verb|\cnt@repeat|; + \begin{algo} + \item si le test est positif, afficher le texte de remplacement de \verb|\xiterate| à l'aide \idx\detokenize; + \item sinon, incrémenter \verb|\cnt@repeat|, ajouter §\xrepeat à \verb|\xiterate| et retourner en 2. + \end{algo} +\end{algo} + +Voici un code \TeX{} qui transcrit cet algorithme, où nous nous servons des macros §\addtomacro et §\ifin vues précédemment. Nous utiliserons aussi la macro \verb|\cnttimestocs| vue dans la solution à la page~\pageref{cnttimes} : + +\showcode/\catcode`\@11 +\newcount\cnt@repeat % définit le compteur de \xrepeat¤\idx*\newcount¤ + +\def\xloop{%¤§*\xloop¤ + \def\xiterate{}% initialiser \xiterate à vide + \cnt@repeat\z@% initialiser le compteur de \xrepeat¤\idx*\z@¤ + \xloop@i% aller à la macro récursive +} + +\long\def\xloop@i#1\xrepeat{%¤\idx*\long §*\xrepeat¤ + \addtomacro\xiterate{#1}% ajoute ce qui est avant le premier \xrepeat¤§*\addtomacro¤ + \exparg\cnttimestocs{\xiterate}\xloop\cnt@loop + % combien de \xloop dans \xiterate¤§*\cnttimestocs¤ + \ifnum\cnt@loop=\cnt@repeat\relax + \expandafter\firstoftwo\else\expandafter\secondoftwo¤§*\firstoftwo§*\secondoftwo¤ + \fi + {% autant que de \xrepeat -> \detokenize pour afficher + "\detokenize\expandafter{\xiterate}"% + } + {\addtomacro\xiterate\xrepeat% sinon, ajouter ce \xrepeat + \advance\cnt@repeat by 1% incrémenter le compteutr de \xrepeat¤\idx*\advance¤ + \xloop@i% et chercher le prochain \xrepeat + }% +} +\let\xrepeat\fi +\catcode`\@12 +\xloop a\xloop b\xloop 12\xrepeat c\xrepeat \xloop X\xrepeat\xrepeat¤§*\xloop§*\xrepeat¤/ + +Cela fonctionne bien : tout ce qui se trouve entre le premier §\xloop et son §\xrepeat apparié (qui est le dernier ici) est bien affiché. Maintenant, au lieu de simplement détokénizer le texte de remplacement de \verb|\xiterate|, nous allons mimer avec cette macro ce que fait l'implémentation \LaTeX{}. De plus, au lieu d'écrire \verb|\xiterate|, il faudra écrire partout dans le code \verb|\xiterate@<n>| de telle sorte que les imbrications de boucles ne redéfinissent pas cette macro et aient chacune la leur. Le nombre \verb|<n>| est le compteur d'imbrications stocké dans le compteur \verb|\cnt@nested|. Celui-ci est initialisé à 0 en dehors de toute macro, incrémenté à chaque fois que §\xloop est rencontré et décrémenté lors de la sortie de boucle. + +Bien entendu, pour former la macro \verb|\xiterate@<n>|, il va falloir faire appel à la paire \idx\csname\ldots\verb|\endcsname| et donc, alourdir sensiblement le code : + +\showcode/\catcode`\@11 +\newcount\cnt@repeat¤\idx*\newcount¤ +\newcount\cnt@nested +\cnt@nested=0 % compteur d'imbrications +\def\xloop{%¤§*\xloop¤ + \global\advance\cnt@nested by 1 % augmente le compteur d'imbrications¤\idx*\global\idx*\advance¤ + \expandafter\def\csname xiterate@\number\cnt@nested\endcsname{}% + \cnt@repeat\z@% initialise le compteur de \xrepeat à 0¤\idx*\z@¤ + \xloop@i% aller à la macro \xloop@i +} +\long\def\xloop@i#1\xrepeat{%¤§*\xrepeat\idx*\long¤ + \expandafter\addtomacro\csname xiterate@\number\cnt@nested\endcsname{#1}%¤\defline\eee§*\addtomacro¤ + \expandafter\expandafter\expandafter\cnttimestocs\expandafter\expandafter\expandafter¤§*\cnttimestocs¤ + {\csname xiterate@\number\cnt@nested\endcsname}\xloop\cnt@loop¤\idx*\csname\idx*\endcsname¤ + \ifnum\cnt@loop=\cnt@repeat\relax + \expandafter\firstoftwo\else\expandafter\secondoftwo¤§*\firstoftwo§*\secondoftwo¤ + \fi + {\expandafter\eaddtomacro\csname xiterate@\number\cnt@nested\expandafter\endcsname¤\defline\bbb§*\eaddtomacro¤ + {\expandafter\expandafter\csname xiterate@\number\cnt@nested\endcsname\fi}% + %\expandafter\show\csname xiterate@\number\cnt@nested\endcsname¤\defline\aaa\idx*\show¤ + \csname xiterate@\number\cnt@nested\endcsname¤\defline\ddd¤ + \letname{xiterate@\number\cnt@nested}\relax¤§*\letname¤ + \global\advance\cnt@nested by -1¤\defline\ccc\idx*\global\idx*\advance¤ + } + {\expandafter\addtomacro\csname xiterate@\number\cnt@nested\endcsname\xrepeat¤§*\addtomacro§*\xrepeat¤ + \advance\cnt@repeat by 1 ¤\idx*\advance¤ + \xloop@i + }% +}% +\let\xrepeat\fi¤§*\xrepeat¤ +\catcode`\@12 + +\newcount\cntxx \cntxx=1 % compteur des lignes +\newcount\cntyy¤\idx*\newcount¤ +\xloop¤§*\xloop¤ + \cntyy=1 % compteur des colonnes + \xloop¤§*\xloop¤ + (\number\cntxx,\number\cntyy)% affiche "(ligne,colonne)" + \ifnum\cntyy<5 % tant que colonne<5 + \advance\cntyy1 % incrémente colonne + , % <- affiche ", " + \xrepeat% et recommence + \ifnum\cntxx<3 % tant que ligne<3 + \advance\cntxx1 % incrémente ligne + \par % va à la ligne +\xrepeat% et recommence¤§*\xrepeat¤/ + +L'utilisation de \verb|\csname...\endcsname| rend la lecture du code bien plus difficile puisqu'elle implique le développement d'un pont d'\idx\expandafter pour faire naitre une macro puis pour accéder à son texte de remplacement d'une macro (lignes \nos\eee{} à \number\eee+3\relax). Malgré cela, intrinsèquement, le code est identique à celui vu précédemment. La seule différence se trouve entre les lignes \nos\bbb{} à \no\ccc. En effet, aux lignes \nos\bbb{}-\number\numexpr\bbb+1\relax{}, au lieu de détokénizer la macro \verb|\xiterate@<n>| pour simplement l'afficher comme on le faisait en analyse préalable, on ajoute au texte de remplacement de cette macro «\idx\expandafter» suivi du nom de la macro, c'est-à-dire \verb|\xiterate@<n>| que l'on fait suivre d'un \verb|\fi|. Ainsi, le texte de remplacement de \verb|\xiterate@<n>| est finalement : + +\centrecode-<ce qui est entre \xloop et \xrepeat> \expandafter\xiterate@<n> \fi- + +On peut d'ailleurs prendre connaissance du texte de remplacement de la macro \verb|\xiterate@<n>| en dé-commentant la ligne \no\aaa{} pour laisser à la primitive \idx\show l'écrire dans le \idx[!log]{fichier} \verb|log|. Pour \verb|\xiterate@1|, on obtient : + +\centrecode|\xiterate@1=macro:-> +\cntyy =1 +\xloop¤§*\xloop¤ + (\number \cntxx ,\number \cntyy ) + \ifnum \cntyy <5 , + \advance \cntyy 1 +\xrepeat¤§*\xrepeat¤ +\ifnum \cntxx <3 + \advance \cntxx 1 + \par + \expandafter\xiterate@1 +\fi.| + +\noindent et pour \verb|\xiterate@2| : + +\centrecode|\xiterate@2=macro:-> +(\number \cntxx ,\number \cntyy ) +\ifnum \cntyy <5 , +\advance \cntyy 1 + \expandafter\xiterate@2 +\fi.| + +\noindent C'est exactement ce que fait l'implémentation \LaTeX{} de la boucle \idx\loop\ldots\idx\repeat avec la macro \idx\iterate. En poursuivant le parallèle avec la boucle \LaTeX{}, il suffit ensuite de lancer la macro \verb|\xiterate@<n>| à la ligne \no\ddd{} et une fois la boucle terminée, on rend \verb|\iterate@<n>| \idx\let-égale à \idx\relax à la ligne \no\number\numexpr\ddd+1\relax. Le tout se termine avec la décrémentation du compteur d'imbrication à la ligne \no\ccc. + +\chapter{Une boucle « foreach in »}\label{doforeach} +\section{Une première approche} +Nous allons construire une boucle où une « variable » --~qui sera comme précédemment une séquence de contrôle~-- prendra successivement toutes les valeurs d'une liste de valeurs, pas nécessairement numériques, séparées par des virgules et à chaque itération, un code sera exécuté. La syntaxe de cette boucle, initiée par la macro §\doforeach[|(], sera\footnote{La commande \texttt{\string\foreach} est définie par le \idx[!pgf]{package} «\texttt{pgf}» et pour éviter tout conflit, il vaut donc mieux choisir un autre nom.} : + +\centrecode-\doforeach\<macro>\in{<valeur 1>,<valeur 2>,...,<valeur n>}{<code>}- + +\noindent où bien entendu, le \verb|<code>| peut contenir la \verb|\<macro>|. + +La méthode, relativement simple, va être de lire toutes les valeurs une première fois, y ajouter à la fin une valeur reconnaissable (un \idx{quark}) et par la suite, lire les valeurs une par une jusqu'à ce qu'apparaisse le \idx{quark}. Nous mettons ici en \oe uvre la méthode recommandée pour lire une série d'arguments (voir page~\pageref{lire.arguments}) : + +\begin{algo} + \item lire la liste des valeurs et lui ajouter «\verb-,\end@foreach,-», où \verb-\end@foreach- est un \idx{quark}. + + Sauvegarder le \verb|<code>| dans une macro (que nous appelons \verb|\loop@code|); + \item lire la valeur qui se trouve avant la virgule et l'assigner à \verb|\<macro>| , + \item si \verb|\<macro>|=\verb-\end@foreach-, finir le processus ; + \item sinon exécuter le \verb|<code>| et retourner en 2. +\end{algo} + +Le point 1 sera dévolu à une macro chapeau chargée de faire les assignations préalables. Les autres points seront contenus dans une macro récursive à argument délimité. Choisissons d'externaliser la macro récursive (c'est-à-dire de la définir en dehors du texte de remplacement de la macro chapeau). Assurons-nous que la macro récursive peut accéder à tous les arguments de la macro chapeau. Aucun problème pour la liste des valeurs qu'elle lira comme argument au fur et à mesure. Le \verb|<code>| ne pose pas de problème puisqu'il est sauvegardé dans \verb|\loop@code|. La variable \verb|\<macro>| n'étant pas sauvegardée, prenons comme solution de la transmettre comme argument à la macro récursive : cette macro (qui n'est qu'un seul token) viendra comme premier argument non délimité, avant la liste des valeurs. + +\showcode/\catcode`\@11 +\def\end@foreach{\end@foreach}% définit un quark¤\idx*{quark}¤ +\long\def\doforeach#1\in#2#3{%¤\idx*\long¤ + \def\loop@code{#3}% assigne le <code> à \loop@code + % appel à \doforeach@i : mettre la macro #1 en premier, puis la liste de valeurs #2 + \doforeach@i#1#2,\end@foreach,% ajouter à la fin ",\end@foreach," + % une fois les boucles finies, neutraliser les macros déjà définies + \let#1\relax \let\loop@code\relax +} + +\long\def\doforeach@i#1#2,{% #1=\<macro> #2=valeur courante¤\defline\aaa\idx*\long¤ + \def#1{#2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach#1% si \end@foreach n'est pas atteint + \antefi% amène le \fi ici¤§*\antefi¤ + \loop@code% exécute le code + \doforeach@i#1% et recommencer + \fi +} +\catcode`@12 +\doforeach\x\in{a,bcd,{efg},hi}{\meaning\x.\par}/ + +La ligne \no\aaa{} montre que la macro \verb|\doforeach@i| admet comme argument \emph{non délimité} \verb|#1| alors que la valeur en cours est l'argument \verb|#2| délimité par la virgule. L'inconvénient, lié à la lecture d'argument, est que si une \verb|<valeur>| est constituée d'un texte entre accolades, ce texte est dépouillé de ses accolades et assigné à la variable. Si l'on souhaitait éviter cet écueil, il faudrait mettre un token (par exemple un \idx\relax) avant chaque \verb|<valeur>| et faire en sorte de manger ce \idx\relax avec \verb|\gobone| avant d'assigner la \verb|<valeur>| à la variable. Par souci de simplicité, cette man\oe uvre ne sera pas programmée. + +\begin{exercice} +On pourrait vouloir modifier le séparateur de liste (qui est la virgule par défaut) pour mettre des nombres décimaux dans la liste d'arguments, par exemple. + +Créer une macro §\defseplist dont l'argument est le séparateur voulu et modifier le code vu ci-dessus pour que la macro §\doforeach tienne compte du séparateur ainsi défini. +\solution +L'idée est de faire de §\defseplist\verb|<séparateur>| une macro «enveloppante» qui va définir, à chaque fois qu'elle sera appelée, les deux macros §\doforeach et \verb|\doforeach@i|, toutes les deux dépendantes de l'argument \verb|#1| de §\defseplist. Dans le code déjà vu, la virgule va être replacée par \verb|#1| et les autres «\verb|#|» qui s'appliquent aux arguments de §\doforeach et \verb|\doforeach@i| vont être doublés pour tenir compte de l'imbrication des macros : + +\showcode/\catcode`\@11 +\def\end@foreach{\end@foreach} +\def\defseplist#1{%¤§*\defseplist¤ + \long\def\doforeach##1\in##2##3{%¤\idx*\long¤ + \def\loop@code{##3}% assigne le <code> à \loop@code + \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i + % après être sorti des boucles, neutraliser les macros déjà définies + \let\loop@code\relax \let##1\relax + }% + \long\def\doforeach@i##1##2#1{%¤\idx*\long¤ + \def##1{##2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach##1% si la fin n'est pas atteinte¤\tidx*{unless}¤ + \antefi% amène le \fi ici¤§*\antefi¤ + \loop@code% exécute le code + \doforeach@i##1% et recommencer + \fi + }% +} +\defseplist{,}% définit les macros avec le séparateur par défaut¤§*\deseplist¤ +\catcode`@12 + +\doforeach\x\in{a,bcd,efg}{Argument courant : "\x".\par}\medbreak¤\idx*\medbreak¤ + +\defseplist{--}% séparateur "--"¤§*\deseplist¤ +\doforeach\nn\in{4,19--0,5--8,575}{Nombre lu : "\nn"\par}/ +\end{exercice} + +\section{Rendre possible l'imbrication} +Il nous reste à rendre possible l'imbrication de boucles §\doforeach et pour cela, comme précédemment avec §\for et §\xloop, nous allons définir un compteur qui sera chargé de compter le niveau d'imbrication et qui fera partie du nom de la macro chargée de stocker le \verb|<code>|. Nous prendrons «\verb|\loop@code@<n>|» : + +\showcode/\catcode`\@11 +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication¤\idx*\newcount¤ +\def\end@foreach{\end@foreach} + +\def\defseplist#1{%¤§*\deseplist¤ + \long\def\doforeach##1\in##2##3{%¤\idx*\long¤ + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication¤\idx*\global¤ + \defname{loop@code@\number\cnt@nest}{##3}% assigne le <code> à \loop@code@<n>¤§*\defname¤ + \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i + % une fois fini : + \let##1\empty% dans ce cas, neutraliser les macros déjà définies¤\idx*\empty¤ + \letname{loop@code@\number\cnt@nest}\empty%¤§*\letname¤ + \global\advance\cnt@nest-1 %sortie de boucle : décrémenter le compteur d'imbrication¤\idx*\global\idx*\advance¤ + }% + \long\def\doforeach@i##1##2#1{%¤\idx*\long¤ + \def##1{##2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach##1% tant que la fin n'est pas atteinte¤\tidx*{unless}¤ + \antefi% amène le \fi ici¤§*\antefi¤ + \csname loop@code@\number\cnt@nest\endcsname% exécute le code¤\defline\aaa¤ + \doforeach@i##1% et recommencer + \fi + }% +} +\catcode`@12 +\defseplist{,}¤§*\deseplist¤ + +\doforeach\xxx\in{1,2,3}{% + Ligne \xxx{} : \doforeach\yyy\in{a,b,c,d,e}{(\xxx,\yyy)}.\par +}/ + +Profitons également de la simplicité relative de ce code pour implémenter une fonctionnalité qui pourrait être d'un grand secours. Si la « variable » est déjà définie lorsque la boucle est rencontrée, il serait judicieux de la sauvegarder pour la restaurer une fois la boucle terminée. Imaginons en effet qu'un utilisateur imprudent --~ou inconscient~-- nomme sa variable \idx\par par analogie avec le mot « paramètre » et construise une boucle de cette façon : + +\centrecode-\doforeach\par\in{a,b,c,y,z}{$\par_i$}- + +La primitive \idx\par serait silencieusement redéfinie à l'entrée de la boucle, pour être rendue \idx\let-égale à \idx\relax à la fin ce qui aurait pour effet de rendre \TeX{} incapable de former un paragraphe. On imagine les dégâts, d'autant plus incompréhensibles que le tout se passe silencieusement. Il nous faut donc tester si la variable existe au début de la boucle et si tel est le cas, la sauvegarder avec \idx\let pour la restaurer à la fin du processus. + +Il existe un test \tidx{ifdefined}\verb|\<commande>| de \idx\eTeX qui est vrai si la \verb|\<commande>| est définie et faux sinon. Ce test ne modifie pas la table de hashage\footnote{Les tests \texttt{\textbackslash @ifundefined\{\codeelement{nom}\}} et \texttt{\textbackslash @ifdefinable\textbackslash\codeelement{nom}} de \LaTeX{} forment la séquence de contrôle \texttt{\textbackslash\codeelement{nom}} à l'aide de \texttt{\textbackslash csname} et testent si elle est égale à \texttt{\textbackslash relax} auquel cas, la séquence de contrôle est considérée non définie. Cette méthode présente deux gros inconvénients : +\begin{itemize} + \item toute macro existante et \texttt{\textbackslash let}-égale à \texttt{\textbackslash relax} est considérée par ces tests comme étant non définie; + \item par l'utilisation de \texttt{\textbackslash csname}, une macro non définie devient définie et \texttt{\textbackslash let}-égale à \texttt{\textbackslash relax}. Un autre dégât collatéral est que la table de hashage est modifiée puisqu'une nouvelle macro est créée. +\end{itemize}}. Il existe aussi un test de \idx\eTeX un peu similaire \tidx{ifcsname}\verb|<nom d'une commande>|\idx\endcsname, qui forme la \verb|<commande>| comme le ferait \idx\csname puis teste si elle existe, sans modifier la table de hashage. + +La méthode sera ici de mettre à profit \tidx{ifdefined} pour tester si la variable existe et la sauvegarder dans \verb|\saved@var@<n>| si le test est positif. À la sortie de la boucle, il suffira de faire la man\oe uvre inverse pour restaurer la variable soit à \idx\relax, soit à ce qu'elle était avant si elle existait auparavant. + +Dans le code ci-dessous, la variable a été nommée «\verb|\par|» ce qui est une chose à ne pas faire. Même si un dispositif a été prévu pour que \verb|\par| soit restaurée à la fin du processus, redéfinir une primitive, surtout aussi primordiale que \verb|\par|, \emph{est un danger qu'il faut à tout prix éviter de courir}. + +\showcode/\catcode`\@11 +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication¤\idx*\newcount¤ +\def\end@foreach{\end@foreach} +\long\def\save@macro#1{% sauvegarde la macro #1¤\idx*\long¤ + \ifdefined#1% si la macro #1 est déjà définie...¤\tidx*{ifdefined}¤ + \letname{saved@var@\number\cnt@nest}#1% ...la sauvegarder¤§*\letname¤ + \fi +} +\long\def\restore@macro#1{% restaure la macro #1¤\idx*\long¤ +% le \csname donne \relax si la sauvegarde n'a pas été faite + \expandafter\let\expandafter#1\csname saved@var@\number\cnt@nest\endcsname +} +\def\defseplist#1{%¤§*\deseplist¤ + \long\def\doforeach##1\in##2##3{%¤\idx*\long¤ + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication¤\idx*\global\idx*\advance¤ + \save@macro##1% sauvegarde la macro ##1 + \defname{loop@code@\number\cnt@nest}{##3}% assigne le <code> à \loop@code@<n>¤§*\defname¤ + \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i + % une fois fini, neutraliser la macro contenant le <code> + \letname{loop@code@\number\cnt@nest}\empty¤\idx*\empty§*\letname¤ + \restore@macro##1% restaurer la \<macro> + \global\advance\cnt@nest-1 % et décrémenter le compteur d'imbrication¤\idx*\global¤ + }% + \long\def\doforeach@i##1##2#1{%¤\idx*\long¤ + \def##1{##2}% stocke la valeur en cours dans la \<macro> + \unless\ifx\end@foreach##1% si la fin n'est pas atteinte¤\tidx*{unless}¤ + \antefi% amène le \fi ici¤§*\antefi¤ + \csname loop@code@\number\cnt@nest\endcsname% exécute le code¤\defline\aaa¤ + \doforeach@i##1% et recommencer + \fi% + }% +} +\catcode`@12 +\defseplist{,}¤§*\deseplist¤ +\doforeach\par\in{a,b,c,y,z}{$\par_i$} + +\meaning\par% \par doit être redevenu une primitive¤\idx*\meaning¤ + +\doforeach\xxx\in{1,2,3}{% + Ligne \xxx{} : \doforeach\yyy\in{a,b,c,d,e}{(\xxx,\yyy)}.\par +}/ + +\section{Boucle à deux variables} +Une amélioration de la macro §\doforeach serait de détecter si la variable est seule, comme envisagé précédemment, ou si l'utilisateur souhaite avoir un \emph{couple} de variables auquel cas, il écrirait : + +\centrecode-\doforeach\<macro1>/\<macro2>\in{x1/y1,x2/y2,...,xn/yn}{<code>}- + +\noindent Chacune des variables \verb|\<macro1>| et \verb|\<macro2>| prendrait successivement les valeurs appariées spécifiées dans l'argument qui suit le \verb|\in|. + +L'idée va être de se servir de la macro §\ifin vue précédemment pour tester si l'argument délimité par «\verb|\in|» contient le caractère «\verb|/|» et agir en conséquence. Si le test est positif, il faut s'orienter vers une macro à argument délimité du type \verb|#1/#2| qui sépare les deux variables. Nous allons donc, selon l'issue du test \verb|\ifin{#1}{/}|, nous orienter vers la macro vue précédemment si le test est négatif ou vers une macro \verb|\doforeach@ii| avec des arguments délimités appropriés. Nous allons profiter de cette nouvelle fonctionnalité pour améliorer et optimiser le code. +\grandsaut + +En premier lieu, la macro \verb|\save@macro| effectuera \emph{dans tous les cas} la sauvegarde de la (ou des) variables avec \verb|\let|, que ces variables soient définies ou pas. La raison de ce choix, qui implique de se passer du test \tidx{ifdefined}, réside dans le fait que la variable est rendue \verb|\let|-égale à \idx\relax à la fin du processus : cela sera \emph{aussi} le cas si la variable est une macro indéfinie. En effet, si une macro $x$ est rendue \verb|\let|-égale à une macro $y$ non définie, $x$ devient également non définie. Par la suite, si $y$ est rendue \verb|\let|-égale à $x$ où la macro $x$ est construite via \idx\csname, la macro $y$ devient \verb|\let|-égale à \idx\relax + +\showcode/\let\foo=\djfhdsldv% \foo est rendue \let-égale à une macro indéfinie +a) \meaning\foo\par +% puis \djfhdsldv est rendue \let-égale à \foo (ou \foo est construit avec \csname) +\expandafter\let\expandafter\djfhdsldv\csname foo\endcsname +b) \meaning\djfhdsldv/ + +L'optimisation que nous pouvons envisager est d'éviter que la macro \idx\csname (qui est lente) ne se trouve dans une boucle comme c'était le cas à la ligne \no\aaa{} du code précédent. Pour cela, nous allons donner le code à exécuter comme argument des macros récursives. L'appel initial aux macros récursives aura donc la structure suivante : + +\centrecode-\doforeach@i{<code>}<var>x1,x2,...,xn,<quark>,- + +\noindent ou + +\centrecode-\doforeach@ii{<code>}<var1>/<var2>x1/y1,x2/y2,...xn/yn,<quark>/<quark>,- + +Ces macros ne liront à chaque itération que le \verb|<code>|, la (ou le couple de) variable(s), ainsi que la valeur courante \verb|xi| ou le couple de valeurs courant \verb|xi/yi|. Par conséquent, l'appel récursif sera de la forme + +\begin{centrage} + \small\verb|\doforeach@i{#1}#2|\quad ou\quad \verb|\doforeach@i{#1}#2/#3| +\end{centrage} + +\noindent où \verb|#2| est la variable et \verb|#2/#3| est le couple de variables. Comme les récursivités sont terminales, les valeurs sont prises dans le code qui reste à lire. + +Comme ces macros récursives effectuent un test \verb|\ifx| à chaque itération pour déterminer si le \idx{quark} marquant la fin des valeurs est atteint, nous devons nous garder de l'erreur qui consisterait à utiliser §\antefi alors que le \verb|<code>| se situe en §\antefi et \verb|\fi|. Il s'agirait d'une erreur, car si le \verb|<code>| contient un test, l'§\antefi (dont l'argument est délimité par \verb|\fi|) prendrait le \verb|\fi| du \verb|<code>| comme délimiteur et romprait l'appariement correct entre \verb|\ifx| et son \verb|\fi|. + +Enfin, il nous faut inventer un moyen de créer la macro §\doforeachexit[|(], que l'utilisateur peut écrire dans le \verb|<code>| et qui termine prématurément toutes les boucles §\doforeach en cours d'exécution. Cette macro doit annuler l'appel récursif vu ci-dessus. L'idée directrice est de modifier ces appels récursifs de cette manière : + +\begin{centrage} + \small\verb|\allow@recurse{\doforeach@i{#1}#2}|\par ou\par \verb|\allow@recurse{\doforeach@i{#1}#2/#3}| +\end{centrage} + +\noindent La macro \verb|\allow@recurse| sera rendue \idx\let-égale à §\identity au début de la boucle pour permettre par défaut que l'appel récursif se fasse. Si l'utilisateur insère §\doforeachexit dans le \verb|<code>|, \verb|\allow@recurse| doit être modifiée pour que cette dernière mange tout ce qui reste. « Tout » signifie l'appel récursif \emph{et} toutes les valeurs (ou couples de valeurs) restant à lire. Comme ce « tout» se termine forcément par le \idx{quark} \verb|\end@foreach| et le séparateur qui ont été insérés par l'appel initial, une macro \verb|\fordib@recurse| à argument délimité effectuant cette tâche est facile à créer : + +\centrecode-\long\def\fordib@recurse##1\end@foreach#1{}- + +\noindent Une fois tout ceci mis en place, la macro §\doforeachexit n'a plus qu'à rendre \verb|\allow@recurse| \idx\let-égale à \verb|\fordib@recurse|. + +\showcode|\catcode`\@11 +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication¤\idx*\newcount¤ +\def\end@foreach{\end@foreach} +\long\def\save@macro#1#2{\letname{saved@var@\number\cnt@nest#1}#2}¤\idx*\long§*\letname¤ +\long\def\save@macro@i#1/#2{\save@macro{a}#1\save@macro{b}#2} +\long\def\restore@macro#1#2{%¤\idx*\long¤ + \expandafter\let\expandafter#2\csname saved@var@\number\cnt@nest#1\endcsname +} +\long\def\restore@macro@i#1/#2{\restore@macro{a}#1\restore@macro{b}#2}¤\idx*\long¤ +\def\defseplist#1{%¤§*\deseplist¤ + \long\def\doforeach##1\in##2##3{%¤\idx*\long §*\doforeach\idx*\long¤ + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication¤\idx*\global\idx*\advance¤ + \global\let\allow@recurse\identity% permet l'appel récursif plus bas¤\idx*\global§*\identity¤ + \ifin{##1}{/}% si ##1 contient "/"¤§*\ifin¤ + {\save@macro@i##1% sauvegarde les macros + \doforeach@ii% appeler la macro récursive avec les arguments + {##3}% 1) code à exécuter + ##1% 2) variables sous la forme \<macro1>/\<macro2> + ##2#1\end@foreach/\end@foreach#1% puis la liste ##2 suivie de + % ",\end@foreach/\end@foreach," + \restore@macro@i##1% une fois sorti de toutes les boucles, restaurer les macros + }% si ##1 ne contient pas "/" + {\save@macro{}##1% sauvegarde la macro + \doforeach@i% appeler la macro récursive + {##3}% mettre en premier le <code> + ##1% puis la variable ##1 en 2e position + ##2#1\end@foreach#1% enfin la liste ##2 suivie de ",\end@foreach," + \restore@macro{}##1% une fois sorti de toutes les boucles, restaurer la macro + }% + \global\advance\cnt@nest-1 % décrémente le compteur d'imbrications¤\idx*\global\idx*\advance¤ + }% + % ##1 = code à exécuter, ##2= variable, ##3=valeur courante + \long\def\doforeach@i##1##2##3#1{%¤\idx*\long¤ + \ifx\end@foreach##3% si la fin est atteinte + \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter:¤§*\identity¤ + {\def##2{##3}% fait l'assignation à la variable + ##1% le code puis + \allow@recurse{\doforeach@i{##1}##2}% recommencer + }% + }% + % ##1 = code à exécuter, ##2/##3= variables, ##4/##5=valeurs courantes + \long\def\doforeach@ii##1##2/##3##4/##5#1{%¤\idx*\long¤ + \ifx\end@foreach##4% si la fin est atteinte + \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter:¤§*\identity¤ + {\def##2{##4}\def##3{##5}% fait l'assignation des deux variables + ##1% le code puis + \allow@recurse{\doforeach@ii{##1}##2/##3}% recommencer + }% + }% + % macro qui, si elle remplace \allow@recurse, annule l'appel récursif + \long\def\forbid@recurse##1\end@foreach#1{}% tout manger jusqu'à "\end@foreach,"¤\idx*\long\defline\aaa¤ +} +\def\doforeachexit{\global\let\allow@recurse\forbid@recurse}¤\idx*\global¤ +\catcode`\@12 +\defseplist{,}¤§*\deseplist¤ +\doforeach\par\in{a,b,c,y,z}{$\par_i$}\medskip¤\idx*\medskip¤ + +\doforeach\sujet/\terminaison\in{Je/e,Tu/es,Il/e,Nous/ons,Vous/ez,Ils/ent} + {\sujet\ programm\terminaison{} en \TeX\par}\medskip¤\idx*\medskip¤ + +Les voyelles lues sont : +\doforeach\ii\in{a,e,i,o,u,y}{\ii\if o\ii\doforeachexit\fi}.\medskip¤\idx*\medskip¤ + +\doforeach\xx\in{a,b,c,d,e,f} + {\doforeach\ii\in{1,2,3,4}{\xx\ii{} \ifnum\ii=3 \doforeachexit\fi}\par}| + +\begin{exercice} +La dernière boucle imbriquée montre que §\doforeachexit sort prématurément de toutes les boucles en cours. + +Comment faudrait-il modifier le code pour que §\doforeachexit ne sorte prématurément \emph{que} de la boucle dans laquelle elle a été appelée, sans modifier le déroulement des boucles de niveau supérieur ? +\solution +Il faut que \verb|\forbid@recurse|, en plus de manger tout jusqu'à la fin de la liste, rende de nouveau possibles les appels récursifs à venir dans d'éventuelles boucles mères. Par conséquent, elle doit redonner à \verb|\allow@recurse| l'équivalence avec §\identity qu'elle a par défaut. La ligne \no\aaa{} doit donc être + +\centrecode-\long\def\forbid@recurse##1\end@foreach#1{\global\let\allow@recurse\identity}-\medskip +\end{exercice}§*\doforeach[|)]§*\doforeachexit[|)]% + +\chapter{Dimensions et boites}\idx*[|(]{dimension} +Avec \TeX, tout ce qui touche de prés ou de loin à la typographie a une étroite proximité avec la géométrie et le placement des boites qui elles-mêmes, entretiennent de proches rapports avec les dimensions. + +Le parti a été pris de présenter les dimensions (fixes et étirables) dans un premier temps, quitte à ce que cette théorie, dénuée des objets auxquels elle s'applique, soit un peu rébarbative. Une fois que ces outils et leurs propriétés ont été décrits, les boites et les réglures seront examinées dans un deuxième temps. + +\section{Les dimensions fixes}\idx*[|(]{dimension fixe} +\idx*[!dimension|(]{registre}\begin{regle} +Une dimension fixe est utilisée pour mesurer une longueur prise au sens géométrique du terme. \TeX{} dispose de 256 \idx[!dimension]{registre}s de dimension (\numprint{32768} avec \idx\eTeX), chacun capable d'héberger une dimension. Pour faire référence à l'un de ces registres, on utilise la primitive \idx\dimen suivie de son numéro (sous la forme d'un \verb|<entier>|). + +Il est possible de demander l'allocation d'un registre inutilisé avec la macro de plain-\TeX{} \idx\newdimen\verb|\<macro>|. La \verb|\<macro>|, définie en coulisse par la primitive \idx\dimendef avec + +\centrecode-\global\dimendef\<macro>=<nombre>- + +sera par la suite équivalente à \idx\dimen\verb|<nombre>| où le \verb|<nombre>| est celui du prochain registre libre au moment de l'allocation. + +Un \verb|<registre de dimension>| est donc soit une \verb|\<macro>| préalablement définie à l'aide de \idx\newdimen, soit «\verb|\dimen<nombre>|» où \verb|<nombre>| est le numéro du registre de dimension auquel on souhaite faire référence. + +Pour assigner une dimension à un registre de dimension, la syntaxe est + +\centrecode-<registre de dimension>= <dimension>- + +\noindent où le signe \verb|=| et l'espace qui le suit sont facultatifs. Pour lire la \verb|<dimension>|, le développement maximal se met en marche et après développement, une \verb|<dimension>| est : + +\begin{itemize} + \item une dimension explicitement écrite, c'est-à-dire un \verb|<nombre>| (entier) ou un nombre décimal suivi d'espaces facultatifs et de deux lettres de catcode 11 ou 12 représentant une des unités de dimensions qu'accepte \TeX{} à savoir \texttt{pt}, \texttt{pc}, \texttt{in}, \texttt{bp}, \texttt{cm}, \texttt{mm}, \texttt{dd}, \texttt{cc}, \texttt{sp}, \texttt{em} et \texttt{ex}. Le séparateur décimal peut indifféremment être le point ou la virgule pourvu que son catcode soit 12. La lecture de la dimension s'arrête au premier token qui suit les deux caractères d'unité. Si ce token est un espace, celui est absorbé. + \item un \verb|<registre de dimension>|. Si ce registre est précédé d'un nombre décimal signé, la dimension obtenue sera celle du registre multipliée par le nombre signé ;\label{multiplication.dimension} + \item un \verb|<registre de ressort>| (voir la définition à la section suivante) auquel cas, la dimension retenue est égale à la composante non étirable du registre de ressort. Si ce registre est précédé d'un nombre décimal signé, la dimension obtenue sera la composante fixe du ressort multipliée par le nombre signé. +\end{itemize} + +La valeur absolue maximale d'une \verb|<dimension>| est contenue dans le registre de dimension \idx\maxdimen et vaut \texttt{16383.99999pt}, ce qui représente \texttt{\convertunit{\maxdimen}{cm}cm}. +\end{regle} + +Pour ses dimensions, \TeX{} n'est pas avare d'unités\idx*[!table des unités]{dimension}. Il en manipule 11 dont voici les définitions : + +\begin{centrage} +\small +\begin{tabular}{c>\ttfamily cr@{${}={}$}lr@{${}={}$}l} + \idx*{unité!table} Unité & \multicolumn1c{Abréviation} & \multicolumn2c{Définition} & \multicolumn2c{Exemple}\\\hline + point\idx*{unité!pt}\idx*{pt (unité)} & pt& \multicolumn2c{}&1 \verb|pt| & \showdim{1pt}\\ + pica\idx*{unité!pc}\idx*{pc (unité)} & pc & \numprint[pc]1&\numprint[pt]{12} & 1 \verb|pc| & \showdim{1pc}\\ + pouce\idx*{unité!in}\idx*{in (unité)} & in & \numprint[in]1&\numprint[pt]{72,27} & 1 \verb|in| & \showdim{1in}\\ + gros point\idx*{unité!bp}\idx*{bp (unité)} & bp & \numprint[bp]{72}&\numprint[in]1 & 1 \verb|bp| & \showdim{1bp}\\ + centimètre\idx*{unité!cm}\idx*{cm (unité)} & cm & \numprint[cm]{2,54}&\numprint[in]1 & 1 \verb|cm| & \showdim{1cm}\\ + millimètre\idx*{unité!mm}\idx*{mm (unité)} & mm & \numprint[mm]{10}&\numprint[cm]1 & 1 \verb|mm| & \showdim{1mm}\\ + point didot\idx*{unité!dd}\idx*{dd (unité)} & dd & \numprint[dd]{1157}&\numprint[pt]{1238} & 1 \verb|dd| & \showdim{1dd}\\ + cicéro\idx*{unité!cc}\idx*{cc (unité)} & cc & \numprint[cc]1&\numprint[dd]{12} & 1 \verb|cc| & \showdim{1cc}\\ + point d'échelle\idx*{unité!sp}\idx*{point d'échelle}\idx*{sp (unité)} & sp & \numprint[sp]{65536}&\numprint[pt]1 & $10^6$ \verb|sp| & \showdim{1000000sp}\\ + cadrat\idx*{unité!em}\idx*{em (unité)} & em & \multicolumn2c{} & \verb|1em| & \showdim{1em}\\ + hauteur d'\oe il\idx*{unité!ex}\idx*{ex (unité)} & ex & \multicolumn2c{} & \verb|1ex| & \showdim{1ex}\\\hline +\end{tabular} +\end{centrage} + +Les deux dernières unités dépendent de la police utilisée et de sa taille. Historiquement, \verb|1em| est la largeur de la lettre «M», mais le créateur de la fonte peut en décider autrement et donner au \verb|em| une valeur différente. Par exemple, dans la police en cours dans ce paragraphe, \verb|1em| vaut \texttt{\convertunit{1em}{mm}mm} alors que la largeur de la boite englobante de la lettre «M» vaut {\setbox0=\hbox{M}\texttt{\convertunit{\wd0}{mm}mm}}. + +La hauteur d'\oe il, ou \verb|ex| est censée représenter la hauteur des lettres sans hampe, ni jambage ni signe diacritique. Dans la police en cours, \verb|1ex| vaut \texttt{\convertunit{1ex}{mm}mm} et la hauteur de la boite englobante de la lettre «x» vaut \texttt{\setbox0=\hbox{x}\convertunit{\dimexpr\ht0 + \dp0 }{mm}mm}. +\grandsaut + +Les \verb|<registres de dimension>|\idx*[!dimension|)]{registre} sont comme les autres registres que nous avons déjà vus (registres d'entiers ou de tokens) : ce ne sont que des sortes de pointeurs vers une zone de mémoire où sont écrits les octets capables, selon un procédé interne à \TeX, de stocker le contenu du registre. Par conséquent, dans le code source, un \verb|<registre de dimension>| ne peut être écrit seul \emph{que} lorsque \TeX{} s'apprête à lire une dimension ou lors d'une assignation. Dans les autres cas, la primitive développable \idx\the, lorsqu'elle précède le registre, en donne le contenu sous forme de tokens de catcode 12\idx*{catcode!12 (autre)}. Lorsqu'on invoque \idx\the pour faire remonter le contenu d'un registre de dimension des entrailles de \TeX{}, on obtient toujours une dimension en points\idx*{unité!pt}\idx*{pt (unité)} où les caractères d'unité «\verb|pt|» ont un catcode de 12. La partie décimale est toujours affichée et éventuellement réduite à un 0, et le séparateur décimal est le point. Le nombre de chiffres est au maximum de 5 après la virgule : + +\showcode/\newdimen\dimA \newdimen\dimB% alloue deux registres de dimension¤\idx*\newdimen¤ +a) \dimA=59.5pt + \the\dimA\qquad% doit afficher 59.5pt¤\idx*\the¤ +b) \dimA=1.5cm + \the\dimA\qquad% convertit à l'affichage 1.5cm en pt +c) \dimB=7pt % assigne 7pt à \dimB + \dimA\dimB% rend \dimA égal à dimB + \the\dimA\qquad +d) \dimA=6\dimB% rend \dimA égal à 6 fois \dimB + \the\dimA¤\idx*\the¤/\grandsaut + +Bien qu'étant traduites en points, toutes ces dimensions ne sont que des multiples de l'unité de longueur que \TeX{} manipule en interne, le « \idx{point d'échelle} » (abréviation «\verb|sp|\idx*{unité!sp}\idx*{sp (unité)}» pour « scale point »\idx*{unité!sp}\idx*{sp (unité)}), plus petite dimension que \TeX{} peut manipuler et dont la longueur vaut approximativement \hbox{$5{,}4\times10^{-9}$}~mètre (soit 5,4 nanomètres). C'est extrêmement petit, de l'ordre de la dimension d'un petit virus. Cette dimension insécable, bien évidemment invisible à l'\oe il nu, donne aux calculs sur les longueurs de \TeX{} une grande précision qui restera très inférieure à ce qui peut être décelé à l'\oe il nu, même après plusieurs arrondis. + +\subsection{Opérations sur les dimensions} +Comme pour les entiers, des opérations sont disponibles pour les dimensions via les primitives \idx[|etc]\advance\forbidindex\advance{} pour l'addition, \idx\multiply et \idx\divide pour la multiplication et la division par un entier. + +Pour calculer $(25\text{\ttfamily pt}\div4+1,72\text{\ttfamily pt})\times3$, voici comment nous pourrions partir d'une dimension de 25pt, la diviser par 4, ajouter 1.72pt et multiplier le tout par 3 : + +\showcode|\newdimen\foo¤\idx*\newdimen¤ +\foo=25pt % assigne 25pt au registre \foo +\divide\foo 4 % le multiplie par 4¤\idx*\divide¤ +\advance\foo1.72pt % lui ajoute 1.72pt¤\idx*\advance¤ +\multiply\foo3 % le multiplie par 3¤\idx*\multiply¤ +\the\foo% affiche la dimension obtenue¤\idx*\the¤| + +\begin{exercice} +Écrire une macro §\removept qui retire l'unité d'une dimension explicite obtenue avec \idx\the. Si la partie décimale est nulle, elle ne sera pas affichée. + +\solution +Le premier réflexe est d'écrire une macro à argument délimité par «\verb|pt|» : + +\centrecode-\def\removept#1.#2pt{#1\ifnum#2>0 .#2\fi}-§*\removept + +Mais cela ne fonctionnera pas puisque les caractères «\verb|pt|» du \idx{texte de paramètre} n'ont pas le catcode 12\idx*{catcode!12 (autre)}. Il faut donc ruser un peu pour définir la macro. Nous pouvons par exemple utiliser une macro temporaire pour stocker «\verb|\def\removept#1.#2pt|» où les délimiteurs «\verb|pt|» ont un catcode égal à 12. Cette macro temporaire sera définie par un \idx\edef pour développer «\idx\string\verb| p|» et «\verb|\string t|» qui donnent les caractères «\verb|pt|» de catcode 12\idx*{catcode!12 (autre)} : + +\showcode/\begingroup% ouvrir un groupe + \edef\temp{\endgroup\def\noexpand\removept##1.##2\string p\string t}%¤§*\removept\idx*\string¤ +\temp{#1\ifnum#2>0 .#2\fi}% et le fermer avant de définir \removept +\newdimen\foo¤\idx*\newdimen¤ +a) \foo=15pt \expandafter\removept\the\foo\qquad¤\idx*\the¤ +b) \foo=3.14pt \expandafter\removept\the\foo¤§*\removept¤/ + +L'astuce consiste à inclure le \idx\endgroup dans le texte de remplacement de \verb|\temp| de telle sorte que lorsque \verb|\temp| se développe puis s'exécute, ce \idx\endgroup provoque la destruction de cette macro temporaire avant même d'avoir fini de lire son texte de remplacement. + +Il est naturel de créer la macro §\dimtodec qui transforme un registre de dimension en nombre décimal : + +\showcode/\def\dimtodec{\expandafter\removept\the}¤§*\dimtodec§*\removept¤ +\newdimen\foo¤\idx*\newdimen¤ +a) \foo=15pt \dimtodec\foo \qquad +b) \foo=3.14pt \dimtodec\foo¤§*\dimtodec¤/ +\end{exercice} + +\subsection{La primitive \texttt{\textbackslash dimexpr}} +La primitive \idx\dimexpr\label{dimexpr} de \idx\eTeX{} est aux calculs sur dimensions ce que \idx\numexpr est aux calculs sur les entiers. Elle rend possibles des calculs sur les dimensions de \TeX{} sans passer par les registres de dimension et les primitives \idx\advance, \idx\multiply et \idx\divide qui donnent un code assez fastidieux. + +\begin{regle} +La primitive \idx\dimexpr de \idx\eTeX{} doit être suivie d'un enchainement d'opérations sur des dimensions qui est évalué selon les règles mathématiques habituelles : parenthèses, opérations (\verb|+|, \verb|*|, \verb|/|), priorités. Cette primitive possède les propriétés suivantes : + +\begin{itemize} + \item tout comme \idx\numexpr, \idx\dimexpr amorce un développement maximal pour évaluer l'\verb|<expression>| qui la suit. L'\verb|<expression>| prend fin au premier caractère qui ne peut faire partie d'un calcul sur les dimensions. La primitive \idx\relax peut marquer la fin d'une \verb|<expression>| et dans ce cas, elle est absorbée par \idx\dimexpr; + \item les espaces dans l'\verb|<expression>| sont ignorés ; + \item dans l'\verb|<expression>|, une \verb|<dimension>| ne peut être multipliée ou divisée (avec \verb|*| ou \verb|/|) que par un \emph{entier}. Dans ce cas, l'entier \emph{doit} être l'opérateur c'est-à-dire qu'il doit suivre la dimension. On doit donc écrire + + \begin{centrage}\verb-<dimension>*<entier>- ou \verb-<dimension>/<entier>-\end{centrage} + \item une \verb|<expression>| évaluée par \idx\dimexpr est du même type qu'un registre de dimension, il s'agit donc d'une représentation \emph{interne} d'une dimension. On peut convertir cette représentation interne en caractères de catcode 12\idx*{catcode!12 (autre)} en faisant précéder \idx\dimexpr de la primitive développable \idx\the. +\end{itemize} +\end{regle} + +Voici quelques exemples où la primitive \idx\dimexpr est mise à contribution : + +\showcode|a) \the\dimexpr 1cm + 0.5cm\relax \qquad¤\idx*\dimexpr¤ +b) \the\dimexpr 1pt + 1pt\relax \qquad¤\idx*\the¤ +c) \the\dimexpr 1pt + 2pt * 3\relax\qquad +d) \the\dimexpr (1pt + 2pt) * 3\relax\qquad +e) \the\dimexpr (1.2pt + 0.8pt) * 5\relax\qquad +f) \newdimen\foo \foo=15pt ¤\idx*\newdimen¤ + \the\dimexpr\foo-(\foo + 1pt) / 4\relax| + +\subsection{Les limites du calcul sur les dimensions}\label{arrondis.sur.dimensions}\idx*[|(]{dimension!arrondi}\idx*[|(]{arrondi (dimension)} +Tout semble se passer pour le mieux et il serait tentant de penser qu'une fois libérée de son unité en \verb|pt|, une dimension se comporte exactement comme un nombre décimal tandis que de son côté, \idx\dimexpr joue le rôle de machine à calculer. La simple addition ci-dessous va tempérer ce jugement : + +\showcode/\the\dimexpr0,7pt + 0.4pt\relax¤\idx*\the\idx*\dimexpr¤/ + +\Qu e se passe-t-il et pourquoi le simple calcul «\texttt{0,7pt + 0.4pt}» ne donne-t-il pas comme résultat \texttt{1.1pt}? Les calculs de dimensions seraient-ils bogués ? La réponse est bien évidemment non, mais c'est le revers de la médaille : puisque toutes les dimensions sont converties en «\verb|sp|\idx*{unité!sp}\idx*{sp (unité)}» en interne pour effectuer les calculs puis sont à nouveau converties en «\verb|pt|» pour le résultat, des arrondis sont inévitablement faits lors de ces deux conversions. Il faut donc s'attendre à ce que ces arrondis viennent dans certains cas se cumuler défavorablement. Voici l'explication de l'erreur constatée : + +\begin{itemize} + \item \verb|0,7pt| vaut $0,7\times\numprint{65536}=\numprint[sp]{45875.2}$ qui est arrondi à \numprint[sp]{45875}\idx*{unité!sp}\idx*{sp (unité)}; + \item \verb|0.4pt| vaut $0.4\times\numprint{65536}=\numprint[sp]{26214.4}$ qui est arrondi à \numprint[sp]{26214}; + \item en tenant compte des arrondis, la somme vaut $\numprint{45875}+\numprint{26214}=\numprint[sp]{72089}$; + \item convertie en pt, cette somme vaut $\numprint{72089}\div65536=\numprint{1,09999084473}$ et \TeX{} donne 5 chiffres après la virgule ce qui fait bien \numprint[pt]{1.09999}. +\end{itemize} + +Le résultat attendu, \verb|1.1pt| vaut $\numprint{1.1}\times{65536}=\numprint[sp]{72089.6}$ qui serait arrondi par \TeX{} à \numprint[sp]{72090}\idx*{unité!sp}\idx*{sp (unité)}. + +Si une dimension est considérée comme une mesure de ce qui doit être affiché, ces erreurs d'arrondis sont absolument insignifiantes puisqu'invisibles à l'\oe il nu; insistons sur le fait qu'il ne s'agit ici que d'une minuscule erreur d'\verb|1sp|\idx*{unité!sp}\idx*{sp (unité)} c'est-à-dire de quelques nanomètres ! Mais pour le programmeur, c'est la douche froide et c'est l'assurance que les dimensions ne peuvent pas jouer le rôle de nombres décimaux pris en tant que nombres \emph{mathématiques}, car une erreur sur une simple addition est inacceptable. On peut donc affirmer que \TeX{} ne dispose de rien qui pourrait tenir lieu de nombres décimaux comme en disposent les autres langages. Bien évidemment, on peut \emph{programmer} le fonctionnement de nombres décimaux (en virgule fixe ou flottante) avec les outils de base de \TeX{} que sont les entiers et les 4 opérations. Construire des opérations sur ces nombres est extrêmement fastidieux\footnote{L'extension «\texttt{\idx*{package!fp}fp}» (pour «fixed point») a relevé ce défi. Cette extension est programmée en \TeX{} et utilise les compteurs pour parvenir à ses fins. Elle définit et manipule des nombres en virgule \emph{fixe} avec une partie entière et une partie décimale de 18 chiffres chacune et effectue toutes les opérations arithmétiques et scientifiques habituelles sur ces nombres en virgule fixe (puissances, exponentielles, logarithmes, lignes trigonométriques, tests, nombres pseudoaléatoires, etc.). Ainsi, il est très facile de calculer que + +\[\frac{\mathrm e^4 \ln\left(1+\cos\frac\pi7\right)}{1-3\arctan(0.35)}+4\sqrt[3]{5+\sqrt{2}}\approx-3491.19785321755\mathbf{4299238} +\] + +Du fait d'arrondis successifs dans cette expression complexe, les décimales en gras sont fausses% et devraient être 5016987 +.}\idx*[|)]{dimension!arrondi}\idx*[|)]{arrondi (dimension)}. Il faut mentionner la macro purement développable §\decadd, programmée en annexe (voir page~\pageref{decadd}), qui permet d'additionner de façon \emph{exacte} deux nombres décimaux signés. + +Il est primordial de comprendre qu'une dimension et un nombre entier ne sont que deux facettes d'une même chose : puisque tous les calculs se font en nombre entier de points d'échelle\idx*{point d'échelle}, une dimension en \TeX{} n'est en réalité que le nombre \emph{entier} de \verb|sp|\idx*{unité!sp}\idx*{sp (unité)} qu'elle représente ! + +\begin{regle} +Toutes les dimensions fixes que \TeX{} manipule sont converties en interne en le nombre \emph{entier} de \idx*{point d'échelle}points d'échelle (\verb|sp|\idx*{unité!sp}\idx*{sp (unité)}) qu'elles représentent. Une dimension s'apparente donc à un entier. + +Si \TeX{} est à un endroit où il s'attend à lire un \verb|<nombre>| entier et qu'il lit un \verb|<registre de dimension>|, il convertit la dimension contenue dans le registre en « \idx{entier contraint} » qui est le nombre de \verb|sp|\idx*{sp (unité)}\idx*{unité!sp} qu'elle représente. Cet entier contraint est le \verb|<nombre>| qui sera pris en compte. +\end{regle} + +Constatons-le avec ce code où la primitive \idx\number qui, attendant un nombre entier, force la conversion de la dimension en \idx{entier contraint} : + +\showcode/a) \newdimen\foodim \foodim=1cm \number\foodim\relax\qquad¤\idx*\newdimen¤ +b) \number\dimexpr 0.7pt + 0.4pt\relax\qquad¤\idx*\number¤ +c) \number\dimexpr 1.1pt\relax/ + +\begin{exercice} +La macro \verb|\for|, programmée à la page~\pageref{for}, ne peut manipuler que des nombres \emph{entiers}. + +Sur le modèle de §\for, créer une macro §\FOR où les nombres (borne mini, maxi et incrément) peuvent être décimaux. + +On utilisera le test \tidx{ifdim}, de syntaxe + +\centrecode-\ifdim<dimension1><signe de comparaison><dimension2> + <code vrai> +\else + <code faux> +\fi- + +qui est aux \verb|<dimensions>| ce que le test \verb|\ifnum| est aux entiers. +\solution +Il y a peu de changements à faire. Il faudra remplacer les tests \tidx{ifnum} par \tidx{ifdim} et rajouter l'unité \verb|pt| lorsque c'est nécessaire. + +\showcode/\catcode`\@11 +\def\FOR#1=#2to#3\do#4#{% + \ifempty{#4}¤§*\ifempty¤ + {\let\FOR@increment\z@} + {\edef\FOR@increment{\the\dimexpr#4pt\relax}}% lit et normalise l'argument optionnel + \ifdim\FOR@increment=\z@% s'il est nul,¤\idx*\z@¤ + \edef\FOR@increment{% le redéfinir à -1pt (si #3<#2) et 1pt sinon + \ifdim\dimexpr#3pt-#2pt\relax<\z@ -1\else 1\fi pt¤\idx*\numexpr\idx*\z@¤ + }% \FOR@increment vaut donc 1 ou -1 + \fi + \ifdim\dimtodec\dimexpr#3pt-#2pt\relax\dimexpr\FOR@increment\relax<\z@¤\defline\aaa¤ + \expandafter\gobone% si argument optionnel incompatible, manger le {<code>} + \else + \edef#1{\dimtodec\dimexpr#2pt\relax}% initialise la \<macro> + \edef\macro@args{% définit et développe les arguments à passer à \FOR@i + %#1=nom de la macro récursive : + \expandafter\noexpand\csname FOR@ii@\string#1\endcsname¤\idx*\csname\idx*\endcsname\idx*\string¤ + \ifdim\FOR@increment<\z@ <\else >\fi% #2=signe de comparaison¤\idx*\z@¤ + {\FOR@increment}% #3=incrément + \noexpand#1% #4=\<macro>¤\idx*\noexpand¤ + {\the\dimexpr#3pt\relax}% #5=dimension n2¤\idx*\number\idx*\numexpr¤ + }% + \antefi% externalise la ligne ci-dessous de la portée du test¤§*\antefi¤ + \expandafter\FOR@i\macro@args% appelle \FOR@i avec les arguments définis ci-dessus + \fi +} + +% #1=nom de la macro récursive de type "\FOR@ii@\<macro>" +% #2=signe de comparaison % #3=incrément +% #4=\<macro> % #5=dimension n2 % #6=<code> à exécuter +\long\def\FOR@i#1#2#3#4#5#6{%¤\idx*\long¤ + \def#1{% définit la sous macro récursive + \unless\ifdim#4pt#2#5\relax% tant que la \<macro> variable n'a pas dépassé n2¤\tidx*{unless}¤ + \afterfi{% rendre la récursivité terminale¤§*\afterfi\idx*{récursivité!terminale}¤ + #6% exécute le code¤\defline\bbb¤ + \edef#4{\dimtodec\dimexpr#4pt+#3\relax}% incrémente la \<macro> + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive¤\defline\ccc¤ +}% +\def\exitFOR#1{% #1=\<macro> correspondant à la boucle de laquelle on veut sortir¤§*\exitFOR¤ + \defname{FOR@ii@\string#1}{}%¤\idx*\csname\idx*\endcsname\idx*\string§*\defname¤ +} + +\def\ifexitFOR#1{% envoie vrai si on est prématurément sorti de la boucle de \<macro> #1¤§*\ifexitFOR¤ + % si la macro récursive est \empty + \expandafter\ifx\csname FOR@ii@\string#1\endcsname\empty¤\idx*\string\idx*\empty¤ + \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai"¤§*\firstoftwo¤ + \else + \expandafter\secondoftwo% sinon, renvoyer "faux"¤§*\secondoftwo¤ + \fi +} +\catcode`\@=12 +a) \FOR\xx=1.5 to -5\do-1{"\xx" }\par + +b) \FOR\ii=1 to 2.742\do.25{"\ii" }\par + +c) \FOR\ii=0 to 1\do0.1{"\ii" \ifdim\ii pt>0.5pt \exitFOR\ii\fi}¤§*\FOR¤/ + +La ligne \no\aaa{} mérite tout de même quelques explications. Il s'agit de trouver le signe de + +\[(\hbox{\verb|#3|}-\hbox{\verb|#2|})\times\hbox{\verb|\for@increment|}\] + +Pour calculer cette expression sous forme de dimension, nous avons tout d'abord transformé \verb|#3-#2| en un décimal sans unité avec la macro §\dimtodec. La macro \verb|\for@increment| est transformée en une dimension de type \verb|<registre>| avec \idx\dimexpr. Le test \tidx{ifdim} voit donc \verb|<x><registre de dimension>| où \verb|<x>| est un décimal signé. Comme l'explique la règle vue précédemment, le tout forme une \verb|<dimension>| égale au produit de \verb|#3-#2| par \verb|\for@increment|. + +Comme le montre le cas c, additionner à chaque itération l'incrément à la dimension précédente conduit parfois à des erreurs d'arrondi gênantes. Pour une addition décimale \emph{exacte}, voir en annexe la macro §\decadd, page~\pageref{decadd}. +\end{exercice} + +\subsection{Étendre les calculs sur les dimensions} +Puisqu'il n'est pas question d'écrire une extension aussi complexe que \verb|fp|, nous allons accepter les erreurs d'arrondis dues à la méthode de calcul sur les dimensions. Aussi, nous allons confondre dimension en \verb|pt| sans unité et nombre décimal; en effet, passer de l'une à l'autre est facile. Il suffit d'appeler la macro §\dimtodec pour le sens \emph{dimension vers décimal} ou d'ajouter l'unité \verb|pt| pour le sens inverse. + +Nous allons essayer de tirer parti de \idx\dimexpr et des règles que nous avons vues pour étendre un peu les possibilités de calcul de \TeX{} sur les nombres décimaux. Si l'addition et la soustraction opèrent sur \emph{deux} nombres décimaux, il n'en est pas de même pour la multiplication et la division qui ne sont capables de multiplier ou diviser par un \emph{entier}. + +\subsubsection{Multiplier deux décimaux} +Essayons tout d'abord de programmer une macro §\decmul qui permet de multiplier deux nombres \emph{décimaux}, chacun se trouvant dans un argument. + +Nous allons exploiter le fait que \verb|<décimal><registre de dimension>| est vue comme une dimension égale au produit des deux quantités. + +\showcode/\newdimen\foo¤\idx*\newdimen¤ +\foo=15.2pt \foo=1.5\foo \the\foo% vaut 15.2*1.5¤\idx*\the¤/ + +Aux erreurs d'arrondi près auxquelles nous nous sommes résignés, le résultat est correct. Voici donc comment programmer la macro §\decmul\label{decmul} : + +\showcode/\catcode`\@11 +\newdimen\dim@a¤\idx*\newdimen¤ +\def\decmul#1#2{%¤§*\decmul¤ + \dim@a=#2pt % range la dimension #2pt dans le registre de dimension + \dim@a=#1\dim@a% multiplier le registre par le décimal #1 + \dimtodec\dim@a% convertir la dimension en décimal¤§*\dimtodec¤ +} +\catcode`\@12 +a) \decmul{15.2}{1.5}\qquad +b) \decmul{48.2}{.375}¤§*\decmul¤/ + +\begin{exercice} +La macro §\decmul programmée ci-dessus n'est pas purement développable. Utiliser la primitive \idx\dimexpr pour qu'elle le devienne. +\solution +La méthode va être de donner à §\dimtodec non pas un vrai registre de dimension, mais une expression évaluée par \idx\dimexpr, puisque cette primitive est du même type qu'un registre de dimension : + +\centrecode-\dimtodec\dimexpr<expression>\relax- + +En ce qui concerne l'\verb|<expression>|, nous allons utiliser la même méthode vue ci-dessus, à savoir \verb|#1<registre>| où le \verb|<registre>| sera remplacé par \idx\dimexpr\verb|#2\relax|. Cela donne : + +\showcode/\def\decmul#1#2{\dimtodec\dimexpr#1\dimexpr#2pt\relax\relax}¤§*\decmul §*\dimtodec\idx*\dimexpr¤ + +a) \decmul{15.2}{1.5}\qquad +b) \edef\foo{\decmul{48.2}{.375}}\meaning\foo¤§*\decmul¤/ +\end{exercice} + +\subsubsection{Diviser par un décimal} +Si nous souhaitons écrire une macro analogue §\decdiv[|(]\verb|{<nba>}{<nbb>}| qui effectue la division du décimal \verb|<nba>| par le décimal \verb|<nbb>|, l'astuce consiste à évaluer avec \idx\numexpr l'entier suivant : + +\[ +\frac{\text{\texttt{\codeelement{nba}pt}}\times\text{\ttfamily 1pt}}{\text{\texttt{\codeelement{nba}pt}}} +\] + +\noindent Comme \idx\numexpr s'attend à des opérations sur des entiers, chaque dimension, préalablement mise sous forme de registre avec \verb|\dimexpr|, sera convertie en entier contraint. Le risque de dépassement de l'entier maximal au numérateur est improbable, car lorsque \idx\numexpr (tout comme \idx\dimexpr) effectue une multiplication immédiatement suivie d'une division, les calculs internes se font sur 64 bits et non sur 32. + +Une fois cet entier calculé, nous lui donnerons l'unité \verb|sp|\idx*{unité!sp}\idx*{sp (unité)} et évaluerons le tout avec \idx\dimexpr. Le résultat, expurgé de son unité avec §\dimtodec, donnera le quotient cherché. + +Pour éviter un \idx\dimexpr inutile, la dimension \verb|1pt|, qui vaut \verb|65536sp|, a été directement convertie en l'entier contraint \verb|65536| : + +\showcode|\def\decdiv#1#2{% divise le décimal #1 par le décimal #2¤§*\decdiv¤ + \dimtodec¤§*\dimtodec\idx*\dimexpr\idx*\numexpr¤ + \dimexpr + \numexpr + \dimexpr #1pt \relax * 65536 / \dimexpr #2pt \relax + \relax + sp¤\idx*{sp (unité)}\idx*{unité!sp}\idx*{sp (unité)}¤ + \relax +} + +1) \decdiv{4.5}{0.075}\qquad% doit donner 60 +2) \decdiv{8}{0.1}\qquad% doit donner 80 +3) \decdiv{3.14}{1.6}\qquad% doit donner 1.9625 +4) \decdiv{687.59829}{5.29871}\qquad% doit donner 129.76706 +4) \edef\foo{\decdiv{0.37}{2.5}}\foo% doit donner 0.148¤§*\decdiv¤| + +Ici encore, il ne s'agit que d'arithmétique \TeX ienne, c'est-à-dire que comme avec §\decmul, les résultats sont entachés d'erreurs d'arrondis et donc, ces macros ne pourront être utilisées que pour des calculs de dimensions et non pas pour des calculs sur des nombres pris en tant qu'entités mathématiques§*\decdiv[|)]. + +\begin{exercice} +Écrire une macro §\convertunit[|(] de syntaxe + +\centrecode-\convertunit{<dimension>}{<unité>}- + +qui convertit la \verb|<dimension>| dans l'\verb|<unité>| spécifiée par les deux caractères caractérisant chaque unité, et d'afficher le résultat sans unité. La macro devra être purement développable. + +\solution +Nous allons procéder comme nous l'avons fait avec la macro §\decdiv : nous allons mettre \idx\numexpr à contribution pour transformer en \idx{entier contraint} toutes les dimensions et multiplier la \verb|<dimension>| par \verb|1pt| et diviser le tout par + \verb|1<unité>| : + +\showcode|\def\convertunit#1#2{%¤§*\dimtodec¤ + \dimtodec + \dimexpr + \numexpr + \dimexpr #1 \relax * 65536 / \dimexpr 1#2 \relax + \relax + sp + \relax +} + +a) \convertunit{15cm}{mm}\qquad +b) \convertunit{9,14in}{cm}\qquad +c) \convertunit{100000sp}{mm}\qquad +d) \convertunit{160.5pt}{cm}\qquad +e) \edef\foo{\convertunit{2,5cm}{cc}}\meaning\foo| +§*\convertunit[|)]\end{exercice}\idx*[|)]{dimension fixe} + +\section{Les dimensions étirables ou ressorts}\idx*[|(]{registre!ressort}\idx*[|(]{dimension étirable (ressort)} +\subsection{\Qu'est-ce qu'un ressort ?} +Intéressons-nous maintenant aux ressorts qui ont vocation à remplir des zones susceptibles d'avoir des tailles variables (souvent pour des raisons typographiques). + +\begin{regle} +Un ressort est une dimension assortie de composantes étirables optionnelles (éventuellement infinies) de sorte qu'il peut, selon le contexte et sa définition, se comprimer ou s'étirer. \TeX{} offre 256 registres de ressorts et ce nombre monte à \numprint{32768} avec \idx\eTeX. La primitive \idx\skip suivie d'un \verb|<nombre>| permet d'accéder à un registre spécifique par son numéro. + +Plain-\TeX{} fournit la macro \idx\newskip\verb|\<macro>| qui met à profit la primitive \idx\skipdef selon cette syntaxe + +\centrecode-\skipdef\<macro>=<nombre>- + +pour rendre cette \verb|\<macro>| équivalente à \idx\skip\verb|<nombre>|, où le \verb|<nombre>| est celui du prochain registre libre. Dès lors, un \verb|<registre de ressort>|\idx*{registre!ressort} est soit \idx\skip suivi d'un numéro, soit une \verb|\<macro>| préalablement définie par \idx\newskip. + +Pour assigner un ressort à un \verb|<registre de ressort>|, il faut écrire : + +\centrecode-<registre de ressort>= <ressort>- + +\noindent où le signe «\verb|=|» et l'espace qui le suit sont optionnels. Pour lire le \verb|<ressort>|, le développement maximal se met en marche. Un \verb|<ressort>| est une \verb|<dimension>| éventuellement suivie d'une composante d'étirement optionnelle de la forme «\verb|plus <étirement>|» puis d'une composante de compression optionnelle de la forme «\verb|minus <étirement>|». + +Si elles sont présentes toutes les deux, ces deux composantes optionnelles doivent \emph{nécessairement} être déclarées dans cet ordre. Si elles ne sont pas spécifiées, les composantes étirables valent \verb|0pt| par défaut. + +Un \verb|<étirement>| est :\label{dimension.ressort}\idx*{registre!ressort!étirement} +\begin{itemize} + \item soit une composante fixe qui est une \verb|<dimension>| comme «\verb|1.5pt|» ou «\verb|0.25cm|»; + \item soit une composante infinie qui prend la forme d'un coefficient d'infini suivi de «\verb|fil|», «\verb|fill|» ou «\verb|filll|», où ces 3 mots-clé représentent des infinis de \emph{forces} différentes : «\verb-filll-» est infiniment plus grand que «\verb|fill|», quels que soient les coefficients qui les précèdent et de la même façon, «\verb|fill|» est infiniment plus grand que «\verb|fil|». + + Le coefficient d'infini est un décimal signé dont la valeur absolue peut prendre toutes les valeurs entre 0 et \verb|16383.99999|. +\end{itemize} +\end{regle}\idx*[|(]{registre!ressort!étirement} + +À la manière de \idx\dimexpr qui fait des calculs sur les dimensions, la primitive \idx\glueexpr effectue des calculs sur les ressorts : + +\showcode|\the\glueexpr 5pt plus 2pt minus 1.5pt + 7pt plus0.5pt minus 3pt\relax \par¤\idx*\glueexpr\idx*\the¤ +\the\glueexpr 25pt plus2fil + 35pt plus 0.1fill\relax| +\grandsaut + +Envisageons maintenant quelques exemples pour mieux comprendre comment les ressorts fonctionnent et quelles sont leurs « forces » relatives. + +Intéressons-nous tout d'abord aux composantes d'étirement finies. Le ressort «\texttt{10pt plus1.5pt minus 2.5pt}» a une longueur naturelle de \numprint[pt]{10} mais peut s'allonger jusqu'à \numprint[pt]{11.5} et se comprimer jusqu'à \numprint[pt]{7.5}. De la même façon, le ressort «\texttt{0pt minus5pt}» a une longueur naturelle nulle, mais peut se comprimer jusqu'à une longueur négative de \numprint[pt]{-5}. + +Venons-en aux composantes d'étirement infinies. Supposons qu' un ressort $R_1$ soit égal à «\texttt{1.5cm plus2fill}». Il a une longueur naturelle de \numprint[cm]{1.5} et peut s'allonger autant que nécessaire comme le spécifie sa composante étirable. Si un autre ressort $R_2$ est défini par «\texttt{2.5cm plus3fill}» et si nous demandons à ces deux ressorts de remplir un espace de \numprint[cm]{10}, la longueur à combler est de \numprint[cm]{6} puisque la somme des deux longueurs naturelles vaut \numprint[cm]{4}. Le ressort $R_1$ s'allongera de 2/5 la longueur à combler et $R_2$ de 3/5 de cette longueur, comme le spécifient les coefficients d'infini. $R_1$ mesurera $1.5+6\times2/5=\numprint[cm]{3.9}$ alors que $R_2$ mesurera $2.5+6\times3/5=\numprint[cm]{6.1}$. En voici l'illustration ci-dessous où les dimensions naturelles sont en traits pleins et les étirements en pointillés. Si aucune opération dans l'impression de ce livre n'a provoqué d'altération d'échelle, les dimensions données ci-dessus doivent exactement se retrouver dans ce schéma : + +\begingroup +\medbreak +\ttfamily +\small +\hfill +\leavevmode\lower5pt\hbox{\hbox to1.5cm{\hss 1.5cm\kern2pt}\hbox to2.4cm{\kern2pt plus 2fill\hss}\hbox to2.5cm{\hss2.5cm\kern2pt}\hbox to 3.6cm{\kern2pt plus 3fill\hss}}\hfill\null + +\hfill\vrule width0.4pt height 1ex depth 0pt +\vrule width1.5cm height0.4pt depth 0pt +\vrule width0.2pt height 1ex depth 0pt +\hbox to2.4cm{\xleaders\hbox{\vrule width2pt height.4pt\kern1pt }\hfill}% +\vrule width0.4pt height 1ex depth 0pt +\vrule width2.5cm height0.4pt depth 0pt +\vrule width0.4pt height 1ex depth 0pt +\hbox to3.6cm{\xleaders\hbox{\vrule width2pt height.4pt\kern1pt }\hfill}% +\vrule width0.4pt height 1ex depth 0pt \hfill\null +\medbreak\endgroup + +Enfin, si un ressort $R_3$ égal à «\texttt{3cm plus 20fil}» et un ressort $R_4$ égal à «\texttt{2cm plus 0.1fill}» doivent combler un espace de \numprint[cm]{20}, seul $R_4$ s'allongera pour mesurer \numprint[cm]{17} car son infini «\verb|fill|» est infiniment plus grand que «\verb|fil|».\idx*[|)]{registre!ressort!étirement} + +\subsection{Insérer des espaces de dimensions choisies} +\subsubsection{Insertion d'espaces insécables} +\begin{regle} +Pour insérer une espace insécable, on utilise la primitive \idx\kern suivie d'une \verb|<dimension>| qui sera celle de l'espace insérée. + +L'espace sera insérée dans le mode en cours : elle sera horizontale si \TeX{} est en mode horizontal\idx*{mode!horizontal} à ce moment-là et sera verticale s'il est en mode vertical\idx*{mode!vertical}. + +L'espace ainsi créé est \emph{insécable} car aucune coupure de ligne ou de page ne pourra s'y faire. +\end{regle} + +\showcode/foo% les caractères font entrer TeX en mode horizontal¤\idx*{mode!horizontal}¤ +\kern0.5cm % espace insérée en mode horizontal¤\idx*{mode!horizontal}¤ +bar\par% \par fait passer en mode vertical¤\idx*{mode!vertical}¤ +\kern0.5cm % espace insérée en mode vertical +boo/ + +L'espace verticale entre la frontière basse de «bar» et la frontière haute de «boo» n'est pas \verb|0.5cm|, car le ressort d'interligne vient s'ajouter à la dimension insécable insérée par \idx\kern. + +\subsubsection{Insertion d'espaces sécables} +À la différence des espaces insécables, un espace est dit « \emph{sécable} » s'il peut céder sa place à une coupure (de ligne ou de page). Le verbe « céder » suggère que si une coupure est faite, l'espace est retiré de la liste en cours et est remplacé par une coupure. + +\begin{regle} +La primitive \idx\hskip, suivie d'un \verb|<ressort>| insère une espace (dont les dimensions sont celles permises par le ressort\idx*{registre!ressort}) dans la liste horizontale en cours. La primitive \idx\vskip en est le pendant pour le mode vertical\idx*{mode!vertical}. + +Si \TeX{} rencontre une de ces deux primitives et ne se trouve pas dans le mode qu'elles exigent, alors le mode courant se termine et \TeX{} passe dans le mode requis. + +Les espaces ainsi insérées sont sécables, c'est-à-dire susceptibles d'être remplacées par des coupures de ligne ou de pages. De plus, elles sont ignorées lorsqu'elles se trouvent immédiatement avant la fin du \idx[!espaces ignorés]{paragraphe} pour \idx\hskip ou de la fin de la page pour \idx\vskip. +\end{regle} + +Il est utile de savoir qu'en mode horizontal\idx*{mode!horizontal}, le token espace (de catcode 10\idx*{catcode!10 (espace)} et de code de caractère 32) se comporte comme un ressort\idx*{registre!ressort} : il a une dimension naturelle et peut, dans une certaine mesure, se comprimer ou se dilater (lire page~\pageref{espace.dimensions.fonte}). +\grandsaut + +Certaines primitives sont en réalité des ressort\idx*{registre!ressort}s. Il est important de distinguer deux types de « ressorts-primitives » : + +\begin{itemize} + \item ceux qui sont \emph{modifiables} et qui se comportent comme une macro définie avec \idx\newskip. Ces ressorts-primitives sont susceptibles de recevoir un \verb|<ressort>| par assignation. La plupart de ces ressorts-primitives concernent des réglages typographiques; + \item ceux, non modifiables, dont la valeur est prédéfinie et invariable. +\end{itemize} + +Le tableau ci-dessous, sans être exhaustif, liste quelques ressorts utilisés par \TeX{}, qu'ils soient de simples macros définies avec \idx\newskip (partie du haut), des ressorts-primitives modifiables (écrits en gras dans la partie du milieu) ou ressorts-primitives non modifiables (précédés d'une «$*$» dans la partie basse). + +Dans chaque catégorie, la valeur par défaut assignée par plain-\TeX{} ou le comportement (pour les ressorts-primitives non modifiables) est donné dans la colonne « Définition » tandis que le mode dans lequel le \idx*{registre!ressort}ressort est utilisé est donné dans la colonne de droite.\medbreak + +\begingroup + \small + \LTpre2\parskip \LTpost\parskip\label{tableau.ressort}% + \begin{longtable}{>\ttfamily r@{}c@{}>\ttfamily l@{\kern1em}c}\hline + Nom && Définition & Mode\\\hline\endhead + \idx\hideskip&${}={}$&-1000pt plus 1fill& h\\ + \idx\centering&${}={}$&0pt plus 1000pt minus 1000pt& h\\ + \idx\z@skip&${}={}$&0pt plus0pt minus0pt& h ou v\\ + \idx\smallskipamount&${}={}$&3pt plus 1pt minus 1pt&v\\ + \idx\medskipamount&${}={}$&6pt plus 2pt minus 2pt&v\\ + \idx\bigskipamount&${}={}$&12pt plus 4pt minus 4pt&v\\\hline + \bfseries\idx\baselineskip&${}={}$&12pt&v\\ + \bfseries\idx\lineskip&${}={}$&1pt&v\\ + \bfseries\idx\leftskip&${}={}$&0pt&h\\ + \bfseries\idx\rightskip&${}={}$&0pt&h\\ + \bfseries\idx\parskip&${}={}$&0pt plus 1pt& v\\ + \bfseries\idx\parfillskip&${}={}$&0pt plus 1fil& h\\\hline + \llap{${}^*$}\idx\hfil&${}\equiv{}$&\string\hskip{} 0pt plus 1fil&h\\ + \llap{${}^*$}\idx\hfilneg&${}\equiv{}$&\string\hskip{} 0pt plus -1fil&h\\ + \llap{${}^*$}\idx\hfill&${}\equiv{}$&\string\hskip{} 0pt plus 1fill&h\\ + \llap{${}^*$}\idx\vfil&${}\equiv{}$&\string\vskip{} 0pt plus 1fil&v\\ + \llap{${}^*$}\idx\vfilneg&${}\equiv{}$&\string\vskip{} 0pt plus -1fil&v\\ + \llap{${}^*$}\idx\vfill&${}\equiv{}$&\string\vskip{} 0pt plus 1fill&v\\ + \llap{${}^*$}\idx\hss&${}\equiv{}$&\string\hskip{} 0pt plus1fil minus1fil&h\\ + \llap{${}^*$}\idx\vss&${}\equiv{}$&\string\vskip{} 0pt plus1fil minus1fil&v\\\hline + \end{longtable} +\endgroup + +\subsection{Les ressorts typographiques} +Intéressons-nous aux ressorts-primitives écrits en gras dans le tableau précédent. Ces ressorts sont examinés lorsque le paragraphe est composé\idx*{paragraphe!composition}, c'est-à-dire lorsque \idx\par est exécuté. Il est donc inutile de modifier plusieurs fois ces ressorts dans le même paragraphe puisque seule la dernière valeur sera prise en compte. + +Cela implique en particulier que si un paragraphe\idx*{paragraphe!composition} est composé dans un groupe (semi-simple ou non) dans lequel certains de ces ressorts sont modifiés, il est nécessaire de composer le paragraphe \emph{avant} de fermer le groupe. En écrivant \idx\par avant la fin du groupe, on s'assure que les valeurs des ressorts, modifiées dans le groupe, seront bien prises en compte. En revanche, rejeter le \idx\par hors du groupe constitue une erreur, car les valeurs, restaurées trop tôt à ce qu'elles étaient avant les modifications, rendent inutiles ces modifications internes au groupe. + +\subsubsection{Ressort d'interligne}\label{ressort.interligne}\idx*[|(]{paragraphe!espace interligne} +Les boites contenant deux lignes consécutives d'un paragraphe ne sont généralement pas jointives. Le schéma ci-dessous illustre la situation : la distance entre les lignes de base des deux lignes consécutives est notée $\Delta$ tandis que la distance entre les frontières des deux boites est notée $\delta$. + +\begin{centrage} + \begin{tikzpicture}[inner sep=0pt,outer sep=0pt,minimum size=0pt,baseline,line width=0.4pt] + \node[anchor=base west,draw,inner sep=-0.2pt]at(0,0)(boiteA){\vrule width0pt height.6cm depth.3cm \vrule height0pt width 3cm depth0pt };\draw[fill,black,overlay](boiteA.base west)circle(1pt);% + \draw[gray,overlay]([xshift=-1cm]boiteA.base west)--([xshift=1cm]boiteA.base east)node[pos=1,anchor=south east,black,outer sep=1pt]{\tiny ligne de}node[pos=1,anchor=north east,black,outer sep=1pt]{\tiny base}; + \node[anchor=base west,draw,inner sep=-0.1pt]at(0,-1.2)(boiteB){\vrule width0pt height.5cm depth.2cm \vrule height0pt width 4cm depth0pt }; + \draw[fill,black,overlay](boiteB.base west)circle(1pt);% + \draw[gray,overlay]([xshift=-1cm]boiteB.base west)--([xshift=1cm]boiteB.base east)node[pos=1,anchor=south east,black,outer sep=1pt]{\tiny ligne de}node[pos=1,anchor=north east,black,outer sep=1pt]{\tiny base}; + \draw[stealth-stealth]([xshift=-1.2cm]boiteA.base west)--([xshift=-1.2cm]boiteB.base west)node[pos=.5]{\footnotesize\llap{$\Delta$}\kern.75em }; + \draw[stealth-stealth]([xshift=-.2cm]boiteA.south west)--([xshift=-.2cm]boiteB.north west)node[pos=.5]{\footnotesize\llap{$\delta$}\kern.75em }; + \draw[stealth-stealth]([xshift=-.6cm]boiteA.south west)--([xshift=-.6cm]boiteA.base west)node[pos=.5]{\footnotesize\llap{$p$}\kern.75em }; + \draw[stealth-stealth]([xshift=-.6cm]boiteB.north west)--([xshift=-.6cm]boiteB.base west)node[pos=.5]{\footnotesize\llap{$h$}\kern.75em }; + \end{tikzpicture} +\end{centrage} + +Plus généralement, si des boites sont empilées les unes au-dessous des autres en mode vertical, un ressort-primitive vertical «\idx\baselineskip» est inséré entre elles. Ce ressort-primitive, appelé « \idx{ressort d'interligne} », mesure l'espace insérée entre les \emph{lignes de base} de ces boites. + +Si la géométrie des boites (précisément la quantité $p+h$) fait que $\delta$ devient strictement inférieur à une certaine limite stockée dans la dimension-primitive \idx\lineskiplimit, alors les deux boites sont empilées verticalement de telle sorte que $\delta$ soit égal au ressort-primitive \idx\lineskip. Mathématiquement, si + +\[\Delta-(p+h)<\text{\ttfamily\char`\\lineskiplimit}\]\idx*\lineskiplimit +\noindent alors l'insertion de \idx\baselineskip entre les lignes de base est abandonnée et le ressort-primitive \idx\lineskip est inséré \emph{entre les frontières des boites}. + +Plain-\TeX{} effectue les initialisations suivantes : + +\centrecode-\baselineskip=12pt \lineskiplimit=0pt \lineskip=1pt- + +La macro \idx\nointerlineskip doit être appelée en mode vertical et annule l'insertion du prochain \idx{ressort d'interligne} entre deux boites. Cette macro est à « un seul coup » et devra être appelée à nouveau si plus tard, on souhaite annuler l'insertion du ressort d'interligne entre deux autres boites. + +\showcode/Une première ligne\par +\nointerlineskip% n'insère pas de ressort d'interligne ici¤\idx*\nointerlineskip¤ +Un second paragraphe constitué de plusieurs lignes. +Un second paragraphe constitué de plusieurs lignes. +Un second paragraphe constitué de plusieurs lignes. +\par% le ressort d'interligne sera inséré ici +Une dernière ligne/ + +La macro \idx\offinterlineskip, désactive durablement l'insertion du ressort d'interligne. Il est prudent de l'utiliser dans un groupe afin d'en limiter la portée. + +\showcode/\begingroup +\offinterlineskip¤\idx*\offinterlineskip¤ +La macro \litterate-\offinterlineskip-, en modifiant de façon appropriée les trois¤\idx*\offinterlineskip§*\litterate¤ +primitives \litterate-\baselineskip-, \litterate-\lineskip- et¤\idx*\offinterlineskip¤ +\litterate-\lineskiplimit-, rend les boites consécutives jointives.¤\idx*\baselineskip\idx*\lineskip\idx*\lineskiplimit¤ + +On peut constater dans ces deux paragraphes où \litterate-\offinterlineskip- a été¤\idx*\offinterlineskip§*\litterate¤ +appelée, que les lignes sont placées verticalement au plus près les unes des autres ce +qui rend la lecture très pénible et démontre que le ressort d'interligne est une¤\idx*{ressort d'interligne}¤ +nécessité typographique !\par +\endgroup +Désactiver le ressort d'interligne ne se justifie que lorsque l'on doit composer¤\idx*{ressort d'interligne}¤ +des boites contenant autre chose que du texte, sauf à vouloir des effets +typographiques spéciaux./ + +Les deux cas les plus courants où la quantité $p+h$ est élevée sont : + +\begin{enumerate} + \item la boite du haut est très profonde et $p$ est grand (boite de type \idx\vtop); + \item la boite du bas est très haute et $h$ est grand (boite de type \idx\vbox). +\end{enumerate} + +En voici l'illustration où nous allons construire une boite du haut profonde et où nous augmentons \idx\baselineskip afin de mieux visualiser le problème : + +\showcode/\baselineskip=12pt +début \vtop{\hbox{ligne du haut ligne du haut ligne du haut}¤\idx*\vtop¤ +\hbox{ligne du bas ligne du bas ligne du bas} +} +\par +Et la suite du texte suite du texte suite du texte/ + +On constate que l'espace vertical entre la « ligne du bas » de la \idx\vtop et la ligne suivante n'est pas \idx\baselineskip. En effet, \idx\lineskip a été inséré entre la frontière de la \idx\vtop et la première ligne du paragraphe qui suit. Pour rétablir un espacement correct, il faut mentir à \TeX{} sur la profondeur de la boite précédente. En mode vertical\idx*{mode!vertical}, la primitive \idx\prevdepth (qui mesure la longueur $p$ du schéma), ayant le type d'une dimension, contient la profondeur de la précédente boite placée dans la liste verticale. Après être sorti de la \idx\vtop, cette valeur est élevée du fait de la profondeur de la boite. L'astuce consiste à sauvegarder $p$ dans une macro à la fin de la \idx\vtop de telle sorte que cette macro contienne la profondeur \emph{de la dernière ligne} de la boite. Comme on ne peut accéder à \idx\prevdepth qu'en mode vertical\idx*{mode!vertical}, il suffit donc après le \idx\par d'assigner à \idx\prevdepth la valeur sauvegardée. + +\showcode/\baselineskip=12pt +début \vtop{\hbox{ligne du haut ligne du haut ligne du haut}¤\idx*\vtop¤ +\hbox{ligne du bas ligne du bas ligne du bas} +\xdef\sprevdepth{\the\prevdepth}% sauvegarde la valeur de \prevdepth¤\idx*\xdef¤ +} +\par\prevdepth=\sprevdepth\relax% ment sur la boite précédente¤\idx*\prevdepth¤ +Et la suite du texte suite du texte suite du texte/\idx*[|)]{paragraphe!espace interligne} + +\subsubsection{Les ressorts de paragraphe}\idx*[|(]{paragraphe!ressorts de paragraphe} +Avant de commencer un paragraphe, \TeX{} ajoute à la liste verticale l'espace mesuré par le ressort-primitive \idx\parskip (il n'est pas ajouté si la liste verticale est vide, c'est-à-dire au début d'une page ou d'une boite verticale). Ce ressort vient donc s'additionner au \idx{ressort d'interligne} vu précédemment. Plain-\TeX{} procède à l'assignation suivante : + +\centrecode-\parskip= 0pt plus 1pt- + +Après la fin de la dernière ligne d'un paragraphe, \TeX{} insère horizontalement le ressort-primitive \idx\parfillskip. Plain-\TeX{} demande à ce que + +\centrecode-\parfillskip= 0pt plus1fil- + +\begingroup +\hbadness=10000 % pour éviter une avertissement de boite insuffisemment remplie +\parfillskip=0pt\relax +\noindent ce qui signifie qu'un ressort d'étirement infini est inséré en fin de paragraphe. L'effet d'un tel ressort est de « pousser » le texte de la dernière ligne de façon à ce qu'il ne se justifie pas. On peut bien sûr modifier ce ressort pour obtenir des effets typographiques comme dans ce paragraphe où ce ressort a été rendu nul sans étirement afin que la dernière ligne soit justifiée. Cet effet doit être utilisé avec parcimonie, car les espaces intermots de cette dernière ligne, beaucoup trop larges ici, démontrent que cet effet peut parfois être très inesthétique. +\par +\endgroup +\medbreak + +Afin de bien voir la modification de la composition du paragraphe que cela entraine, voici le paragraphe précédent avec un ressort de fin de paragraphe ayant sa valeur par défaut :\smallbreak + +\noindent ce qui signifie qu'un ressort d'étirement infini est inséré en fin de paragraphe. L'effet d'un tel ressort est de « pousser » le texte de la dernière ligne de façon à ce qu'il ne se justifie pas. On peut bien sûr modifier ce ressort pour obtenir des effets typographiques comme dans ce paragraphe où ce ressort a été rendu nul sans étirement afin que la dernière ligne soit justifiée. Cet effet doit être utilisé avec parcimonie, car les espaces intermots de cette dernière ligne, beaucoup trop larges ici, démontrent que cet effet peut parfois être très inesthétique. + +\subsubsection{Ressorts d'extrémités de ligne} +Les ressorts \idx\leftskip et \idx\rightskip sont insérés avant et après le contenu de chaque ligne (\idx\rightskip est ajouté après \idx\parfillskip à la dernière ligne du paragraphe). Lorsqu'ils sont égaux à \verb|0pt| comme c'est le cas par défaut, le début et la fin de la ligne coïncident avec les limites de la zone de texte et le texte est dit « justifié » : ce sont les espaces entre les mots qui s'ajustent pour permettre cet effet typographique. La modification de ces ressorts d'extrémités de ligne permet des compositions qui contrarient cette justification. + +Ainsi, donner une composante infinie à ces ressorts revient à les faire « pousser » avec une force infinie de telle sorte que les espaces entre les mots seront contraints à prendre leur dimension \emph{naturelle} ce qui cassera la justification. Il est cependant plus judicieux de donner comme étirement une composante \emph{finie} de telle sorte que celle-ci ne contraigne pas les espaces intermots. Il est utile de savoir qu'une composante \emph{infinie} des ressorts d'extrémité de ligne décourage \TeX{} d'effectuer une seconde passe lors de la composition du paragraphe. Seule la première passe est donc faite et les coupures de mots, envisagées à la seconde passe, ne seront donc jamais faites (lire page~\pageref{provoquer.toutes.les.coupures}). + +On voit que les ressorts d'extrémités de ligne entretiennent un rapport avec les espaces intermots dont il est temps de parler. Chaque police possède des registres internes définissant la largeur naturelle de l'espace intermot, son possible étirement et sa possible compression (lire page~\pageref{espace.dimensions.fonte}). Le ressort-primitive \idx\spaceskip permet, lorsqu'il reçoit une assignation, d'écraser localement le contenu des registres de la police concernant les dimensions de l'espace intermot. Il est donc possible de supprimer la composante étirable de l'espace intermot afin que des espaces intermots trop longs et disgracieux soient évités dans une composition non justifiée. +\grandsaut + +Les effets typographiques les plus courants obtenus à l'aide des ressorts d'extrémité de ligne sont : + +\begin{itemize} + \item composition au fer à gauche en donnant une composante étirable finie (par exemple 2 ou 3 \verb|em|) à \idx\rightskip et en laissant \idx\leftskip nul; + \item composition au fer à droite en inversant les rôles des deux ressorts dans la man\oe uvre décrite au point précédent; + \item centrage du contenu des lignes en donnant la \emph{même} composante étirable aux deux ressorts. Cette composante doit ici être assez grande pour permettre le centrage de lignes très courtes. Le choix de \verb|1000pt| est fait par plain-\TeX{} tandis que \LaTeX{} fait le choix de \texttt{0pt plus 1fil} pour sa macro \idx\centering. On peut également choisir \verb|0.5|\idx\hsize où \idx\hsize est la dimension-primitive qui contient la largeur de composition. Enfin, pour que la dernière ligne soit centrée, il faut également annuler \idx\parfillskip qui sinon, deviendrait prépondérant par rapport à \idx\leftskip et \idx\rightskip. +\end{itemize} + +Voici un exemple de méthode pour centrer deux paragraphes\idx*{paragraphe!centrage} entiers : + +\showcode/\def\dummytext{Voici un texte qui ne revêt aucune importance + et dont le seul but est de meubler artificiellement un paragraphe. } +\begingroup% à l'intérieur d'un groupe,¤\idx*\begingroup¤ + \leftskip=0pt plus 0.5\hsize\relax¤\idx*\leftskip\idx*\hsize¤ + \rightskip=\leftskip\relax % modifier les ressort d'extrémités¤\idx*\rightskip\idx*\leftskip¤ + \spaceskip=0.3em plus 0.05em\relax% dimension de l'espace intermot¤\idx*\spaceskip¤ + \parfillskip=0pt \relax% annuler le ressort de fin de paragraphe¤\idx*\parfillskip¤ + \dummytext\dummytext\dummytext% corps du paragraphe + \par% compose la paragraphe courant + Juste une phrase% + \par% compose la paragraphe courant +\endgroup% avant de sortir du groupe¤\idx*\endgroup¤/ + +En suivant cette méthode, il est facile de construire une macro §\narrowtext qui compose le paragraphe précédent et qui diminue désormais la largeur de composition de 20\% de la largeur totale\idx*{paragraphe!diminuer la largeur}, sachant que cette largeur est contenue dans la dimension-primitive \idx\hsize. Le « bloc » de composition ainsi constitué devra être centré par rapport aux frontières de la zone de texte. Il faut pour cela augmenter (via la primitive \idx\advance) chacun des deux ressorts d'extrémités de ligne de \verb|.1\hsize|. La macro \verb|\endnarrowtext| signera la fin de la composition en largeur diminuée; elle composera le paragraphe en cours avec \idx\par et fermera le groupe semi-simple ouvert par §\narrowtext. + +\showcode/\def\narrowtext{%¤§*\narrowtext¤ + \par + \begingroup + \advance\leftskip 0.1\hsize¤\idx*\advance\idx*\leftskip¤ + \advance\rightskip 0.1\hsize¤\idx*\advance\idx*\rightskip¤ +} +\def\endnarrowtext{\par\endgroup} +\def\dummytext{Ceci est un texte sans + importance destiné à meubler un paragraphe. } + +\dummytext\dummytext\dummytext +\narrowtext + \dummytext\dummytext\dummytext + \narrowtext + \dummytext\dummytext\dummytext + \endnarrowtext + \dummytext\dummytext\dummytext +\endnarrowtext/ + +Comme aucune des macros §\narrowtext ou \verb|\endnarrowtext| n'admet d'argument, il n'y a aucun risque de mettre entre elles un contenu très long qui aurait pu inutilement surcharger la mémoire de \TeX{} s'il avait été maladroitement incorporé dans un argument. L'autre avantage est que la lecture d'arguments est assortie du gel des catcodes qui n'existe pas ici.\idx*[|)]{paragraphe!ressorts de paragraphe} + +\subsection{Ressorts prédéfinis} +Les ressorts-primitives modifiables --~ceux qui étaient précédés d'une étoile dans le tableau de la page~\pageref{tableau.ressort}~-- se comportent exactement comme s'ils étaient des macros définies avec \idx\newskip. La seule différence est que leur valeur est figée. On remarque que \idx\hfil et \idx\vfil sont compensés (c'est-à-dire annulés) par \idx\hfilneg et \idx\vfilneg. + +Les ressorts \idx\hss et \idx\vss sont eux encore plus curieux puisque, de dimension nulle, ils peuvent s'étirer ou se comprimer infiniment. Nous verrons leur intérêt plus loin. + +Voici comment nous pouvons utiliser \idx\hfill pour composer au fer à droite, au fer à gauche, centrer une ligne ou bien encore, régulièrement espacer zones de texte sur une ligne : + +\showcode/a) \hfill Composition au fer à droite\par +b) Composition au fer à gauche\hfill\kern0pt\par +c) \hfill Centrage\hfill\kern0pt\par +d) Des \hfill mots\hfill régulièrement\hfill espacés/ + +Comment se justifie la présence de ces « \idx\kern\verb|0pt|» après les \idx\hfill en \idx{fin de ligne} ? Tout vient de la règle précédente qui stipule qu'un ressort qui se situe juste avant la fin d'un paragraphe (autrement dit juste avant \idx\par) est \emph{ignoré}. Ce \verb|\kern0pt| n'a d'autre but que de mettre quelque chose (on appelle cela un n\oe ud) entre le ressort et \idx\par. Bien évidemment, la dimension de ce n\oe ud doit être nulle afin que son encombrement ne fausse pas la largeur des éléments pris en compte. Au lieu d'écrire \verb|\kern0pt|, il est également courant d'employer la macro \idx\null définie par plain-\TeX{} dont le texte de remplacement est une boite horizontale vide, c'est-à-dire \idx\hbox\verb|{}|\idx*[|)]{registre!ressort}\idx*[|)]{dimension étirable (ressort)}\idx*[|)]{dimension}. + +\section{Les boites}\label{boites}\idx*[|(]{boite} +Dans le monde de \TeX{}, presque tout ce qui concerne la composition est lié aux boites. Les dimensions sont notamment utilisées pour mesurer les caractéristiques géométriques de ces boites. Les ressorts eux, sont chargés de remplir des zones dans ces boites. + +Avant de commencer, il est important de comprendre qu'une boite reçoit du code source transmis sous la forme d'un argument, mais prend en compte le résultat typographique de ce code \emph{une fois qu'il a été composé}. Les assignations sont mises à part pour être exécutés lorsque la boite est affichée. Ainsi, mettre quelque chose dans une boite implique donc au préalable le travail complet de composition. + +\subsection{Fabriquer des boites} +Si \TeX{}, de par son fonctionnement lors de la composition, fabrique automatiquement des boites pour les placer sur la page, des primitives permettent à l'utilisateur de fabriquer ses propres boites avec la possibilité d'y mettre un contenu arbitraire. + +\begin{regle} + \relax\TeX{} dispose de trois primitives pour enfermer un matériel dans une boite : + + \begin{itemize} + \item \idx\hbox\verb|{<code>}| construit une boite \emph{horizontale} contenant du matériel (qui est le résultat de la composition du \verb|<code>|) disposé en mode horizontal\idx*{mode!horizontal} et compatible avec ce mode; + \item \idx\vbox\verb|{<code>}| et \idx\vtop\verb|{<code>}| bâtissent des boites \emph{verticales}, susceptibles donc de recevoir du matériel disposé en mode vertical\idx*{mode!vertical} et compatible avec ce mode. + \end{itemize} + + Dans les trois cas, l'intérieur d'une boite tient lieu de groupe, c'est-à-dire que les assignations faites dans le \verb|<code>| restent locales à la boite. + + Le \verb|<code>| n'est pas lu en une fois comme s'il était l'argument d'une macro; il est lu \emph{au fur et à mesure} que la boite se remplit et donc au fil de cette lecture, des changements de catcode sont permis. + + Une boite ainsi construite peut être : + + \begin{itemize} + \item affichée lorsque \idx\hbox, \idx\vbox ou \idx\vtop sont placés dans le flux d'entrée de \TeX. Dans ce cas, la boite est ajoutée à la liste en cours selon le mode dans lequel \TeX{} travaille à ce moment (\idx*{boite!mode}horizontal\idx*{mode!horizontal} ou vertical\idx*{mode!vertical}). Il est important de noter que ni \idx\hbox ni \idx\vbox ou \idx\vtop ne provoquent de changement de mode, même si à l'intérieur de ces boites, un mode «interne»\idx*{mode!interne} est imposé; + \item stockée dans un registre de boite (voir la section suivante). + \end{itemize} +\end{regle} + +Le code ci-dessous démontre que les boites s'affichent selon le mode en cours : + +\showcode/a% le caractère "a" fait passer en mode horizontal¤\idx*{mode!horizontal}¤ +\hbox{b}c\hbox{d}% les \hbox sont ajoutées en mode horizontal¤\idx*{mode!horizontal}¤ +\medbreak +...à comparer avec... +\medbreak +a \par% \par fait passer en mode vertical¤\idx*{mode!vertical}¤ +\hbox{b}% \hbox est ajoutée à la liste verticale +c% "c" fait passer en mode horizontal¤\idx*{mode!horizontal}¤ +\hbox{d}% la \hbox est ajoutée en mode horizontal/ + +Il est important de se souvenir de cette règle lorsqu'on construit une macro dont le premier acte est d'afficher une boite construite avec une des trois primitives vues ci-dessus. En effet, on souhaite souvent placer d'autres choses à droite de cette boite (en mode horizontal donc). Si la macro est appelée en mode vertical, le placement se fera \emph{en dessous}, et non pas \emph{après} comme attendu. Pour se prémunir de ce risque, il est \emph{très sage} de mettre un \idx\leavevmode avant la boite pour quitter le mode vertical\idx*{mode!vertical}. Ce faisant, la boite sera composée en mode horizontal\idx*{mode!horizontal} et le comportement recherché sera obtenu. +\grandsaut + +Les boites construites avec \idx\vbox ou \idx\vtop empilent verticalement des éléments. Ces éléments sont susceptibles d'être mis dans une liste verticale car, à l'intérieur de \idx\vbox ou \idx\vtop, on est dans le mode vertical (plus exactement en mode vertical \emph{interne}\idx*{mode!vertical interne}). Ce sont par exemple des paragraphes (qui ont une largeur égale à \idx\hsize), des boites formées avec une des trois primitives, des ressorts verticaux insérés avec \idx\vskip, des réglures (que nous verrons un peu plus loin), etc. + +Autant la largeur d'une \idx\hbox est clairement la largeur du matériel que l'on y a mis, autant celle d'une \idx\vbox ou \idx\vtop est moins évidente; La largeur d'une boite verticale est celle de l'élément empilé verticalement qui a la plus grande largeur. Mais attention, si on écrit «\verb|\vbox{foobar}|» ou «\verb|\vtop{foobar}|», \TeX{} composera un paragraphe pour ce seul mot et mettra cet élément dans la boite. Or, la largeur des paragraphes est égale à la dimension-primitive \idx\hsize qui, dans le mode vertical principal, contient la largeur de la zone de texte. L'intérieur d'une boite jouant le rôle de groupe, on peut certes modifier \idx\hsize dans une boite verticale pour imposer aux paragraphes une largeur choisie. Sans cette man\oe uvre pour modifier \idx\hsize, la largeur de la boite sera la même que celles des paragraphes de la page, ce qui est peut-être un peu surdimensionné pour un paragraphe aussi court que «foobar» ! Au contraire, si l'on écrit «\verb|\vbox{\hbox{foobar}}|» ou «\verb|\vtop{\hbox{foobar}}|», l'élément qu'est la \idx\hbox sera inséré (en mode vertical\idx*{mode!vertical}) et au final, la boite verticale aura la largeur de cet élément, c'est-à-dire la largeur du mot « foobar ». + +\begin{regle} +Les primitives \idx\vbox et \idx\vtop définissent des boites dans lesquelles des éléments sont empilés verticalement. + +La différence entre \idx\vbox et \idx\vtop réside dans le \idx{point de référence} de la boite finale. Dans les deux cas, les éléments sont empilés verticalement, le premier étant en haut et le dernier en bas. Avec \idx\vbox, le point de référence de la boite est le point de référence du \emph{dernier} élément (celui du bas) alors qu'avec \idx\vtop, son point de référence est celui du \emph{premier} élément (celui du haut). +\end{regle} + +Le \idx{point de référence} d'une boite étant sur la \idx{ligne de base}, on comprend donc que \idx\vbox construit des empilements ayant des éléments au-dessus de cette ligne et donc produit en général des boites de grande hauteur et faible profondeur. À l'opposé, \idx\vtop produit des empilements ayant des éléments sous cette \idx{ligne de base} et donc des boites de petite hauteur et grande profondeur. +\grandsaut + +Dans l'exemple ci-dessous, on met dans une \idx\vbox et une \idx\vtop des éléments dont la largeur est contrôlé puisque ce sont des \idx\hbox. On peut observer, grâce aux points qui représentent approximativement la \idx{ligne de base} la différence fondamentale entre une \idx\vbox et une \idx\vtop : + +\showcode/Ligne de base : .........% +\vbox{\hbox{ligne du haut}\hbox{ligne du milieu}\hbox{ligne du bas}}%¤\idx*\vbox¤ +.........% +\vtop{\hbox{ligne du haut}\hbox{ligne du milieu}\hbox{ligne du bas}}.........¤\idx*\vtop¤/ + +Voici ce qui se passe en dessinant les boites et leur \idx{point de référence} : + +\begin{centrage} +\footnotesize +\leavevmode\setbox0=\hbox{ligne du milieu} +Ligne de base.........% +\drawbox{\vbox{\raggedright\hsize\wd0 \drawbox{ligne du haut}\par\drawbox{ligne du milieu}\par\drawbox{ligne du bas}}}% +.........% +\drawbox{\vtop{\raggedright\hsize\wd0 \drawbox{ligne du haut}\par\drawbox{ligne du milieu}\par\drawbox{ligne du bas}}}% +.........% +\end{centrage} + +Nous allons maintenant faire de même avec un élément dont la largeur n'est pas contrôlée, c'est-à-dire un paragraphe entier composé de deux phrases identiques contenues dans la macro \verb|\dummytext|. Afin que les deux boites tiennent horizontalement dans l'espace qui leur est réservé, la largeur des boites verticales sera imposée par \idx\hsize à \numprint[cm]4 : + +\showcode/\def\dummytext{Bla, bla, bla, un texte sans importance. } +Ligne de base.........% +\vbox{\hsize=4cm \dummytext\dummytext}%¤\idx*\vbox¤ +.........% +\vtop{\hsize=4cm \dummytext\dummytext}.........¤\idx*\vtop¤/ + +\subsection{Registres de boite}\idx*[|(]{boite!registre} +Une boite simplement affichée avec \idx\hbox, \idx\vbox ou \idx\vtop ne permet pas de travailler sur cette boite. En revanche, une boite \emph{stockée} dans un registre ouvre d'autres perspectives puisqu'elle est mémorisée par \TeX. Dès lors, il devient possible d'effectuer des opérations sur ces boites. + +Avant de poursuivre, il encore une fois est important de clarifier le sens du mot « stockage ». Autant les macros et les registres de tokens stockent du \emph{code source tokénizé} c'est-à-dire du matériau brut initial, autant les registres de boites stockent d'un côté le résultat typographique d'un \verb|<code>| une fois qu'il a été composé et de l'autre les assignations faites dans ce \verb|<code>|. Le stockage par macro et par boite sont deux stockages opposés en ce sens que chacun effectue une sauvegarde à l'un des deux bouts de la chaine qui mène du code source à l'affichage final. +\grandsaut + +Nous commençons à être familiers avec les registres puisque, hormis les macros, il en existe pratiquement pour chaque type de donnée que manipule \TeX{} (tokens, entiers, dimensions, ressorts). Les boites ne font évidemment pas exception à la règle, même si la syntaxe des registres de boites est légèrement différente de celle des autres registres. La raison incombe au fait que \TeX{} ne possède pas de primitive \verb|\boxdef| alors qu'il possède \idx\toksdef, \idx\countdef, \idx\dimendef et \idx\skipdef. + +\begin{regle} +\relax\TeX{} dispose de 256 registres de boites (\numprint{32768} avec \idx\eTeX). + +On demande à \TeX{} d'allouer un numéro de registre de boite par l'intermédiaire de la macro \idx\newbox\verb|\<macro>| et ce faisant, l'assignation suivante est faite + +\centrecode-\global\chardef\<macro>=<nombre>- + +où le \verb|<nombre>| est un numéro de registre de boite libre. + +Ce faisant, la \verb|\<macro>| devient équivalente à \idx\char\verb|<nombre>|. Ainsi, \verb|\<macro>| produit le caractère de code \verb|<nombre>|, mais lorsque \TeX{} s'attend à lire un nombre (comme c'est le cas lorsqu'il attend un numéro de boite), \verb|\<macro>| est lue comme l'entier \verb|<nombre>|. Du point de vue de la mécanique interne, un \verb|<registre>| de boite est donc un \verb|<nombre>|. + +On accède à la boite contenue dans un registre par \idx\box\verb|<registre>|. + +Pour procéder à l'assignation, on écrit\idx*{assignation!registre de boite}\idx*\setbox : + +\centrecode-\setbox<registre>= <boite valide>- + +\noindent où une \verb|<boite valide>| est soit une boite contenue dans un registre et écrite sous la forme \idx\box\verb|<registre>|, soit une boite construite avec les primitives \idx\hbox, \idx\vbox ou \idx\vtop. Le signe \verb|=| et l'espace qui le suit sont facultatifs. + +Pour afficher une boite stockée dans un \verb|<registre>|, on écrit \idx\box\verb|<registre>|. La boite est affichée selon le mode en cours (horizontal ou vertical) : le type de boite n'a pas d'influence sur le mode dans lequel elle est affichée. + +Lorsqu'on utilise la primitive \idx\box pour accéder à la boite stockée dans un registre, la boite contenue dans le registre est irrémédiablement perdue et le registre devient vide, c'est-à-dire qu'il ne contient aucune boite. Si on souhaite conserver la boite dans le registre, on doit utiliser la primitive \idx\copy au lieu de \idx\box. +\end{regle} + +\begin{regle} +Le test\tidx*{ifvoid}% + +\centrecode-\ifvoid<registre><code vrai>\else<code faux>\fi- + +se comporte comme les autres tests de \TeX. Il revoit \verb|<code vrai>| si le \verb|<registre>| de boite est vide (c'est-à-dire s'il ne contient aucune boite). +\end{regle} + +Il est admis que la boite \no0\idx*{boite!\no0} est une boite « brouillon » qui peut être utilisée ponctuellement si l'on ne veut pas mobiliser un nouveau registre de boite. +\grandsaut + +L'exemple ci-dessous illustre ce que nous venons d'aborder. Nous allons tout d'abord allouer un nouveau registre \verb|\foobox| par \idx\newbox et nous y stockerons une boite horizontale contenant le mot « foobar ». Ensuite, nous allons utiliser le test \tidx{ifvoid} dans plusieurs cas de figure afin de montrer que le registre devient vide après avoir exécuté la primitive \idx\box. + +\showcode/\newbox\foobox¤\idx*\newbox¤ +a) \setbox\foobox=\hbox{foobar}% registre non vide¤\idx*\setbox¤ + Le registre est \ifvoid\foobox vide\else non vide\fi\par +b) \setbox\foobox=\hbox{foobar} + "\box\foobox" et le registre est \ifvoid\foobox vide\else non vide\fi\par +c) \setbox\foobox=\hbox{foobar} + "\copy\foobox" et le registre est \ifvoid\foobox vide\else non vide\fi\par +d) \setbox\foobox=\hbox{} + Le registre est \ifvoid\foobox vide\else non vide\fi¤\tidx*{ifvoid}¤/ + +On remarque que \idx\box vide le registre (cas b) tandis que \idx\copy le préserve (cas c). Il est également important de noter que le registre n'est pas vide (cas d) s'il contient une boite vide. +\grandsaut + +Deux autres tests sur les registres de boite méritent d'être signalés : + +\begin{centrage} +\small\tidx{ifhbox}\verb|<registre>|\quad et\quad\tidx{ifvbox}\verb|<registre>| +\end{centrage} + +\noindent sont vrais si le \verb|<registre>| contient respectivement une boite horizontale ou verticale.\idx*[|)]{boite!registre} + +\subsection{Dimensions des boites}\idx*[|(]{boite!dimension} +\begin{regle} +Les dimensions d'une boite stockée dans un \verb|<registre>| sont : +\begin{itemize} + \item sa longueur horizontale, accessible par \idx\wd\verb|<registre>| ; + \item sa hauteur, qui s'étend au-dessus de la \idx{ligne de base}, accessible par \idx\ht{}\linebreak[1]\verb|<registre>| ; + \item enfin, sa profondeur qui s'étend au-dessous de la \idx{ligne de base} et qui est accessible par \idx\dp\verb|<registre>|. +\end{itemize} + +Les primitives \idx\wd, \idx\ht et \verb|\dp| suivies d'un \verb|<registre>| se comportent comme des registres de dimension. +\end{regle} + +\noindent Pour fixer les idées, voici un schéma où les \idx*{boite!englobante}boites englobantes, rectangles qui représentent l'encombrement d'une boite tel qu'il est pris en compte par \TeX, sont tracées et les trois dimensions sont écrites pour quatre lettres différentes :\medbreak + +\noindent\hskip0.1\linewidth\hbox to0pt{\dimenbox{g}\hss}\hskip0.5\linewidth\hbox to0pt{\dimenbox{o}\hss}\medbreak +\noindent\hskip0.1\linewidth\hbox to0pt{\dimenbox{P}\hss}\hskip0.5\linewidth\hbox to0pt{\dimenbox{\itshape f}\hss}\medbreak + +On peut observer notamment avec la lettre «\textit{f}\kern1pt» que la boite englobante\idx*{boite!englobante} ne correspond pas à la plus petite boite qui contient les contours de la lettre. Pour d'évidentes raisons de typographie, il est \emph{nécessaire} que certaines parties de la lettre «\textit{f}» dépassent de la boite englobante\idx*{boite!englobante} sinon, au lieu de «\textit{foo}», nous aurions un horrible «\textit{f\kern1.65ptoo}» ! + +De plus, contrairement à ce que l'on pourrait croire, la profondeur de lettres comme «o» ou «P» n'est pas nulle ! Très faible certes, mais pas nulle\ldots{} Le concepteur de la \idx{fonte} «Libertine» avec laquelle est écrit ce livre en a décidé ainsi. +\grandsaut + +Passons à la pratique et mesurons maintenant les dimensions de boites construites successivement avec les trois primitives \idx\hbox, \idx\vbox et \idx\vtop. Les boites verticales seront des empilements de \idx\hbox, chacune contenant un mot de la phrase «Programmer est facile.» + +\showcode/\newbox\foobox¤\idx*\newbox¤ +\setbox\foobox=\hbox{Programmer est facile.}¤\idx*\setbox¤ +<<\copy\foobox>>¤\idx*\copy¤ +mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut.¤\idx*\the\idx*\wd\idx*\dp¤ +\medbreak¤\idx*\medbreak¤ + +\setbox\foobox=\vbox{\hbox{Programmer}\hbox{est}\hbox{facile.}}¤\idx*\setbox¤ +<<\copy\foobox>> +mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. +\medbreak¤\idx*\medbreak¤ + +\setbox\foobox=\vtop{\hbox{Programmer}\hbox{est}\hbox{facile.}}¤\idx*\setbox\idx*\vtop¤ +<<\copy\foobox>> +mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut.¤\idx*\the\idx*\wd\idx*\dp¤/ + +La faible profondeur de la \idx\vbox provient une fois encore du design de la \idx{fonte} Libertine qui donne une faible profondeur à certaines lettres qui composent le mot du bas «facile». La verticalité \emph{totale} des boites verticales (c'est-à-dire la somme de leur hauteur et de leur profondeur) \idx\vbox et \idx\vtop est la même dans les deux cas. + +\begin{exercice} +Programmer une macro §\vdim\verb|{<registre>}| qui calcule la verticalité de la boite contenue dans le \verb|<registre>|. +\solution +Il suffit de donner à \idx\dimexpr l'addition de sa hauteur et sa profondeur : + +\showcode/\def\vdim#1{\dimexpr\ht#1+\dp#1\relax}¤§*\vdim¤ +a) \setbox\foobox=\vbox{\hbox{Programmer}\hbox{est}\hbox{facile.}}¤\idx*\setbox¤ + Verticalité de la \litterate-\vbox- = \the\vdim\foobox\par¤§*\litterate\idx*\the¤ +b) \setbox\foobox=\vtop{\hbox{Programmer}\hbox{est}\hbox{facile.}}¤\idx*\setbox¤ + Verticalité de la \litterate-\vtop- = \the\vdim\foobox¤§*\litterate\idx*\the¤/ +\end{exercice} + +\begin{exercice} +Inventer un procédé qui permette de compter combien de caractères affichables contient un argument. On appellera la macro §\countallchar\verb|{<texte>}| et on supposera que le \verb|<texte>| est uniquement constitué de caractères affichables (catcodes 11 et 12) et d'espaces. +\solution +Au point où nous en sommes, la solution la plus immédiate est de mettre cet argument dans une boite \idx\hbox où la \idx{fonte} sera à chasse fixe. Il suffit de mesurer cette boite, de mesurer un caractère et d'effectuer la division entre les entiers contraints de ces deux dimensions pour afficher le nombre de caractères. Ici, nous allons même afficher la division conduisant au résultat : + +\showcode|\def\countallchar#1{%¤§*\countallchar¤ + Il y a % + \setbox0=\hbox{\tt#1}% met #1 dans la boite¤\idx*\setbox¤ + \edef\arglength{\number\wd0 }% stocke la largeur de la boite en sp¤\idx*\number\idx*\wd\idx*{sp (unité)}¤ + \setbox0=\hbox{\tt A}% met "A" dans la boite¤\idx*\setbox¤ + \edef\charlength{\number\wd0 }% stocke la largeur d'un caractère + $\number\arglength/\charlength % affiche la division¤\idx*\wd¤ + =\number\numexpr\arglength/\charlength\relax$ % affiche le quotient¤\idx*\wd¤ + caractères% +} +\countallchar{abcd efgh}\par +\countallchar{A5 xW5 64 a1}\par +\countallchar{affligeant}¤§*\countallchar¤| + +Une méthode similaire peut être déployée pour compter combien de fois figure un caractère affichable $\alpha$ dans un argument. L'idée est de mesurer la boite contenant l'argument composé en fonte à chasse fixe et stocker cette longueur dans $L$. Ensuite, le caractère $\alpha$ est supprimé avec la macro §\substin et la man\oe uvre est répétée : $l$ est la nouvelle longueur. Le nombre d'occurrences de $\alpha$ est la différence $L-l$ divisée par la longueur de la boite contenant $\alpha$ : + +\showcode|\catcode`@11 +\def\countchar#1#2{% + \setbox\z@\hbox{\tt#2}% met #2 dans boite 0¤\idx*\setbox\idx*\tt §*\defactive\idx*\string\idx*\hbox\idx*\z@¤ + \edef\len@a{\number\wd\z@}% mesure la boite¤\idx*\wd¤ + \setbox\z@\hbox{\tt\substin{#2}{#1}{}}% recommencer sans "#1"¤\idx*\setbox§*\defactive¤ + \edef\len@b{\number\wd\z@}% mesure la boite + \setbox\z@\hbox{\tt A}% met "A" dans la boite¤\idx*\setbox\idx*\string¤ + \edef\charlength{\number\wd\z@}% stocke la largeur du caractère + \number\numexpr(\len@a-\len@b)/\charlength% et affiche le quotient +} +\catcode`@12 +a) \countchar{a}{abracadabra}\qquad +b) \countchar{b}{zigzag}\qquad +c) \countchar{ }{a bc de f ghi j k }¤§*\countchar¤| +\end{exercice}\idx*[|)]{boite!dimension} + +\subsection{Déplacer des boites}\idx*[|(]{boite!déplacement} +\TeX{} met à disposition des primitives pour déplacer des boites dans la direction que l'on veut (haut, bas, gauche, droite). + +\begin{regle} +Les primitives \idx\lower ou \idx\raise doivent être suivies d'une \verb|<dimension>|, le tout devant précéder une boite horizontale, qu'elle soit écrite par l'intermédiaire de \idx\hbox ou d'un registre. L'effet est d'abaisser pour \idx\lower ou d'élever pour \idx\raise la boite de la \verb|<dimension>| spécifiée. + +Les primitives \idx\moveleft et \idx\moveright partagent la même syntaxe, mais agissent sur des boites \emph{verticales} en les déplaçant vers la gauche ou vers la droite. +\end{regle} + +Voici comment on peut enfermer des mots dans une \idx\hbox et les élever ou les abaisser d'une dimension arbitraire : + +\showcode/Programmer \raise1ex\hbox{en} \lower1ex\hbox{\TeX} \lower2ex\hbox{est} facile.¤\idx*\raise\idx*\lower\idx*\hbox¤/ + +Il faut d'ailleurs noter que le logo «\TeX», obtenu en exécutant la macro \index{TeX@\texttt{\char92 TeX}}\verb|\TeX|, est construit en utilisant \idx\lower pour abaisser la lettre «E» après rebroussé chemin vers la gauche à l'aide de \idx\kern. La lettre «X» est également déplacée vers la gauche avec un autre \idx\kern. Voici comment est définie la macro \index{TeX@\texttt{\char92 TeX}}\TeX{} par plain-\TeX : + +\centrecode|\def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX}| +\grandsaut + +Il peut sembler frustrant que l'on ne puisse pas « centrer » une boite contenant du matériel vertical par rapport à l'axe qu'est la \idx{ligne de base} puisque \idx\vbox et \idx\vtop empilent ce matériel respectivement au-dessus ou au-dessous de cette ligne. L'idée serait d'abaisser ces boites avec \idx\lower de la dimension égale à + +\[\frac{\hbox{hauteur}-\hbox{profondeur}}2\] + +\noindent étant entendu que si cette quantité est négative, abaisser d'une dimension négative revient à élever la boite. En coulisses, cette méthode nous contraint à mettre la boite dans un registre pour pouvoir la mesurer afin d'accéder à sa hauteur et sa profondeur. La macro §\cbox va mettre en application cette méthode et sera amenée à recevoir du matériel vertical dans les mêmes conditions que \idx\vbox afin de centrer la boite par rapport à la \idx{ligne de base} : + +\showcode|\def\cbox#1{%¤§*\cbox¤ + \setbox0\vbox{#1}% met le contenu dans une \vbox¤\idx*\setbox\idx*\vbox¤ + \lower\dimexpr(\ht0-\dp0)/2\relax\box0 % l'abaisse¤\idx*\lower\idx*\dimexpr\idx*\ht\idx*\dp\idx*\box¤ +} + +......\cbox{\hbox{$x^2$}}......\cbox{\hbox{foo}\hbox{et bar}}......% +\cbox{\hbox{Programmer}\hbox{en \TeX}\hbox{est facile}}.......¤§*\cbox¤| + +\TeX{} dispose de la primitive \idx\vcenter, qui fonctionne en mode mathématique\idx*{mode!mathématique}, dont l'argument est constitué de matériel vertical (comme l'est celui de \idx\vbox ou \idx\vtop) et qui place la boite ainsi créé de telle sorte que ses frontières haute et basse soient équidistantes de «l'axe mathématique». Deux précisions doivent être apportées : + +\begin{itemize} + \item la primitive ne peut être appelée qu'en mode mathématique\idx*{mode!mathématique}, mais le contenu de la boite n'hérite pas de ce mode, c'est-à-dire qu'il sera composé en mode texte, sauf si bien sûr, on demande explicitement à passer en mode mathématique\idx*{mode!mathématique} à l'intérieur de la boite (les boites \idx\hbox, \idx\vbox et \idx\vtop partagent aussi cette propriété) ; + \item l'axe mathématique \emph{n'est pas} à la même hauteur que la \idx{ligne de base}; il s'agit de deux axes différents. L'axe mathématique est horizontal et se trouve approximativement à équidistance des traits horizontaux du signe «$=$». Il représente l'axe de symétrie horizontal de plusieurs signes mathématiques alors que la \idx{ligne de base} est approximativement tangente au bas des lettres qui n'ont pas de jambage comme on le voit dans l'égalité ci-dessous : + \begin{centrage} + \begingroup\huge + \setbox0\hbox{$\vcenter{}$}% + \xdef\htmath{\the\ht0}% + \endgroup + \begin{tikzpicture}[inner sep=0pt,outer sep=0pt,minimum size=0pt,baseline,line width=0.2pt] + \node[anchor=base west](equation){\huge $a\kern.125em(b+c)=ab+ac$}; + \draw[gray]([xshift=-2cm]equation.base west)--([xshift=0.2cm]equation.base east)node[pos=0,anchor=south west,outer sep=1pt,black]{\scriptsize ligne}node[pos=0,anchor=north west,outer sep=1pt,black]{\scriptsize de base}; + \draw[gray]([xshift=-0.2cm,yshift=\htmath]equation.base west)--([xshift=2cm,yshift=\htmath,black]equation.base east)node[pos=1,anchor=south east,outer sep=1pt,black]{\scriptsize axe}node[pos=1,outer sep=1pt,anchor=north east,black]{\scriptsize math}; + \end{tikzpicture} + \end{centrage} +\end{itemize} + +Par conséquent, une boite centrée avec \idx\vcenter ne sera pas exactement à la même hauteur que la même boite centrée avec la macro §\cbox vue précédemment. La différence se voit sans peine à l'\oe il nu dans cet exemple : + +\showcode|......\cbox{\hbox{foo}\hbox{et bar}}......$\vcenter{\hbox{foo}\hbox{et bar}}$......¤\idx*\vcenter§*\cbox¤|\idx*[|)]{boite!déplacement} + +\begin{exercice} +Inventer un procédé pour que la macro §\htmath affiche à quelle distance de la ligne de base se trouve l'axe mathématique. +\solution +Si une \idx\hbox contient une \idx\vcenter vide, la hauteur de la \idx\hbox sera précisément la distance cherchée. + +\showcode/\def\htmath{\begingroup¤§*\htmath¤ + \setbox0=\hbox{$\vcenter{}$}\the\ht0 ¤\idx*\setbox\idx*\vcenter\idx*\the\idx*\ht¤ +\endgroup +} +L'axe mathématique se trouve à \htmath{} de la ligne de base./ +\end{exercice} + +\subsection{Choisir la dimension d'une boite}\idx*[|(]{boite!dimension choisie} +\begin{regle} +On peut imposer une dimension aux boites en le spécifiant avec le mot-clé «\idx*{boite!mot-clé «to»}\verb|to|» suivi d'un espace optionnel puis d'une \verb|<dimension>|. Et donc, si l'on écrit + +\centrecode-\hbox to 3cm{<contenu>}- + +\noindent cela créera une boite horizontale de longueur \numprint[cm]{3}, quel que soit le \verb|<contenu>|. Si le contenu ne mesure pas \emph{exactement} \numprint[cm]{3} et ne contient aucun élément étirable, il y aura soit un débordement, soit un sous-remplissage de boite, chacun respectivement signalé par les messages d'avertissement\idx*{message d'avertissement} «\texttt{Overfull \string\hbox}» ou «\texttt{Underfull \string\hbox}» dans le \idx[!log]{fichier} \verb|log|. La même syntaxe s'applique pour \idx\vbox et \idx\vtop sauf que la dimension est imposée dans le sens \emph{vertical}. + +On peut également écrire le mot-clé «\idx*{boite!mot-clé «spread»}\verb|spread|» utilisé à la place de «\verb|to|» si l'on souhaite construire une boite dont la dimension finale sera sa dimension naturelle augmentée de la \verb|<dimension>|. Si cette dimension est négative, la boite créée aura une dimension inférieure à sa dimension naturelle. +\end{regle} + +Voici un exemple qui montre comment une boite est affichée avec sa largeur naturelle puis étirée de \verb|5pt| et \verb|10pt| grâce à un ressort \idx\hfil placé entre les syllabes «\verb|foo|» et «\verb|bar|»: + +\showcode/1) \hbox{foobar}\par¤\idx*\hbox¤ +2) \hbox spread 5pt{foo\hfil bar}\par +3) \hbox spread10pt{foo\hfil bar}/ + +\Qu e ce soit via «\verb|to|» ou «\verb|spread|», il est nécessaire que contenu de la boite ait un ou plusieurs ressorts. Ils devront avoir des composantes étirables convenablement choisies pour qu'en s'étirant ou en se comprimant, ils puissent compenser l'excès ou le manque de place pour le contenu. Un des ressorts les plus utilisés dans ce cas est \idx\hss pour les boites horizontales et \idx\vss pour les boites verticales. Rappelons qu'ils ont une longueur nulle et peuvent s'étirer ou se comprimer infiniment. +\grandsaut + +On peut facilement créer des macros qui affichent du texte \emph{en surimpression} soit à droite, soit à gauche de la position courante. Ces deux macros, \idx\rlap et \idx\llap, dont le nom signifie « right overlap » (surimpression vers la droite) ou « left overlap » (surimpression vers la gauche) sont ainsi définies dans le \idx[!plain-\TeX]{format} plain-\TeX : + +\centrecode-\def\rlap#1{\hbox to\z@{#1\hss}}¤\idx*\z@¤ +\def\llap#1{\hbox to\z@{\hss#1}}- + +Examinons tout d'abord \idx\rlap. Son argument \verb|#1| est mis dans une boite horizontale de longueur nulle (\idx\z@ représente la dimension \verb|0pt|). Un sur-remplissage de la boite semble inévitable, car le contenu \verb|#1| prend une certaine place horizontale. C'est là que le ressort \idx\hss placé en fin de boite va intervenir : pour satisfaire la longueur de la boite nulle, il va se comprimer pour atteindre une longueur négative exactement égale à l'opposé de la longueur de l'argument \verb|#1|. La somme des longueurs de \verb|#1| et de \idx\hss satisfait bien la dimension nulle imposée. + +\begin{centrage} + \footnotesize + \begin{tikzpicture} + \useasboundingbox (-3,-2) rectangle (4,1); + \draw[line width=1pt](0,-2)--(0,0)node[above,align=center,text width=1.5cm]{Position courante}; + \node[draw,anchor=mid west,align=center,text width=3cm,inner sep=0pt,outer sep=0pt](arg1)at(0,-.5){\vrule height2.5ex depth1.5ex width0pt Argument \ttfamily \#1}node[anchor=mid west] at(4,-.5){Étape \no1}; + \draw [-stealth](.15,-1.5)--(0,-1.5); + \draw[decorate,decoration={zigzag,segment length=0.3cm,amplitude=1mm}] (.15,-1.5)--(3cm,-1.5) + node[below,pos=.5,yshift=-.1cm]{ressort \ttfamily\string\hss} + node[anchor=mid west] at(4,-1.5){Étape \no2}; + \end{tikzpicture} +\end{centrage} + +Son homologue \idx\llap fonctionne de la même façon sauf que le ressort \idx\hss est placé en début de boite : il va donc se comprimer en premier ce qui se traduit à l'affichage par un recul vers la gauche de la longueur de \verb|#1|. Ensuite, \verb|#1| est composé et rattrape exactement la longueur négative de \idx\hss. + +\begin{centrage} + \footnotesize + \begin{tikzpicture} + \useasboundingbox (-3,-2) rectangle (4,1.5); + \draw[line width=1pt](0,-2)--(0,0.5)node[above,align=center,text + width=1.5cm]{Position courante}; + \node[draw,anchor=mid east,align=center,text width=3cm,inner sep=0pt,outer sep=0pt](arg1)at(0,-1.5){\vrule height2.5ex depth1.5ex width0pt Argument\ttfamily \#1}node[anchor=mid west] at(.5,-1.5){Étape \no2}; + \draw [-stealth](-2.85,-.5)--(-3,-.5); + \draw[decorate,decoration={zigzag,segment length=0.3cm,amplitude=1mm}] (-2.85cm,-0.5)--(0,-0.5) + node[above,pos=.5,yshift=.1cm]{ressort \ttfamily\string\hss} + node[anchor=mid west] at(.5,-0.5){Étape \no1}; + \end{tikzpicture} +\end{centrage} + +L'exemple ci-dessous montre comment ces deux macros peuvent utilisées pour écrire soit à gauche, soit à droite de la position courante en surimpression. Dans les deux cas, le caractère «|» est utilisé pour symboliser la position courante lorsque \TeX{} rencontre ces deux macros : + +\showcode=foobar|\rlap{/////}123456\qquad foobar|\llap{/////}123456¤\idx*\rlap\idx*\llap¤= + +Il est curieux que \idx*{Knuth Donald}D.~\textsc{Knuth} n'ait pas implémenté la macro §\clap qui permettrait de centrer le contenu de son argument sur la position courante sans que l'encombrement horizontal ne soit pris en compte : + +\centrecode-\def\clap#1{\hbox to0pt{\hss#1\hss}}-§*\clap + +Bien sûr, le fonctionnement de cette macro est en tout point semblable à \idx\rlap et \idx\llap sauf que les deux ressorts se compriment de façon identique ce qui donne un centrage du contenu par rapport à la position courante: + +\begin{centrage} + \footnotesize + \begin{tikzpicture} + \useasboundingbox (-3,-3) rectangle (4,1); + \draw[line width=1pt](0,-3)--(0,0)node[above,align=center,text width=1.5cm]{Position courante}; + \draw [-stealth](-1.35,-.5)--(-1.5,-.5); + \draw[decorate,decoration={zigzag,segment length=0.3cm,amplitude=1mm}] (-1.35cm,-0.5)--(0,-0.5) + node[below,pos=.5,yshift=-.1cm]{\ttfamily\string\hss} + node[anchor=mid west] at(2,-0.5){Étape \no1}; + \node[draw,align=center,text width=3cm,inner sep=0pt,outer sep=0pt](arg1)at(0,-1.5){\vrule height2.5ex depth1.5ex width0pt Argument \ttfamily\#1}node[anchor=mid west] at(2,-1.5){Étape \no2}; + \draw [-stealth](0.15,-2.5)--(0,-2.5); + \draw[decorate,decoration={zigzag,segment length=0.3cm,amplitude=1mm}] (1.5,-2.5)--(.15,-2.5) + node[below,pos=.5,yshift=-.1cm]{\ttfamily\string\hss} + node[anchor=mid west] at(2,-2.5){Étape \no3}; + \end{tikzpicture} +\end{centrage} + +Des effets d'empilement peuvent être créés avec les macros \idx\raise et \idx\lower qui déplacent verticalement la prochaine boite horizontale d'une dimension choisie : + +\showcode/\def\clap#1{\hbox to0pt{\hss#1\hss}}¤\idx*\hss§*\clap¤ +a) avant la macro|\clap{SURIMPRESSION}après la macro\medbreak +b) avant la macro|\raise2.5ex\clap{Au-dessus}\lower2.5ex\clap{Au-dessous}% + après la macro\medbreak¤\idx*\raise\idx*\lower§*\clap¤ +c) avant la macro|\raise2.5ex\llap{Au-dessus avant}\lower2.5ex\rlap{Au-dessous après}% + après la macro¤\idx*\rlap\idx*\llap¤/\idx*[|)]{boite!dimension choisie} + +\newpage% TODO : pour ne pas que le titre de la sous section soit orphelin en bas de page +\subsection{Redimensionner une boite}\idx*[|(]{boite!redimensionnement} +\begin{regle} +Une fois qu'une boite est stockée dans un \verb|<registre>|, il est possible de modifier une ou plusieurs de ses dimensions. Il suffit d'écrire + +\begin{centrage} +\small$\left.\vcenter{\ttfamily\hbox{\string\wd}\hbox{\string\ht}\hbox{\string\dp}}\right\}$\verb|<registre>= <dimension>|\idx*\wd\idx*\ht\idx*\dp +\end{centrage} + +\noindent pour que la largeur, hauteur ou profondeur de la boite prenne la dimension indiquée, indépendamment de ce qu'est le contenu de la boite. Ces assignations sont toujours \emph{globales}\idx*{assignation!toujours globale}. En procédant de cette façon, le contenu reste intact, mais \TeX{} est trompé et voit désormais la boite avec les nouvelles dimensions. +\end{regle} + +Un peu à la manière de \idx\rlap, il devient ainsi possible de superposer une boite au texte qui la suit : + +\showcode |\setbox0=\hbox{\tt//////////}¤\idx*\tt\idx*\setbox¤ +\wd0=0pt % fait croire à TeX que la larguer de la boite est nulle¤\idx*\wd¤ +Voici \copy0 du texte partiellement barré...¤\idx*\copy¤| + +Est-il possible de redonner au contenu d'une boite ainsi modifiée une nouvelle virginité quant à ses dimensions, et faire en sorte que ce contenu occupe à nouveau ses dimensions naturelles ? + +\begin{regle} +Si un \verb|<registre>| de boite contient une boite horizontale ou verticale, il est possible d'extraire le contenu de la boite avec \idx\unhbox\verb|<registre>| ou \idx\unvbox\verb|<registre>|. La liste horizontale ou verticale contenue dans la boite est alors ajoutée à la liste courante en cours. Après cette opération, le registre de boite devient vide, c'est à dire positif au sens de \tidx{ifvoid}. + +Pour ne pas vider le registre de boite, il faut employer \idx\unhcopy ou \idx\unvcopy. +\end{regle} + +L'important à retenir de cette règle est que la liste ainsi extraite retrouve ses dimensions naturelles, même si les dimensions de la boite avaient été modifiées après coup. + +\showcode/\def\printdim{largeur=\the\wd0 \qquad hauteur=\the\ht0 \qquad profondeur = \the\dp0 }¤\idx*\the\idx*\wd\idx*\ht¤ +\setbox0=\hbox{Programmer en \TeX{} est facile}¤\idx*\setbox¤ +a) \printdim\par +b) \wd0=0pt \ht0=0pt \dp0=0pt% rend toutes le dimensions nulles¤\idx*\wd\idx*\ht\idx*\dp¤ + \printdim\par +c) \setbox0=\hbox{\unhbox0 }% reprend les dimensions d'origine¤\idx*\hbox\idx*\unhbox\idx*\setbox¤ + \printdim/ + +Le point essentiel est que + +\centrecode-\setbox<registre>=\hbox{\unhbox<registre>}-\idx*\unhbox + +\noindent redonne au \verb|<registre>| de boite les dimensions naturelles de la boite qu'il contient. La méthode donnée pour une boite horizontale est également valable pour une boite verticale en remplaçant \idx\hbox par \idx\vbox ou \idx\vtop et \idx\unhbox par \idx\unvbox.\idx*[|)]{boite!redimensionnement} + +\subsection{Tester si un registre de boite est vide}\label{tester.boite.vide}\idx*[|(]{boite!registre!vide} +Nous avons vu que le test \tidx{ifvoid}\verb|<registre>| est vrai si le registre de boite est vide. Il est tout de même très important d'insister sur la différence entre un registre vide (c'est à dire ne contenant aucune boite) et un registre contenant une boite vide. + +Voici un exemple qui illustre le fait que si un registre contient une boite vide, il n'est pas vide ! + +\showcode/\setbox0=\hbox{}% le registre 0 contient une boite vide¤\idx*\setbox¤ +Le registre \ifvoid0 est vide\else n'est pas vide\fi¤\tidx*{ifvoid}¤/ + +Dès lors se pose la question : « comment tester si un registre de boite est vide ?» La réponse dépend de ce qu'on entend par « vide ». Le parallèle devient évident avec un argument de macro qui n'était pas « vide » s'il contenait une macro vide telle que \idx\empty (voir page~\pageref{argument.vide}). Pour répondre à la question « comment tester si un registre de boite est vide », nous allons construire un test purement développable §\ifzerodimbox de syntaxe + +\centrecode-\ifzerodimbox{<nombre>}{<code vrai>}{<code faux>}- + +qui renvoie \verb|<code vrai>| si le registre est vide ou s'il contient une boite dont toutes les dimensions sont nulles, et faux dans tous les autres cas. + +\showcode/\catcode`\@11 +\def\ifzerodimbox#1{% #1=registre de boite¤§*\ifzerodimbox¤ +% revoie vrai si le registre est vide ou contient une boite de dimensions nulles + \csname% former la macro "\firstoftwo" ou "\secondoftwo" + \ifvoid#1first%% si le registre est vide "first"¤\tidx*{ifvoid}¤ + \else% sinon + \ifdim\wd#1=\z@% si la largeur¤\idx*\wd\idx*\z@¤ + \ifdim\ht#1=\z@% la hauteur¤\idx*\ht¤ + \ifdim\dp#1=\z@ first% et la profondeur=0pt, "first"¤\idx*\dp\idx*\z@¤ + \else second% dans les autres cas "second" + \fi + \else second% + \fi + \else second% + \fi + \fi + oftwo% compléter avec "oftwo" + \endcsname +} +\catcode`\@12 +a) \setbox0=\hbox{}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par¤\idx*\setbox\tidx*{ifvoid}¤ +b) \box0 % affiche la boite vide, le registre est maintenant "void" + \ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +c) \setbox0=\hbox{x}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par¤\idx*\setbox¤ +d) \wd0=0pt \ht0=0pt \dp0=0pt % rend toutes les dimensions nulles + \ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par¤\idx*\setbox¤ +e) \setbox0=\vbox{}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)¤\tidx*{ifvoid}§*\ifzerodimbox¤/ + +Le test est négatif dans le cas c, seul cas où le registre contient une boite de dimensions non nulles. Il est positif ailleurs, que la boite soit vide (cas a et e), que la boite soit non vide mais redimensionnée (cas d) ou que le registre soit vide (cas b). +\grandsaut + +Le moteur \idx\eTeX{}\idx*{moteur!etex} nous donne une autre méthode, plus rapide et plus fiable pour tester si un registre de boite contient une boite \emph{vide}. Nous nous mettrons à l'abri du faux positif de l'exemple précédent (cas d) d'une boite non vide, mais redimensionnée pour que toutes ses dimensions soient nulles. + +La primitive \idx\lastnodetype de \idx\eTeX{} est de type registre d'entier et à tout moment, selon sa valeur qui varie de $-1$ à 15, caractérise la nature du dernier n\oe ud composé. Les différents types sont exposés dans le manuel d'\idx\eTeX. Le type qui nous intéresse ici est celui qui est qualifié par l'entier $-1$ et qui caractérise un n\oe ud vide, c'est-à-dire résultant d'une liste vide. + +L'idée est donc de composer le contenu de la boite, et de tester hors de cette boite si \idx\lastnodetype est strictement négatif, seul cas où la boite sera vide. Nous allons mettre en \oe uvre cette méthode pour construire une macro \emph{non développable} §\ifvoidorempty de syntaxe + +\centrecode|\ifvoidorempty<registre>{<code vrai>}{<code faux>}| + +\noindent qui renverra \verb|<code vrai>| si le \verb|<registre>| est vide ou contient une boite vide : + +\showcode/\def\ifvoidorempty#1{% teste si le registre #1 est vide ou contient une boite vide¤§*\ifvoidorempty¤ + \ifvoid#1\relax¤\tidx*{ifvoid}¤ + \expandafter\firstoftwo + \else + \begingroup% dans un groupe + \setbox0=% affecter à la boite 0¤\idx*\setbox¤ + \ifhbox#1\hbox\bgroup\unhcopy% un boite horizontale¤\idx*\hbox\tidx*{ifhbox}\idx*\unhcopy¤ + \else \vbox\bgroup\unvcopy% ou verticale¤\idx*\vbox\idx*\unvcopy¤ + \fi% dans laquelle on compose + #1\relax% #1 en dimensions naturelles + \expandafter\egroup% sauter la fin de la boite + \expandafter% et le \endgroup + \endgroup + \ifnum\lastnodetype=-1 % et tester si le dernier noeud est vide¤\idx*\lastnodetype¤ + \expandafter\expandafter\expandafter\firstoftwo + \else + \expandafter\expandafter\expandafter\secondoftwo + \fi + \fi +} +a) \setbox0=\hbox{}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par¤\idx*\setbox\tidx*{ifvoid}¤ +b) \box0 % affiche la boite vide, le registre est maintenant "void" + \ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par +c) \setbox0=\hbox{x}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par¤\idx*\setbox¤ +d) \wd0=0pt \ht0=0pt \dp0=0pt % rend toutes les dimensions nulles + \ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par¤\idx*\setbox¤ +e) \setbox0=\vbox{}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)¤\tidx*{ifvoid}\idx*\setbox§*\ifvoidorempty¤/\idx*[|)]{boite!registre!vide} + +\subsection{Mise en application} +Comment pourrions-nous enfermer dans une boite \idx\vtop des éléments mis dans des \idx\hbox qui soient tous centrés horizontalement les uns par rapport aux autres ? Le but est d'écrire + +\centrecode/\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}/ + +\noindent pour obtenir ceci + +\begin{centrage} +\small +texte avant...\vtop{\halign{\hfil#\hfil\cr +Ligne du haut\cr Très grande ligne de milieu\cr Ligne de bas pour finir\crcr}}...texte après +\end{centrage} + +Si nous empilons des \idx\hbox dans une \idx\vtop, nous ne pouvons pas centrer les \idx\hbox, car nous ne pouvons écrire ni \idx\hfill ni \idx\hss dans une \idx\vtop puisque le matériel y est vertical et que ces primitive, exclusivement horizontales, y sont interdites. Agir sur les ressorts de paragraphe \idx\leftskip et \idx\rightskip est sans effet puisque dans cette boite, \TeX{} ne compose pas de paragraphe, il ne fait qu'empiler des \idx\hbox verticalement. + +La solution va consister à trouver la longueur $l$ de la plus longue \idx\hbox, puis composer toutes les boites \idx\hbox avec une longueur imposée $l$ en ayant pris soin de centrer le contenu avec des ressorts \idx\hss placés en début et en fin de boite. La macro \verb|\doforeach| vue précédemment nous sera d'un grand secours. La variable, que nous appelons \verb|\htext|, contiendra tout à tour à tour chaque élément de la liste, mais il faudra procéder en deux passes successives : + +\begin{itemize} + \item la première trouvera, en les examinant tour à tour, la \idx\hbox la plus longue et cette dimension maximale sera stockée dans le registre de dimension \verb|\maxhsize|; + \item lors de la deuxième passe, nous écrirons à chaque itération + + \centrecode-\hbox to\maxhsize{\hss\htext\hss}- + + pour enfermer l'élément courant dans une \idx\hbox de longueur \verb|\maxhsize| et centré dans celle-ci à l'aide des ressorts-primitives \idx\hss. +\end{itemize} + +Pour tester si la longueur de l'élément considéré est plus grande que celle du précédent, il faut d'abord initialiser \verb|\hmaxsize| à la plus petite dimension permise qui est l'opposé de la plus grande dimension \idx\maxdimen, registre de dimension défini dans plain-\TeX{} et qui vaut \verb|16383.99999pt|. Ensuite, si la dimension de l'élément est strictement supérieure à \verb|\hmaxsize|, nous actualiserons \verb|\hmaxsize| à la longueur de l'élément examiné. En fin de boucle, nous serons assurés que \verb|\hmaxsize| est la plus grande longueur.§*\cvtop[|(] + +\showcode|\newdimen\hmaxsize¤\idx*\newdimen¤ +\def\cvtop#1{%¤§*\cvtop¤ + \hmaxsize=-\maxdimen% initialise à la plus petite longueur¤\idx*\maxdimen¤ + \doforeach\htext\in{#1}% pour chaque élément :¤§*\doforeach¤ + {\setbox0=\hbox{\htext}% stocker l'élément "\htext" dans une \hbox¤\idx*\hbox\idx*\setbox¤ + \ifdim\wd0 >\hmaxsize% si sa longueur est supérieure à \hmaxsize¤\tidx*{ifdim}¤ + \hmaxsize=\wd0 % mettre à jour \hmaxsize¤\idx*\wd¤ + \fi + }% + \vtop{% dans une \vtop...¤\idx*\vtop¤ + \doforeach\htext\in{#1}% pour chaque élément :¤§*\doforeach¤ + {\hbox to\hmaxsize{\hss\htext\hss}% le centrer dans une \hbox de longueur \hmaxsize¤\idx*\hss¤ + }% + }% +} + +texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% +...texte après| + +\begin{exercice} +Proposer une autre façon de faire sans enfermer \verb|\htext| dans une \idx\hbox. +\solution +Chaque élément peut être composé comme un paragraphe. Pour cela, la longueur de la \idx\vtop doit être égale à \verb|\hmaxsize|, l'indentation doit être nulle et pour que les paragraphes soient centrés, \idx\leftskip et \idx\rightskip sont rendus égaux à \texttt{0pt plus 1fil}. + +Cela donne : + +\showcode|\newdimen\hmaxsize¤\idx*\newdimen¤ +\def\cvtop#1{%¤§*\cvtop¤ + \hmaxsize=-\maxdimen% initialise à la plus petite longueur¤\idx*\maxdimen¤ + \doforeach\htext\in{#1}% pour chaque élément :¤§*\doforeach¤ + {\setbox0=\hbox{\htext}% stocker l'élément "\htext" dans une \hbox¤\idx*\hbox\idx*\setbox¤ + \ifdim\wd0 >\hmaxsize% si sa longueur est supérieure à \hmaxsize¤\tidx*{ifdim}¤ + \hmaxsize=\wd0 % mettre à jour \hmaxsize¤\idx*\wd¤ + \fi + }% + \vtop{% dans une \vtop...¤\idx*\vtop¤ + \hsize\hmaxsize % longueur de la \vtop = \maxhsize + \parindent=0pt % pas d'indentation¤\idx*\parindent¤ + \leftskip=0pt plus1fil minus0pt \rightskip=\leftskip% ressorts de centrage¤\idx*\leftskip\idx*\rightskip¤ + \doforeach\htext\in{#1}% pour chaque élément :¤§*\doforeach¤ + {\htext\par}% le composer et finir le paragraphe + }% +} + +texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% +...texte après|§*\cvtop[|)] +\end{exercice} + +Ces façons de faire sont un peu tirée par les cheveux. En effet, nous venons de programmer ce que fait (entre autres) la primitive de \TeX{} « \idx\halign\verb|{<code>}| ». Le but ici n'est pas d'en décrire toutes ses fonctionnalités, car elles sont très nombreuses et pour certaines, très techniques\footnote{Pour en savoir plus, il est sage de se tourner vers le \TeX book, chapitre 22.}. + +Pour aller à l'essentiel, cette primitive opère en mode vertical\idx*{mode!vertical} et construit des alignements (autrement dit, des tableaux) constitués de lignes, elles-mêmes partagées en cellules. Ce qui est remarquable, c'est que lorsque \idx\halign lit son argument, elle compose \emph{tous} les contenus des cellules et par conséquent, fixe comme longueur d'une colonne la plus grande des longueurs rencontrées dans cette colonne. + +Du côté de la syntaxe, chaque ligne s'achève par la primitive \idx\cr et il est prudent de placer \idx\crcr en fin de tableau. Au sein d'une ligne, le token «\verb|&|» (de catcode \number\catcode`\&) signifie « passer à la colonne suivante». La syntaxe d'une ligne est donc : + +\centrecode-<cellule 1>&<cellule 2>& ... &<cellule n>\cr- + +Raffinement supplémentaire, il est possible pour toutes les cellules d'une même colonne de définir un code qui sera inséré avant leur contenu et un code qui sera inséré après. Pour cela, la primitive \idx\halign admet un \emph{préambule} qui est placé avant le premier \idx\cr. La syntaxe de ce préambule reprend la syntaxe d'une ligne, mais le \verb|<contenu>| d'une cellule est symbolisé par le token «\cidx\#». Ainsi, pour une colonne (c'est-à-dire compris entre deux «\cidx[ (caractère d'alignement)]\&»), si on écrit dans le préambule + +\centrecode-<code avant>#<code après>- + +\noindent alors, le texte \verb|<code avant>| sera inséré avant le code contenu dans les cellules et \verb|<code après>| sera inséré après. + +Dans l'exemple ci-dessous, la colonne \no1 est définie pour que «\verb|X|» soit affiché avant chaque cellule et «\verb|**\ignorespaces|» après, où \idx\ignorespaces nous assure qu'en dehors du préambule, les espaces insérés avant le premier \verb|&| ne compteront pas. Pour la deuxième colonne, \cidx\# est seul et donc les cellules de cette colonne seront composées sans aucun ajout. Enfin, la colonne \no3 est composée au fer à droite en insérant \idx\hfil à gauche de \cidx\# : + +\showcode/\halign{X#**\ignorespaces&#&\hfil #\cr % préambule¤\idx*\halign\idx*\ignorespaces¤ +foo &foo bar&123 456\cr % première ligne¤\idx*\cr¤ +deuxième ligne &1 &12\cr % deuxième ligne +a &\TeX &1 2 3 4 5 6\cr % troisième ligne +\crcr}¤\idx*\crcr¤/ + +Pour tirer parti de \idx\halign dans le code de §\cvtop[|(], nous allons définir un alignement à une colonne et nous spécifierons «\idx\hfil{}\cidx\#{}\idx\hfil» en préambule afin que les contenus soient centrés. À l'aide de §\substtocs, nous stockerons dans une macro \verb|\temp| l'argument de §\cvtop où les virgules auront été remplacées par \idx\cr. Il suffira ensuite de placer la macro \verb|\temp| juste après le \idx\cr qui marque la fin du préambule pour que \TeX{} la développe (car le début de chaque cellule est développé) pour constituer le corps du tableau : + +\showcode|\def\cvtop#1{%¤§*\cvtop¤ + \vtop{% \vtop assure que l'on est en mode vertical¤\idx*\vtop\idx*{mode!vertical}¤ + \substtocs\temp{#1}{,}{\cr}% dans \temp, remplacer les "," par "\cr"¤§\substtocs\idx*\cr¤ + \halign{%¤\idx*\halign¤ + \hfil##\hfil\cr% préambule : centrer le contenu¤\idx*\hfil¤ + \temp\crcr}% mettre \temp dans l'alignement¤\idx*\crcr¤ + }% \temp est détruite en sortant de la boite +} + +texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% +...texte après| + +Ce code en suggère un autre, capable de calculer à l'aide de \idx\halign, la plus grande longueur de textes. Pour cela, la macro §\calcmaxdim, de syntaxe + +\centrecode-\calcmaxdim\<macro>{<texte 1>,texte 2>,...,<texte n>}- + +\noindent stockera dans la \verb|\<macro>| la plus grande longueur des \verb|<texte>|. + +\showcode/\def\calcmaxdim#1#2{%¤§*\calcmaxdim¤ + \setbox0=\vtop{% \vtop assure que l'on est en mode vertical (interne)¤\idx*\vtop\idx*\setbox¤ + \substtocs\temp{#2}{,}{\cr}% dans \temp, remplacer les "," par "\cr"¤§*\substtocs\idx*\cr¤ + \halign{##\hfil\cr% préambule : composer au fer à gauche¤\idx*\halign\idx*\hfil¤ + \temp\crcr}% mettre \temp dans l'alignement¤\idx*\crcr¤ + }% \temp est détruite en sortant de la boite + \edef#1{\the\wd0 }%¤\idx*\the\idx*\wd¤ +} +a) La plus grande longueur vaut + \calcmaxdim\foo{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}\foo + +b) Vérification : \setbox0=\hbox{Très grande ligne du milieu}\the\wd0 ¤\idx*\setbox\idx*\the\idx*\wd¤/§*\cvtop[|)]\idx*[|)]{boite} + +\section{Les réglures}\idx*[|(]{réglure} +\subsection{Tracer des lignes} +Nous allons maintenant nous intéresser à des boites un peu particulières, les boites de réglure ou «réglures», c'est-à-dire des boites entièrement remplies de noir. + +\begin{regle} +Les primitives \idx\hrule et \idx\vrule, qui opèrent respectivement en \idx*{mode!vertical}mode \emph{vertical} et \emph{horizontal}\idx*{mode!horizontal}, tracent des réglures. Par défaut, les dimensions de ces réglures sont : + +\begin{centrage} + \small + \begin{tabular}{rcc}\hline + &\idx\hrule&\idx\vrule\\\hline + largeur (width) & \verb|*| & 0.4pt\\ + hauteur (height)& 0.4pt&\verb|*|\\ + profondeur (depth)&0pt&\verb|*|\\\hline + \end{tabular} +\end{centrage} + +L'étoile signifie que la dimension est celle de la boite qui contient la réglure. + +Si l'on souhaite passer outre les valeurs par défaut et imposer une ou plusieurs dimensions, on doit faire suivre ces deux primitives d'un (ou plusieurs) mots-clés parmi «\verb|width|», «\verb|height|» ou «\verb|depth|» et en respectant cet ordre. S'ils sont présents, ces mots-clés doivent être suivis de la \verb|<dimension>| que l'on souhaite imposer. + +Si plusieurs spécifications contradictoires sur une dimension sont spécifiées, la dernière est prise en compte. +\end{regle} + +Voici par exemple ce que l'on obtient si on écrit «\idx\vrule» dans le code source immédiatement après ces deux points :\vrule. On constate que la réglure, qui opère en mode horizontal, a la largeur par défaut de \verb|0.4pt| et a pour hauteur et profondeur celles de la boite dans laquelle elle est enfermée. Cette boite est la ligne en cours de composition. + +Passons maintenant à \idx\hrule et voici l'effet de «\idx\hrule» juste après ces deux points :\hrule +La réglure prend la totalité de la largeur de la boite qui la contient. La boite dont il est question dans ce cas est la page elle-même, de largeur \idx\hsize, dans laquelle les éléments sont empilés verticalement. On remarque que pour satisfaire son mode de fonctionnement, \idx\hrule passe en mode vertical\idx*{mode!vertical}. Puisque nous étions à l'intérieur d'un paragraphe, celui-ci a été composé avant le passage en mode vertical. Par ailleurs, le \idx{ressort d'interligne} n'est pas inséré, ni avant la réglure ni après. Une réglure \idx\hrule est donc dessinée \emph{au plus près} verticalement de ce qui la suit et de ce qui la précède. + +L'exemple suivant montre que les primitives de réglures utilisées sans mot-clé adaptent leurs dimensions à celles de la boite qui les contient. Ici, on dessine des \idx\hrule au sommet et au bas d'une \idx\vbox dans la première ligne alors que l'on trace des \idx\vrule avant et après une \idx\vtop à la deuxième ligne : + +\showcode/a) .......\vbox{\hrule\hbox{foo}\hbox{ligne du bas}\hrule}.......\medbreak¤\idx*\vbox\idx*\hrule\idx*\hbox¤ +b) .......\vrule\vtop{\hbox{foo}\hbox{ligne du bas}}\vrule.......¤\idx*\vtop\idx*\vrule¤/ + +On voit clairement au cas a que les \idx\hrule à l'intérieur de \idx\vbox prennent chacune la largeur totale de la boite. En ce qui concerne les \idx\vrule qui se trouvent avant et après la \idx\vtop au cas b, elles ont adapté leur profondeur et hauteur pour s'étendre sur la hauteur totale de la boite qui ici est la ligne en cours. + +Voici maintenant des réglures dont les dimensions sont imposées par mots-clés : + +\showcode/Une réglure de 1.5cm :\hrule width1.5cm +foo\vrule width 2pt height .5cm depth .2cm bar/ + +Ici encore, la \idx\vrule est en contact avec la \idx\hrule puisque le \idx{ressort d'interligne} n'est pas inséré. + +\begin{exercice} +Écrire une macro §\showdim[|(], opérant en mode horizontal\idx*{mode!horizontal}, dont l'argument est une dimension et qui représente sous forme d'une réglure horizontale la dimension donnée. + +Par exemple, «§\showdim\verb-{1.5cm}-» produit «\showdim{1.5cm}» où les traits verticaux de début et de fin ont une hauteur de \verb|1ex|. Toutes les réglures ont une épaisseur de \verb|0.4pt|. + +\solution +Voici le code de cette macro : + +\showcode/\def\showdim#1{%¤§*\showdim¤ + \vrule width 0.4pt height 1ex depth 0pt % trait vertical gauche¤\idx*\vrule\defline\aaa¤ + \vrule width #1 height0.4pt depth 0pt % réglure horizontale de longueur #1¤\defline\bbb¤ + \vrule width 0.4pt height 1ex depth 0pt % trait vertical droit¤\idx*\vrule\defline\ccc¤ + \relax% stoppe la lecture de la précédente dimension +} + +a) une longueur de 1 cm : \showdim{1cm}\par +b) une longueur de 137,4 pt : \showdim{137,4pt}\par +c) une longueur de 2 mm : \showdim{2mm}/ + +Le code est très simple. En premier lieu (ligne \no\aaa), la primitive \idx\vrule qui opère en mode horizontal\idx*{mode!horizontal} trace une barre verticale d'\verb|1ex| de hauteur et de \verb|0.4pt| d'épaisseur. Ensuite, à la ligne \no\bbb, nous la faisons suivre d'une ligne horizontale dont la longueur a été forcée à la dimension contenue dans l'argument et dont la hauteur est \verb|0.4pt|. Nous finissons à la ligne \no\ccc{} avec la même barre verticale qu'au début.§*\showdim[|)] +\end{exercice} + +\subsection{Encadrer}\idx*[|(]{encadrer} +On commence à deviner qu'il serait assez facile de programmer une macro §\FRbox qui encadre tout ce qui est dans son argument, argument devant être composé en mode horizontal\idx*{mode!horizontal} : + +\centrecode|\FRbox{<contenu>}|§*\FRbox + +Les deux paramètres importants pour l'aspect visuel sont l'épaisseur des réglures et l'espacement entre elles et le contenu. Chacun de ces paramètres sera un registre de dimension; §\frboxrule pour l'épaisseur et §\frboxsep pour l'espacement\footnote{Ne pas confondre les noms de ces registres avec \texttt{\string\fboxrule} et \texttt{\string\fboxsep} qui sont les registres définis par \LaTeX{} pour les boites encadrées avec la macro \texttt{\string\fbox}.}. + +En ce qui concerne la méthode pour encadrer un contenu arbitraire, il y a deux façons différentes de tracer les réglures : + +\begin{centrage} + \leavevmode + \frboxrule=0.2pt \frboxsep=1pt + \small + \vbox{% + \offinterlineskip + \hbox{\FRbox{\vrule height 1mm width\dimexpr2.2cm+2\frboxsep+2\frboxrule}}% + \hbox{% + \FRbox{\vrule width1mm height\dimexpr1cm-2\frboxsep-2\frboxrule }% + \vbox to1cm{\vss\hbox to2cm{\hss\verb|<contenu>|\hss}\vss}% + \FRbox{\vrule width1mm height\dimexpr1cm-2\frboxsep-2\frboxrule }% + }% + \hbox{\FRbox{\vrule height 1mm width\dimexpr2.2cm+2\frboxsep+2\frboxrule}}% + }\hskip0pt plus1fil + \hbox{% + \FRbox{\vrule width1mm height\dimexpr1.2cm+2\frboxsep+2\frboxrule }% + \vbox to\dimexpr1.2cm+4\frboxsep+4\frboxrule{% + \hbox{\FRbox{\vrule height1mm width\dimexpr2cm-2\frboxsep-2\frboxrule }}% + \vskip 0pt plus 1fil + \hbox to2cm{\hss \verb|<contenu>|\hss}% + \vskip 0pt plus 1fil + \hbox{\FRbox{\vrule height1mm width\dimexpr2cm-2\frboxsep-2\frboxrule }}% + }% + \FRbox{\vrule width1mm height\dimexpr1.2cm+2\frboxsep+2\frboxrule }% + }% +\end{centrage} + +\noindent Décidons-nous par exemple pour la structure de droite. Pour arriver à nos fins, nous allons construire une \idx\vbox dans laquelle nous allons insérer, de haut en bas : + +\begin{itemize} + \item une réglure horizontale ; + \item une espace verticale égale à §\frboxsep ; + \item le contenu (précédé et suivi d'espaces égales à §\frboxsep); + \item une espace verticale égale à §\frboxsep ; + \item une réglure horizontale. +\end{itemize} + +\noindent Nous obtiendrons donc : + +\begin{centrage} + \leavevmode + \small + \frboxrule=0.2pt \frboxsep=1pt + \hbox{% + \vbox to\dimexpr1.2cm+4\frboxsep+4\frboxrule{% + \hbox{\FRbox{\vrule height1mm width\dimexpr2cm-2\frboxsep-2\frboxrule }}% + \vskip 0pt plus 1fil + \hbox to2cm{\hss\verb|<contenu>|\hss}% + \vskip 0pt plus 1fil + \hbox{\FRbox{\vrule height1mm width\dimexpr2cm-2\frboxsep-2\frboxrule }}% + }% + }% +\end{centrage} + +\Qu 'ils soient horizontaux ou verticaux, tous les espaces seront insérés avec la primitive \idx\kern qui agit selon le mode en cours. Voici la construction obtenue pour un contenu égal au mot « Programmation » : + +\showcode/¤\verb-\newdimen\frboxrule \newdimen\frboxsep-\idx*\newdimen§*\frboxrule§*\frboxsep¤ +\frboxsep=5pt \frboxrule=1pt ¤§*\frboxsep §*\frboxrule¤ +\leavevmode¤\idx*\leavevmode¤ +...% +\vbox{%¤\idx*\vbox¤ + \hrule height\frboxrule% réglure supérieure¤\idx*\hrule¤ + \kern\frboxsep% espace verticale haute¤\idx*\kern¤ + \hbox{\kern\frboxsep Programmation\kern\frboxsep}% contenu + espaces horizontales¤\idx*\hbox¤ + \kern\frboxsep% espace verticale basse¤§*\frboxsep¤ + \hrule height\frboxrule% réglure inférieure¤§*\frboxrule¤ + }% +.../ + +\noindent Il ne reste plus qu'à envelopper le tout dans une boite horizontale \idx\hbox, après avoir fait précéder la \idx\vbox de \idx\vrule qui seront les réglures verticales à droite et à gauche de l'encadrement§*\FRbox : + +\showcode|¤\verb-\newdimen\frboxrule \newdimen\frboxsep-\idx*\newdimen§*\frboxrule§*\frboxsep¤ +\frboxsep=5pt \frboxrule=0.6pt +\def\FRbox#1{% /!\ ne change pas le mode H ou V en cours¤§*\FRbox¤ + \hbox{% mettre à la suite horizontalement les 3 choses suivantes :¤\idx*\hbox¤ + \vrule width\frboxrule% 1) réglure gauche¤\idx*\vrule¤ + \vbox{% 2) un empilement vertical comprenant¤\idx*\vbox¤ + \hrule height\frboxrule% a) réglure supérieure¤\idx*\hrule ¤ + \kern\frboxsep% b) espace verticale haute¤\idx*\kern ¤ + \hbox{% c) contenu + espaces en mode H + \kern\frboxsep#1\kern\frboxsep¤\defline\ccc¤ + }% + \kern\frboxsep% d) espace verticale basse¤\defline\aaa§*\frboxsep¤ + \hrule height\frboxrule% e)réglure inférieure¤\defline\bbb¤ + }% + \vrule width\frboxrule% 3) réglure droite¤\idx*\vrule§*\frboxrule¤ + }% +} +Ligne de base : ...\FRbox{Programmation}...% +\frboxrule=2pt \FRbox{Programmation}...% +\frboxsep=0pt \FRbox{Programmation}...% +\frboxrule0.4pt \FRbox{Programmation}...¤§*\FRbox¤| + +On constate que la \idx{ligne de base} du contenu des boites n'est pas alignée avec la \idx{ligne de base} de la ligne en cours. En fait, comme la \idx\vbox empile les éléments au-dessus de la \idx{ligne de base} du dernier élément, le \idx{point de référence} de la \idx\vbox se trouve sur la \idx{ligne de base} de la réglure inférieure tracée à la ligne \no\bbb, c'est-à-dire au niveau du bord \emph{inférieur} de cette réglure\footnote{Si nous avions écrit \texttt{depth} au lieu de \texttt{height} à la ligne \no\bbb, le point de référence aurait été au niveau du bord \emph{supérieur} de la réglure du bas.}. Le problème qui se pose est le suivant : comment construire une boite verticale où l'on place des listes de matériels verticaux + +\centrecode-<liste a> +<liste b>- + +\noindent et faire en sorte que le \idx{point de référence} de la boite ainsi construite soit sur la \idx{ligne de base} du dernier élément vertical de \verb|<liste a>| ? Faire appel à \idx\vbox ou \idx\vtop seules ne peut convenir. Il fait \emph{combiner} ces deux boites. L'idée est d'enfermer \verb|<liste a>| dans une \idx\vbox et mettre cette \idx\vbox comme premier élément vertical d'une \idx\vtop qui englobe le tout. La boite ainsi construite a la structure suivante\label{frbox.ligne.de.base} : + +\centrecode-\vtop{%¤\idx*\vtop¤ + \vbox{<liste a>} + <liste b> +}- + +De par les définitions de \idx\vbox et \idx\vtop, le \idx{point de référence} de la boite externe \idx\vtop se trouvera sur la \idx{ligne de base} du dernier élément vertical de \verb|<liste a>|. Voici un exemple qui illustre cette manipulation : + +\showcode/Persuadez-vous que : +\vtop{¤\idx*\vtop¤ + \vbox{¤\idx*\vbox¤ + \hbox{Programmer}¤\idx*\hbox¤ + \hbox{en} + \hbox{\TeX} + \hbox{est}% <- ligne de base de la \vtop + } + \hbox{\it tout sauf}¤\idx*\it¤ + \hbox{facile.} +}/ + +Nous allons appliquer cette structure pour créer une nouvelle macro §\frbox[|(] afin que, contrairement à §\FRbox, le point de référence du cadre soit au niveau de la \idx{ligne de base} du contenu. Il faut donc rejeter hors de la \idx\vbox les matériels verticaux des lignes \nos\aaa{} et \bbb{} : + +\showcode/%\newdimen\frboxrule \newdimen\frboxsep +\frboxrule=0.4pt \frboxsep=2pt +\def\frbox#1{% ne pas changer le mode H ou V en cours¤§*\frbox¤ + \hbox{% enferme dans une \hbox¤\idx*\hbox¤ + \vrule width\frboxrule% réglure gauche¤\idx*\vrule§*\frboxrule¤ + \vtop{%¤\idx*\vtop¤ + \vbox{% 1er élément de la \vtop¤\idx*\vbox¤ + \hrule height\frboxrule% réglure supérieure¤\idx*\hrule¤ + \kern\frboxsep% espace haut¤\idx*\kern¤ + \hbox{%¤\idx*\hbox¤ + \kern\frboxsep% espace gauche + #1% contenu + \kern\frboxsep% espace droite¤ §*\frboxsep¤ + }% + }% puis autres éléments de la \vtop, sous la ligne de base + \kern\frboxsep% espace bas + \hrule height\frboxrule% réglure inférieure + }% + \vrule width\frboxrule% réglure droite¤\idx*\vrule §*\frboxrule¤ + }% +} +Ligne de base : ......\frbox{Programmation}......% +\frboxrule=2pt \frbox{Programmation}......% +\frboxsep=0pt \frbox{Programmation}......% +\frboxrule0.4pt \frbox{Programmation}......¤§*\frbox¤/ + +\begin{exercice} +Créer une macro §\centretitre\verb|{<texte>}| dont l'argument doit être composé dans un cadre (via §\frbox) qui prend toute la largeur de composition, c'est-à-dire \idx\hsize. L'argument doit être centré dans ce cadre. +\solution +Tout d'abord, nous allons placer en début de macro un \verb|\medbreak| ce qui aura pour effet de composer le paragraphe en cours si la macro est appelée en mode horizontal\idx*{mode!horizontal} et sautera une espace verticale. Ensuite, la primitive \idx\noindent interdira le placement de la boite d'indentation en début de paragraphe, et provoquera le passage en mode horizontal\idx*{mode!horizontal}. + +Nous allons composer le \verb|<texte>| dans une boite verticale \idx\vbox. Sa dimension horizontale, spécifiée par \idx\hsize, doit tenir compte des réglures droite et gauche (chacune de dimension §\frboxrule) ainsi que les espaces à droite et à gauche entre le \verb|<texte>| et les réglures (chacun de dimension §\frboxsep). La dimension horizontale de la boite sera donc + +\begin{centrage} + \small$\hbox{\verb|\hsize|}-2\hbox{§\frboxrule}-2\hbox{§\frboxrule}$\idx*\hsize +\end{centrage} + +Pour que le centrage soit effectif, nous jouerons sur les ressorts \idx\leftskip et \idx\rightskip sans oublier d'annuler le ressort de fin de paragraphe\idx*{paragraphe!centrage} \idx\parfillskip. + +\showcode/\def\centretitre#1{%¤§*\centretitre¤ + \medbreak% passe en mode v puis saute une espace verticale¤\idx*\medbreak¤ + \noindent% pas d'indentation et passe en mode horizontal¤\idx*\noindent\idx*{mode!horizontal}¤ + \frbox{% encadre¤§*\frbox¤ + \vbox{% une boite verticale¤\idx*\vbox¤ + \hsize=\dimexpr\hsize-2\frboxrule-2\frboxsep\relax¤\idx*\hsize\idx*\dimexpr §*\frboxsep §*\frboxrule¤ + \parindent=0pt % pas d'indentation¤\idx*\parindent¤ + \leftskip=0pt plus1fil \rightskip=\leftskip% ressorts de centrage¤\idx*\leftskip\idx*\rightskip¤ + \parfillskip=0pt % annule le ressort de fin de paragraphe¤\idx*\parfillskip¤ + #1% insère le titre + \endgraf% et le compose¤\idx*\endgraf¤ + }% + }% + \medbreak% passe en mode v puis saute une espace verticale¤\idx*\medbreak¤ + \ignorespaces% mange les espaces situés après la macro \centretitre¤\idx*\ignorespaces¤ +} +\frboxrule=0.8pt \frboxsep=5pt +Voici un +\centretitre{Titre} +puis un +\centretitre{Texte très long pour composer un titre qui va prendre plusieurs +lignes et pour s'assurer que la composition s'effectue correctement}¤§*\centretitre¤/ +\end{exercice}§*\frbox[|)]\idx*[|)]{encadrer} + +\idx*[|(]{souligner}\begin{exercice} +Programmer une macro §\souligne\verb|{<texte>}| qui souligne d'un trait de 0.4pt d'épaisseur le \verb|<texte>|. Le trait de soulignement devra se situer à \verb|1pt| au-dessous du \verb|<texte>| : + +\begin{centrage} +\small +\begin{tabular}{>{\ttfamily\hfill}r<{\kern1.5em \normalfont affiche\kern1.5em}@{}l} +\string\souligne\{Du texte\}&\souligne{Du texte}\\ +\string\souligne\{Du texte profond\}&\souligne{Du texte profond} +\end{tabular} +\end{centrage} + +Proposer une variante §\Souligne où le trait devra se situer à 1pt au-dessous de la \idx{ligne de base}, quelle que soit la profondeur du texte à souligner : + +\begin{centrage} +\small +\begin{tabular}{>{\ttfamily\hfill}r<{\kern1.5em \normalfont affiche\kern1.5em}@{}l} +\string\Souligne\{Du texte\}&\Souligne{Du texte}\\ +\string\Souligne\{Du texte profond\}&\Souligne{Du texte profond} +\end{tabular} +\end{centrage} + +\solution +Nous devons tracer une réglure ayant comme dimension horizontale celle de la \idx\hbox dans laquelle est placé l'argument. Afin de pouvoir mesurer cette boite, nous allons stocker \verb|\hbox{#1}| dans le registre de boite \no0\idx*{boite!\no0}. + +Mais avant de poursuivre, regardons d'un peu plus près les dimensions qui suivent les mots-clés \verb|height|, \verb|width| et \verb|depth|, et considérons-les comme des dimensions \emph{signées}. Car c'est ainsi que \TeX{} travaille; rien ne dit qu'une dimension, fût-elle de réglure, doit être positive ! Par exemple, pour tracer une réglure de \verb|0.4pt| d'épaisseur à \verb|10pt| de hauteur, nous pouvons fixer sa hauteur à \verb|10.4pt| et sa profondeur à \verb|-10pt| : + +\showcode/Une réglure en hauteur : \vrule width 1cm height 10.4pt depth -10pt ¤\idx*\vrule¤/ + +De même, pour tracer une réglure d'épaisseur \verb|0.4pt| à \verb|2pt| sous la \idx{ligne de base}, il suffit de fixer sa profondeur à \verb|2.4pt| et sa hauteur à \verb|-2pt| : + +\showcode/Une réglure en dessous: \vrule width 1cm depth 2.4pt height -2pt ¤\idx*\vrule¤/ + +Revenons à notre problème\ldots{} Nous stockerons la \idx\hbox dans le registre de boite \no0\idx*{boite!\no0}. Puis, nous allons appliquer l'astuce exposée ci-dessus pour tracer une réglure de \verb|0.4pt| d'épaisseur à une distance de \verb|\dp0+1pt| sous la \idx{ligne de base}. Pour cela, nous allons stocker dans ce même registre \no0\idx*{boite!\no0} une \idx\vrule dont la longueur est celle du texte à souligner, soit \verb|\wd0|, de profondeur \verb|\dp0+1.4pt| et de hauteur négative égale à \verb|-\dp0-1pt|. Nous allons ensuite rendre nulles toutes les dimensions de la boite afin que son encombrement soit nul dans toutes les directions. Enfin, après être éventuellement sorti du mode vertical\idx*{mode!vertical}, nous affichons cette boite \no0\idx*{boite!\no0} avant le texte : + +\showcode/\def\souligne#1{%¤§*\souligne¤ + \setbox0=\hbox{#1}% stocke le contenu dans le registre no 0¤\idx*\setbox¤ + \setbox0=\hbox{% puis, dans une \hbox, construit une réglure¤\idx*\setbox¤ + \vrule width\wd0 % de la longueur du contenu¤\idx*\vrule\idx*\wd¤ + depth\dimexpr\dp0 + 1.4pt\relax % dp = profondeur texte + 1.4pt¤\idx*\dimexpr¤ + height\dimexpr-\dp0 - 1pt\relax % ht = -profondeux texte - 1pt + }% + \wd0=0pt \dp0=0pt \ht0=0pt % annule toutes les dimensions¤\idx*\wd\idx*\ht\idx*\dp¤ + \leavevmode \box0 % affiche la réglure¤\idx*\leavevmode\idx*\box¤ + #1% puis le contenu +} +Voici \souligne{du texte normal}.\par +Voici \souligne{du texte profond}.¤§*\souligne¤/ + +Compte tenu des dimensions utilisées, le bord supérieur de la réglure est très exactement à \verb|1pt| sous la plus basse partie du texte à souligner. + +La variante §\Souligne est plus simple : il ne faut pas tenir compte de la profondeur du registre \no0\idx*{boite!\no0}. + +\showcode/\def\Souligne#1{%¤§*\Souligne¤ + \setbox0=\hbox{#1}%¤\idx*\setbox¤ + \setbox0=\hbox{\vrule width\wd0 depth1.4pt height-1pt }%¤\idx*\vrule\idx*\wd¤ + \wd0=0pt \dp0=0pt \ht0=0pt ¤\idx*\wd\idx*\ht\idx*\dp¤ + \leavevmode \box0 #1%¤\idx*\leavevmode\idx*\box¤ +} +Voici \Souligne{du texte normal}.\par +Voici \Souligne{du texte profond}.¤§*\Souligne¤/ + +Les macros présentées ici ne permettent pas de souligner un texte trop long ou trop proche d'une \idx{fin de ligne}, car une réglure, \emph{a fortiori} enfermée dans une \idx\hbox, ne peut franchir une coupure de ligne. + +Une autre méthode, impliquant \idx\lower, aurait également été envisageable : + +\showcode/\def\Souligne#1{% + \setbox0=\hbox{#1}%¤\idx*\setbox¤ + \lower 1pt % abaisser à 1pt sous la ligne de base¤\idx*\lower¤ + \rlap{% une \hbox en surimpression vers la droite¤\idx*\rlap¤ + \vrule width\wd0 height0pt depth0.4pt % contenant le soulignement + }% + #1% puis afficher le <texte> +} +Voici \Souligne{du texte normal}.\par +Voici \Souligne{du texte profond}.¤§*\Souligne¤/ + +La méthode n'est pas équivalente car dans ce dernier cas, la réglure qui tient lieu de soulignement est prise en compte dans la boite englobante de §\Souligne\verb|<texte>|, ce qui n'était pas le cas dans le premier code. +\end{exercice}\idx*[|)]{souligner} + +\subsection{Empiler des boites}\idx*[|(]{boite!empilement} +Essayons maintenant de jouer avec les boites et pour cela, créons une macro §\stackbox capable d'empiler des boites encadrées, toutes identiques, aussi bien horizontalement que verticalement. Le fait qu'elles soient identiques implique évidemment que les dimensions de leurs contenus ne sont pas trop différentes. La syntaxe de la macro §\stackbox pourrait être : + +\centrecode-\stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}- + +\noindent Et le résultat serait : + +\begin{centrage} +\small\vbox{\frboxsep=3pt \stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}} +\end{centrage} + +En ce qui concerne les dimensions des boites, on pourrait parcourir lors d'une première passe tous les arguments afin de trouver la plus grande des hauteurs, largueurs et profondeurs et ensuite prendre ces maximums comme dimension de chaque boite, qui seraient alors composées lors d'une seconde passe. Afin de ne pas inutilement compliquer le code\footnote{La vraie et inavouable raison est que renoncer à faire deux passes va nous donner l'occasion de découvrir les « \idx{strut}s » !}, nous allons plutôt décider que l'utilisateur va lui-même fixer la largueur \emph{interne} des cadres à l'aide d'un registre de dimension \verb|\stackwd| créé pour l'occasion : ceci suppose évidemment qu'aucune boite ne doit avoir un contenu dont la largeur excède \verb|\stackwd|. + +Parcourir l'ensemble des contenus va être facilitée avec la macro §\doforeach à qui nous donnerons la liste d'éléments d'une même ligne. Tout cela se fait aisément avec une macro \verb|\stackbox@i| à arguments délimités par «\verb|\\|» dont le but est d'isoler et lire la ligne en cours. Son texte de paramètre sera : + +\centrecode-\def\stackbox@i#1\\{...}- + +En premier lieu, la macro chapeau se contentera d'appeler la macro récursive \verb|\stackbox@i| de cette façon : + +\centrecode-\stackbox@i#1\\\quark\\-§*\quark + +\noindent Ceci a deux avantages : c'est l'assurance que \verb|\\| est présent dans l'argument, car après tout, on pourrait ne pas l'écrire dans l'argument de §\stackbox si on ne voulait qu'une seule ligne. L'autre avantage est que la dernière ligne à lire est le \idx{quark} «§\quark». Si la \tidx[|etc]{ifx}-égalité de l'argument \verb|#1| avec §\quark est vraie, nous serons alors surs que la ligne précédente était la dernière et donc, finir le processus. Nous aurons donc lu la totalité des lignes une première fois dans la macro chapeau puis chaque ligne une seule fois dans la macro récursive. Chaque ligne a été donc lue deux fois exactement. C'est la méthode préconisée par la règle de la page~\pageref{lire.arguments} pour lire une série d'arguments. + +La macro \idx\nobreak, insérée avant chaque ligne restante, interdit toute coupure de page entre deux lignes de boites consécutives. Cette macro est définie dans plain-\TeX{} et son texte de remplacement est « \idx\penalty\verb|10000| ». Sachant que toute pénalité supérieure à \numprint{10000} est comprise comme \numprint{10000}, il s'agit là de la plus forte \idx{pénalité} possible ce qui explique qu'une coupure de page ne se fera jamais à cet endroit. + +\showcode/\newdimen\stackwd \stackwd=3em % dimension horizontale interne des cadres¤\idx*\newdimen¤ +\catcode`@11 +\def\stackbox#1{%¤§*\stackbox¤ + \par% termine le paragraphe en cours + \noindent¤\idx\noindent¤ + \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i¤§*\quark¤ + \par +} + +\def\stackbox@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin¤\tidx*{unless}§*\quark¤ + \hfill % ressort infini de centrage (et fait passer en mode horizontal)¤\idx*\hfill\idx*{mode!horizontal}¤ + \noindent¤\idx*\noindent¤ + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante...¤§*\doforeach¤ + {\frbox{% ...encadrer¤§*\frbox¤ + \hbox to\stackwd{% une \hbox de largeur \stackwd¤\idx*\hbox¤ + \hss% ressort de centrage¤\idx*\hss¤ + \current@item% l'élément courant + \hss% ressort de centrage + } + }% fin de la \frbox + }% fin \doforeach + \hfill% ressort infini de centrage¤\idx*\hfill¤ + \null% assure que le dernier ressort est pris en compte¤\idx*\null¤ + \par% finir le paragraphe + \nobreak% interdire une coupure de page¤\idx*\nobreak¤ + \nointerlineskip% ne pas insérer le ressort d'interligne¤\idx*\nointerlineskip¤ + \expandafter\stackbox@i% et recommencer + \fi +} +\catcode`@12 + +\frboxrule=0.5pt \frboxsep=3pt ¤§*\frboxsep §*\frboxrule¤ +\stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}¤§*\stackbox¤/ + +Le résultat est loin d'être celui escompté ! \Qu oique pas si loin que ça, après tout : les positions horizontales sont bonnes. Le bug vient du fait que nous n'avions pas prévu que les dimensions verticales (hauteur et profondeur) des éléments influaient naturellement sur la boite encadrée construite. On peut y remédier en insérant un « \idx{strut}\footnote{On dit « poutre » en français mais ce mot est assez peu usité.} » : c'est une réglure invisible, mais qui s'étend sur une dimension horizontale ou verticale. La question que l'on se pose immédiatement est « comment rendre une réglure invisible ?» Tout simplement en mettant sa largeur à \verb|0pt|, auquel cas elle aura une dimension verticale ou bien en mettant sa hauteur et profondeur à \verb|0pt| et dans ce cas, elle aura alors une dimension horizontale. Comme il est assez difficile de montrer des choses invisibles, voici une réglure de type \idx\vrule rendue visible (son épaisseur est \verb|0.2pt|) après la lettre « a » : + +\showcode/a\vrule width0.2pt height15pt depth0pt \quad¤\idx*\vrule¤ +a\vrule width0.2pt height0pt depth5pt \quad +a\vrule width0.2pt height10pt depth10pt \quad +a\vrule width1cm height0.2pt depth0pt¤\idx*\vrule¤/ + +Il suffit d'imposer \verb|0pt| comme dimension d'épaisseur pour rendre cette réglure invisible, mais bien réelle en ce qui concerne son encombrement. Voici comment la mettre en évidence en encadrant au plus proche l'ensemble formé par la lettre «a» et le \idx{strut} : + +\showcode/\frboxsep0pt %encadrement au plus proche¤§*\frboxsep¤ +\leavevmode¤\idx*\leavevmode¤ +\frbox{a\vrule width0pt height15pt depth0pt }\quad¤§*\frbox\idx*\vrule¤ +\frbox{a\vrule width0pt height0pt depth5pt }\quad +\frbox{a\vrule width0pt height10pt depth10pt }\quad +\frbox{a\vrule width1cm height0pt depth0pt }¤\idx*\vrule§*\frbox¤/ + +\begin{exercice} +Construire une macro §\rectangle\verb|{<x>}{<y>}| où \verb|<x>| et \verb|<y>| sont des dimensions et qui construit un rectangle dont les dimensions internes (c'est-à-dire de bord interne à bord interne) sont \verb|<x>| et \verb|<y>|. +\solution +Il faut construire une §\frbox dans laquelle nous allons mettre \emph{deux} \idx{strut}s, l'un pour la dimension verticale et l'autre pour l'horizontale. + +\showcode/\def\rectangle#1#2{%¤§*\rectangle¤ + \begingroup% dans un groupe + \frboxsep = 0pt % encadrer au plus proche¤§*\frboxsep¤ + \frboxrule= 0.4pt % en traits assez fins + \frbox{%¤§*\frbox¤ + \vrule width#1 height0pt depth0pt %strut horizontal¤\idx*\vrule¤ + \vrule width0pt height#2 depth0pt %strut vertical¤\idx*\vrule¤ + }% + \endgroup% fermer le groupe +} +Carré de 0.5 cm : \rectangle{0.5cm}{0.5cm}\smallskip¤\idx*\smallskip¤ + +Rectangle de 2.5cm par 3pt : \rectangle{2.5cm}{3pt}¤§*\rectangle¤/ +\end{exercice} + +Pour nos boites créées avec §\stackbox, nous allons mettre dans chacune d'elles un \idx{strut} dont les dimensions verticales sont les plus grandes que l'on peut rencontrer dans un texte « normal ». Nous prendrons les dimensions verticales d'une \idx\hbox contenant «Àgjp». Bien évidemment, si un élément contient un texte dont les dimensions verticales excèdent celles du \idx{strut}, le problème réapparaitra. + +Il y a un autre défaut, plus difficile à détecter dans notre macro §\stackbox : les réglures des boites adjacentes ne se superposent pas, aussi bien horizontalement que verticalement, ce qui fait que leurs épaisseurs s'additionnent. Lorsque §\frboxrule est faible (\verb|0.5pt| ici), cela n'a pas d'influence visuelle notable, mais ce défaut serait beaucoup plus visible si §\frboxrule valait 2pt par exemple. Nous allons donc insérer une espace négative avec \verb|\kern-\frboxrule| entre chaque boite horizontalement et entre chaque ligne verticalement. Le dernier \idx\kern horizontal, qui viendra après la dernière boite dans la boucle, sera annulé par la primitive \idx\unkern que nous mettons après la boucle : + +\showcode|\newdimen\stackwd \stackwd=3em ¤\idx*\newdimen¤ +\catcode`\@11 +\def\stackbox#1{%¤§*\stackbox¤ + \par% termine le paragraphe en cours + \begingroup% dans un groupe semi-simple + \parindent=0pt% pas d'indentation¤\idx*\parindent¤ + \parskip=0pt% annuler le \parskip¤\idx*\parskip¤ + \setbox0\hbox{Àgjp}% boite pour le strut¤\idx*\setbox\idx*\hbox¤ + \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut¤\idx*\vrule\idx*\z@¤ + \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i¤§*\quark¤ + \unkern% annule la dernière compensation verticale + \par + \endgroup +} +\def\stackbox@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin¤\tidx*{unless}§*\quark¤ + \hfill % ressort infini de centrage (passe en mode horizontal)¤\idx*\hfill\idx*{mode!horizontal}¤ + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante...¤§*\doforeach¤ + {\frbox{% ...encadrer¤§*\frbox¤ + \hbox to\stackwd{% une \hbox de largeur \stackwd contenant¤\idx*\hbox¤ + \hss% 1) ressort de centrage¤\idx*\hss¤ + \stack@strut% 2) strut de dimension verticale + \current@item%3) l'élement courant + \hss}% 4)ressort de centrage + }% fin de la \fbox + \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales¤\idx*\kern¤ + }% fin de \doforeach + \unkern% annuler la dernière compensation horizontale¤\idx*\unkern¤ + \hfill% ressort infini de centrage¤\idx*\hfill¤ + \null% fait prendre en compte le dernier ressort¤\idx*\null¤ + \par% termine le paragraphe + \nobreak% interdit une coupure de page¤\idx*\nobreak¤ + \nointerlineskip% sinon, ne pas ajouter le ressort d'interligne¤\idx*\nointerlineskip¤ + \kern-\frboxrule% superposer les réglures horizontales¤\idx*\kern§*\frboxrule¤ + \expandafter\stackbox@i% et recommencer + \fi +} +\frboxrule=0.5pt ¤§*\frboxrule¤ +\frboxsep=3pt ¤§*\frboxsep¤ +\stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}| + +\begin{exercice} +Modifier la macro §\stackbox en §\lineboxed[|(] de telle sorte que pour chaque ligne, les boites aient une largeur telle qu'elles occupent la totalité de la ligne. +\solution +Pour que la largeur des boites d'une ligne soit telle que toutes ces boites remplissent la ligne, nous devons d'abord savoir combien il y a de boites dans chaque ligne. Pour cela, il faut compter combien de virgules il y a dans l'argument \verb|#1| et ajouter 1 ou, méthode plus simple, rajouter une virgule à \verb|#1| et effectuer le comptage. La macro §\cnttimestocs se chargera de ce travail et assignera le nombre de boites $n$ dans une macro. + +La largeur disponible pour tous les contenus cumulés de toutes les boites de la ligne s'obtient en retirant de \idx\hsize l'épaisseur §\frboxrule des réglures verticales et les espaces §\frboxsep entre ces réglures et les contenus des boites. S'il y a $n$ boites, il y a $n+1$ réglures verticales et $2n$ espaces §\frboxsep. La largeur interne d'une boite, stockée dans la macro \verb|\dim@box| sera donc + +\[ +\frac{\hbox{\ttfamily\char`\\hsize}-(n+1)\hbox{\ttfamily\char`\\frboxrule}-2n\hbox{\ttfamily\char`\\frboxsep}}{n} +\] + +\showcode|\catcode`\@11 +\def\lineboxed#1{%¤§*\lineboxed¤ + \par% termine le paragraphe en cours + \begingroup% dans un groupe semi-simple + \parindent=0pt% pas d'indentation¤\idx*\parindent¤ + \parskip=0pt% annuler le \parskip¤\idx*\parskip¤ + \setbox0\hbox{Àgjp}% boite pour le strut¤\idx*\setbox\idx*\hbox¤ + \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut¤\idx*\vrule\idx*\z@¤ + \lineboxed@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle la macro récursive¤§*\quark¤ + \unkern% annule la dernière compensation verticale + \par + \endgroup +} +\def\lineboxed@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin¤\tidx*{unless}§*\quark¤ + \cnttimestocs{#1,}{,}\nb@args% reçoit le nombre d'arguments dans la ligne courante¤§*\cnttimestocs¤ + \edef\dim@box{\the\dimexpr(\hsize-\frboxrule*(\nb@args+1)- + \frboxsep*2*\nb@args)/\nb@args}%¤\idx*\the\idx*\dimexpr§*\frboxrule§*\frboxsep¤ + \hbox{%¤\idx*\hbox¤ + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante...¤§*\doforeach¤ + {\frbox{% ...encadrer¤§*\frbox¤ + \hbox to\dim@box{% une \hbox de largeur \dim@box contenant + \hss% 1) ressort de centrage¤\idx*\hss¤ + \stack@strut% 2) strut de dimension verticale + \current@item%3) l'élement courant + \hss}% 4)ressort de centrage + }% fin de la \fbox + \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales¤\idx*\kern§*\frboxrule¤ + }% fin de \doforeach + \unkern% annuler la dernière compensation horizontale¤\idx*\unkern¤ + }% + \par% termine le paragraphe + \nobreak% interdit une coupure de page¤\idx*\nobreak¤ + \nointerlineskip% sinon, ne pas ajouter le ressort d'interligne¤\idx*\nointerlineskip¤ + \kern-\frboxrule% superposer les réglures horizontales¤\idx*\kern§*\frboxrule¤ + \expandafter\lineboxed@i% et recommencer + \fi +} +\catcode`\@12 +\lineboxed{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}\medbreak¤\idx*\medbreak¤ + +\frboxrule=1.5pt ¤§*\frboxrule¤ +\frboxsep=3pt ¤§*\frboxrule¤ +\lineboxed{,,,,,,,\\,,\\,,,,,,}|§*\lineboxed[|)]\end{exercice}\idx*[|)]{boite!empilement} + +\subsection{Dessiner des quadrillages}\idx*[|(]{quadrillages} +Comment parler de réglure sans parler de quadrillage ? Car s'il est bien un domaine où elles sont adaptées, c'est bien celui-là. + +Mettons à profit ce que nous savons pour écrire une macro §\grid[|(], capable de dessiner des quadrillages tels que celui-ci : + +\begin{centrage} +\xunit=1.5cm \yunit=1cm \mainrule=1pt \subrule=0.2pt +\leavevmode\grid{5}{4}{2}{10} +\end{centrage} + +Avant d'entrer dans le vif du sujet, quel est le nombre de paramètres qu'il est nécessaire de spécifier ? L'observation de ce quadrillage permet de se faire une idée. Il faut définir la largeur et hauteur d'un carreau-unité que nous stockerons dans deux registres de dimension (\verb|\xunit| et \verb|\yunit|) ainsi que l'épaisseur des lignes principales et secondaires stockées dans deux autres registres de dimension (\verb|\mainrule| et \verb|\subrule|). Puis, il faut passer à la macro §\grid le nombre de carreaux-unité que l'on veut horizontalement et verticalement ainsi que le nombre de subdivisions horizontales et verticales dans chaque carreau. Il semble donc naturel de construire une macro utilisant 4 registres de dimension et admettant 4 arguments. + +Le quadrillage ci-dessus a été tracé en exécutant + +\centrecode-\grid{5}{4}{2}{10}- + +\noindent pour demander qu'il comporte 5 carreaux-unité horizontalement, chacun divisé en 4 et 2 carreaux-unité verticalement, chacun divisé en 10. De plus, les registres de dimension ont été initialisés avec + +\centrecode-\xunit=1.5cm \yunit=1cm \mainrule=1pt \subrule=0.2pt- + +\subsubsection{Réglures horizontales} +Construisons la macro §\grid pas à pas et décidons que cette macro mettra des éléments dans une \idx\vbox.Nous allons dans un premier temps empiler des éléments dans une liste verticale pour tracer les réglures horizontales. Il est bien entendu que l'épaisseur des réglures ne doit pas perturber les espacements verticaux, c'est pourquoi elles doivent être mises dans une sous-boite de hauteur nulle. Nous allons donc définir l'analogue de §\clap mais pour les \idx\vbox. La macro suivante, §\vlap enferme son argument dans une boite verticale et rend la hauteur du tout égale à \verb|0pt| : + +\centrecode-\def\vlap#1{\vbox to0pt{\vss#1\vss}}-\idx*\vss + +Chaque réglure horizontale doit avoir comme largeur \verb|\xunit*#1|, que nous stockerons dans une macro pour l'utiliser ensuite. Pour que les espacements verticaux soient rigoureusement exacts, le ressort inter-ligne doit être neutralisé et pour cela, \idx\offinterlineskip sera appelé au début de la \idx\vbox. Voici un premier essai où ne sont construites que les réglures horizontales principales. On prend les unités égales à \numprint[cm]{0.5} par souci de gain de place. + +\showcode/\xunit=0.5cm \yunit=0.5cm \mainrule=0.8pt \subrule=0.2pt +\def\vlap#1{\vbox to0pt{\vss#1\vss}}¤§*\vlap\idx*\vss¤ +\catcode`@11 +\def\grid#1#2#3#4{%¤§*\grid¤ + \vbox{% empiler les éléments verticalement¤\idx*\vbox¤ + \offinterlineskip% pas de ressort d'interligne¤\idx*\offinterlineskip¤ + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale ds réglures¤\idx*\the\idx*\dimexpr¤ + \for\ii = 1 to #3 \do1% pour chaque carreau vertical (\ii=variable muette)¤§*\for¤ + {\vlap{\hrule width\total@wd height\mainrule}% tracer la réglure horizontale¤\idx*\hrule§*\vlap¤ + \kern\yunit% insérer l'espace vertical¤\idx*\kern¤ + }% + \vlap{\hrule width\total@wd height\mainrule}% dernière réglure horizontale¤§*\vlap¤ + }% +} +\catcode`@12 +\setbox0\hbox{\grid{4}{}{3}{}}% range la quadrillage dans le registre no 0¤\idx*\setbox\idx*\hbox¤ +Essai \copy0{} qui a pour +largeur=\convertunit{\wd0}{cm} cm et pour hauteur=\convertunit{\ht0}{cm} cm¤§*\convertunit\idx*\ht\idx*\wd¤/ + +Pour s'assurer que les dimensions du quadrillage sont parfaitement correctes, le quadrillage a été placé dans une \idx\hbox stockée dans le registre de boite \no0\idx*{boite!\no0}. Ainsi, nous pouvons mesurer sa largeur et sa hauteur (converties en \verb|cm| avec la macro §\convertunit vue précédemment) : les dimensions obtenues sont bien \numprint[cm]2 de largeur et \numprint[cm]{1.5} en hauteur qui correspondent à 4\verb|\xunit| et à 3\verb|\yunit|. +\grandsaut + +Venons-en aux lignes horizontales de subdivisions. Elles seront placées immédiatement \emph{sous} la position courante de la réglure principale en cours, moyennant un ressort \idx\vss inséré en fin de \idx\vbox. Le code suivant illustre cette structure : + +\showcode/Début\vbox to0pt{\hbox{sous}\hbox{la ligne de base}\vss}suite¤\idx*\vss¤/ +\grandsaut + +Dans le cas présent, nous placerons sous la position de la réglure principale en cours le matériel suivant, répété \verb|#4|${}-{}1$ fois : + +\begin{itemize} + \item l'espace verticale de dimension \verb|\yunit/#4|; + \item la réglure secondaire, enfermée dans une §\vlap, de façon à ce que son épaisseur n'entre pas en ligne de compte. +\end{itemize} + +\noindent Remarquons qu'il n'y a pas lieu de tester si \verb|#4|${}>1$, car la boucle §\for suivante + +\centrecode-\for\jj = 2 to #4 \do 1 {<code à exécuter>}- + +\noindent ne s'exécutera que si le test est vrai. + +\showcode|\xunit=0.5cm \yunit=0.5cm \mainrule=0.8pt \subrule=0.2pt +\def\vlap#1{\vbox to0pt{\vss#1\vss}}¤§*\vlap\idx*\vss¤ +\catcode`@11 +\def\grid#1#2#3#4{%¤§*\grid¤ + \vbox{% empiler les éléments verticalement¤\idx*\vbox¤ + \offinterlineskip% pas de ressort d'interligne¤\idx*\offinterlineskip¤ + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale de la boite¤\idx*\the\idx*\dimexpr¤ + \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% hauteur verticale de la subdivision + \for\ii = 1 to #3 \do% pour chaque unité verticale en partant du haut, tracer :¤§*\for¤ + {\vlap{\hrule width\total@wd height\mainrule}% la réglure horizontale principale¤§*\vlap¤ + % et dessous, les réglures horizontales secondaires : + \vbox to\z@{% dans une \vbox de hauteur nulle,¤\idx*\vbox\idx*\z@¤ + \for\jj = 2 to #4 \do 1% insérer #4-1 fois sous la position courante :¤§*\for¤ + {\kern\sub@unit % l'espace verticale¤\idx*\kern¤ + \vlap{\hrule width\total@wd height\subrule}% et la réglure secondaire¤§*\vlap\idx*\vrule¤ + }% + \vss% ressort qui se comprime pour satisfaire la hauteur nulle¤\idx*\vss¤ + }% + \kern\yunit% insérer l'espace vertical entre réglures principales¤\idx*\kern¤ + }% + \vlap{\hrule width\total@wd height\mainrule}% dernière réglure principale du bas¤§*\vlap¤ + }% +} +\catcode`@12 +\setbox0=\hbox{\grid{4}{}{3}{5}}¤\idx*\setbox¤ +Essai \copy0{} qui a pour +largeur=\convertunit{\wd0}{cm} cm et pour hauteur=\convertunit{\ht0}{cm} cm¤\idx*\wd\idx*\ht§*\convertunit¤| + +\subsubsection{Réglures verticales} +Pour les réglures verticales, nous allons procéder exactement de la même façon qu'avec les réglures horizontales. Nous allons les tracer avant les réglures horizontales et le tout sera enfermé dans une \idx\vbox chapeau contenant la totalité du quadrillage. + +Afin que ces réglures verticales ne prennent aucun encombrement vertical, nous les enfermerons dans une \idx\vbox de hauteur nulle et ferons en sorte que le contenu de cette boite se place au-dessous de la plus haute position de la \idx\vbox chapeau en utilisant à nouveau cette structure : + +\centrecode-\vbox to0pt{<réglures verticales>\vss}-\idx*\vss + +La totalité des réglures verticales sera mise dans une \idx\rlap, c'est-à-dire en débordement à droite de la position courante : + +\centrecode-\vbox to0pt{\rlap{<réglures verticales>}\vss}-\idx*\vss + +Il est maintenant clair que, puisque la \idx\rlap a une largeur nulle, cette \idx\vbox aura toutes ses dimensions nulles. Cela signifie que toutes les réglures verticales seront dessinées sans que la position courante ne change. + +\showcode|\mainrule=0.8pt \subrule=0.2pt +\def\vlap#1{\vbox to0pt{\vss#1\vss}}¤§*\vlap\idx*\vss¤ +\catcode`@11 +\def\grid#1#2#3#4{%¤§*\grid¤ + \vbox{% empiler les éléments verticalement¤\idx*\vbox¤ + \offinterlineskip% pas de ressort d'interligne¤\idx*\offinterlineskip¤ + % #################### Tracé des réglures verticales #################### + \vbox to\z@{% dans une \vbox de hauteur nulle¤\idx*\vbox\idx*\z@¤ + \edef\total@ht{\the\dimexpr\yunit*#3\relax}% hauteur totale¤\idx*\the\idx*\dimexpr¤ + \edef\sub@unit{\the\dimexpr\xunit/#2\relax}% espace entre 2 subdivisions + \rlap{% mettre à droite de la position sans bouger¤\idx*\rlap¤ + \for\ii = 1 to #1 \do 1% pour chaque unité horizontale¤§*\for¤ + {\clap{\vrule width\dimexpr\mainrule height\total@ht}% réglure principale¤\idx*\vrule§*\clap¤ + \rlap{% mettre à droite de la position sans bouger¤\idx*\rlap¤ + \for\jj = 2 to #2 \do 1% insérer #2-1 fois + {\kern\sub@unit % l'espace horizontal¤\idx*\kern¤ + \clap{\vrule width\subrule height\total@ht}% et la réglure verticale¤§*\clap\idx*\vrule¤ + }% + }% + \kern\xunit % insérer l'espace entre réglures horizontales + }% + \clap{\vrule width\mainrule height\total@ht}% dernière réglure principale¤\idx*\vrule§*\clap¤ + }% + \vss% compense la hauteur=0pt de la \vbox¤\idx*\vss¤ + }% + % #################### Tracé des réglures horizontales #################### + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale de la boite¤\idx*\the\idx*\dimexpr¤ + \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% espace entre 2 subdivisions + \for\ii = 1 to #3 \do 1% pour chaque carreau vertical en partant du haut :¤§*\for¤ + {\vlap{\hrule width\total@wd height\mainrule}% réglure horizontale principale¤§*\vlap¤ + % et dessous, les réglures secondaires : + \vbox to\z@{% dans une \vbox de hauteur nulle,¤\idx*\vbox\idx*\z@¤ + \for\jj = 2 to #4 \do 1% insérer #4-1 fois sous la position courante :¤§*\for¤ + {\kern\sub@unit % l'espace vertical¤\idx*\kern¤ + \vlap{\hrule width\total@wd height\subrule}% et la réglure secondaire¤§*\vlap\idx*\vrule¤ + }% + \vss% ressort qui se comprime pour satisfaire la hauteur nulle¤\idx*\vss¤ + }% + \kern\yunit% insérer l'espace vertical entre réglures principales¤\idx*\kern¤ + }% + \vlap{\hrule width\total@wd height\mainrule}% dernière réglure horizontale¤§*\vlap¤ + }% +} +\catcode`@12 +Grille 1 : \xunit=1cm \yunit=1cm \grid{5}{10}{2}{10}\smallskip¤\idx*\smallskip¤ + +Grille 2 : \xunit=0.5cm \yunit=0.5cm \grid{7}{1}{3}{1}\smallskip¤\idx*\smallskip¤ + +Grille 3 : \xunit=1.5cm \yunit=0.5cm \grid{4}{3}{3}{2}|§*\grid[|)]\idx*[|)]{réglure}\idx*[|)]{quadrillages} + +\section{Répétition de motifs}\label{repetition.motifs} +\subsection{\texttt{\char`\\leaders} et ses complices}\idx*[|(]{\leaders} +Venons-en maintenant à une primitive intéressante qui a la capacité de répéter une boite autant de fois que nécessaire pour remplir un espace donné, que ce soit en mode horizontal\idx*{mode!horizontal} ou vertical\idx*{mode!vertical}. Cette primitive est \idx\leaders et la syntaxe pour le mode horizontal\idx*{mode!horizontal} est la suivante : + +\centrecode-\leaders<boite ou réglure>\hskip<ressort>- + +\noindent La syntaxe est la même pour le mode vertical\idx*{mode!vertical} sauf que \idx\vskip est utilisé au lieu de \idx\hskip. En réalité, \idx\leaders est juste une assignation pour le \verb|<ressort>| qui spécifie tout simplement avec quel motif il doit être rempli. Car contrairement aux \idx\kern, un ressort a vocation à être rempli avec quelque chose ! Si \idx\leaders est absent comme cela a été le cas jusqu'à présent, ce motif est vide ce qui conduit à des ressorts qui laissent des espaces blancs. + +Intéressons-nous au mode horizontal\idx*{mode!horizontal}, celui en grande majorité pour lequel est utilisée \idx\leaders. Supposons que la boite de \numprint[cm]{1.5} de long suivante + +\centrecode-\hbox to1.5cm{\hss A\hss}- + +\noindent doive remplir un espace de \numprint[cm]{10}. La primitive \idx\leaders va coller ces boites les unes aux autres de façon à remplir le plus grand espace possible. Ce collage se fera bord à bord en partant de la gauche et laissera, si la longueur à remplir n'est pas un multiple de la longueur du motif, une espace laissée vide à la fin. Dans le code ci-dessous, nous spécifions «\verb|\hbox to10cm|» pour imposer que la dimension horizontale à remplir soit de \numprint[cm]{10}. Les \idx\vrule placées de part et d'autre de cette \idx\hbox permettent de visualiser le début et la fin de cette boite : + +\showcode/\vrule\hbox to10cm{\leaders\hbox to1.5cm{\hss A\hss}\hfill}\vrule¤\idx*\vrule\idx*\leaders\idx*\hbox\idx*\hfill¤/ + +Comme on peut facilement s'en assurer par calcul mental, seules 6 copies de la boites ont été collées les unes aux autres pour remplir un espace de \numprint[cm]9, laissant une espace vide de \numprint[cm]1 à la fin. + +Deux variantes de cette primitive existent et permettent de répartir l'éventuelle espace non remplie : +\begin{itemize} + \item avec \idx\cleaders, elle est également répartie avant la première et après la dernière boite répétée ; + \item avec \idx\xleaders, elle est également répartie avant la première, après la dernière et entre chaque boite répétée. +\end{itemize} + +Le plus visuellement compréhensible est de définir cette boite «{\frboxsep=-\frboxrule \frbox{\hbox to1.5cm{\hss A\hss}}}», de \numprint[cm]{1.5} de long et encadrée au plus proche par ce code : + +\centrecode/\frboxsep=-\frboxrule \frbox{\hbox to1.5cm{\hss A\hss}}/ + +\noindent et regarder les différences de comportement entre les différentes variantes. Pour bien visualiser l'espace de \numprint[cm]{10} à remplir, le caractère actif «\cidx\~» a été programmé pour imprimer une réglure horizontale de \verb|1em| de longueur juste avant et juste après cette espace : + +\showcode/\frboxsep=-\frboxrule \def~{\leavevmode\raise.75ex\hbox{\vrule height.2pt width1em}}¤§*\frboxsep\cidx*\~\idx*\raise\idx*\hbox\idx*\leavevmode\idx*\vrule¤ +~\hbox to10cm{\leaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~¤\idx*\hfill\idx*\hss\idx*\leaders§*\frbox¤ + +~\hbox to10cm{\cleaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~¤\idx*\cleaders¤ + +~\hbox to10cm{\xleaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~¤\idx*\xleaders§*\frbox¤/ + +Au lieu d'un encadrement contenant un texte, on peut aussi mettre une réglure ou même plusieurs dans une boite pour faire des dessins rudimentaires : + +\showcode+a) \leavevmode\hbox to10cm{\leaders\hrule\hfill}\smallskip¤\idx*\leavevmode\idx*\smallskip¤ + +b) \hbox to10cm{\leaders\hbox{\vrule height0.2pt width2.5mm \kern1.5mm}\hfill}¤\idx*\leaders\idx*\leavevmode\idx*\hfill¤ + +c) \vrule width0.2pt height1ex depth0pt % première réglure verticale + \hbox to10cm{% puis répétition de "_|" + \leaders\hbox{\vrule width2em height0.2pt \vrule width0.2pt height1ex}\hfill}¤\idx*\vrule¤ + +d) \hbox to10cm{\leaders\hbox{%¤\idx*\leaders\idx*\leavevmode¤ + \vrule height.2pt width.5em% 1/2 palier bas¤\idx*\vrule¤ + \vrule height5pt width0.2pt% montée au palier haut + \vrule height5pt depth-4.8pt width1em% palier haut + \vrule height5pt width0.2pt% descente au palier bas + \vrule height.2pt width.5em% 1/2 palier bas¤\idx*\vrule¤ + }\hfill}+ + +La macro \idx\hrulefill, qui remplit l'espace disponible avec une réglure horizontale, est programmée de la façon suivante dans plain-\TeX{} : + +\centrecode-\def\hrulefill{\leaders\hrule\hfill}- + +\noindent C'est d'ailleurs un cas où la primitive \idx\hrule peut être appelée en mode horizontal\idx*{mode!horizontal} pour produire une réglure horizontale alors que son mode de fonctionnement est le mode vertical\idx*{mode!vertical}.\idx*[|)]{\leaders} + +\subsection{Retour sur le quadrillage} +On entrevoit que la primitive \idx\leaders, de par sa propriété qui est d'effectuer une répétition, peut nous aider à tracer un quadrillage sans faire explicitement appel à une boucle ! Pour y arriver, nous allons tout d'abord nous employer à construire un carreau-unité avec les subdivisions adéquates que \idx\leaders répètera horizontalement et verticalement pour créer le quadrillage. + +Commençons verticalement. S'il faut $n$ subdivisions dans une espace verticale de \verb|\yunit|, il faut empiler verticalement dans une boite de hauteur \verb|\yunit| : + +\begin{enumerate} + \item une réglure horizontale d'épaisseur \verb|\mainrule| dont la dimension verticale sera forcée à 0pt; + \item placer autant de fois qu'il est possible ce matériel vertical suivant (dont la dimension est \verb|\yunit|/$n$) : + \begin{enumerate} + \item une espace verticale de dimension \verb|\yunit|/$n$; + \item une réglure horizontale d'épaisseur \verb|\subrule| dont la dimension verticale sera forcée à 0pt; + \end{enumerate} + \item une espace verticale de dimension \verb|\yunit|/$n$; + \item une réglure horizontale d'épaisseur \verb|\mainrule| dont la dimension verticale sera forcée à 0pt. +\end{enumerate} + +Comme les composants du point \no2 prennent à chaque fois \verb|\yunit|/$n$ d'espace vertical et que les deux derniers composants prennent aussi cette espace, la répétition de l'élément 2 aura donc lieu $n-1$ fois. + +\showcode|\mainrule=0.8pt \subrule=0.2pt +\def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V + \vbox to\yunit{% dans un \vbox de hauteur \yunit¤\idx*\vbox¤ + \offinterlineskip% pas de ressort d'interligne¤\idx*\offinterlineskip¤ + \vlap{\hrule height\mainrule width\xunit}% réglure principale du haut¤§*\vlap¤ + \leaders% répéter (ici ce sera donc #2-1 fois)¤\idx*\leaders¤ + \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2¤\idx*\vbox\idx*\dimexpr¤ + {\vss% ressort qui va s'étirer à \yunit/#2¤\idx*\vss¤ + \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt + }\vfill¤\idx*\vfill¤ + \kern\dimexpr\yunit/#2\relax% dernière espace verticale¤\idx*\kern¤ + \vlap{\hrule height\mainrule width\xunit}% réglure principale du bas¤\idx*\hrule§*\vlap¤ + }% +} +\yunit=1cm +\leavevmode \carreau{}{4} puis \carreau{}{10}¤\idx*\leavevmode¤| + +Pour les réglures horizontales, nous allons procéder de même après voir mis tout le code précédent dans une \idx\rlap de telle sorte que son encombrement horizontal soit nul. + +\showcode|\mainrule=0.8pt \subrule=0.2pt +\def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V + % ######## réglures horizontales ######## + \rlap{% mettre à droite de la position sans bouger¤\idx*\rlap¤ + \vbox to\yunit{% dans un \vbox de hauteur \yunit + \offinterlineskip% pas de ressort d'interligne¤\idx*\offinterlineskip¤ + \vlap{\hrule height\mainrule width\xunit}% réglure principale du haut¤§*\vlap¤ + \leaders% répéter (ici ce sera #2-1 fois)¤\idx*\leaders¤ + \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2¤\idx*\vbox\idx*\dimexpr¤ + {\vss% ressort qui va s'étirer à \yunit/#2¤\idx*\vss¤ + \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt + }\vfill% ressort de \leaders¤\idx*\vfill¤ + \kern\dimexpr\yunit/#2\relax% derniere espace verticale¤\idx*\kern¤ + \vlap{\hrule height\mainrule width\xunit}% réglure principale du bas¤\idx*\hrule§*\vlap¤ + }% + }% + % ######## réglures verticales ######## + \hbox to\xunit{% dans une \hbox de longueur \xunit¤\idx*\hbox¤ + \clap{\vrule height\yunit width\mainrule}% réglure principale de gauche¤§*\clap\idx*\vrule¤ + \leaders% répéter (ici ce sera #1-1 fois)¤\idx*\leaders¤ + \hbox to\dimexpr\xunit/#1\relax¤\idx*\dimexpr\idx*\hbox¤ + {\hss% ressort qui va s'étirer à \xunit/#1¤\idx*\hss¤ + \clap{\vrule height\yunit width\subrule}% réglure H de dimension 0 + }\hfill% ressort de \leaders¤\idx*\hfill¤ + \kern\dimexpr\xunit/#1\relax% dernière espace H¤\idx*\kern¤ + \clap{\vrule height\yunit width\mainrule}% réglure principale de droite¤\idx*\vrule§*\clap¤ + }% +} +\yunit=1cm \xunit=2cm +\leavevmode \carreau{3}{4} puis \carreau{8}{10}¤\idx*\leavevmode¤| + +Il suffit maintenant de mettre le tout dans une \idx\hbox. Cette boite a pour exactes dimensions \verb|\xunit| et \verb|\yunit| et il ne reste plus qu'à confier à \idx\leaders le soin de la répéter autant de fois horizontalement et verticalement que l'on veut de carreaux dans ces deux directions : + +\begingroup +\def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V + % #### réglures horizontales + \rlap{% mettre à droite de la position sans bouger¤\idx*\rlap¤ + \vbox to\yunit{% dans un \vbox de hauteur \yunit¤\idx*\vbox¤ + \offinterlineskip% pas de ressort d'interligne¤\idx*\offinterlineskip¤ + \vlap{\hrule height\mainrule width\xunit}% réglure principale¤§*\vlap\idx*\hrule¤ + \leaders% répéter (ici ce sera donc #2-1 fois)¤\idx*\leaders¤ + \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2¤\idx*\dimexpr¤ + {\vss% ressort qui va s'étirer à \yunit/#2¤\idx*\vss¤ + \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt + }\vfill% ressort de \leaders¤\idx*\vfill¤ + \kern\dimexpr\yunit/#2\relax% derniere espace verticale¤\idx*\kern¤ + \vlap{\hrule height\mainrule width\xunit}% réglure principale¤§*\vlap¤ + }% + }% + % #### réglures verticales + \hbox to\xunit{% dans une \hbox de longueur \xunit¤\idx*\vbox¤ + \clap{\vrule height\yunit width\mainrule}% réglure principale¤§*\clap\idx*\vrule¤ + \leaders% réopéter (ici ce sera donc #1-1 fois)¤\idx*\leaders¤ + \hbox to\dimexpr\xunit/#1\relax¤\idx*\hbox¤ + {\hss% ressort qui va s'étirer à \xunit/#1¤\idx*\hss¤ + \clap{\vrule height\yunit width\subrule}% réglure H de dimension 0¤§*\clap¤ + }\hfill% ressort de \leaders¤\idx*\hfill¤ + \kern\dimexpr\xunit/#1\relax% dernière espace H¤\idx*\kern¤ + \clap{\vrule height\yunit width\mainrule}% réglure principale¤\idx*\vrule§*\clap¤ + }% +} +\showcode|\mainrule=0.8pt \subrule=0.2pt +\def\grid#1#2#3#4{%¤§*\grid¤ + \vbox to#3\yunit{% dans une boite verticale de hauteur #3*\yunit¤\idx*\vbox¤ + \leaders% répéter verticalement¤\idx*\leaders¤ + \hbox to#1\xunit{% une boite de longueur #1*\xunit + \leaders% dans laquelle se répète horizontalement¤\idx*\leaders¤ + \hbox{\carreau{#2}{#4}}% le carreau de largeur \xunit + \hfill}%¤\idx*\hfill¤ + \vfill¤\idx*\vfill¤ + }% +} +Grille 1 : \xunit=1cm \yunit=1cm \grid{5}{10}{2}{10}\smallskip¤\idx*\smallskip¤ + +Grille 2 : \xunit=0.5cm \yunit=0.5cm \grid{7}{1}{3}{1}\smallskip¤\idx*\smallskip¤ + +Grille 3 : \xunit=1.5cm \yunit=0.5cm \grid{4}{3}{3}{2}| +\endgroup + +Bien que le résultat soit strictement identique, la façon de tracer le quadrillage avec \idx\leaders comme ci-dessus est très différente de celle employée précédemment avec la boucle §\for. En effet, la méthode avec \idx\leaders est beaucoup plus «laborieuse» puisqu'il s'agit d'une bête répétition d'un élément. Le résultat est trompeur pour l'\oe il, car la répétition horizontale et verticale de cet élément donne l'illusion que les réglures horizontales ou verticales sont tracées en un coup alors qu'il n'en est rien. De plus, toutes les réglures principales communes à deux carreaux-unité adjacents sont même superposées et sont donc tracées deux fois. Personne ne tracerait un quadrillage à la main de cette façon sur une feuille de papier ! À l'inverse, la méthode avec §\for bâtissait le quadrillage de façon plus globale puisque les réglures avaient la dimension maximale et étaient effectivement tracées en un coup, comme on le ferait spontanément à la règle et au crayon. + +\chapter{Fichiers : lecture et écriture}\idx*[|(]{fichier} +\section{Lire un fichier}\idx*[|(]{fichier!lecture} +\subsection{La primitive \texttt{\char`\\ input}}\idx*[|(]\input +\TeX{} peut « lire un fichier », c'est-à-dire qu'il peut lire le contenu du fichier, exactement comme il le fait lorsqu'il lit le code source. Pour cela, on utilise la syntaxe\footnote{Il est peut-être utile de signaler que \LaTeX{}, aussi incroyable que cela puisse paraitre, franchit un interdit en redéfinissant la primitive \texttt{\string\input} pour en faire une macro (non développable). Fort heureusement, la primitive est sauvegardée par \texttt{\string\let} dans la séquence de contrôle \texttt{\string\@@input}.} : + +\centrecode-\input <nom d'un fichier>- + +\noindent Lorsque \TeX{} s'attend à lire le nom d'un fichier, le développement maximal se met en place. Le nom du fichier s'étend jusqu'au premier espace rencontré (et cet espace sera absorbé) ou jusqu'à la première primitive non développable : mettre un espace ou un \idx\relax après la fin d'un nom de fichier est donc une bonne habitude à prendre. + +La primitive \idx\jobname se développe en le nom du fichier « maitre » en cours de compilation. Le fichier « maitre » est le fichier de plus haut niveau, celui qui n'a été appelé par aucun \idx\input. Le nom créé par \idx\jobname se compose de caractères de catcode 12\idx*{catcode!12 (autre)} et ne comporte pas l'extension du fichier, c'est-à-dire le «\verb|.|» et les caractères qui le suivent. + +Le fichier lu par \idx\input peut lui aussi contenir la primitive \idx\input, et ainsi de suite récursivement dans des limites raisonnables. Une fois que la fin du fichier est atteinte ou que la primitive \verb|\endinput| est rencontrée, \TeX{} cesse la lecture du fichier et la reprend dans fichier parent où il s'était interrompu. + +\begin{regle} +Lorsque \TeX{} lit le code source, on peut faire lire à \TeX{} le contenu d'un fichier. La lecture se fera comme se fait celle du code source et tout se passe donc comme si l'intégralité du contenu du fichier était insérée dans le code source à l'endroit où est rencontré : + +\centrecode-\input <nom de fichier>- + +Si le \verb|<nom de fichier>| ne spécifie pas d'extension, \TeX{} rajoute «\verb|.tex|». + +La lecture du fichier s'arrête lorsque la fin du fichier est atteinte ou lorsque la primitive \verb|\endinput| est atteinte. +\end{regle} + +\subsection{\texttt{\char`\\ input} et ses secrets}\label{input.secrets} +Examinons maintenant les autres aspects bien plus \TeX niques de la primitive \idx\input. Cette section, assez ardue, peut être sautée lors d'une première lecture. + +Tout d'abord, la primitive \idx\input est développable lorsqu'on la 1-développe, tout se passe comme si \TeX{} détournait sa tête de lecture pour aller lire la totalité du contenu du fichier. Il faut signaler que la totalité du fichier est lue selon le régime de catcode en vigueur au moment du 1-développement. De la même façon qu'il le fait lorsqu'il lit du code source, \TeX{} insère le caractère de code \idx\endlinechar à la fin de chaque ligne du fichier, y compris la dernière. + +Plusieurs choses assez \TeX niques se passent à la fin d'un fichier : + +\begin{enumerate} + \item avant la fin du fichier, \TeX{} insère le contenu de \idx\everyeof. Cette primitive a un comportement identique à celui d'un registre de tokens et elle est vide par défaut; + \item juste après le code inséré par \idx\everyeof, \TeX{} exécute une routine interne. +\end{enumerate} + +Cette routine interne est en réalité un test auquel procède \TeX{} : il vérifie que l'endroit où il se trouve est compatible avec le statut \idx\outer\footnote{On peut le constater dans le code source «\texttt{tex.web}» du programme \texttt{tex} à la section \no362, le test «\texttt{check\_outer\_validity}» est effectué.}. Avant de poursuivre, il faut définir ce qu'est ce statut : il concerne normalement les macros qui peuvent être définies comme étant «\idx\outer». + +\label{outer}\begin{regle} +Lorsque la primitive \idx\outer précède la définition d'une macro, cette dernière ne pourra pas se situer dans les endroits où \TeX{} lit du code « à grande vitesse »\idx*{lecture à grande vitesse}, à savoir : +\begin{itemize} + \item le texte de remplacement ou le \idx{texte de paramètre} lors de la définition d'une macro ; + \item le code situé dans les branches d'un test; + \item le préambule d'un alignement initié par \idx\halign ou \idx\valign. +\end{itemize} +\end{regle} + +Si une telle macro se trouve dans ces endroits interdits, un message d'erreur sera émis et la compilation stoppée : + +\errcode/\outer\def\foo{Bonjour} +\def\bar{\foo}/{! Forbidden control sequence found while scanning definition of \string\bar.} + +Une action peu connue de \idx\noexpand\verb|<token>|, lorsque \idx\noexpand est développé, est de désactiver pour le \verb|<token>| le test qui vérifie si ce \verb|<token>| est \idx\outer\footnote{Cela est expliqué à la section \no369 de \texttt{tex.web} par \textsc{Knuth} lui-même : «Comme des macros {\string\outer} peuvent se présenter ici, on doit aussi réinitialiser [à "\texttt{normal}"] le \texttt{scanner\_statut} temporairement.»}. On peut donc l'utiliser pour enfreindre la règle précédente et mettre une macro \idx\outer dans le texte de remplacement d'une autre macro : + +\showcode/\outer\def\foo{Bonjour}¤\idx*\outer¤ +\expandafter\def\expandafter\bar\expandafter{\noexpand\foo}¤\idx*\noexpand¤ +\meaning\bar¤\idx*\meaning¤ + +\edef\baz{\noexpand\foo}¤\idx*\noexpand¤ +\meaning\baz/ + +Revenons au test à la fin du fichier : comme il vérifie que l'endroit où il se trouve est compatible avec \idx\outer, il communique à la fin du fichier le statut \idx\outer. Pour désactiver le test, nous allons appliquer la même méthode qu'avec une macro \idx\outer, c'est-à-dire le faire précéder de la primitive \idx\noexpand. + +\begin{regle} +La fin d'un fichier a le statut \idx\outer. + +Pour permettre que la fin d'un fichier se situe dans un endroit normalement interdit à ce statut, il faut placer la primitive \idx\noexpand juste avant la fin du fichier, soit en l'écrivant explicitement, soit via \idx\everyeof avec l'assignation + +\centrecode-\everyeof{\noexpand}-\idx*\everyeof + +\noindent et faire en sorte de développer ce \idx\noexpand. +\end{regle} + +Développer le \idx\noexpand rend très contraignante et quasi impossible la définition d'une macro dont le texte de remplacement serait le contenu d'un fichier. En effet, pour forcer ce développement \emph{avant} que la définition de la macro ne se fasse, il faudrait la définir avec \idx[|etc]\edef\forbidindex\edef{} selon ce schéma : + +\centrecode-\begingroup +\everyeof{\noexpand}¤\idx*\noexpand¤ +\global\edef\<macro>{\input<nom de fichier> } +\endgroup-\idx*\global\idx*\input + +Cette définition est très peu satisfaisante, car la primitive \idx\edef implique que le contenu du fichier est développé au maximum ce qui, sauf cas très particulier, n'est pas ce qui est recherché. De plus, elle rend la définition globale puisqu'un groupe semi-simple a été ouvert pour limiter la portée de la modification de \idx\everyeof. + +\label{input.dans.macro}Heureusement, nous pouvons bien mieux faire. L'idée est d'insérer un délimiteur (ici \verb|\eof@nil|) en fin de fichier via \idx\everyeof. Ce délimiteur sera capturé par une macro auxiliaire à argument délimité qui sera dès lors capable de savoir où se trouve la fin du fichier et affecter à une macro ce qui se trouve dans ce fichier avant ce délimiteur. Toute cette man\oe uvre suppose que le fichier ne contient pas \verb|\eof@nil|. + +Supposons par exemple que le fichier «\texttt{test.txt}» contient : + +\centrecode-Programmer en \TeX{} est facile + +et utile- + +Voici comment programmer une macro §*\filedef[|(] + +\centrecode-\filedef\<macro>{<nom du fichier>}- +\immediate\openout\wtest=test.txt +\exactwrite\wtest|Programmer en \TeX{} est facile + +et utile|% +\immediate\closeout\wtest + +\noindent qui définit une \verb|\<macro>| dont le texte de remplacement est le contenu du fichier : + +\showcode/\catcode`\@11 +\long\def\filedef#1#2{%¤§*\filedef\idx*\long¤ + \begingroup + \let\input\@@input% <- utilisateurs de latex uniquement¤\idx*\input\idx*\@@input\idx*[|etc]\let\forbidindex\let¤ + \everyeof{\eof@nil\noexpand}% insère "\eof@nil\noexpand" à la fin du fichier¤\idx*\everyeof\idx*\noexpand¤ + \expandafter\filedef@i\expandafter#1% développe \input #2¤\defline\ccc¤ + \expandafter\relax\input #2¤\defline\aaa¤ +} +\long\def\filedef@i#1#2\eof@nil{%¤\idx*\long¤ + \endgroup + \expandafter\def\expandafter#1\expandafter{\gobone#2}% mange le \relax¤\defline\bbb¤ +} +\catcode`\@12 +\filedef\foo{test.txt} +\meaning\foo/ + +La ligne «\verb|\let\input\@@input|» redonne à \idx\input son statut de primitive et annule la redéfinition effectuée par \LaTeX\footnote{Ce livre a été compilé en utilisant le format \LaTeX.}. Il est bien évident que les utilisateurs d'autres formats ne doivent pas écrire cette ligne. + +Ensuite, à la ligne \no\aaa, un \idx\relax a été inséré avant le développement de \idx\input. Ceci nous assure que l'argument délimité \verb|#2| de \verb|\filedef@i| ne serait pas dépouillé de ses accolades si jamais le contenu du fichier était constitué d'un texte entre accolades. Ce \idx\relax est mangé à la ligne \no\bbb{} par le \verb|\gobone| avant que la définition ne soit faite. + +L'important est de comprendre qu'une fois le \idx\input 1-développé par le pont d'\idx\expandafter et en tenant compte du contenu de \idx\everyeof, les lignes \nos\ccc{} et \number\numexpr\ccc+1\relax{} deviennent : + +\centrecode-\filedef@i#1\relax<contenu du fichier lu par \input>\eof@nil\noexpand<EOF>- + +\noindent La macro \verb|\filedef@i|, qui ne lit que ce qui se situe jusqu'au \verb|\eof@nil|, ne verra jamais la fin du fichier \verb|<EOF>|. Le but recherché est atteint : le \verb|<EOF>| est précédé d'un \idx\noexpand, et ce \idx\noexpand, en étant exécuté après que \verb|\filedef@i| ait fait son travail, rendra \verb|<EOF>| compatible avec le statut \idx\outer.\idx*[|)]\input§*\filedef[|)] + +\section{Lire dans un fichier} +« Lire un fichier » n'a pas la même signification que « lire \emph{dans} un fichier » qui sous-entend que l'on peut lire, contrôler ce qu'on lit et donc reprendre la main. Pour fonctionner selon ce mode, \TeX{} dispose de 16 canaux d'entrée, numérotés de 0 à 15, chacun correspondant à un flux de lecture. Comme pour les boites, les compteurs, les dimensions et les registres de tokens, plain-\TeX{} fournit la macro \idx\newread\verb|\<macro>| qui assigne\footnote{Cette macro fonctionne comme son homologue \texttt{\string\newbox}. Comme il n'existe pas de primitive \texttt{\string\readdef}, la macro est définie par la primitive \texttt{\string\chardef}.} à la \verb|\<macro>| un entier correspondant à un canal de lecture disponible. + +\begin{regle} +\relax\TeX{} dispose de 15 canaux de lecture portant les numéros de 0 à 15. + +On demande l'allocation d'un canal de lecture par + +\centrecode-\newread\<macro>- + +et ce faisant, la \verb|\<macro>| devient équivalente à un \verb|<nombre>|, qui est un numéro de canal libre. Un \verb|<canal>| est donc un \verb|<nombre>| explicitement écrit ou une macro définie avec \idx\newread. +\medbreak + +Avant de lire dans un fichier, on doit d'abord lier un \verb|<canal>| à un fichier avec\idx*\openin : + +\centrecode-\openin<canal>= <nom du fichier>- + +\noindent Si aucune extension n'est précisée au \verb|<nom du fichier>|, \TeX{} rajoute «\verb|.tex|». Le signe \verb|=| et l'espace qui le suit sont optionnels. Lorsque \TeX{} lit le \verb|<nom du fichier>|, il entre dans une phase de développement maximal et si ce nom est suivi d'un espace, cet espace est absorbé. + +Tout canal ouvert doit être fermé et donc, après avoir fait les opérations de lecture, il convient d'exécuter \idx\closein\verb|<canal>| pour désolidariser le \verb|<canal>| du fichier qui lui avait été attaché avec \idx\openin. +\end{regle} + +Le test \tidx{ifeof}\verb|<canal>| teste si la lecture du fichier lié au \verb|<canal>| est arrivée à la fin du fichier. Employé juste après avoir lié un flux à un fichier via \idx\openin, le test \verb|\ifeof<canal>| sera vrai uniquement si le fichier spécifié n'existe pas. Voici donc comment bâtir une macro §\iffileexists : + +\showcode/¤\string\newread\string\rtest\idx*\newread¤% canal de lecture employé dans tout ce chapitre +\def\iffileexists#1#2{% #1=canal de lecture #2=nom du fichier¤§*\iffileexists¤ + \openin#1=#2¤\idx*\openin¤ + \ifeof#1% le fichier n'existe pas¤\tidx*{ifeof}¤ + \closein#1¤\idx*\closein¤ + \expandafter\secondoftwo% renvoyer faux¤§*\secondoftwo[|etc]\forbidindex\secondoftwo¤ + \else + \closein#1 + \expandafter\firstoftwo% sinon renvoyer vrai¤§*\firstoftwo[|etc]\forbidindex\firstoftwo¤ + \fi +} +a) \iffileexists\rtest{test.txt}{vrai}{faux}\qquad +b) \iffileexists\rtest{foobar.txt}{vrai}{faux}¤§*\iffileexists¤/ + +Dans tout cet exemple, nous avons demandé l'allocation d'un \verb|<canal>| avec « \idx\newread\verb|\rtest| » et désormais, nous utiliserons le canal de lecture \no\verb|\rtest| dans tous les exemples. +\grandsaut + +Un fichier peut-être lu par plusieurs canaux simultanément. Cela signifie que si le besoin s'en fait sentir, on peut positionner des têtes de lecture dans un fichier à plusieurs endroits, chacune pouvant lire le fichier indépendamment des autres. + +\begin{regle} +Lorsqu'un \verb|<canal>| de lecture est ouvert et lié à un fichier, \TeX{} peut lire des \emph{lignes} dans ce fichier les unes après les autres, et assigner la ligne lue à une macro. On utilise la syntaxe\idx*\read + +\centrecode-\read\<canal> to \<macro>- + +\noindent où le mot-clé «to» est obligatoire et éventuellement suivi d'espaces optionnels. + +Ceci a pour effet de placer dans le texte de remplacement de la \verb|\<macro>| tout ce qu'il y a dans la ligne en cours : + +\begin{itemize} + \item les accolades doivent être équilibrées dans la ligne en cours. Si elles ne le sont pas, \TeX{} lit autant de lignes que nécessaire pour qu'elles le soient. Si la fin du fichier est atteinte sans que l'équilibrage des accolades ne soit réalisé, une erreur de compilation surviendra; + \item une fois l'équilibrage des accolades atteint, la primitive \idx\read lit tout ce qui s'étend jusqu'à la «marque de \idx{fin de ligne}» (voir page~\pageref{marque.fin.ligne}); + \item les tokens dans le texte de remplacement de la \verb|\<macro>| tiennent compte du régime de catcode en vigueur lorsque \idx\read est exécutée; \idx\read effectue donc une « tokénisation», c'est-à-dire la transformation d'\idx{octet}s bruts en tokens; + \item le caractère de code \idx\endlinechar est inséré à la fin du texte de remplacement de la\verb|\<macro>|; + \item l'assignation est globale si \idx\read est précédé de \idx\global. +\end{itemize} + +La primitive de \idx\eTeX{}\idx\readline, dont la syntaxe est + +\centrecode-\readline\<canal> to \<macro>- + +\noindent agit comme \idx\read sauf qu'elle insère dans le texte de remplacement de la \verb|\<macro>| tous les tokens lus dans la ligne (y compris le caractère de code \idx\endlinechar). L'espace aura le catcode 10\idx*{catcode!10 (espace)} et tous les autres caractères le catcode 12\idx*{catcode!12 (autre)}. +\end{regle} + +Pour fixer les idées, si le canal \no\verb|\rtest| est lié à un fichier et que la ligne suivante à lire dans ce fichier est : + +\centrecode-Programmer en \TeX{} est facile- + +\noindent Si l'on écrit «\verb-\read\rtest to \foo-», tout se passe comme si on avait fait l'assignation d'une macro sans argument : + +\centrecode-\def\foo{Programmer en \TeX{} est facile }- + +\noindent Il est important de noter que l'espace en fin de texte provient du caractère de code \idx\endlinechar : celui-ci étant par défaut «\verbidx*[ (retour charriot)]{^^M}\verb|^^M|» de catcode \number\catcode`\^^M {}\idx*{catcode!5 (retour charriot)}, il est lu comme un espace comme le stipule la règle page~\pageref{regle.catcode5}. Pour mettre ce comportement en évidence dans l'exemple ci-dessous, le fichier \verb|filetest.txt| contiet : +\immediate\openout\wtest= filetest.txt +\exactwrite\wtest|Programmer en \TeX{} est facile + +et utile|% +\immediate\closeout\wtest + +\centrecode-Programmer en \TeX{} est facile + +et utile- + +Notons qu'il y a 3 espaces entre les mots « est » et « facile » et regardons ce que donne le code suivant : + +\showcode|\openin\rtest =filetest.txt¤\idx*\openin¤ +\read\rtest to \foo% lit la première ligne¤\idx*\read¤ +1) Signification : \meaning\foo.\par% signification de ce qui a été lu¤\idx*\meaning¤ +1) Exécution : \foo\par +\read\rtest to \foo% lit la deuxième ligne +2) Signification : \meaning\foo.\par +2) Exécution : \foo\par +\read\rtest to \foo% lit la dernière ligne¤\idx*\read¤ +3) Signification : \meaning\foo.\par¤\idx*\meaning¤ +3) Exécution : \foo +\closein\rtest¤\idx*\closein¤| + +Les points après chaque \idx\meaning\verb|\foo| sont ici destinés à mettre en évidence l'espace en fin de texte de remplacement. + +Voici le même exemple, cette fois-ci avec la primitive \idx\readline : + +\showcode|\openin\rtest =filetest.txt¤\idx*\openin¤ +\readline\rtest to \foo% lit la première ligne¤\idx*\readline¤ +1) Signification : \meaning\foo.\par% signification de ce qui a été lu¤\idx*\meaning¤ +1) Exécution : \foo\par +\readline\rtest to \foo% lit la deuxième ligne +2) Signification : \meaning\foo.\par +2) Exécution : \foo\par +\readline\rtest to \foo% lit la dernière ligne¤\idx*\readline¤ +3) Signification : \meaning\foo.\par¤\idx*\meaning¤ +3) Exécution : \foo +\closein\rtest¤\idx*\closein¤| + +Une première question se pose naturellement : d'où viennent les virgules que l'on observe à la fin du texte de remplacement de \verb|\foo| ? Elles sont la preuve que le caractère de code \idx\endlinechar, placé en fin de ligne, a été détokénizé par \idx\readline pour devenir un inoffensif caractère de catcode 12\idx*{catcode!12 (autre)}. On voit une virgule car \idx\endlinechar a pour valeur \number\endlinechar{} et que le caractère ayant ce code dans la \idx{fonte} utilisée est la virgule : + +\showcode/Valeur de \string\endlinechar = \number\endlinechar\par¤\idx*\string\idx*\number\idx*\endlinechar¤ +Caractère correspondant : << \char\endlinechar{} >>¤\idx*\char¤/ + +Le second point qui peut surprendre est qu'au premier cas, le texte de remplacement de \verb|\foo| contient \emph{tous les espaces} (ils sont 3) entre les mots «est» et «facile», alors qu'un seul était lu par \idx\read. La subtile différence entre \idx\read et \idx\readline est que cette dernière ignore les catcodes en cours et insère dans le texte de remplacement de la macro \emph{tous} les caractères lus dans la ligne; elle insère donc \emph{tous} les espaces, en leur donnant un catcode de 10. + +\begin{exercice} +Proposer une méthode pour que la macro \idx\read ne lise que ce qui se trouve dans la ligne courante, \emph{sans} y ajouter le caractère de code \idx\endlinechar. + +Appliquer cette méthode pour écrire une macro §\xread[|(] qui agit de la même façon que \idx\read avec la même syntaxe : + +\centrecode|\xread<canal> to \<macro>| + +mais qui place dans le texte de remplacement de la \verb|\<macro>| le contenu de la ligne sans le caractère de code \idx\endlinechar. +\solution +Il suffit d'opérer localement et définir le \idx\endlinechar à $-1$ et utiliser un \idx\global pour que la +\verb|\<macro>| survive à la fermeture du groupe : + +\showcode|\openin\rtest =filetest.txt¤\idx*\openin¤ +Ligne 1 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la première ligne¤\idx*\endlinechar\idx*\global\idx*\read¤ +\meaning\foo.\par% donne ce qui a été lu¤\idx*\meaning¤ +Ligne 2 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la deuxième ligne +\meaning\foo.\par +Ligne 3 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la dernière ligne¤\idx*\endlinechar\idx*\global\idx*\read¤ +\meaning\foo.¤\idx*\meaning¤ +\closein\rtest¤\idx*\closein¤| + +Contrairement à l'exemple précédent, comme le caractère de code \idx\endlinechar n'est pas inséré à la fin des lignes, la seconde ligne est bien lue comme étant vide. + +Pour la macro \verb|\xread|, nous n'allons pas utiliser de \idx\global car il est toujours plus sûr de se passer, lorsque c'est possible, d'assignations globales qui provoquent des effets de bord difficiles à maitriser. Nous allons tout d'abord sauvegarder dans la macro \verb|\restoreendlinechar| le code qui permet de restaurer \idx\endlinechar. Cette macro aura comme texte de remplacement + +\centrecode-\endlinechar=<valeur actuelle de \endlinechar>- + +Toute la difficulté vient ensuite des espaces optionnels qui se trouvent après le mot-clé «\verb|to|». En effet, si nous déclarons la macro §\xread ainsi + +\centrecode-\def\xread#1to#2{<code à définir>}- + +\noindent rien ne permet d'affirmer que l'argument \verb|#2| sera la \verb|\<macro>| : si des espaces optionnels sont écrits après «\verb|to|», alors \verb|#2| sera un espace. Il va donc falloir tester l'argument \verb|#2| et ne le prendre en compte que lorsqu'il sera une séquence de contrôle, ce qui suppose la mise en place d'une macro récursive. Pour mener à bien le test, nous mettrons en \oe uvre la macro §\ifcs programmée à la page~\pageref{ifcs.la.vraie}. + +\showcode/\def\xread#1to#2{% + \ifcs{#2}% si #2 est une séquence de contrôle + {\edef\restoreendlinechar{\endlinechar=\the\endlinechar}% + \endlinechar=-1 % supprime le caractère mis en fin de ligne + \read#1to#2% lit la ligne et l'assigne à la macro #2 + \restoreendlinechar\relax% restaure le \endlinechar + }% si #2 n'est pas une séquence de contrôle, + {\xread#1to}% ignorer #2, et recommencer +} + +\openin\rtest =filetest.txt¤\idx*\openin¤ +Ligne 1 : \xread\rtest to \foo% lit la première ligne +\meaning\foo.\par% donne ce qui a été lu¤\idx*\meaning¤ +Ligne 2 : \xread\rtest to \foo% lit la deuxième ligne +\meaning\foo.\par +Ligne 3 : \xread\rtest to \foo% lit la dernière ligne +\meaning\foo.¤\idx*\meaning¤ +\closein\rtest¤\idx*\closein¤/ + +Une première remarque consiste à dire que la syntaxe de §\xread n'est \emph{pas exactement identique} à celle de \idx\read. En effet, la macro récursive §\xread ignore les arguments \verb|#2| tant qu'ils ne sont pas une séquence de contrôle. L'appel farfelu suivant est donc tout à fait valide : + +\centrecode-\xread\rtest toW a aZcd 1 2345\foo- + +Mais surtout, il existe une méthode bien plus élégante pour programmer §\xread, non récursive, ayant exactement la même syntaxe que \idx\read, mais qui fait intervenir la primitive \idx\afterassignment que nous verrons plus tard (voir page~\pageref{afterassignment}). Le code est donné à titre indicatif : + +\showcode|\def\xread{% doit être suivie de "<nombre> to \<macro>" + \edef\restoreendlinechar{\endlinechar=\the\endlinechar}%¤\idx*\endlinechar\idx*\the¤ + \endlinechar=-1 % neutralise \endlinechar + \afterassignment\restoreendlinechar% après l'assignation, restaurer \endlinechar¤\idx*\afterassignment¤ + \read% attend <nombre> to \<macro> pour effectuer l'assignation¤\idx*\read¤ +} +\catcode`\@12 + +\openin\rtest =filetest.txt¤\idx*\openin¤ +Ligne 1 : \xread\rtest to \foo% lit la première ligne +\meaning\foo.\par% donne ce qui a été lu¤\idx*\meaning¤ +Ligne 2 : \xread\rtest to \foo% lit la deuxième ligne +\meaning\foo.\par +Ligne 3 : \xread\rtest to \foo% lit la dernière ligne +\meaning\foo.¤\idx*\meaning¤ +\closein\rtest¤\idx*\closein¤|§*\xread[|)] +\end{exercice} + +\subsection{Faire une recherche dans un fichier} +Envisageons maintenant un cas pratique et imaginons qu'une liste de fournitures soit contenue dans un fichier «\verb|founitures.txt|» dont le contenu est le suivant : + +\immediate\openout\wtest= fournitures.txt +\immediate\write\wtest{Ref,Item,Prix,Fournisseur^^J% +1201a,article 1,25.90,\noexpand\TeX{} marchand ^^J% +4562u,article 2,120,fournisseur 1^^J% +1721d,article 3,57.10,fournisseur 2}% +\immediate\closeout\wtest +\centrecode|Ref,Item,Prix,Fournisseur +1201a,article 1,25.90,\TeX{} marchand +4562u,article 2,120,fournisseur 1 +1721d,article 3,57.10,fournisseur 2 +<etc>| + +On remarque que la première ligne du fichier n'est pas à proprement parler une ligne de données, elle indique le nom des champs qui composent les lignes suivantes. + +Supposons que ce fichier soit dans le même répertoire\footnote{Ou qu'un lien symbolique vers ce fichier soit présent dans le répertoire de compilation.} que le fichier maitre et que nous souhaitions chercher dans ce fichier une ligne par sa référence, c'est-à-dire par ce qui est contenu dans le premier champ. Le but est de mettre au point une macro §\searchitem appelée de cette façon + +\centrecode-\searchitem<canal>{fournitures.txt}{1721d}\foo-§*\searchitem + +\noindent de telle sorte que la ligne où se trouve le premier champ «\verb|1721d|» soit trouvée. La macro purement développable \verb|\foo|\verb|{<champ>}| sera créée où son argument précisera de quel \verb|<champ>| on souhaite obtenir la valeur. Par exemple, + +\begin{centrage}\small + \begin{tabular}{r@{\quad affiche \quad}l} + \verb|\foo{ref}|&1721d\\ + \verb|\foo{item}|&article 3\\ + \verb|\foo{prix}|&57.10\\ + \verb|\foo{fournisseur}|&fournisseur 2 + \end{tabular} +\end{centrage} + +La macro \verb|\foo| est ici prise pour l'exemple, mais naturellement, le choix du nom de cette macro est à la convenance de l'utilisateur. + +Fixons-nous trois contraintes supplémentaires : + +\begin{itemize} + \item si le fichier n'existe pas ou si la référence cherchée n'existe pas, la macro \verb|\foo| définie doit être \idx\let-égale à \verb|\gobone|. De cette façon, elle absorbera son argument et ne produira aucun affichage; + \item si un champ n'existe pas, rien ne doit être affiché. Par exemple, \verb|\foo{truc}| ne doit produire aucun affichage; + \item lors de la recherche, toute ligne vide doit être ignorée. +\end{itemize} + +La première chose à faire va être d'initialiser la macro \verb|\foo| (argument \verb|#4|) sera initialisée à \verb|\gobone|. Ensuite, la première ligne du fichier sera lue, ses champs seront parcourus avec §\doforeach afin de stocker leur nom écrit en lettres minuscules dans une macro \verb|\fieldname<i>| ou \verb|<i>| est le numéro du champ. + +Par la suite, nous pourrons commencer à lire les lignes de données : chaque ligne sera envoyée à une macro auxiliaire \verb|\test@field| chargée d'examiner leurs champs. Le premier champ de la ligne en cours sera comparé à celui qui est recherché et si le test est positif, il incombera à la macro \verb|\test@filed| de procéder aux assignations nécessaires des macros + +\begin{itemize} + \item \verb|\foo.<nom du champ 1>|%{\ttfamily\textbackslash\boxtoken{foo.\codeelement{nom du champ 1}}} + \item \verb|\foo.<nom du champ 2>|{\ttfamily\textbackslash\boxtoken{foo.\codeelement{nom du champ 2}}} + \item etc +\end{itemize} + +\noindent chacune \verb|\let|-égale à \verb|\fieldname<i>|. + +Enfin, la macro purement développable \verb|\foo|, prenant comme argument un \verb|<champ>|, sera créée. Elle testera avec \tidx{ifcsname} si la macro \verb|\foo.<champ>| existe, auquel cas elle formera cette macro via \idx\csname. + +\showcode|\def\macroname{% se développe en le nom de la macro qui suit sans ¤§*\macroname¤ + % le caractère d'échappement + \ifnum\escapechar>-1 % si le caractère d'échappement est positif¤\idx*\escapechar¤ + \ifnum\escapechar<256 % et inférieur à 256, développer les 2 "\fi" + \expandafter\expandafter\expandafter\expandafter% et le "\string", puis + \expandafter\expandafter\expandafter\gobone% manger le "\" avec \gobone + \fi + \fi + \string% doit être suivi d'une macro¤\idx*\string¤ +} +\catcode`\@11 +\newcount\field@cnt +\def\searchitem#1#2#3#4{% #1= canal #2=nom fichier #3=référence #4=macro à définir¤§*\searchitem¤ + \let#4\gobone% pour l'instant, #4=\gobone + \openin#1=#2\relax¤\idx*\openin¤ + \unless\ifeof#1% si le fichier existe¤\tidx*{unless}\tidx*{ifeof}¤ + \lowercase{\def\sought@firstfield{#3}}% stocke le 1er champ à chercher¤\idx*\lowercase¤ + \edef\macro@name{\macroname#4}% nom de la macro sans "\"¤§*\macroname¤ + \xread#1 to \current@line% lire la première ligne¤§*\xread¤ + \field@cnt=0 % initialiser le compteur de champs + % ################ sauvegarde du nom des champs ################ + \expsecond{\doforeach\current@field\in}\current@line% pour chaque champ¤§*\expsecond¤ + {\advance\field@cnt1 % incrémenter le compteur de champs¤\idx*\advance¤ + \lowercase\expandafter{% e texte de remplacement de \current@field en minuscule¤\idx*\lowercase¤ + \expandafter\def\expandafter\current@field\expandafter{\current@field}% + }% + % sauvegarder chaque champ de la 1re ligne (qui sont les intitulés) dans une macro + \letname{fieldname\number\field@cnt}=\current@field¤§*\letname¤ + }% + \edef\field@num{\number\field@cnt}% nombre de champs + % ################ lecture des lignes de données ################ + \loop% tant que...¤\idx*\loop¤ + \unless\ifeof#1\relax% ...la fin du fichier n'est pas atteinte¤\tidx*{unless}\tidx*{ifeof}¤ + \xread#1 to \current@line% lire une ligne¤§*\xread¤ + \unless\ifx\current@line\empty% si elle n'est pas vide¤\idx*\empty¤ + % examniner les champs qu'elle contient (aller à \test@field) + \expsecond{\expandafter\test@field\current@line\@nil}\macro@name%¤\defline\aaa §*\expsecond¤ + \fi + \repeat¤\idx*\repeat¤ + \fi + \closein#1\relax¤\idx*\closein¤ +} + +\def\test@field#1,#2\@nil#3{% #1=champ no 1 #2=autres champs #3=nom de la macro sans "\" + \def\current@firstfield{#1}% stocke le premier champ de la ligne en cours + \ifx\current@firstfield\sought@firstfield% s'il est égal à celui cherché + \defname{#3.\csname fieldname1\endcsname}{#1}% définir la macros \<#3."champ 1">¤§*\defname¤ + \field@cnt=1 % initialiser le compteur de champ + \doforeach\current@field\in{#2}% puis, pour i>2, définir les macros \<#3."champ i">¤§*\doforeach¤ + {\advance\field@cnt1 % incrémenter le compteur de champ + \letname{#3.\csname fieldname\number\field@cnt\endcsname}=\current@field¤§*\letname¤ + }% + \defname{#3}##1{% et définir la macro \<#3>¤§*\defname¤ + \ifcsname#3.##1\endcsname% si la macro \<#3."argument"> existe déjà¤\tidx*{ifcsname}¤ + \csname#3.##1\expandafter\endcsname% l'exécuter après avoir mangé le \fi + \fi + }% + \fi +} +\catcode`\@12 + +\searchitem\rtest{fournitures.txt}{4562u}\monarticle¤§*\searchitem¤ +réf = \monarticle{ref}, +dénomination = \monarticle{item}, +prix = \monarticle{prix}, +fournisseur = \monarticle{fournisseur}, +champ non existant = \monarticle{foobar}. + +\searchitem\rtest{fournitures.txt}{truc}\essai% référence "truc" n'existe pas¤§*\searchitem¤ +réf = \essai{ref}, +dénomination = \essai{item}, +prix = \essai{prix}, +fournisseur = \essai{fournisseur}.| + +La macro §\macroname, donnée tout au début, doit être suivie d'une \verb|\<macro>| et se développe en le nom de cette \verb|\<macro>| sans l'éventuel caractère d'échappement. + +\begin{exercice} +Programmer une amélioration de la macro §\searchitem pour que la recherche puisse se faire sur n'importe quel champ. Les champs seront spécifiés par leur numéro et non plus par leur nom. La syntaxe sera : + +\centrecode-\searchitem<canal>{<nom fichier>}{<numéro champ>=<valeur>}\<macro>- + +La modification de la syntaxe concerne le 3\ieme{} argument et l'introduction du \verb|<numéro champ>| que l'on souhaite chercher. Si ce \verb|<numéro champ>| et le signe \verb|=| sont absent, le \verb|<numéro champ>| sera pris égal à 1. + +On supposera que le fichier «\verb|basecourse.txt|» recueille les données des participants à une course à pied et contient : +\immediate\openout\wtest= basecourse.txt +\exactwrite\wtest|Nom,Prénom,Classement,Naissance,Licencié,Temps +Demay,Pierre,237,27/8/1986,oui,2h37 +Leblanc,Nicolas,187,4/9/1978,non,2h05 +Valet,Bruno,78,25/11/1989,oui,1h47 +Hachel,André,283,2/3/1972,non,2h42 +Jalabert,Jean,165,19/1/1982,Oui,2h01|% +\immediate\closeout\wtest + +\centrecode-Nom,Prénom,Classement,Naissance,Licencié,Temps +Demay,Pierre,237,27/8/1986,oui,2h37 +Leblanc,Nicolas,187,4/9/1978,non,2h05 +Valet,Bruno,78,25/11/1989,oui,1h47 +Hachel,André,283,2/3/1972,non,2h42 +Jalabert,Jean,165,19/1/1982,Oui,2h01- + +Si l'on écrit + +\centrecode-\searchitem\rtest{basecourse.txt}{3=283}\coureur- + +alors, §\searchitem doit chercher la ligne où le 3\ieme{} champ vaut 283. Par la suite, la macro \verb|\coureur| doit admettre un argument \emph{numérique} \verb|<i>| et \verb|\coureur{<i>}| doit renvoyer la valeur du champ \no\verb|<i>|. Par exemple, \verb|\coureur{2}| doit renvoyer «André». +\solution +Les modifications à apporter ne sont pas très nombreuses. + +Tout d'abord, il faut lire la première ligne et l'ignorer : elle n'est d'aucune utilité. Ensuite, il faut traiter correctement l'argument \verb|#3| selon qu'il contient le signe \verb|=| ou pas. À l'issue de ce traitement, on a stocké le numéro du champ et sa valeur dans 2 macros pour les utiliser plus tard. + +Dans le code précédent, la boucle \idx\loop était parcourue jusqu'à ce que \tidx{ifeof} soit vrai. Une petite amélioration consiste à permettre une sortie prématurée si la ligne est trouvée. Pour cela, un booléen créé avec \idx\newif sera initialisé à vrai et mis à faux s'il faut sortir de la boucle \idx\loop : + +\begin{itemize} + \item soit lorsque le test \tidx{ifeof} est vrai; + \item soit parce que la ligne a été trouvée. +\end{itemize} + +\showcode/\def\macroname{% se développe en le nom de la macro qui suit sans ¤§*\macroname¤ + % le caractère d'échappement + \ifnum\escapechar>-1 % si le caractère d'échappement est positif¤\idx*\escapechar¤ + \ifnum\escapechar<256 % et inférieur à 256, développer les 2 "\fi" + \expandafter\expandafter\expandafter\expandafter% et le "\string", puis + \expandafter\expandafter\expandafter\gobone% manger le "\" avec \gobone + \fi + \fi + \string% doit être suivi d'une macro¤\idx*\string¤ +} +\catcode`\@11 +\newcount\field@cnt +\newif\ifsearch@repeat¤\idx*\newif¤ +\def\assign@arg#1=#2\@nil{% + \def\sought@fieldnumber{#1}% no du champ à chercher + \def\sought@fielvalue{#2}% et sa valeur +} +\def\searchitem#1#2#3#4{% #1= canal #2=nom fichier #3=champ cherché #4=macro à définir¤§*\searchitem¤ + \let#4\gobone% pour l'instant, #4=\gobone + \openin#1=#2\relax%¤\idx*\openin¤ + \unless\ifeof#1% si le fichier existe¤\tidx*{unless}\tidx*{ifeof}¤ + \edef\macro@name{\macroname#4}% nom de la macro sans "\"¤§*\macroname¤ + \xread#1 to \current@line% lire et ignorer la première ligne¤§*\xread¤ + \ifin{#3}{=}% si #3 contient =¤§*\ifin¤ + {\assign@arg#3\@nil}% trouver le no de champ et sa valeur + {\def\sought@fieldnumber{1}% sinon, no du champ = 1 + \def\sought@fielvalue{#3}% et sa valeur = #3 + }% + % ################ lecture des lignes de données ################ + \search@repeattrue% poursuite de la boucle loop : vraie + \loop% tant que...¤\idx*\loop¤ + \ifeof#1\relax% ...la fin du fichier n'est pas atteinte¤\tidx*{unless}\tidx*{ifeof}¤ + \search@repeatfalse% sortir de la boucle loop + \else + \xread#1 to \current@line% lire une ligne¤§*\xread¤ + \unless\ifx\current@line\empty% si elle n'est pas vide¤\idx*\empty¤ + % examniner les champs qu'elle contient (aller à \test@field) + \expsecond{\expandafter\test@field\current@line\@nil}\macro@name%¤\defline\aaa §*\expsecond¤ + \fi + \fi + \ifsearch@repeat% ne poursuivre que si le booléen en vrai + \repeat¤\idx*\repeat¤ + \fi + \closein#1\relax¤\idx*\closein¤ +} + +\def\test@field#1\@nil#2{% #1=champs #2=nom de la macro sans "\" + \field@cnt=0 % initialiser le compteur de champ + \doforeach\current@field\in{#1}% parcourir les champs de la ligne en cours¤§*\doforeach¤ + {\advance\field@cnt1 % incrémenter le compteur de champ + \ifnum\field@cnt=\sought@fieldnumber\relax% si c'est le bon numéro de champ + \ifx\current@field\sought@fielvalue% et si le champ correspond à celui cherché + \search@repeatfalse% sortir de la boucle loop + \doforeachexit% sortir de la boucle \doforeach en cours¤§*\doforeachexit¤ + \fi + \fi + }% + \unless\ifsearch@repeat% si la ligne a été trouvée + \field@cnt=0 % initialiser le compteur de champ + \doforeach\current@field\in{#1}% parcourir à nouveau les champs de la ligne¤§*\doforeach¤ + {\advance\field@cnt1 % incrémenter le compteur de champ + \letname{#2.\number\field@cnt}=\current@field% faire l'assignation¤§*\letname¤ + }% + \defname{#2}##1{% et définir la macro \<#2>¤§*\defname¤ + \ifcsname#2.##1\endcsname% si la macro \<#2."argument"> existe déjà¤\tidx*{ifcsname}¤ + \csname#2.##1\expandafter\endcsname% l'exécuter après avoir mangé le \fi + \fi + }% + \fi +} +\catcode`\@12 +a) \searchitem\rtest{basecourse.txt}{3=283}\foo + "\foo1", "\foo2", "\foo3", "\foo4", "\foo5", "\foo6", "\foo7" + +b) \searchitem\rtest{basecourse.txt}{Valet}\bar + "\bar1", "\bar2", "\bar3", "\bar4", "\bar5", "\bar6", "\bar{abcd}"/ +\end{exercice} + +\subsection{Afficher le contenu exact d'un fichier} +Nous allons maintenant programmer une macro §\showfilecontent[|(] de syntaxe + +\centrecode-\showfilecontent<canal>{<nom du fichier>}- + +\noindent qui affiche le contenu du fichier passé en argument, en police à chasse fixe. Le but est d'imiter pour les fichiers la primitive \idx\meaning qui donne le texte de remplacement d'une macro. + +Il va falloir agir sur les codes de catégorie avec les macros \idx\do et \idx\dospecials de plain-\TeX{} pour rendre tous les tokens inoffensifs, sauf l'espace qui sera rendu actif avec \idx\obeyspaces. + +Le fichier «\verb|readtext.txt|» utilisé dans l'exemple contient 3 lignes : + +\centrecode-Programmer en \TeX{} est facile + +et utile- + +\immediate\openout\wtest=readtest.txt +\exactwrite\wtest|Programmer en \TeX{} est facile + +et utile|% +\immediate\closeout\wtest + +\showcode|\def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier¤§*\showfilecontent¤ + \begingroup + \tt% sélectionner la fonte à chasse fixe¤\idx*\tt¤ + \openin#1=#2\relax¤\idx*\openin¤ + \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et¤\tidx*{ifeof}¤ + Le fichier n'existe pas% afficher le message + \else% le fichier existe + \def\do##1{\catcode`##1=12 }%¤\idx*\do¤ + \dospecials% neutraliser tous les tokens spéciaux¤\idx*\dospecials¤ + \obeyspaces% rendre l'espace actif¤\idx*\obeyspaces\defline\bbb¤ + \loop¤\idx*\loop¤ + \xread#1 to \currline% lire une ligne¤§*\xread¤ + \unless\ifeof#1% si la fin du fichier n'est pas atteinte¤\tidx*{unless}\tidx*{ifeof}¤ + \leavevmode\par% aller à la ligne¤\idx*\leavevmode\defline\aaa¤ + \currline% afficher la ligne lue + \repeat% recommencer¤\idx*\repeat¤ + \fi + \closein#1\relax¤\idx*\closein¤ + \endgroup +} + +Contenu du fichier : "\showfilecontent\rtest{readtest.txt}", affiché tel quel| + +Il est intéressant de noter qu'un \idx\obeyspaces est écrit à la ligne \no\bbb{} car + +\centrecode-\letactive\ =\space-§*\letactive\idx*\space{} + +\noindent ne fonctionnerait pas puisque que §\letactive, tout comme §\defactive, \emph{nécessite} que le caractère «\verb|~|» soit actif. Or, ce n'est plus le cas ici puisque \idx\dospecials l'a rendu de catcode 12. + +En revanche, placer \verb|\letactive\ =\space|§*\letactive\idx*\space{} avant le \idx\dospecials aurait fonctionné. + +\begin{exercice} +Expliquer pourquoi le guillemet initial ne se situe pas juste avant le mot «\verb|Début|» et proposer une solution pour que cela soit le cas. +\solution +Chaque ligne affichée, contenue dans la macro \verb|\currline|, est précédée de \idx\leavevmode\idx\par (ligne \no\aaa), y compris pour la première ligne lue. De par la structure de la macro §\showfilecontent, un \idx\par est donc toujours inséré entre le guillemet initial et le premier caractère du fichier. + +Une solution originale est de garder «\verb|\leavevmode\par|» sauf que l'on va mettre une macro «magique» §\magicpar en lieu et place de \idx\par. Cette macro aux vertus spéciales doit n'avoir aucun effet pour l'affichage la première fois et exécuter un \idx\par les autres fois. Pour répondre à ces contraintes, §\magicpar doit se modifier elle-même en \idx\par la première fois qu'elle sera exécutée. Voici comment la définir : + +\centrecode-\def\magicpar{\let\magicpar=\par}- + +Avec une telle définition, §\magicpar ne va rien afficher la première fois qu'elle sera exécutée, mais va silencieusement se redéfinir pour devenir égale à \idx\par. Nous avons bien créé une macro locale au groupe semi-simple, qui est neutre pour l'affichage la première fois et équivalente à \idx\par ensuite. + +\showcode|\def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier¤§*\showfilecontent¤ + \begingroup + \tt% sélectionner la fonte à chasse fixe¤\idx*\tt¤ + \openin#1=#2\relax¤\idx*\openin¤ + \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et + Le fichier n'existe pas% afficher le message + \else% le fichier existe + \def\do##1{\catcode`##1=12 }%¤\idx*\do¤ + \dospecials% neutraliser tous les tokens spéciaux¤\idx*\dospecials¤ + \obeyspaces% rendre l'espace actif¤\idx*\obeyspaces¤ + \def\magicpar{\let\magicpar=\par}%¤§*\magicpar¤ + \loop¤\idx*\loop¤ + \xread#1 to \currline% lire une ligne¤§*\xread¤ + \unless\ifeof#1% si la fin du fichier n'est pas atteinte¤\tidx*{unless}\tidx*{ifeof}¤ + \leavevmode\magicpar% former le paragraphe (sauf à la 1er itération)¤\idx*\leavevmode¤ + \currline% afficher la ligne + \repeat% recommencer¤\idx*\repeat¤ + \fi + \closein#1\relax¤\idx*\closein¤ + \endgroup +} + +Contenu du fichier : "\showfilecontent\rtest{readtest.txt}", affiché tel quel| +\end{exercice}§*\showfilecontent[|)]\idx*[|)]{fichier!lecture} + +\section{Écrire dans un fichier}\idx*[|(]{fichier!écriture} +\subsection{La primitive \texttt{\textbackslash write}}\idx*[|(]\write +Il est facile de deviner par symétrie que si \TeX{} dispose de 16 canaux pour lire un fichier, il en dispose aussi de 16 pour écrire dans un fichier. Et le pendant de la macro \idx\newread est évidemment \idx\newwrite, qui en partage la syntaxe et qui alloue globalement un canal d'écriture libre. + +\begin{regle} +\relax\TeX{} dispose de 15 canaux d'écriture portant les numéros de 0 à 15. + +On demande l'allocation d'un canal d'écriture par\idx*\newwrite + +\centrecode-\newwrite\<macro>- + +et ce faisant, la \verb|\<macro>| devient équivalente à un \verb|<nombre>|, qui est un numéro de canal libre. Un \verb|<canal>| est donc un \verb|<nombre>| explicitement écrit ou une macro définie avec \idx\newwrite. +\medbreak + +Avant d'écrire dans un fichier, on doit d'abord lier un \verb|<canal>| à un fichier avec\idx*\openout : + +\centrecode-\openout<canal>= <nom du fichier>- + +\noindent et lorsque les opérations d'écriture sont terminées, on désolidarise le \verb|<canal>| du fichier par \idx*\closeout + +\centrecode-\closeout<canal>= <nom du fichier>- + +\noindent Une ligne vide est insérée en fin de fichier. + +Pour écrire une ligne dans le fichier, on écrit : + +\centrecode-\write<canal>{<texte>}- + +\noindent où le \verb|<texte>| est un code dans lequel les accolades sont équilibrées. L'écriture dans le fichier n'a pas lieu lorsque la commande \idx\write est rencontrée, mais lorsque la page courante est composée. Le \verb|<texte>| est stocké dans la mémoire de \TeX{} et est développé au maximum lorsque \TeX{} écrit dans le fichier. Si l'utilisateur souhaite bloquer le développement, il lui appartient de le bloquer à l'aide des méthodes vues à la page~\pageref{bloquer.developpement.maximum}. +\grandsaut + +Les opérations liées à l'écriture dans un fichier (\idx\openout, \idx\write et \idx\closeout) ont la particularité d'avoir lieu lorsque la page est composée, c'est-à-dire plus tard dans le temps que lorsque les instructions sont rencontrées. Ce décalage temporel est bien utile lorsqu'on souhaite écrire un \idx{numéro de page} puisque l'on s'assure que le bon numéro de page sera écrit, mais il est indésirable pour écrire des données non sensibles au positionnement dans le document. Pour s'affranchir de cette désynchronisation temporelle, il faut faire précéder \idx\openout, \idx\write et \idx\closeout de la primitive « \idx\immediate » qui force les opérations d'écriture à être faites immédiatement lorsqu'elles sont rencontrées. +\end{regle} + +Dans tout le reste du chapitre, nous utiliserons le canal d'écriture \verb|\wtest| que nous allons allouer avec \idx\newwrite\verb|\wtest|. + +Voici deux tentatives successives d'écriture dans un même fichier : + +\showcode|¤\string\newwrite\string\wtest¤% sera le canal d'écriture dans tout ce chapitre¤\idx*\newwrite¤ +\immediate\openout\wtest=writetest.txt % lie \wtest au fichier¤\idx*\immediate\idx*\openout¤ +\immediate\write\wtest{Programmer en \noexpand\TeX{} est facile.}% écrit une ligne¤\idx*\write\idx*\noexpand¤ +\immediate\write\wtest{Et utile...}% puis une autre +\immediate\closeout\wtest% défait la liaison¤\idx*\closeout¤ +a) Contenu du fichier :\par +"\showfilecontent\rtest{writetest.txt}"% affiche le contenu du fichier¤§*\showfilecontent¤ +\medbreak¤\idx*\medbreak¤ +% 2e tentative :¤\defline\aaa¤ +b) Contenu du fichier :\par +\immediate\openout\wtest=writetest.txt % lie \wtest au fichier +\immediate\write\wtest{Essai d'écriture}% écrit une ligne¤\idx*\write¤ +\immediate\closeout\wtest% défait la liaison¤\idx*\immediate¤ +"\showfilecontent\rtest{writetest.txt}"% affiche le contenu du fichier¤§*\showfilecontent¤| + +Remarquons à partir de la ligne \no\aaa, que si on lie un canal à un fichier déjà existant et que l'on écrit à nouveau dans ce fichier, le contenu précédent est effacé, c'est-à-dire que l'écriture recommence au début du fichier. + +Remarquons également que l'argument de \idx\write est lu par \TeX{} avant d'être détokénizé et écrit dans le fichier. La perte d'informations lorsque \TeX{} lit du code source a donc lieu. Ici en particulier, les espaces consécutifs dans «\verb*|est facile|» ont été lus comme un seul espace et écrits comme tel dans le fichier. + +Enfin, il faut noter que \idx\write, lorsqu'il détokénize le \verb|<texte>|, rajoute un espace après les séquences de contrôle. + +\subsection{Programmer des variantes de \texttt{\textbackslash write}} +\subsubsection{La macro \texttt{\char`\\noexpwrite}}§*\noexpwrite +Si la primitive \idx\write développe au maximum ce qu'elle va écrire, il est logique de construire un équivalent qui lui, ne procèdera à aucun développement. Parmi des méthodes à notre disposition pour bloquer le développement, la plus pratique est de recourir à \idx\unexpanded : + +\showcode/\def\noexpwrite#1#2{% #1=numéro de canal #2=texte à écrire¤§*\noexpwrite¤ + \write#1{\unexpanded{#2}}%¤\idx*\write\idx*\unexpanded¤ +} +\immediate\openout\wtest=writetest.txt ¤\idx*\openout¤ +\immediate\noexpwrite\wtest{Programmer en \TeX{} est facile.}%¤\idx*\immediate¤ +\immediate\noexpwrite\wtest{Et utile...}%¤§*\noexpwrite¤ +\immediate\closeout\wtest¤\idx*\closeout¤ +Contenu du fichier :\par +\showfilecontent\rtest{writetest.txt}% affiche le contenu du fichier¤§*\showfilecontent¤/ + +Ici encore, comme l'argument \verb|#2| a été lu par la macro §\noexpwrite, les mêmes pertes d'information que précédemment ont eu lieu. Le seul moyen d'écrire dans le fichier ce qu'il y a \emph{exactement} dans le code source est de procéder comme avec la macro §\litterate (voir page~\pageref{litterate}). + +\subsubsection{La macro \texttt{\char`\\exactwrite}}\label{exactwrite}§*\exactwrite[|(] +Nous allons créer l'équivalent de la macro §\litterate pour l'écriture dans un fichier, c'est-à-dire une macro qui écrit dans un fichier \emph{tous} les caractères qu'on lui donne à lire. Appelons cette macro §\exactwrite et par commodité, sa syntaxe va ressembler à celle de §\litterate. La primitive \idx\immediate sera appelée et donc l'écriture sera synchrone. On suppose qu'un \verb|<canal>| a déjà été lié au fichier dans lequel on veut écrire. La syntaxe sera : + +\centrecode-\exactwrite{<canal><car><texte><car>- + +\noindent Ici, \verb|<car>| sera un caractère choisi par l'utilisateur qui servira de délimiteur au \verb|<texte>| que l'on veut écrire dans le fichier, avec la limitation classique que \verb|<car>| doit être choisi de telle sorte qu'il ne figure pas dans le \verb|<texte>|. + +On va procéder comme avec §\litterate en rendant inoffensifs des tokens en mettant leurs catcodes à 12, sauf qu'ici, cela va concerner \emph{tous} les octets, de 0 à 255. C'est certes un remède de cheval, mais de cette façon, tous les caractères peuvent être écrits sur un fichier et ce quels que soient l'\idx{encodage} du fichier source et le régime de catcode en cours. + +La méthode va être la suivante : + +\begin{algo} + \item lire le \verb|<canal>|; + \item ouvrir un groupe, mettre le catcode de tous les octets à 12; + \item lire le \verb|<car>| (qui sera de catcode 12 à cause du point précédent) ; + \item isoler le \verb|<texte>| qui se trouve jusqu'au prochain \verb|<car>|; + \item si ce \verb|<texte>| contient le retour charriot «\verb|^^M|\verbidx*[ (retour charriot)]{^^M}» (de catcode 12) : + \begin{algo} + \item écrire dans le fichier le texte se trouvant avant \verb|^^M|; + \item retourner en 5) avec en prenant \verb|<texte>| égal à ce qu'il y a près \verb|^^M|; + \end{algo} + \item sinon écrire le \verb|<texte>| dans le fichier et fermer le groupe. +\end{algo} + +Pour forcer les catcodes de tous les octets à 12, une simple boucle §\for suffit : + +\centrecode-\for\xx=0 to 255 \do{\catcode\xx=12 }- + +\noindent Ensuite, comme à partir du point \no3, tout ce qui est lu est constitué de caractères de catcode 12, il va falloir définir un retour charriot de catcode 12\idx*{catcode!12 (autre)} qui nous servira de délimiteur d'argument. On peut s'y prendre ainsi : + +\centrecode-{\catcode`\^^M=12 \gdef\EOL@char{^^M}}-\idx*\gdef + +Dans les grandes lignes, la tâches vont se répartir de la façon suivante. Les points \nos1 et 2 sont dévolus à la macro chapeau §\exactwrite. Une macro auxiliaire \verb|\exactwrite@i| va lire le \verb|<car>|, forcément de catcode 12, qui sera son unique argument. Une fois qu'elle s'est emparé de ce \verb|<car>|, elle peut définir une sous-macro \verb|\exactwrite@ii| dont l'argument délimité par ce \verb|<car>| sera le \verb|<texte>| qu'elle transmettra à la macro récursive \verb|\exactwrite@iii| qui se chargera des points \no5 à 6. + +\showcode/\catcode`\@11 +\def\exactwrite#1{% #1=numéro de canal¤§*\showfilecontent¤ + \begingroup + \def\canal@write{#1}% sauvegarde le numéro de canal + \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12¤§*\for\idx*{catcode!12\space(autre)}¤ + \exactwrite@i% aller lire le <car> +} + +\def\exactwrite@i#1{% #1 est le <car> de catcode 12 + \def\exactwrite@ii##1#1{% ##1 est le <texte> à envoyer vers le fichier + \exactwrite@iii##1\@nil% envoyer <texte> à \exactwrite@iii + }% + \exactwrite@ii% va lire tout ce qui se trouve jusqu'au prochain <car> +} + +{\catcode`\^^M 12 \gdef\EOL@char{^^M}}% définit le caractère <EOL> de catcode 12¤\verbidx*[ (retour charriot)]{^^M}\idx*\gdef¤ + +\def\exactwrite@iii#1\@nil{% #1 = <texte> à écrire dans le fichier + \expsecond{\ifin{#1}}\EOL@char% si #1 contient "retour charriot"¤§*\expsecond§*\ifin¤ + {\write@line#1\@nil% écrire la première ligne de #1 + } + {\immediate\write\canal@write{#1}% sinon : dernière ligne atteinte, l'écrire¤\idx*\immediate\idx*\write¤ + \endgroup% puis sortir du groupe + }% +} + +% les \expandafter provoquent le 1-développement de \EOL@char : +\expandafter\def\expandafter\write@line\expandafter#\expandafter1\EOL@char#2\@nil{% + \immediate\write\canal@write{#1}% écrit la ligne (ce qui se trouve avant ^^M)¤\idx*\immediate\idx*\write¤ + \exactwrite@iii#2\@nil% recommencer avec ce qui se trouve après "^^M" +} +\catcode`\@12 + +\immediate\openout\wtest=writetest.txt % lie le canal \wtest au fichier¤\idx*\immediate\idx*\openout¤ +\exactwrite\wtest|Programmer en \TeX{} est facile ! + +Et très utile.| +\immediate\closeout\wtest% et fermer le fichier + +Le contenu du fichier "writetest.txt" est :\par +"\showfilecontent\rtest{writetest.txt}"¤§*\showfilecontent¤/\idx*[|)]\write + +Plus précisément, voici comment un éditeur de fichiers\footnote{Programme dont la principale fonctionnalité est de lire les octets d'un fichier un par un, de les afficher ainsi que les caractères ASCII correspondants s'ils sont affichables.} voit le fichier généré : + +\begin{centrage} + \ttfamily\footnotesize + \setbox0\hbox{0} + \edef~{\hbox to\the\wd0{\hss\noexpand\small\noexpand\textbullet\hss}} + \catcode`\:12 \catcode`\!12 + \obeyspaces + \catcode`\/0 + \catcode`\{12 \catcode`\}12 + \catcode`\[1 \catcode`\]2 + \catcode`\\12 + /tabular[r|l|l]% + 0000&50 72 6F 67 72 61 6D 6D 65 72 20 65 6E 20 5C 54&Programmer en \T/\% + 0010&65 58 7B 7D 20 65 73 74 20 20 20 66 61 63 69 6C&eX{} est facil/\% + 0020&65 20 21 0A 0A 45 74 20 74 72 E8 73 20 75 74 69&e !~~Et très uti/\% + 0030&6C 65 2E 0A &le.~% + /endtabular% +/end[centrage]% + +L'affichage se divise en trois parties, à gauche l'«offset» (en hexadécimal), c'est-à-dire le numéro d'ordre du premier \idx{octet} de la ligne (le premier octet du fichier portant le numéro 0), au centre les octets contenus dans le fichier en notation hexadécimale et à droite les caractères correspondant aux octets (dans un codage sur un octet, le plus souvent \latin). Les caractères non affichables sont représentés par « {\small\textbullet} ». Constatons en premier lieu que le caractère «è» est codé sur \emph{un seul} \idx{octet} (qui est \Hex{E8}) puisque l'\idx{encodage} utilisé dans le code source de ce livre est \latin\footnote{Si l'encodage avait été \utf, le caractère «è» aurait été codé sur deux octets : \Hex{C3} \Hex{A8}}. Remarquons aussi que les seuls caractères non affichables portent le code de caractère \Hex{0A} qui est 13 en décimal; ce sont donc les retours charriot. Il est normal d'avoir un retour charriot en fin de fichier, car comme nous l'avons vu, \TeX{} insère une ligne vide en fin de fichier.§*\exactwrite[|)] + +\subsection{Utilisation pratique} +Mettons à profit ce que nous savons pour écrire une macro §\exo[|(], utile pour rédiger des exercices et afficher leur barème. Supposons que «\verb|\exo{3.5pt}|» soit exécutée. Elle devra afficher le texte suivant : + +\medbreak +\noindent\vrule height1ex width1ex depth0pt\kern1ex\textbf{Exercice 3}\leaders\hbox to5pt{\hss.\hss}\hfill3.5pt/15 +\medbreak + +\noindent où «15» est le total des points de toutes les macros §\exo du document. + +Remarquons que la macro \verb|\exo| a été appelée 2 fois auparavant puisque cet appel génère le texte de l'exercice \no3. + +La principale difficulté est que le total n'est pas connu lorsque la macro \verb|\exo| est exécutée puisque d'éventuels autres appels de cette macro qui sont à venir dans le code. C'est donc un cas où il va falloir se servir d'un fichier auxiliaire pour transmettre ce total entre deux compilations avec la contrepartie que lors d'une compilation : +\begin{itemize} + \item le fichier n'existe pas, ce qui signifierait qu'il s'agit de la première compilation du document ou que le fichier auxiliaire a été effacé. Dans ce cas, il faudra afficher un total qui représente cette situation particulière, par exemple «\#\#»; + \item le total est faux parce que depuis la dernière compilation, l'auteur du document a modifié le barème d'un ou plusieurs exercices ou a changé le nombre d'exercices. +\end{itemize} + +\noindent Dans ces cas, il faudra lancer \emph{deux} compilations successives pour obtenir le total correct. +\grandsaut + +Avant tout, appelons \verb|\total@points| la macro dont le texte de remplacement sera soit «\verb|##|», soit le total des points. Il reste à prendre une décision; comment pouvons-nous stocker dans un fichier le total des points au fur et à mesure que le document est compilé ? Nous pouvons procéder en 3 temps : + +\begin{enumerate} + \item au début du document, appeler une macro \verb|\initexo| qui va tester si le fichier auxiliaire existe. + \begin{itemize} + \item dans l'affirmative, elle va exécuter le code qu'il contient avec \idx\input. Ce code définira la macro \verb|\total@points| avec le barème de la précédente compilation; + \item sinon, elle va placer dans le texte de remplacement de \verb|\total@points| le code «\verb|\char`\#\char`\#|» qui affiche «\char`\#\char`\#». + \end{itemize} + + \item elle va ouvrir ce fichier en écriture y insérer les caractères «\verb|\exocalctotal|». Puis à chaque fois que la macro \verb|\exo{<barème>}| est rencontrée, ajouter les caractères «\verb|+<barème>|» au fichier; + \item à la fin du document, la macro \verb|\stopexo| écrira «\verb|\relax\endtotal|» dans le fichier et le fermera. +\end{enumerate} + +Admettons par exemple que «\verb|\exo{5pt}|», «\verb|\exo{3.5pt}|» et «\verb|\exo{3pt}|» sont rencontrées dans le document. Alors, à la fin de la compilation (et au début de la suivante), le fichier auxiliaire contiendra : + +\centrecode-\exocalctotal+5pt+3.5pt+3pt\relax\endtotal- + +Il nous reste à dire un mot sur la macro \verb|\exocalctotal|. Elle sera définie pour que son argument soit délimité par \verb|\endtotal| et son texte de remplacement placera dans la macro \verb|\total@points| la somme de tous les points. Pour ce faire, \idx\dimexpr et §\dimtodec seront employées, avec les éventuels inconvénients concernant les erreurs d'arrondis qui peuvent survenir (voir page~\pageref{arrondis.sur.dimensions}) : + +\centrecode-\def\exocalctotal#1\endtotal{\edef\total@points{\dimtodec\dimexpr#1}}- + +Le fichier contenant les points sera nommé comme le fichier maitre (nom qui est le développement de \idx\jobname), mais avec l'extension «\verb|.pts|» + +\catcode`\W12 +\showcode W\catcode`\@11 +\newcount\exo@number% compteur pour le numéro d'exercice¤\idx*\newcount¤ + +\def\exocalctotal#1\endtotal{\edef\total@points{\dimtodec\dimexpr#1}}¤§*\dimtodec¤ + +\def\initexo#1{% + \def\exo@canal{#1}% sauvegarde le canal d'écriture + \exo@number=0 % initialiser le compteur d'exo + \iffileexists\exo@canal{\jobname.pts}% si le fichier .pts existe¤§*\iffileexists\idx*\jobname¤ + {\input \jobname.pts }% le lire et exécuter son contenu¤\idx*\input\idx*\jobname¤ + {\def\total@points{\char`\#\char`\#}}% sinon, définir un total alternatif¤\idx*\char¤ + \immediate\openout\exo@canal=\jobname.pts % ouvrir le fichier .pts¤\idx*\immediate\idx*\jobname\idx*\openout¤ + \immediate\write\exo@canal{\noexpand\exocalctotal}% et commencer à y écrire dedans¤\idx*\noexpand\idx*\write¤ +} + +\def\exo#1{% définti la macro qui affiche l'exercice + \bigbreak% sauter une grande espace verticale + \immediate\write\exo@canal{+#1}% écrire "+#1" dans le fichier .pts¤\idx*\immediate\idx*\write¤ + \advance\exo@number by 1 % incrémenter le numéro de l'exercice¤\idx*\advance¤ + \noindent\vrule height1ex width1ex depth0pt % trace le carré¤\idx*\noindent\idx*\vrule¤ + \kern1ex% insérer une espace horizontale¤\idx*\kern¤ + {\bf Exercice \number\exo@number}% afficher "Exercice <nombre>"¤\idx*\bf\idx*\number¤ + \leaders\hbox to.5em{\hss.\hss}\hfill% afficher les pointillés¤\idx*\leaders\idx*\hbox\idx*\hss¤ + #1/\total@points% puis #1/<total> + \smallbreak% composer la ligne précédente et sauter une espace verticale¤\idx*\smallbreak¤ +} + +\def\stopexo{% + \immediate\write\exo@canal{\relax\noexpand\endtotal}%¤\idx*\immediate\idx*\write\idx*\noexpand¤ + \immediate\closeout\exo@canal¤\idx*\closeout¤ +} +\catcode`\@12 +\initexo\wtest + +\hfill{\bf Interrogation écrite. Sujet : \TeX{}}\hfill\null¤\idx*\hfill\idx*\null¤ +\par +\leavevmode\hfill\vrule height.4pt width 2cm depth0pt\hfill\null¤\idx*\leavevmode\idx*\vrule¤ +\exo{3pt} +Élaborer un test \litterate/\ifonebyte{<texte>}{<vrai>}{<faux>}/ qui teste, pour une¤§*\litterate¤ +compilation avec un moteur 8 bits, si le \litterate/<texte>/ est codé sur un seul +octet. Ce test pourrait être utilisé pour déterminer si l'encodage d'un document +est à plusieurs octets (comme l'est UTF8) en prenant comme \litterate/<texte>/ +les caractères <<~é~>>, <<~à~>>, etc. + +\exo{5,5pt} +Si \verb-#1- est un nombre entier, quel est le test fait par ce code ? +\smallbreak¤\idx*\smallbreak¤ +\hfill +\litterate/\if\string l\expandafter\firstto@nil\romannumeral#1\relax\@nil/ +\hfill\null¤\idx*[|etc]\string\forbidindex\string\idx*\romannumeral\idx*\hfill\idx*\null§*\firstto@nil§*\litterate¤ + +\exo{4,5pt} +On a vu que pour provoquer un $n$-développement, les \litterate/\expandafter/se plaçaient +en nombre égal à $2^n-1$ devant chaque token précédant celui que l'on veut développer. +Or, ce nombre est {\it impair\/}. Trouver un exemple ou un cas particulier où il faut¤\idx*\it\idx*\/¤ +placer un nombre {\it pair\/} d'\litterate/\expandafter/ devant un token (on pourra ¤\idx*\it\idx*\/¤ +envisager le cas de 2 \litterate/\expandafter/).¤§*\litterate¤ +\stopexo W +\catcode`\W11 + +La macro §\dimtodec est ici utilisée puisque le nombre de points n'est pas forcément entier. D'ailleurs, le côté pratique des « points » est que l'argument de \verb|\exo| est considéré comme un barème par l'utilisateur, mais il est vu comme une vraie dimension par \idx\dimexpr ! + +La man\oe uvre effectuée avec \verb|\exo| est finalement très courante. Écrire dans un fichier lors d'une compilation pour lire des éléments dans ce fichier à la compilation suivante est utilisé dans beaucoup de contextes\footnote{Le format \LaTeX{} écrit des informations de localisation de sectionnement et de références croisées dans le fichier «\texttt{.aux}» via le canal \texttt{\string\@auxout}.}. Pour afficher une table des matières en début de document, il faudrait, à l'aide d'une macro spéciale de sectionnement, d'abord stocker dans un fichier les titres assortis de leurs numéros de page, sachant que le \idx{numéro de page} courant est contenu dans le compteur \idx\count\verb|0| pour plain-\TeX{} et \LaTeX{}\catcode`\@11 \footnote{Le nom qui lui est donné diffère selon le format : plain-\TeX{} demande «\idx\countdef{}\idx\pageno{}\texttt{=0} » tandis que \LaTeX{} préfère «\idx\countdef{}\idx\c@page{}\texttt{=0} ».}\catcode`\@12. Dès lors, à la compilation suivante et sous réserve qu'aucune modification du fichier source n'ait été effectuée entre temps, les instructions écrites dans le fichier reflètent les bons numéros de page. + +La construction d'un index repose sur le même stratagème.§*\exo[|)] +\grandsaut + +Pour les lecteurs consciencieux, voici les réponses aux exercices donnés dans cet exemple : + +\begingroup +\small +\parindent0pt +\solution[exercice 1] +La solution apparait immédiatement en utilisant la macro §\ifempty et \verb|\gobone|, qui va enlever un \idx{octet} à \verb|#1| et que nous prenons soin de développer avant de tester : + +\centrecode|\def\ifonebyte#1{\expandafter\ifempty\expandafter{\gobone#1}}| + +\solution[exercice 2] +Le test est positif si la lettre «\verb|l|» est la lettre de gauche du nombre écrit en chiffres romains. Ce cas ne se présente que si l'entier est compris entre 50 et 89 (qui s'écrivent «\texttt{\romannumeral50} » et «\texttt{\romannumeral89} »). Ce test est donc positif pour les entiers allant de 50 à 89 compris et négatif pour tous les autres. + +\solution[exercice 3] +Le code \verb|\expandafter\expandafter<token>| procède au 1-développement de \verb|<token>| puis au 1-développement du deuxième token du code qui résulte de ce développement. + +Voici un cas où 2 \idx\expandafter se justifient : + +\showcode/\def\identite{Foo Bar}% Prénom Nom +\def\beforespace#1 #2\nil{#1} +\def\afterspace#1 #2\nil{#2} +\def\prenom{\beforespace\identite\nil} +\def\nom{\afterspace\identite\nil} +Mon prénom : \expandafter\expandafter\prenom¤\defline\aaa¤ + +Mon nom : \expandafter\expandafter\nom/ + +À la ligne \no\aaa, l'\idx\expandafter de gauche effectue ce développement : +\medbreak +\hfill\verb|\prenom|\quad$\longrightarrow$\quad\verb|\beforespace\identite\nil|\hfill\null +\medbreak +\noindent et celui de droite 1-développe \verb|\itentite| en «\verb|Foo Bar|» de telle sorte que le code finalement obtenu est : + +\centrecode-\beforespace Foo Bar\nil- +\endgroup\idx*[|)]{fichier!écriture} + +\section{\texttt{\textbackslash newlinechar} et \texttt{\textbackslash endlinechar}} +L'entier \idx\newlinechar représente le code du caractère qui ordonne à \TeX{} de commencer une nouvelle ligne lors d'une opération avec \idx\write. Dès lors, il devient possible d'écrire en une opération plusieurs lignes dans un fichier. Plain-\TeX{} assigne $-1$ à cet entier ce qui signifie qu'aucun caractère ne revêt cette propriété. \LaTeX, en revanche, y range l'entier \number`\^^J{} qui est le code de caractère de \verb|LF| (le caractère \verbidx*[ (fin de ligne)]{^^J}\verb|^^J|). + +\showcode|\newlinechar`\^^J ¤\idx*\newlinechar\verbidx*[ (fin de ligne)]{^^J}¤ +\immediate\openout\wtest=test1.txt ¤\idx*\immediate\idx*\openout¤ +\immediate\write\wtest{Une première ligne^^JEt une seconde.}¤\idx*\write¤ +\immediate\closeout\wtest ¤\idx*\closeout¤ +\showfilecontent\rtest{test1.txt}¤§*\showfilecontent¤| + +\Qu ant à l'entier \idx\endlinechar, il s'applique également lors des opérations de lecture, c'est-à-dire que le caractère dont le code est \idx\endlinechar sera inséré à la fin de chaque ligne lue, qu'elle se trouve dans le fichier à lire ou dans le code source. Si ce caractère est modifié localement afin d'obtenir des effets spéciaux lors de la lecture d'un fichier, il faut penser à commenter les fins de ligne de la partie du code source concernée pour que ces effets ne s'y appliquent pas aussi. + +\showcode|\newlinechar`\^^J ¤\idx*\newlinechar\verbidx*[ (fin de ligne)]{^^J}¤ +\immediate\openout\wtest=test2.txt ¤\idx*\immediate\idx*\openout¤ +\immediate\write\wtest{Une première ligne^^JEt une seconde.}¤\idx*\write¤ +\immediate\write\wtest{Et la dernière.}¤\idx*\write¤ +\immediate\closeout\wtest ¤\idx*\closeout¤ + +{\endlinechar`\X % insère "X" à chaque fin de ligne¤\idx*\endlinechar¤ +\openin\rtest=test2.txt % les fins de lignes sont commentées¤\idx*\openin¤ +\loop% pour éviter que \endlinechar ¤\idx*\loop¤ + \read\rtest to \foo% ne soit inséré à chaque fin de¤\idx*\read¤ + \unless\ifeof\rtest% ligne du code source¤\tidx*{unless}\tidx*{ifeof}¤ + \meaning\foo\par% affiche le texte de remplacement de \foo¤\idx*\meaning¤ +\repeat%¤\idx*\repeat¤ +\closein\rtest}%| + +\begin{exercice} +Utiliser \idx\newlinechar pour simplifier la macro §\exactwrite vue à la page~\pageref{exactwrite}. +\solution +Rappelons-nous que le \verb|<texte>|, susceptible de contenir des retours à la ligne, était lu de telle sorte que tous les octets aient un catcode de 12. Au lieu d'écrire une macro récursive qui lit une seule ligne à chaque itération pour l'écrire dans le fichier, il est bien plus élégant et rapide d'assigner à \idx\newlinechar l'entier \verb|`\^^M|\verbidx*[ (retour charriot)]{^^M}. Une fois ceci fait, il ne reste plus qu'à écrire en une seule fois le \verb|<texte>| dans le fichier : + +\showcode/\catcode`\@11 +\def\exactwrite#1{% #1=numéro de canal¤§*\exactwrite¤ + \begingroup + \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12¤§*\for\idx*{catcode!12\space(autre)}¤ + \newlinechar=`\^^M % caractère de fin de ligne = retour charriot¤\idx\newlinechar\idx*{caractère de fin de ligne}¤ + \exactwrite@i{#1}% donne le <canal> d'écriture comme premier argument +} + +\def\exactwrite@i#1#2{% #2 est le caractère délimiteur de catcode 12 + \def\exactwrite@ii##1#2{% ##1 est le <texte> à envoyer vers le fichier + \immediate\write#1{##1}% écrire le <texte> dans le fichier + \endgroup% puis, sortir du groupe + }% + \exactwrite@ii% traiter tout ce qui se trouve jusqu'au prochain #2 +} +\catcode`\@12 + +\immediate\openout\wtest=writetest.txt % lie le canal \wtest au fichier¤\idx*\openout¤ +\exactwrite\wtest|Programmer en \TeX{} est facile !¤§*\exactwrite¤ + +Et très utile.| +\immediate\closeout\wtest% et fermer le fichier + +Le contenu du fichier "writetest.txt" est :\par +"\showfilecontent\rtest{writetest.txt}"¤§*\showfilecontent¤/ +\end{exercice} + +\section{Autres canaux d'entrée-sortie} +En plus des 16 canaux de lectures ou d'écriture, \TeX{} dispose de canaux spéciaux. En ce qui concerne la lecture, si le \verb|<canal>| qui suit \idx\read n'est pas compris entre 0 et 15 ou si aucun fichier n'a pu être ouvert (notamment parce que le fichier n'existe pas) ou encore si la fin du fichier a été atteinte (et donc le test \tidx{ifeof} est devenu vrai) alors, la lecture de la ligne courante se fait à partir du terminal : \TeX{} attend que l'utilisateur tape un texte au clavier et finisse par la touche « entrée » (\bioLegacyKeyGlyph{E_n_t_e_r})\footnote{Ce moyen de collecter interactivement des informations n'est possible que lorsque l'exécutable pfd\TeX{} est appelé \emph{sans} l'option «\texttt{-interaction=nonstopmode}» qui l'interdit !}. Un message d'invite est affiché sur le terminal sauf lorsque le \verb|<canal>| est strictement négatif. Si on écrit + +\centrecode|\read16 to \foo| + +\noindent alors, l'invite est «\verb|\foo=|» + +\grandsaut +Du côté de l'écriture, la primitive \idx\message\verb|{<texte>}| a pour action d'écrire le \verb|<texte>| sur le terminal et dans le \idx[!log]{fichier} \verb|log| après avoir développé au maximum ce \verb|<texte>|. Il convient de s'assurer que ce \verb|<texte>| est purement développable et dans le cas contraire, bloquer le développement de certaines de ces parties avec \idx\noexpand ou \idx\unexpanded. + +Si on utilise \idx\write\verb|<canal>|, si le \verb|<canal>| n'est pas compris entre 0 et 15 ou si le flux spécifié par le \verb|<canal>| n'a pas été lié à un fichier par \idx\openout, l'écriture est dirigée vers le \idx[!log]{fichier} \verb|log| et vers le terminal, et uniquement vers le \idx[!log]{fichier} \verb|log| si le \verb|<canal>| est strictement négatif. + +Il est donc assez facile de converser avec l'utilisateur : + +\errcode|\newlinechar`\^^J ¤\idx*\newlinechar\verbidx*[ (fin de ligne)]{^^J}¤ +\message{^^JQuel est votre nom ? }¤\idx*\message¤ +\xread-1 to \username¤§*\xread¤ +\message{Bonjour \username.^^J% +Depuis combien d'années pratiquez-vous TeX ? } +\read-1 to \yeartex ¤\idx*\read¤ +\message{%¤\idx*\message¤ + \ifnum\yeartex<5 Cher \username, vous êtes encore un débutant ! + \else\ifnum\yeartex<10 Bravo \username, vous êtes un TeXpert ! + \else\ifnum\yeartex<15 Félicitations \username, vous êtes un TeXgourou. + \else Passez à autre chose, par exemple à Metafont et Metapost ! + \fi\fi\fi^^J}|{Quel est votre nom ? Christian\par +Bonjour Christian.\par +Depuis combien d'années pratiquez-vous TeX ? 8\par +Bravo Christian, vous êtes un TeXpert !} +\grandsaut + +Voici pour finir comment \TeX{} pourrait, par dialogue avec l'utilisateur et en procédant par dichotomie, trouver un nombre entre 1 et 100 choisi au hasard par l'utilisateur : + +\errcode|\catcode`\@11 +\def\answer@plus{+}\def\answer@minus{-}\def\answer@equal{=}% +\def\nb@found#1{% macro appelée lorsque le nombre (argument #1) est trouvé + \message{^^JVotre nombre est #1.^^JMerci d'avoir joué avec moi.^^J}%¤\idx*\message¤ +} + +\def\nbguess#1#2{% + \message{Choisissez un nombre entre #1 et #2.^^J¤\idx*\message¤ + Tapez entrée lorsque c'est fait.}% + \read-1 to \tex@guess% attend que l'on tape sur "entrée"¤\idx*\read¤ + \nbguess@i{#1}{#2}% +} + +\def\nbguess@i#1#2{% + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si #1<#2 (donc le nombre n'est pas trouvé), + % mettre dans \tex@guess la troncature de la moyenne de #1 et #2 + {\edef\tex@guess{\number\truncdiv{\numexpr#1+#2\relax}2}%¤§*\truncdiv¤ + \message{Je propose \tex@guess.^^J% afficher sur le terminal¤\idx*\message¤ + Votre nombre est-il plus grand (+), plus petit (-) ou égal (=) : }% + \read -1 to \user@answer% lire la réponse de l'utilisateur¤\idx*\read¤ + \edef\user@answer{% + \expandafter\firstto@nil\user@answer\relax\@nil% ne garder que le 1er caractère¤§*\firstto@nil¤ + }% + \ifxcase\user@answer% envisager les cas "+", "-" et "="¤§*\ifxcase¤ + \answer@plus{\exparg\nbguess@i{\number\numexpr\tex@guess+1\relax}{#2}}%¤§*\exparg¤ + \answer@minus{\expsecond{\nbguess@i{#1}}{\number\numexpr\tex@guess-1\relax}}%¤§*\expsecond¤ + \answer@equal{\nb@found\tex@guess}% + \elseif% si la réponse ne commence pas par "+", "-" ou "="¤§*\elseif¤ + \message{Je n'ai pas compris votre réponse}% afficher message erreur¤\idx*\message¤ + \nbguess@i{#1}{#2}% et recommencer avec les mêmes nombres + \endif¤§*\endif¤ + } + % si #1>=#2, le nombre est trouvé + {\nb@found{#1}}% +} +\catcode`\@12 + +\nbguess{1}{100}|{Choisissez un nombre entre 1 et 100.\par +Tapez entrée lorsque c'est fait.\par +\leavevmode\par +Je propose 50.\par +Votre nombre est plus grand (+), plus petit (-) ou égal (=) : -\par +\leavevmode\par +Je propose 25.\par +Votre nombre est plus grand (+), plus petit (-) ou égal (=) : +\par +\leavevmode\par +Je propose 37.\par +Votre nombre est plus grand (+), plus petit (-) ou égal (=) : -\par +\leavevmode\par +Je propose 31.\par +Votre nombre est plus grand (+), plus petit (-) ou égal (=) : -\par +\leavevmode\par +Je propose 28.\par +Votre nombre est plus grand (+), plus petit (-) ou égal (=) : +\par +\leavevmode\par +Je propose 29.\par +Votre nombre est plus grand (+), plus petit (-) ou égal (=) : +\par +\leavevmode\par +Votre nombre est 30.\par +Merci d'avoir joué avec moi.}\idx*[|)]{fichier} + +\chapter{Autres algorithmes} +Dans ce chapitre qui sera dépourvu d'exercice, nous allons nous attacher à écrire quelques algorithmes --~des classiques de la programmation~-- en \TeX{}. + +\section{Macros de calcul} +\subsection{La suite de Syracuse} +Pour générer une suite de \idx[ (suite de)|(]{Syracuse}, le procédé consiste à choisir un nombre entier strictement positif. S'il est pair, on le divise par 2 ; s'il est impair, on le multiplie par 3 et on ajoute 1. En répétant l'opération, on génère une suite d'entiers appelée « suite de Syracuse ». Si à partir d'un certain rang, l'entier 1 est atteint, la suite de valeurs 1; 4; 2 se répète infiniment et ces trois valeurs constituent ce que l'on appelle le « cycle trivial ». La conjecture de Syracuse, non démontrée, affirme que, quel que soit l'entier choisi au départ, on arrive au bout d'un nombre fini d'itérations à la valeur 1 et donc au cycle trivial. + +Nous allons programmer une macro §\syracuse\verb|{<nombre>}|, purement développable qui va afficher toutes les valeurs générées selon le procédé décrit ci-dessus, et s'arrêter lorsque la valeur 1 est atteinte. + +Nous aurons recours au test + +\centrecode-\ifodd<nombre><code vrai>\else<code faux>\fi-\tidx*{ifodd} + +qui est vrai si le \verb|<nombre>| est impair. + +\showcode|\def\syracuse#1{%¤§*\syracuse¤ + #1% affiche le nombre + \ifnum#1>1 % si le nombre est >1 + , % afficher une virgule+espace + \ifodd#1 % s'il est pair¤\tidx*{ifodd}¤ + \exparg\syracuse% appeler la macro \syracuse¤§*\syracuse¤ + {\number\numexpr3*#1+1% avec 3*n+1¤\idx*\number\idx*\numexpr¤ + \expandafter\expandafter\expandafter}% après avoir rendu la macro terminale¤\idx*{récursivité!terminale}¤ + \else % s'il est pair + \expandafter\syracuse\expandafter% appeler la macro \syracuse¤§*\syracuse¤ + {\number\numexpr#1/2% avec n/2 + \expandafter\expandafter\expandafter}% après avoir rendu la macro terminale + \fi + \else% si le nombre est 1 + .% afficher un point + \fi +} +a) \syracuse{20}\par +b) \syracuse{14}\par +c) \syracuse{99}\par +d) \edef\foo{\syracuse{15}}\meaning\foo¤§*\syracuse¤| + +Les \idx\expandafter qui se trouvent à la fin de l'argument de §\syracuse lors de l'appel récursif sont développés par \idx\numexpr et rendent la récursivité terminale\idx*{récursivité!terminale}. Il est cependant possible d'écrire un code comportant moins d'\idx\expandafter en mettant le test \tidx{ifodd} à l'intérieur du nombre évalué par \idx\numexpr : + +\showcode|\def\syracuse#1{%¤§*\syracuse¤ + #1% afficher le nombre + \ifnum#1>1 % si le nombre est >1 + , % afficher une virgule+espace + \exparg\syracuse{% appeler la macro \syracuse¤§*\syracuse¤ + \number\numexpr% avec le nombre :¤\idx*\number\idx*\numexpr¤ + \ifodd#1 3*#1+1% 3n+1 si #1 est impair¤\tidx*{ifodd}¤ + \else #1/2% n/2 sinon + \fi% + \expandafter}% avant de rendre la récursivité terminale¤\idx*{récursivité!terminale}¤ + \else + .% si #1=1, afficher un point + \fi +} +a) \syracuse{20}\par +b) \syracuse{14}\par +c) \syracuse{99}\par +d) \edef\foo{\syracuse{15}}\meaning\foo¤§*\syracuse¤|\idx*[ (suite de)|)]{Syracuse} + +\subsection{Calculer une factorielle}\idx*[|(]{factorielle (calcul)} +La factorielle d'un nombre entier positif $n$, notée $n!$, est égale au produit des $n$ premiers entiers strictement positifs. Par convention $0!=1$. + +L'algorithme récursif classique est le suivant où l'on considère que l'argument de la macro §\factorielle[|(] est positif ou nul : +\begin{algo} + \item si \verb|#1|${}={}0$, \verb|\factorielle{0}|${}={}1$; + \item sinon, \verb|\factorielle{#1}|${}={}$\verb|#1|${}\times{}$\verb|\factorielle{#1-1}|. +\end{algo} +L'algorithme est d'une simplicité biblique et sa traduction en \TeX{} est immédiate : + +\showcode/\def\factorielle#1{%¤§*\factorielle¤ + \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {1}% "1" si #1=0} + {#1*\exparg\factorielle{\number\numexpr#1-1}}% ¤\idx*\number\idx*\numexpr¤ +} +a) \factorielle{0}\qquad +b) \factorielle{3}\qquad +c) \edef\foo{\factorielle{8}}\meaning\foo/ + +La première chose qui saute aux yeux est que l'expression est bien générée, mais qu'elle n'est pas évaluée par un \idx\numexpr. + +Pour y remédier, l'idée va être de partir d'une macro chapeau qui se chargera de lancer \idx\number{}\idx\numexpr afin de provoquer, par développement maximal, la génération de l'enchainement des multiplications obtenu précédemment et son calcul. Cette macro chapeau ajoutera également le \idx\relax final pour stopper le calcul effectué par \idx\numexpr. + +\showcode/\catcode`\@11 +\def\factorielle#1{%¤§*\factorielle¤ + \number\numexpr\factorielle@i{#1}\relax% appelle \factorielle@i¤\idx*\number\idx*\numexpr¤ + % en lançant le développement maximal +} +\def\factorielle@i#1{% + \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {1}% "1" si #1= + {#1*\exparg\factorielle@i{\number\numexpr#1-1}}% ¤\idx*\number\idx*\numexpr¤ +} +\catcode`\@12 +a) \factorielle{0}\qquad +b) \factorielle{3}\qquad +c) \edef\foo{\factorielle{8}}\meaning\foo/§*\factorielle[|)]\idx*[|)]{factorielle (calcul)} + +\subsection{Calculer un PGCD}\idx*[|(]{PGCD} +Le célèbre algorithme d'\textsc{Euclide}\idx*{algorithme d'Euclide} permet de calculer le plus grand diviseur commun à deux entiers, voici comment procéder : +\begin{algo} + \item soit $a$ le plus grand nombre et $b$ le plus petit + \item effectuer la division euclidienne $a\div b$ et appeler $r$ le reste; + \item si $r=0$, le PGCD est le diviseur précédent $b$; + \item sinon, retourner en 2 en prenant $a\longleftarrow b$ et $b\longleftarrow r$ +\end{algo} + +Cet algorithme est des plus faciles à traduire en \TeX{}. La macro chapeau §\PGCD[|(], purement développable, se chargera du point \no1, une macro auxiliaire du point \no2 et une autre macro auxiliaire des deux derniers points. La récursivité se jouera sur les deux macros auxiliaires : + +\showcode|\catcode`\@11 +\def\PGCD#1#2{%¤§*\PGCD¤ + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\PGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument + {\PGCD@i{#1}{#2}}% +} + +\def\PGCD@i#1#2{% #1=a #2=b avec a>b + \exptwoargs\PGCD@ii% appeler la macro récursive avec¤§*\exptwoargs¤ + {\number\numexpr#1-#2*\truncdiv{#1}{#2}}% le reste de a/b¤§*\truncdiv¤ + {#2}% et le divisieur b +} + +\def\PGCD@ii#1#2{% #1=reste r #2=diviseur b + \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤\idx*\z@¤ + {#2}% si le reste est nul, renvoyer b + {\PGCD@i{#2}{#1}}% sinon, recommencer avec b et r +} +\catcode`\@12 +a) \PGCD{120}{75}\qquad +b) \PGCD{64}{180}\qquad +c) \PGCD{145}{64}\qquad +d) \edef\foo{\PGCD{1612}{299}}\meaning\foo| + +Il peut être utile d'écrire les divisions successives qui permettent d'arriver au résultat. Par exemple, si l'on écrit \verb|\calcPGCD{39}{15}|, on aurait les étapes suivantes : + +\begingroup\parskip=0pt +$39=15\times2+9$\par +$15=9\times1+6$\par +$9=6\times1+3$\par +$6=3\times2+0$\par\endgroup + +La macro §\calcPGCD se chargera d'afficher les étapes ci-dessus. Comme il s'agit d'une macro qui produit un résultat typographique, il n'y a pas besoin de la rendre purement développable. Peu de choses changent; un \verb|\edef| stocke le quotient pour éviter de le calculer deux fois et pour chaque ligne de calcul, les nombres sont affichés lorsqu'ils sont disponibles. Le mode math commence donc dans la première macro auxiliaire et se termine dans la seconde, une fois que le reste a été calculé. + +\showcode|\catcode`\@11 +\def\calcPGCD#1#2{%¤§*\calcPGCD¤ + \ifhmode\par\fi% si en mode horizontal, former le paragraphe¤\tidx*{ifhmode}¤ + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\calcPGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument + {\calcPGCD@i{#1}{#2}}% +} + +\def\calcPGCD@i#1#2{% #1=a #2=b avec a>b + \edef\calcPGCD@quotient{\number\truncdiv{#1}{#2}}% stocke le quotient + $#1=\calcPGCD@quotient\times#2% en mode maths, afficher "a=q*b" (à suivre) + \exptwoargs\calcPGCD@ii% appeler la macro récursive avec¤§*\exptwoargs¤ + {\number\numexpr#1-#2*\calcPGCD@quotient}% le reste de a/b¤§*\truncdiv¤ + {#2}% et le divisieur b +} + +\def\calcPGCD@ii#1#2{% #1=reste r #2=diviseur b + +#1$\par% (suite du mode math) afficher "+r", fermer le mode math et \par + \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi%¤\idx*\z@¤ + {}% si le reste est nul, ne rien faire + {\calcPGCD@i{#2}{#1}}% sinon, recommencer avec b et r +} +\catcode`\@12 +\calcPGCD{39}{15}\medbreak¤\idx*\medbreak¤ +\calcPGCD{1612}{299}| + +Pour améliorer la lisibilité de ces lignes de calcul, un raffinement typographique supplémentaire pourrait être d'aligner sur une même verticale les signes «$=$», «$\times$» et «$+$». Comme ces signes sont toujours les mêmes, le meilleur moyen d'y parvenir va être de bâtir un alignement avec \idx[|(]\halign où ces signes feront partie prenante du préambule de façon à ne pas être écrits à chaque ligne. + +Examinons donc tout d'abord le préambule. Il y aura 4 colonnes, chacune étant réservée à un nombre comme dans + +\[39=15\times2+9\] + +Décidons que la première colonne est composée au fer à droite (le nombre sera au plus près du signe «$=$»), elle sera déclarée \idx*\hfil\verb|$\hfil#$| dans le préambule. + +Pour la seconde colonne, nous allons placer le signe «$=$» avant le contenu de la colonne. Il est important de comprendre qu'en mode mathématique\idx*{mode!mathématique}, le signe «$=$» doit être précédé de quelque chose pour qu'une espace mathématique soit insérée avant lui\footnote{Tous les composants d'une formule mathématique sont appelés \emph{atomes}. + +Pour de plus amples informations sur le type d'atomes mathématiques composant une formule (le signe $=$ étant de type «\textit{Rel}») et les espaces qu'ils génèrent, voir le \TeX book pages 184-185, 200 ainsi que l'annexe~G.} : + +\showcode/\frboxsep=0pt % encadrer au plus proche¤§*\frbox¤ +\leavevmode\frbox{$=$} n'est pas identique à \frbox{${}={}$}¤\idx*\leavevmode§*\frbox¤/ + +En effet, dans le premier cas, ce qui se trouve à gauche de l'atome de relation «$=$» est vide, il n'y a donc aucun espace mathématique qui est ajouté. En revanche, dans la seconde boite, ce qui se trouve à gauche de «$=$» est «\verb|}|» qui est un atome « \textit{Close} » et, en vertu des règles exposées dans le \TeX book, l'espace mathématique inséré à gauche de «$=$» devient le même qu'avec un atome « \textit{Ord} » comme l'est un chiffre ou une lettre. + +La conséquence de tout cela est que la deuxième colonne aura comme préambule + +\centrecode-${}=\hfil#$- + +\noindent où le \idx\hfil nous assure que le nombre, représenté par \verb|#|, sera composé au fer à droite (il sera donc au plus près du signe «$\times$» qui suit). + +Puisque les atomes «$\times$» et «$+$» étant de type \textit{Rel}, le même raisonnement doit être suivi pour la 3\ieme{} et 4\ieme{} colonnes qui auront comme préambules + +\centrecode-${}\times#\hfil$-\idx*\times + +\noindent et + +\centrecode-${}+#\hfil$- + +Un deuxième problème est qu'une cellule d'un alignement est équivalente à un groupe. Par conséquent, pour pouvoir se transmettre dans l'alignement, l'assignation du quotient doit être globale. Enfin, le \tidx{ifnum} et le \verb|\fi| doivent se trouver dans la même cellule. + +\showcode|\catcode`\@11 +\def\calcPGCD#1#2{%¤§*\calcPGCD¤ + \vtop{% mettre l'alignement dans une \vtop + \halign{% les "#" doivent être doublés puisqu'à l'intérieur d'une macro¤\idx*\halign¤ + $\hfil##$&${}=\hfil##$&${}\times##\hfil$&${}+##\hfil$% préambule¤\idx*\hfil\idx*\times¤ + \cr% fin du préambule et début de la première cellule¤\idx*\cr¤ + \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\calcPGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument + {\calcPGCD@i{#1}{#2}}% + \crcr% fin de l'alignement¤\idx*\crcr¤ + }% + }% +} + +\def\calcPGCD@i#1#2{% #1=a #2=b avec a>b + \xdef\calcPGCD@quotient{\number\truncdiv{#1}{#2}}% stocke le quotient¤\idx*\xdef¤ + #1 & \calcPGCD@quotient & #2 &% afficher "a=q*b" (à suivre) + \exptwoargs\calcPGCD@ii% appeler la macro récursive avec¤§*\exptwoargs¤ + {\number\numexpr#1-#2*\calcPGCD@quotient}% le reste de a/b¤§*\truncdiv¤ + {#2}% et le divisieur b +} + +\def\calcPGCD@ii#1#2{% #1=reste r #2=diviseur b + #1% (suite de l'alignement) afficher "+r" + \cr% et terminer la ligne en cours¤\idx*\cr¤ + \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi%¤\idx*\z@¤ + {}% si le reste est nul, ne rien faire + {\calcPGCD@i{#2}{#1}}% sinon, recommencer avec b et r +} +\catcode`\@12 +a) \calcPGCD{39}{15}\medbreak¤\idx*\medbreak¤ +b) \calcPGCD{1612}{299}|\idx*[|)]\halign§*\PGCD[|)]\idx*[|)]{PGCD} + +\subsection{Convertir un nombre en base 2}\idx*[|(]{base (conversion)} +Pour convertir un entier positif $n$ quelconque écrit en base 10 en base $b$, voici l'algorithme à utiliser : + +\begin{algo} + \item effectuer la division de $n$ par $b$. Appeler $q$ est le quotient et $r$ le reste; + \item si $q\neq 0$, retourner en 1 avec $n\longleftarrow q$; + \item sinon, le nombre cherché est constitué des restes, lus du \emph{dernier au premier}. +\end{algo} + +Voici les divisions euclidiennes qu'il faut poser pour convertir 43 en base 2 : +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\makeatletter +\newbox\nb@box +\newcount\nb@a +\newcount\nb@b +\newcount\iter@ +\newcommand\division[2][2]{\def\dividende@{#2}\def\base@{#1}\iter@\@ne\division@{#2}{#1}} +\newcommand\division@[2]{% + \setbox\nb@box\hbox{\kern0.5em#1\kern0.5em}% + \nb@a#1 \nb@b#1 \divide\nb@b#2 + \vtop{% + \begingroup + \multiply\nb@b#2 \advance\nb@a-\nb@b + \hbox to\wd\nb@box{\hfil#1\hfil}% + \vskip3pt\hrule height0pt width\wd\nb@box\vskip3.4pt + \hbox to\wd\nb@box{\hfil\textbf{\number\nb@a}\kern0.5em}% + \expandafter\xdef\csname reste@\number\iter@\endcsname{\number\nb@a}% + \endgroup}% + \setbox\nb@box\hbox{8}\vrule height\ht\nb@box depth3.5ex + \setbox\nb@box\hbox{\kern0.5em\ifnum#2>\nb@b #2\else\number\nb@b\fi\kern0.5em}% + \vtop{% + \hbox to\wd\nb@box{\kern0.5em#2\hfil}% + \vskip3pt\hrule height0.4pt width\wd\nb@box\vskip3pt + \hbox{% + \csname @\ifnum\nb@b>\z@ first\else second\fi oftwo\endcsname + {\advance\iter@\@ne\gdef\maxiter{\number\iter@}%\idx*\gdef + \expandafter\division@\expandafter{\number\nb@b}{#2}}% + {\kern0.5em\number\nb@b\xdef\maxiter{\number\iter@}}}% + }% +} +\newcommand\afficheresultat[1]{% + \csname reste@#1\endcsname + \ifnum#1>\@ne + \expandafter\afficheresultat + \else + \expandafter\@gobble + \fi{\number\numexpr#1-1}% +} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\par\hfill\division{43}\hfill\null +\medbreak + +Et donc, en lisant les restes dans l'ordre inverse, le nombre \dividende@{} s'écrit \afficheresultat\maxiter{} en base 2. +\makeatother + +Appelons §\baseconv[|(]\verb|{<n>}| la macro qui va convertir en base 2 le nombre \verb|<n>| écrit en base 10. Donnons-nous une difficulté supplémentaire de taille : le code devra être purement développable. Ainsi, écrire \verb|\edef\foo{\baseconv{43}}| met dans le texte de remplacement de \verb|\foo| les caractères \texttt{\afficheresultat\maxiter}. +\grandsaut + +Dans un premier temps, nous allons écrire la macro \verb|\baseconv| de façon à ce qu'elle écrive les restes dans l'ordre où ils sont trouvés. Afin que la macro soit purement développable, la macro §\truncdiv sera mise à contribution. + +\showcode|\catcode`\@11 +\def\baseconv#1{%¤§*\baseconv¤ + \unless\ifnum#1=\z@ % si #1 est différent de 0¤\tidx*{unless}\idx*\z@¤ + \number\numexpr#1-2*\truncdiv{#1}2\relax% écrire le reste¤\idx*\number\idx*\numexpr §*\truncdiv\defline\aaa¤ + \exparg\baseconv{\number\truncdiv{#1}2\expandafter}% recommencer avec #1/2¤§*\truncdiv¤ + \fi% après que le \fi ait été lu +} +\catcode`\@12 +a) \baseconv{43}\qquad +b) \baseconv{32}\qquad +c) \edef\foo{\baseconv{159}}\meaning\foo¤\idx*\meaning§*\baseconv¤| + +Cela fonctionne, les restes sont bien écrits au fur et à mesure de leur calcul à la ligne \no\aaa. Certes, la macro pourrait être optimisée puisque §\truncdiv\verb|{#1}2| est calculé deux fois, mais laissons cela pour l'instant. La question qui se pose est «comment s'y prendre pour inverser l'ordre des restes ?» + +L'idée est de copier la méthode des « réservoirs» mise en \oe uvre dans la macro §\reverse programmée à la page~\pageref{reverse}. Pour ce faire, la macro §\baseconv ne va devenir qu'une macro chapeau qui va transmettre à la vraie macro récursive \verb|\baseconv@i| deux arguments non délimités : le premier, vide au début, contiendra la liste des restes écrits dans l'ordre désiré. Le second sera le nombre \verb|<n>|. Le fonctionnement de cette macro sera des plus simples. + +Si \verb|<n>| est nul, l'affichage de l'argument \verb|#1| sera fait. Sinon, la macro \verb|\baseconv@i| + +\begin{itemize} + \item ajoutera \emph{avant} les autres restes le chiffre + + \centrecode/\number\numexpr#2-2*\truncdiv{#2}2\relax/ + + \noindent Notons que la primitive \idx\number ne s'applique ici qu'à \idx\numexpr\verb|...|\idx\relax qui a le statut de registre d'entier. Il n'y a donc aucun risque pour \idx\number de lire un entier supérieur à $2^{31}-1$ si les autres restes qui sont à la suite sont en grand nombre ; + \item remplacera l'argument \verb|#2| par \verb|\number\truncdiv{#2}2|; + \item une fois ceci fait, elle s'appellera elle-même. +\end{itemize} + +\showcode|\catcode`\@11 +\def\baseconv#1{%¤§*\baseconv¤ + \baseconv@i{}{#1}% +} + +\def\baseconv@i#1#2{% #1=restes #2=n + \ifnum#2=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% si n=0 + {#1}% si <n>=0, afficher tous les restes + {% sinon, recommencer en + \exptwoargs\baseconv@i% ajoutant le reste courant avant #1¤§*\exptwoargs¤ + {\number\numexpr#2-2*\truncdiv{#2}2\relax #1} + {\number\truncdiv{#2}2}% et en prenant n:=n/2¤§*\truncdiv¤ + }% +} + +\catcode`\@12 +a) \baseconv{43}\qquad +b) \baseconv{32}\qquad +c) \edef\foo{\baseconv{159}}\meaning\foo| + +\subsection{Aller vers des bases plus élevées} +Pourquoi ne pas aller plus loin et convertir un nombre en une base arbitraire ? La limitation réside dans le fait que les symboles utilisés pour les chiffres ne sont pas extensibles à l'infini. Nous avons les 10 chiffres et les 26 lettres (que nous prendrons majuscules) ce qui nous fait 36 signes différents, que nous appellerons « \verb|<chiffre>| ». Nous allons donc d'emblée nous limiter à une conversion vers une base inférieure ou égale à 36. + +Le premier travail est de construire une macro §\basedigit, purement développable, de syntaxe + +\centrecode|\basedigit{<nombre>}| + +\noindent qui se développe en un \verb|<chiffre>| selon le \verb|<nombre>| inférieur à 36 passé en argument. La méthode bête et méchante du \tidx{ifcase} et des 36 \tidx{or} va être privilégiée ici. + +Pour que le \verb|<chiffre>| puisse être facilement accessible, l'ensemble + +\centrecode-\romannumeral\basedigit{<nombre>}- + +\noindent doit donner le \verb|<chiffre>| par 1-développement du \idx\romannumeral. Cela implique de 1-développer les \tidx{or} restants et le \verb|\fi| avant que \idx\romannumeral ait fini son travail : + +\showcode/\catcode`\@11 +\def\z@@{\expandafter\z@\expandafter}¤\idx*\z@¤ +\def\basedigit#1{%¤\tidx*{ifcase}§*\basedigit¤ + \ifcase#1 + \z@@ 0% + \or\z@@ 1\or\z@@ 2\or\z@@ 3\or\z@@ 4\or\z@@ 5\or\z@@ 6\or\z@@ 7%¤\tidx*{or}¤ + \or\z@@ 8\or\z@@ 9\or\z@@ A\or\z@@ B\or\z@@ C\or\z@@ D\or\z@@ E% + \or\z@@ F\or\z@@ G\or\z@@ H\or\z@@ I\or\z@@ J\or\z@@ K\or\z@@ L% + \or\z@@ M\or\z@@ N\or\z@@ O\or\z@@ P\or\z@@ Q\or\z@@ R\or\z@@ S% + \or\z@@ T\or\z@@ U\or\z@@ V\or\z@@ W\or\z@@ X\or\z@@ Y\or\z@@ Z%¤\tidx*{or}¤ + \fi +} +\long\def\>#1<{\detokenize{#1}}¤\idx*\long\idx*\detokenize¤ +a) \expandafter\>\romannumeral\basedigit{23}<\quad¤\idx*\romannumeral¤ +b) \expandafter\>\romannumeral\basedigit{6}<¤\idx*\romannumeral§*\basedigit¤ +\catcode`@12/ + +Le test \tidx{ifcase}\verb|<n>|, développé par \idx\romannumeral, avale tout jusqu'au \verb|<n>|\ieme{} \tidx{or}. Par exemple, si l'argument \verb|<n>| est 3, \idx\romannumeral va développer le test et trouver ceci : + +\centrecode-\z@@ C\or \z@@ D......\fi- + +\noindent La macro \verb|\z@@| sera 1-développée et \idx\romannumeral va voir + +\centrecode-\expandafter\z@\expandafter C\or \z@@ D......\fi- + +\noindent \idx\romannumeral poursuit le développement puisque n'ayant toujours pas trouvé de nombre. Le pont d'\idx\expandafter va 1-développer «\verb|\or \z@@ D...\fi|», faisant disparaitre le tout. Il va rester + +\centrecode-\z@ C-\idx*\z@ + +\noindent Ensuite, le \idx\z@ va stopper la lecture du nombre par \idx\romannumeral, laissant «\verb|C|». +\grandsaut + +Pour que la macro §\baseconv reste purement développable, il va falloir lui transmettre à chaque itération tous les arguments dont elle a besoin et en particulier la base, qui sera un argument figé et lu à chaque fois. + +De plus, pour éviter que § \truncdiv\verb|{<n>}{<b>}| soit calculé deux fois, ce quotient, une fois calculé, sera transmis comme argument supplémentaire à une autre sous macro. + +Voici le code complet : + +\showcode|\catcode`\@11 +\def\baseconv#1#2{% #1=base #2=nombre à convertir¤§*\baseconv¤ + \ifnum#1<37 % base maxi = 36 (10 signes chiffres + 26 signes lettres) + \antefi¤§*\antefi¤ + \baseconv@i{}{#2}{#1}% + \fi +} +\def\baseconv@i#1#2#3{% #1=restes #2=n #3=base + \ifnum#2=\z@ \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {#1}% si n=0, afficher tous les restes + {% si non, transmettre en dernier argument + \expsecond{\baseconv@ii{#1}{#2}{#3}}%¤§*\expsecond¤ + {\number\truncdiv{#2}{#3}}% le quotient¤§*\truncdiv¤ + }% +} +\def\baseconv@ii#1#2#3#4{% #1=restes #2=n #3=base #4=q + \exparg\baseconv@i% recommencer, en ajoutant le <chiffre> avant les restes + {\romannumeral\basedigit{\number\numexpr#2-#4*#3\relax}#1}%¤\idx*\romannumeral§*\basedigit¤ + {#4}% et en remplaçant n par q + {#3}% +} +\def\z@@{\expandafter\z@\expandafter}%¤\idx*\z@¤ +\def\basedigit#1{%¤\tidx*{ifcase}§*\basedigit¤ + \ifcase#1 + \z@@ 0% + \or\z@@ 1\or\z@@ 2\or\z@@ 3\or\z@@ 4\or\z@@ 5\or\z@@ 6\or\z@@ 7%¤\tidx*{or}¤ + \or\z@@ 8\or\z@@ 9\or\z@@ A\or\z@@ B\or\z@@ C\or\z@@ D\or\z@@ E% + \or\z@@ F\or\z@@ G\or\z@@ H\or\z@@ I\or\z@@ J\or\z@@ K\or\z@@ L% + \or\z@@ M\or\z@@ N\or\z@@ O\or\z@@ P\or\z@@ Q\or\z@@ R\or\z@@ S% + \or\z@@ T\or\z@@ U\or\z@@ V\or\z@@ W\or\z@@ X\or\z@@ Y\or\z@@ Z%¤\tidx*{or}¤ + \fi +} +\catcode`\@12 +a) "\baseconv{20}{21587}"\qquad +b) "\baseconv{16}{32}"\qquad +c) \edef\foo{\baseconv{16}{159}}% + "\meaning\foo"\qquad¤\idx*\meaning¤ +d) "\baseconv{2}{43}"¤§*\baseconv¤|§*\baseconv[|)]\idx*[|)]{base (conversion)} + +\section{Macros manipulant les listes}\idx*[|(]{liste d'éléments} +Donnons-nous la définition suivante : une « liste » est une collection ordonnée d'« éléments » séparés les uns des autres par un « séparateur » que nous prendrons égal à la virgule. Un « élément » est un ensemble de tokens où les accolades éventuelles qui le composent sont équilibrées et qui ne contient pas le séparateur, sauf si celui-ci est entre accolades. + +Le but est d'exécuter les actions suivantes sur une liste donnée : trouver un élément par son numéro, trouver le numéro d'un élément, supprimer un élément, le déplacer, insérer un élément à une position, et « exécuter » la liste, c'est-à-dire que chaque élément deviendra l'argument d'une macro et le code obtenu sera lu par \TeX{}. + +Une liste est stockée par \verb|\def| dans le texte de remplacement d'une macro. Une telle macro recevra le nom de \verb|\<macrolist>|. + +\subsection{Trouver un élément d'une liste}§*\finditem[|(] +Trouver le \verb|<n>|\ieme{} élément se fera à l'aide de la macro §\finditem selon cette syntaxe: + +\centrecode-\finditem\<macrolist>{<n>}- + +\noindent Le \verb|<n>|\ieme{} élément de la liste sera renvoyé. La macro sera purement développable, sous réserve que l'élément renvoyé le soit. + +Du côté de la mise en \oe uvre, nous utiliserons la méthode classique : la macro §\finditem sera une macro chapeau. Afin qu'elle soit purement développable, la macro récursive \verb|\finditem@i| doit recevoir à chaque itération \emph{tous} les paramètres dont elle a besoin sous forme d'arguments, c'est-à-dire + +\begin{enumerate} + \item la position courante, initialisée à 1 par la macro chapeau et incrémentée à chaque itération; + \item la position de l'élément à trouver; + \item l'élément courant qui précède le reste de la liste, que la macro chapeau aura pris soin de terminer par une virgule et un quark. +\end{enumerate} + +Le travail de la macro consistera en deux tests imbriqués. Le premier qui regardera si la liste est arrivée à sa fin (en comparant l'élément courant au quark) et l'autre si la position courante correspond à la position cherchée. + +\showcode|\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\finditem#1{% #1 = \<macrolist>, la position est lue plus tard par \finditem@i¤§*\finditem¤ + \exparg\finditem@i{#1}% 1-développe la \<macrolist>¤§*\exparg¤ +} +\def\finditem@i#1#2{% #1 = liste #2=position cherchée + \finditem@ii{1}{#2}#1,\quark@list,% appelle la macro récursive +} +\def\finditem@ii#1#2#3,{% #1=position courante #2=position cherchée #3=élément courant + \ifx\quark@list#3\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si la fin de liste est atteinte, ne rien renvoyer + {% sinon + \ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si la position est la bonne + \finditem@iii{#3}% renvoyer #3 et manger les éléments restants + } + {% si la position n'est pas la bonne, recommencer en incrémentant #1 + \exparg\finditem@ii{\number\numexpr#1+1}{#2}%¤§*\exparg¤ + }% + }% +} +\def\finditem@iii#1#2\quark@list,{% renvoyer #1 et manger le reste de la liste + #1%¤\defline\aaa¤ +} +\catcode`\@12 +\def\liste{a,bcd,{ef},g,hij,kl} +a) \edef\foo{\finditem\liste5}\meaning\foo\qquad +b) \edef\bar{\finditem\liste3}\meaning\bar| + +L'élément «\verb|{ef}|» est dépouillé de ses accolades lorsqu'il est lu par la macro récursive \verb|\finditem@ii|. Pour nous prémunir de cette erreur, nous devons insérer un token (nous prendrons \idx\relax) avant chaque élément, et supprimer ce \idx\relax à chaque fois que figure l'élément \verb|#3| dans la macro \verb|\finditem@ii|, ainsi qu'à la ligne \no\aaa{} où il est l'argument \verb|#1| de la macro. + +De plus, nous allons aussi définir une macro §\finditemtocs, qui assignera l'élément trouvé à une macro selon la syntaxe + +\centrecode-\finditemtocs\<macrolist>{<n>}\<macro>- + +Comme cette variante n'a pas à être purement développable, il lui suffira de définir \verb|\finditemtocs@ii| pour exécuter une l'assignation. Tout le reste sera identique à §\finditem. + +\showcode/\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\finditem#1{% #1 = \<macro>, la position est lue plus tard par \finditem@i + \exparg\finditem@i{#1}% 1-développe la \<macro>¤§*\exparg¤ +} +\def\finditem@i#1#2{% #1 = liste #2=position cherchée¤§*\finditem¤ + \ifnum#2>\z@% ne faire quelque chose que si la position est >0 + \antefi¤§*\antefi¤ + \finditem@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive + \fi +} +\def\finditem@ii#1#2#3,{% #1=position courante #2=position cherché #3=élément courant + \expandafter\ifx\expandafter\quark@list\gobone#3% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si la fin de liste est atteinte, ne rien renvoyer + {% sinon + \ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si la position est la bonne + \finditem@iii{#3}% renvoyer #3 et manger les éléments restants + } + {% si la position n'est pas la bonne, recommencer en incrémentant #1 + \exparg\finditem@ii{\number\numexpr#1+1}{#2}\relax¤§*\exparg¤ + }% + }% +} +\def\finditem@iii#1#2\quark@list,{% renvoyer #1 et manger le reste de la liste + \gobone#1%¤\defline\aaa¤ +} +\def\finditemtocs#1#2#3{% #1 = \<macro> #2=position #3=macro à définir¤§*\finditemtocs¤ + \def\finditemtocs@iii##1##2\quark@list,{% renvoyer #1 et manger le reste de la liste + \expandafter\def\expandafter#3\expandafter{\gobone##1}%¤\defline\aaa¤ + }% + \let#3=\empty¤\idx*\empty¤ + \exparg\finditemtocs@i{#1}{#2}% 1-développe la \<macro>¤§*\exparg¤ +} +\def\finditemtocs@i#1#2{% #1 = liste #2=position cherchée + \ifnum#2>\z@% ne faire quelque chose que si la position est >0¤\idx*\z@¤ + \antefi\finditemtocs@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive¤§*\antefi¤ + \fi +} +\def\finditemtocs@ii#1#2#3,{% +% #1=position courante #2=position cherché #3=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone#3% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si fin de liste ne rien faire. Sinon, si position bonne + {\ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\finditemtocs@iii{#3}% renvoyer #3 et manger les éléments restants + }% si la position n'est pas la bonne, recommencer en incrémentant #1 + {\exparg\finditemtocs@ii{\number\numexpr#1+1}{#2}\relax%¤§*\exparg¤ + }% + }% +} +\catcode`\@12 +\def\liste{a,bcd,{ef},g,hij,kl} +a) "\finditem\liste5"\qquad +b) \edef\bar{\finditem\liste3}\meaning\bar\qquad +c) \finditemtocs\liste{3}\foo\meaning\foo¤§*\finditemtocs¤/§*\finditem[|)] + +\subsection{Trouver la position d'un élément} +Il faut annoncer la couleur d'entrée : la macro §\positem, chargée de trouver la position d'un élément et de syntaxe + +\centrecode-\positem\<macrolist>{<élément>}- + +\noindent ne sera pas purement développable. En effet, il faudra comparer l'élément courant à l'élément cherché et pour ce faire, un test \verb|\ifx| comparera deux macros contenant ces éléments. Définir ces macros rend donc la macro non développable. + +Si l'élément cherché n'est pas dans la liste, la position renvoyée sera égale 0. +\grandsaut + +À nouveau, une variante §\positemtocs sera programmée. Toutefois, nous allons essayer de ne pas procéder comme avec §\finditem et ne pas réécrire presque complètement le code de la macro §\positem en modifiant deux ou trois choses, ce qui est lourd et assez maladroit. Il est possible de faire mieux. Pour cela, il faut définir ce que l'on appelle en programmation un «\emph{\idx[ (méthode de programmation)]{hook}}». Un hook est un appel à une macro que l'on met à certains endroits du code afin d'exécuter l'action faite par la macro. Pour modifier cette action, il suffit de reprogrammer le hook et non pas réécrire des portions entières du code initial. + +Dans le cas présent, le hook sera nommé «\verb|\positem@endprocess|». Pour la macro §\positem, se hook sera défini pour donner la position, préalablement calculée et stockée dans la macro \verb|\pos@item|. Le hook sera donc défini par + +\centrecode-\def\positem@endprocess{\pos@item}- + +Pour la macro §\positemtocs, ce \idx[ (méthode de programmation)]{hook} sera programmé pour rendre la macro à définir \verb|\let|-égale à \verb|\pos@item|. + +\showcode/\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\positem#1{% #1 = \<macrolist>, la position est lue plus tard par \positem@i¤§*\positem¤ + \def\positem@endprocess{\pos@item}% hook : afficher la position¤\idx*[ (méthode de programmation)]{hook}¤ + \exparg\positem@i{#1}% 1-développe la \<macrolist>¤§*\exparg¤ +} +\def\positem@i#1#2{% #1 = liste #2=élément cherché + \def\sought@item{#2}% définir l'élément cherché + \positem@ii{1}\relax#1,\quark@list,% appelle la macro récursive +} +\def\positem@ii#1#2,{% #1=position courante #2=\relax + élément courant + \expandafter\def\expandafter\current@item\expandafter{\gobone#2}% + \ifx\current@item\quark@list\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\def\pos@item{0}% si la fin de liste est atteinte, renvoyer 0 + \positem@endprocess% et aller au hook + }% sinon + {\ifx\current@item\sought@item\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\def\pos@item{#1}% si la position est la bonne, définir la position + \positem@goblist% et manger les éléments restants + }% si la position n'est pas la bonne, recommencer en incrémentant #1 + {\exparg\positem@ii{\number\numexpr#1+1}\relax¤§*\exparg¤ + }% + }% +} +\def\positem@goblist#1\quark@list,{\positem@endprocess}% manger la liste et aller au hook + +\def\positemtocs#1#2#3{% #1=\<macrolist> #2=élément à chercher #3=macro à définir¤§*\positemtocs¤ + \def\positem@endprocess{\let#3=\pos@item}% hook : mettre le résultat dans #3¤\idx*[ (méthode de programmation)]{hook}¤ + \exparg\positem@i{#1}{#2}% 1-développe la \<macrolist>¤§*\exparg¤ +} + +\catcode`\@12 +\def\liste{a,bcd,{ef},g,hij,,kl} +a) \positem\liste{g}\qquad +b) \positem\liste{ef}\qquad +c) \positem\liste{{ef}}\qquad +d) \positem\liste{}\medbreak¤§*\positem¤ + +\def\liste{a,bcd,{ef},g,hij,,kl} +a) \positemtocs\liste{g}\foo\meaning\foo\qquad +b) \positemtocs\liste{ef}\foo\meaning\foo\qquad +c) \positemtocs\liste{{ef}}\foo\meaning\foo\qquad +d) \positemtocs\liste{}\foo\meaning\foo\qquad¤§*\positemtocs¤/ + +\subsection{Insérer un élément dans une liste}§*\insitem[|(] +Passons maintenant à la macro §\insitem qui insère un élément dans une liste à une position spécifiée, position qui sera la position de l'élément dans la liste modifiée après l'insertion + +\centrecode-\insitem\<macrolist>{<position>}{<élément>}- + +Si la \verb|<position>| est inférieure ou égale à 1, l'élément devra être inséré à la première position. Si la \verb|<position>| est strictement supérieure à la longueur de la liste, l'élément viendra en dernière position. Remarquons que rendre la macro §\insitem purement développable serait un non-sens puisqu'elle n'a pas vocation à renvoyer quelque chose, mais modifie la \verb|\<macrolist>| qui contient la liste d'éléments. + +Nous allons procéder un peu comme avec §\finditem, mais nous stockerons dans une macro temporaire \verb|\item@list| les éléments mangés en y ajoutant l'élément à insérer lorsque la position voulue est atteinte. Par ailleurs, la macro récursive sera écrite comme une sous-macro de §\insitem afin de pouvoir facilement accéder à ses arguments. + +\showcode/\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\insitem#1#2#3{% #1 = macro #2=position cherchée #3=élément à insérer + \let\item@list=\empty + \ifnum#2<1 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si position < 1 + {\addtomacro\item@list{#3,}% ajouter l'élement à insérer en premier + \eaddtomacro\item@list#1% puis la liste entière + } + % si la position > 1 + {% définir la macro récursive + \def\insitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone##2% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\addtomacro\item@list{#3}}% si fin de liste, ajouter l'élément en dernier + {% sinon, si la position cherchée est atteinte + \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\addtomacro\item@list{#3,}% ajouter l'élement + \add@remainlist##2,% et ##2 (en supprimant le \relax) et le reste + }% si la position n'est pas atteinte + {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément + \exparg\insitem@i{\number\numexpr##1+1}\relax% et recommencer + }% + }% + }% + % appel de la macro récursive + \expandafter\insitem@i\expandafter1\expandafter\relax#1,\quark@list,% + }% + \let#1=\item@list% rendre #1 égal au résultat +} + +\def\add@remainlist#1,\quark@list,{% + \eaddtomacro\item@list{\gobone#1}% ajouter #1 ainsi que les autres +} +\catcode`\@12 +\def\liste{a,bra,{cA},da,brA}\insitem\liste3{XX}\meaning\liste\par +\def\liste{a,bra,{cA},da,brA}\insitem\liste0{XX}\meaning\liste\par +\def\liste{a,bra,{cA},da,brA}\insitem\liste9{XX}\meaning\liste¤§*\insitem¤/§*\insitem[|)] + +\subsection{Supprimer un élément} +Pour supprimer un élément de la liste donné par son numéro avec §\delitem, nous allons agir comme ci-dessus avec la différence suivante : si la position est strictement inférieure à 1 ou strictement supérieure au numéro du dernier élément, ne rien faire. + +Supprimer un élément va être un peu plus difficile qu'en insérer un. La difficulté vient du fait que lorsqu'on lit un élément, on lit aussi la virgule qui le suit puisqu'elle agit comme délimiteur de la macro récursive. Si l'élément à supprimer est le dernier de la liste, il restera donc à lire le quark et la virgule qui sont insérés à l'appel de la macro récursive. On ne peut donc pas définir la macro \verb|\add@remainlist| avec un délimiteur «\verb|,\quark@list,|» mais avec ce délimiteur-là : «\verb|\quark@list,|». Nous la nommerons \verb|\add@reaminingitems|. + +\showcode/\catcode`\@11 +\def\quark@list{\quark@list}% quark de fin de liste +\def\delitem#1#2{% #1 = macro #2=position cherchée¤§*\delitem¤ + \ifnum#2>0 % ne faire quelque chose que si la position est >0 + \def\delitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone##2% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si fin de liste, ne rien faire¤\defline\ccc¤ + {% sinon, si la position cherchée est atteinte + \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\add@reaminingitems% et ##2 (en supprimant le \relax) et le reste + }% si la position n'est pas la bonne + {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément¤§*\eaddtomacro\defline\bbb¤ + \exparg\delitem@i{\number\numexpr##1+1}\relax% et recommencer¤§*\exparg¤ + }% + }% + }% + \let\item@list=\empty% initialiser la macro tempporaire + % appel de la macro récursive + \expandafter\delitem@i\expandafter1\expandafter\relax#1,\quark@list,%¤\defline\aaa¤ + \let#1=\item@list% rendre #1 égal au résultat + \fi +} + +\def\add@reaminingitems#1\quark@list,{% + \eaddtomacro\item@list{#1}% ajouter tout jusqu'au quark +} +\catcode`\@12 +\for\xx=0 to 8 \do{%¤§*\for¤ +\def\liste{a,bcd,{ef},g,hij,kl} +position \xx{} : \expandafter\delitem\expandafter\liste\xx\meaning\liste.\par +}/ + +Nous le constatons, mais il fallait s'y attendre : une virgule parasite se trouve à la fin de chaque liste traitée par la macro. Si l'élément à supprimer n'est pas le dernier, cette virgule provient de la ligne \no\aaa{} où elle est insérée avant le quark et non mangée par la macro \verb|\add@reaminingitems|. En revanche, si l'élément à supprimer est le dernier, cette virgule provient de la ligne \no\bbb{} où elle a été insérée à l'itération précédente. + +Dans tous les cas, il nous faut trouver un moyen de supprimer cette virgule. Le moyen le plus rapide est de définir une macro + +\centrecode|\def\remove@lastcomma#1,\@nil{\def\item@list{#1}}| + +Il faut s'assurer que cette macro reçoit le texte de remplacement de \verb|\item@list| (qui se finit par une virgule dont on veut se débarrasser) suivi d'un \verb|\@nil|. Il faut donc insérer la ligne suivante juste après la ligne \no\aaa{} de la macro §\delitem : + +\centrecode-\expandafter\remove@lastcomma\item@list\@nil- + +\subsection{Déplacer un élément} +Pour déplacer un item d'une position $x$ à une position $y$, il faut déjà isoler cet item avec §\finditemtocs, le supprimer avec §\delitem et enfin l'insérer avec §\insitem à la position $y$. + +\showcode/\catcode`\@11 +\def\moveitem#1#2#3{% #1 = liste, #2=position départ, #3=position arrivée¤§*\moveitem¤ + \ifnum#2>0 % ne faire quemque chose que si #2>0 + \finditemtocs#1{#2}\temp@item% sauvegarder l'élément¤§*\finditemtocs¤ + \delitem#1{#2}% supprimer l'élément¤§*\delitem¤ + \expsecond{\insitem#1{#3}}\temp@item% insérer l'élément¤§*\expsecond§*\insitem¤ + \fi +} +\catcode`\@12 +% déplace "b" en 5e position +a) \def\liste{a,b,c,d,e,f} \moveitem\liste25 "\liste"\par +% déplace "d" en 1e position +b) \def\liste{a,b,c,d,e,f} \moveitem\liste41 "\liste"\par +% déplace "c" en 9e position +c) \def\liste{a,b,c,d,e,f} \moveitem\liste39 \liste"\par +% position départ=0 -> sans effet +d) \def\liste{a,b,c,d,e,f} \moveitem\liste02 "\liste"¤§*\moveitem¤/ + +\subsection{Exécuter une liste} +La dernière chose qu'il nous reste à faire est d'exécuter une liste et pour ce faire, programmer une macro §\runlist. Pour cela, chacun des éléments de la liste sera placé dans l'argument d'une \verb|\<macro>|. Donnons-nous cette syntaxe : + +\centrecode-\runlist{\<macrolist>}\with\<macro>-§*\runlist + +\noindent Tous les éléments de la \verb|<liste>| seront placés tour à tour dans l'argument d'une \verb|\<macro>| acceptant un seul argument, et tout se passera comme si nous avions écrit + +\centrecode-\<macro>{<élément 1>}\<macro>{<élément 2>}...\<macro>{<élément n>}- + +\noindent et que ce code soit exécuté par \TeX. + +La méthode consiste à manger chaque élément, l'écrire comme argument de la \verb|\<macro>| et s'arrêter lorsque la fin de la liste est atteinte. Il va donc falloir stoker dans une macro auxiliaire nommée \verb|\collect@run| les \verb|\<macro>{<élément i>}| au fur et à mesure que les éléments seront rencontrés et exécuter cette macro à la toute fin du processus. Pour être tout à fait propre en programmation, nous allons effectuer la collecte dans un groupe semi-simple de telle sorte que la macro \verb|\collect@run| soit locale à ce groupe et n'existe plus ensuite, étant entendu que \verb|\collect@run| doit être exécutée \emph{hors} du groupe semi-simple. + +\showcode/\catcode`\@11 +\def\runlist#1\with#2{% #1=liste #2=macro¤§*\runlist¤ + \def\runlist@i##1,{% + \ifx\quark@list##1\relax\else% si la fin n'est pas atteinte + \addtomacro\collect@run{#2{##1}}% ajouter "\<macro>{<élément>}""¤§*\addtomacro¤ + \expandafter\runlist@i% et recommencer en lisant l'élément suivant + \fi + }% + \begingroup% fait la collecte dans un groupe + \let\collect@run=\empty% initialiser la macro¤\defline\bbb¤ + \expandafter\runlist@i#1,\quark@list,% appeler \runlist@i + \expandafter\endgroup% ferme le groupe et détruit \collect@run¤\defline\aaa¤ + \collect@run% après l'avoir développé ! +} +\catcode`\@12 +\newcount\foocnt +\foocnt=0 % compteur utilisé pour numéroter les éléments dans la macro \foo¤\idx*\newcount¤¤\idx*\long¤ +\def\foo#1{% la macro qui va exécuter chaque élément de la liste. #1 = l'élément + \advance\foocnt1 % incrémente le compteur¤\idx*\advance¤ + L'argument \number\foocnt\ est : {\bf #1}\par%¤\idx*\number¤ +} +\def\liste{a,bra,ca,da,BRA}% +\runlist\liste\with\foo%¤§*\runlist¤/ + +Remarquons l'astuce à la ligne \no\aaa{} qui consiste à « sauter » le \idx\endgroup à l'aide d'un un \idx\expandafter pour 1-développer la macro \verb|\collect@run| avant que le \idx\endgroup ne la détruise ! C'est une astuce assez commune que de sauter une fin de groupe avec un ou plusieurs \idx\expandafter pour gagner le territoire se trouvant à l'extérieur du groupe tout en restant imprégné de ses propriétés. Cette méthode permet donc à peu de frais\footnote{Tout au plus quelques \texttt{\string\expandafter} avant le \texttt{\string\endgroup} éventuellement relayés à l'extérieur du groupe par d'autres \texttt{\string\expandafter} ou tout autre procédé permettant de propager le développement jusqu'au lieu où l'on veut exporter les propriétés du groupe.} d'exporter au dehors de son territoire les propriétés d'un groupe avant sa fermeture. + +Ici, nous aurions pu procéder autrement et initialiser \verb|\collet@run| de cette façon à la ligne \no\bbb{} + +\centrecode-\def\collect@run{\endgroup}- + +\noindent et écrire en lieu et place des lignes \nos\aaa-\number\numexpr\aaa+1\relax{} un simple \verb|\collect@run| qui, en se développant, aurait fait apparaitre le \idx\endgroup au début de son texte de remplacement qui aurait provoqué sa destruction. + +\subsection{Enlever les espaces extrêmes} +Il nous reste un dernier pas à accomplir, celui de se jouer des espaces laissés avant ou après les éléments d'une liste. En effet, il serait commode de saisir + +\centrecodespc-\def\liste{ a, b c , def, g h, ij }- + +\noindent et de disposer d'une macro §\sanitizelist\verb|\liste| qui rendrait la liste égale à + +\centrecodespc-a,b c,def,g h,ij- + +Cette macro §\sanitizelist supposerait qu'il existe une macro capable de retirer \emph{tous} les espaces qui se trouvent au début ou à la fin d'un argument, sans évidemment toucher aux espaces «intérieurs». + +\subsubsection{Supprimer les espaces du début} +La mise au point d'une macro §\removefirstspaces qui retire tous les espaces qui se trouvent au \emph{début} de son argument demande tout d'abord de programmer un test §\ifspacefirst\verb|{<texte>}{<vrai>}{<faux>}| qui est vrai si son premier argument commence par un espace. Pour éviter d'envisager le cas d'un \verb|<texte>| vide, la lettre «\verb|W|» sera ajoutée à la fin de cet argument. + +\showcode/\catcode`\@11 +\def\ifspacefirst#1{%¤§*\ifspacefirst¤ + \expandafter\ifspacefirst@i\detokenize{#1W} \@nil% "W" se prémunit d'un argument vide +} +\def\ifspacefirst@i#1 #2\@nil{\ifempty{#1}}% renvoyer vrai s'il n'y a rien avant " "¤§*\ifempty¤ +\catcode`\@12 +a) \ifspacefirst{a bc d}{vrai}{faux}\qquad +b) \ifspacefirst{ a bc d}{vrai}{faux}\qquad +c) \ifspacefirst{ }{vrai}{faux}\qquad +d) \ifspacefirst{}{vrai}{faux}\qquad +e) \ifspacefirst{{ } }{vrai}{faux}\qquad +f) \ifspacefirst{ {x} }{vrai}{faux}¤§*\ifspacefirst¤/ + +Le côté pratique de \idx\detokenize est qu'il permet des accolades dans le \verb|<texte>| malgré le fait que ce \verb|<texte>| soit l'argument d'une macro à argument délimité. + +La méthode mise en \oe uvre sera simple : pour supprimer tous les espaces au début d'un argument, il faut tester s'il commence par un espace et dans l'affirmative, manger cet espace avec une macro §\gobspace puis recommencer avec ce qui a été obtenu. La macro §\gobspace attendra donc un espace juste après elle et sera donc délimitée par un espace. Pour la définir, il n'est pas possible d'écrire + +\centrecodespc|\def\gobspace {}| + +\noindent car dans le code source, un espace qui suit une séquence de contrôle est ignoré (voir règle page~\pageref{regle.espace}). Pour contourner cette règle, nous allons mettre en place un pont d'\idx\expandafter pour 1-développer \idx\space en \verb*| | avant que \idx\def n'entre en action : + +\showcode/\catcode`\@11 +\expandafter\def\expandafter\gobspace\space{}¤§*\gobspace\idx*\space¤ + +\def\removefirstspaces#1{%¤§*\removefirstspaces¤ + \ifspacefirst{#1}% si #1 commence par un espace¤§*\ifspacefirst¤ + {\exparg\removefirstspaces{\gobspace#1}}% recommencer sans le 1er espace¤§*\gobspace¤ + {#1}% sinon, renvoyer l'argument¤\defline\aaa¤ +} +\catcode`\@12 +a) "\removefirstspaces{12 {\bf3}4 567}"\qquad +b) "\removefirstspaces{ 12 {\bf3}4 567}"\qquad +c) \edef\foo{\space\space\space 12 34 567}¤\idx*\space¤ + "\exparg\removefirstspaces\foo"¤§*\expsecond§*\removefirstspaces¤/ + +Le dernier cas permet de vérifier que la récursivité fonctionne lorsque le texte de remplacement de \verb|\foo| commence par \emph{trois} espaces, chacun issu du développement de \idx\space. + +Essayons d'aller un peu plus loin et faisons en sorte que cette macro se développe en un nombre connu de fois. Nous allons recourir à un \idx\romannumeral, placé au tout début du texte de remplacement de §\removefirstspaces afin de lancer le développement maximal. Il faudra le stopper en écrivant \verb|{\z@#1}| à la ligne \no\aaa. On s'offre donc une macro qui effectue son travail en deux développements : le premier pour extraire son texte de remplacement et le deuxième qui lance celui de \idx\romannumeral. + +\showcode/\catcode`\@11 +\def\removefirstspaces{%¤§*\removefirstspaces¤ + \romannumeral% lance le développement maximal¤\idx*\romannumeral¤ + \removefirstspaces@i% et passe la main à la macro récursive +} + +\def\removefirstspaces@i#1{% + \ifspacefirst{#1}% si #1 commence par un espace¤§*\ifspacefirst¤ + {\exparg\removefirstspaces@i{\gobspace#1}}% recommencer sans le 1er espace¤§*\gobspace§*\exparg¤ + {\z@#1}% sinon, renvoyer l'argument où \z@ stoppe l'action de \romannumeral¤\idx*\z@¤ +} +\catcode`\@12 +\long\def\>#1<{"\detokenize{#1}"}¤\idx*\long\idx*\detokenize¤ + +a) \expandafter\expandafter\expandafter\>\removefirstspaces{12 {\bf3}4 567}<\qquad¤\idx*\bf§*\removefirstspaces¤ +b) \expandafter\expandafter\expandafter\>\removefirstspaces{ 12 {\bf3}4 567}<\qquad +c) "\removefirstspaces{ 12 {\bf3}4 }"¤§*\removefirstspaces¤/ + +\subsubsection{Supprimer les espaces de fin} +Programmons maintenant la macro §\removelastspaces\verb|{<texte>}| qui supprime les espaces qui se trouvent à la fin de son argument. Bien que l'algorithme soit un peu plus compliqué, nous allons \emph{aussi} utiliser des arguments délimités. + +Pour décrire la méthode, prenons «\verb|W|» comme délimiteur en supposant pour l'instant que cette lettre ne figure pas dans l'argument. L'idée se décompose en plusieurs étapes : + +\begin{enumerate} + \item ajouter «\verb*|W W|» à la fin de l'argument; + \item du résultat obtenu, prendre ce qui se trouve avant le premier «\verb*| W|»; + \item ajouter «\verb|W|» à la fin du résultat obtenu; + \item du résultat précédent, prendre ce qui se trouve avant le premier «\verb|W|». +\end{enumerate} + +Énuméré comme cela, il est quasi impossible de se représenter les différentes étapes et comprendre comment évolue l'argument. Aussi, nous allons prendre deux exemples pour décrire les deux cas de figure pouvant se présenter, «\verb*|a|» qui ne finit pas par un espace et «\verb*|b |» qui se trouve dans le cas opposé : + +\begin{centrage} +\small +\begin{tabular}{clcc} + \no d'étape & decription & \verb*|a| & \verb*|b |\\\hline + 1& ajout de «\verb*|W W|» & \verb*|aW W|&\verb*|b W W|\\ + 2& ce qui est avant «\verb*| W|» & \verb*|aW|&\verb*|b|\\ + 3& ajout de «\verb*|W|» & \verb*|aWW|&\verb*|bW|\\ + 4& ce qui est avant «\verb*|W|» & \verb*|a|&\verb*|b|\\\hline +\end{tabular} +\end{centrage} + +Le cheminement est désormais plus clair et il est plus facile de se persuader que la méthode fonctionne quels que soient «\verb|a|» et «\verb|b|». + +La méthode présente cependant un petit défaut; elle indique ce qui se trouve \emph{avant} les délimiteurs, mais ne dit pas un mot de ce qu'elle laisse \emph{après}. Nous allons donc aussi récolter les reliquats qui sont laissés après les délimiteurs. + +Imaginons qu'au lieu de \verb*|W W|, nous ajoutions en fin d'argument «\verb*|W W\@nil|» lors de la première étape et que l'on fasse fonctionner les étapes déjà décrites. Le fait nouveau sera qu'à la fin, on récolte les reliquats se trouvant avant le \verb|\@nil|. \Qu'y trouverait-on ? Tout ce qui a été laissé après les délimiteurs des lignes \no2 et 4. Le tableau est repris sauf qu'ici, les lignes grises montrent ce qui est laissé après ces délimiteurs : + +\begin{centrage} +\small +\begin{tabular}{clcc} + \no d'étape & description + & \verb*|a| & \verb*|b |\\\hline + 1& ajout de «\verb*|W W|» & \verb*|aW W|&\verb*|b W W|\\ + 2& ce qui est avant «\verb*| W|» & \verb*|aW|&\verb*|b|\\ + \rowcolor{gray!40} & ce qui est après «\verb*| W|» & \verb|<vide>|&\verb*| W|\\ + 3& ajout de «\verb*|W|» & \verb*|aWW|&\verb*|bW|\\ + 4& ce qui est avant «\verb*|W|» & \verb*|a|&\verb*|b|\\ + \rowcolor{gray!40} & ce qui est après «\verb*|W|» & \verb*|W|&\verb|<vide>|\\\hline +\end{tabular} +\end{centrage} + +Avant le \verb|\@nil| se trouve la concaténation des reliquats partiels, c'est-à-dire : + +\begin{itemize} + \item «\verb|W|» dans le cas «\verb|a|»; + \item «\verb*| W|» dans le cas «\verb*|b |». +\end{itemize} + +Il est intéressant de remarquer que si le reliquat \emph{commence} par un espace, cela signifie que l'argument se finissait par un espace. Dans le cas présent, puisque nous souhaitons supprimer \emph{tous} les espaces à la fin de l'argument, il va donc falloir mettre en place une récursivité et recommencer dans le cas où le reliquat commence par un espace. + +Réglons enfin le délimiteur «\verb|W|» qui nous a servi pour exposer la méthode, mais qui ne peut être utilisé dans les vraies conditions, car trop probable dans un vrai argument. Remplaçons-le par un caractère bien plus improbable mais pas trop «exotique», par exemple le caractère de code 0 (qui s'écrit \verb|^^00|) et de catcode 12\footnote{Rien n'empêcherait de lui assigner un catcode de 3 pour le rendre plus exotique.}\idx*{catcode!12 (autre)}. + +\showcode/\catcode`\@11 +\edef\catcodezero@saved{\number\catcode0 }% stocke le catcode de ^^00 +\catcode0=12 % le modifie à 12 +\def\removelastspaces#1{% + \romannumeral% lance le développement maximal + \removelastspaces@i\relax#1^^00 ^^00\@nil% mettre un \relax au début¤\defline\aaa¤ + % et passer la main à \removelastspaces@i +} +\def\removelastspaces@i#1 ^^00{% prendre ce qui est avant " W" + \removelastspaces@ii#1^^00% ajouter "W" +} +\def\removelastspaces@ii#1^^00#2\@nil{% #1=ce qui est avant "W" #2=reliquat + \ifspacefirst{#2}% si le reliquat commence par un espace + {\removelastspaces@i#1^^00 ^^00\@nil}% recommencer sans passer par \removelastspaces + {\expandafter\z@\gobone#1}% sinon supprimer le \relax ajouté au début + % et stopper l'action de \romannumeral avec \z@ +} +\catcode0=\catcodezero@saved\relax% restaure le catcode de ^^00 +\catcode`\@12 +\long\def\>#1<{"\detokenize{#1}"}¤\idx*\long\idx*\detokenize¤ + +a) \expandafter\expandafter\expandafter\>\removelastspaces{ 12 {\bf3}4 }<\qquad +b) \expandafter\expandafter\expandafter\>\removelastspaces{12 {\bf3}4}<\qquad +c) "\removelastspaces{ 12 {\bf3}4 }"¤§*\removelastspaces¤/ + +La première remarque concerne le \idx\relax mis avant \verb|#1| tout au début afin d'éviter que \verb|#1| ne soit dépouillé de ses accolades s'il s'agit d'un texte entre accolades. Ce \idx\relax est retiré juste avant que \idx\z@ ne stoppe l'action de \idx\romannumeral. + +L'autre remarque, essentielle, est que l'appel récursif de la ligne \no\aaa{} n'appelle pas la macro chapeau §\removelastspaces. Cela évite de lancer à chaque itération un nouveau \idx\romannumeral qui ne serait pas stoppé par la suite et provoquerait des erreurs de compilation. + +\subsubsection{Supprimer les espaces extrêmes} +Tout est en place pour construire une macro §\removetrailspaces, chargée de supprimer les espaces en trop se trouvant au début et à la fin de son argument. Il suffit d'assembler correctement les macros vues précédemment. Cet assemblage mérite une explication\ldots{} Si \verb|#1| est l'argument de cette macro, on voudrait pouvoir écrire + +\centrecode-\removelastspaces{\removefirstspaces{<sp>#1<sp>}}-§*\removefirstspaces§*\removelastspaces + +\noindent où \verb|<sp>| représente d'éventuels espaces extrêmes de \verb|#1|. Mais n'oublions pas que \TeX{} prend les arguments tels quels, sans les remplacer par ce qu'ils sont après développement. + +Pour que §\removefirstspaces\verb|{<sp>#1<sp>}| soit développé, nous allons d'abord lancer le développement maximal avec \idx\romannumeral en tout début de texte de remplacement de §\removetrailspaces. Comme précédemment, cet artifice nous assurera que cette macro donne son résultat en deux développements. + +Dans le code ci-dessus, convenons que «\textbullet» représente un \idx\expandafter. Le pont d'\idx\expandafter, lui-même développé par \idx\romannumeral, va 2-développer «§\removefirstspaces\verb|{<sp>#1<sp>}|» : + +\begin{centrage} + \small + \idx\romannumeral\textbullet\textbullet\textbullet§\removelastspaces\textbullet\textbullet\textbullet\verb|{|§\removefirstspaces\verb|{<sp>#1<sp>}}| +\end{centrage} + +\noindent Après que les \idx\expandafter aient joué leur rôle, la macro §\removefirstspaces a effectué son travail et on obtient : + +\centrecode-\removelastspaces{#1<sp>}-§*\removelastspaces + +\noindent Enfin, comme \idx\romannumeral n'a toujours pas trouve de nombre, le développement se poursuit, la macro §\removelastspaces se 2-développe et nous obtenons : + +\centrecode-#1- + +Mais à ce stade, \idx\romannumeral est toujours en action et nous n'avons pas pensé à la stopper. Si \verb|#1| commence par un nombre au sens de \TeX{}, ce nombre sera capturé par \idx\romannumeral pour former les chiffres romains et sinon, \TeX{} se plaindra d'une erreur de compilation du type «\texttt{Missing number}». + +Pour régler ce problème, il faut revenir en arrière et réfléchir où mettre \idx\z@ pour qu'à la dernière étape ci-dessus, le \verb|#1| soit précédé de \idx\z@. Le bon endroit est juste avant §\removefirstspaces, mais surtout pas avant \verb|#1|, car ce \idx\z@ cacherait les éventuels espaces au début de \verb|#1|. Remarquons au passage que ce token supplémentaire «\idx\z@» implique la prolongation du pont d'\idx\expandafter. Voici les étapes du développement jusqu'au résultat final: + +\begin{centrage} + \small + \idx\romannumeral\textbullet\textbullet\textbullet§\removelastspaces\textbullet\textbullet\textbullet\verb|{|\textbullet\textbullet\textbullet\verb|\z@|§\removefirstspaces\verb|{<sp>#1<sp>}}|\smallbreak + §\removelastspaces\verb|{\z@#1<sp>}|\smallbreak + \verb|\z@#1|\smallbreak + \verb|#1| +\end{centrage} + +Cette méthode donne le code suivant : + +\showcode/\catcode`\@11 +\def\removetrailspaces#1{%¤§*\removetrailspaces¤ + \romannumeral% lance le développement maximal¤\idx*\romannumeral¤ + \expandafter\expandafter\expandafter% le pont d'\expandafter + \removelastspaces¤§*\removelastspaces¤ + \expandafter\expandafter\expandafter% fait agir \removefirstspaces en premier + {% + \expandafter\expandafter\expandafter + \z@% stoppe le développement initié par \romannumeral¤\idx*\z@¤ + \removefirstspaces{#1}%¤§*\removefirstspaces¤ + }% +} +\catcode`\@12 +\long\def\>#1<{"\detokenize{#1}"} + +a) \expandafter\expandafter\expandafter\>\removetrailspaces{ 12 {\bf3}4 }<\qquad¤\idx*\bf¤ +b) \expandafter\expandafter\expandafter\>\removetrailspaces{12 {\bf3}4}<\par +c) \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter + \foo\expandafter\expandafter\expandafter{\removetrailspaces{ 12 {\bf3}4 }}% + signification : \meaning\foo.\par¤\idx*\meaning¤ +c) exécution : "\foo"\par +d) "\removetrailspaces{ 12 {\bf3}4 }"¤§*\removetrailspaces¤/ + +\subsubsection{La macro \texttt{\char`\\sanitizelist}} +Le plus dur est fait ! Nous allons maintenant pouvoir finir en douceur en écrivant la macro §\sanitizelist qui supprime tous les espaces extrêmes des éléments d'une liste. Le confort dont nous profitons ici est que §\removetrailspaces ne demande que deux développements pour donner son résultat. + +Au fur et à mesure de la lecture de la liste, nous allons collecter dans la macro \verb|\item@list| les éléments purgés de leurs espaces indésirables. Ici encore, nous placerons un \verb|\relax| avant chaque élément (argument \verb|#1|), sous réserve de le supprimer ensuite avec \verb|\gobone|. L'ajout de l'élément en cours \verb|#1| au collecteur \verb|\item@list| se fera donc par : + +\centrecode-\addtomacro\item@list{\removetrailspaces{\gobone#1},}- + +\noindent sauf qu'il faut développer les arguments avant que les macros ne s'en emparent. Ici, \verb|\gobone| doit être 1-développé en premier puis §\removetrailspaces doit être 2-développé. Cela va être le festival des \verb|\expandafter|, ou plutôt l'invasion ! Un \verb|\expandafter| est représenté par $\bullet$ ci-dessous. + +\begin{centrage} +\small +$\bullet\bullet\bullet\bullet\bullet\bullet\bullet$% +\verb|\addtomacro|% +$\bullet\bullet\bullet\bullet\bullet\bullet\bullet$% +\verb|\item@list|\par +$\bullet\bullet\bullet\bullet\bullet\bullet\bullet$% +\verb|{|$\bullet$\verb|\removetrailspaces|$\bullet$\verb|{\gobone#1},}| +\end{centrage} + +\showcode/\catcode`\@11 +\def\sanitizelist#1{% #1 = liste¤§*\sanitizelist¤ + \let\item@list\empty% initialise le réceptacle de la liste assainie¤\idx*\empty¤ + \def\sanitizelist@endprocess{% définit le hook de fin¤\idx*[ (méthode de programmation)]{hook}¤ + \expandafter\remove@lastcomma\item@list\@nil% supprimer dernière virgule + \let#1=\item@list% et assigner le résultat à #1 + }% + \expandafter\sanitizelist@i\expandafter\relax#1,\quark@list,% aller à la macro récursive +} + +\def\sanitizelist@i#1,{% #1="\relax" + élément courant + \expandafter\ifx\expandafter\quark@list\gobone#1% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\sanitizelist@endprocess% si fin de liste, hook de fin¤\idx*[ (méthode de programmation)]{hook}¤ + }% si la fin de la liste n'est pas atteinte : + {\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter + \addtomacro% 1-développer \gobone pour retirer \relax + \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter + \item@list% puis 2-développer \removetrailspaces + \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter + {\expandafter\removetrailspaces\expandafter{\gobone#1},}% "#1"¤§*\removetrailspaces¤ + \sanitizelist@i\relax% et continuer avec l'élément suivant + }% +} + +\catcode`\@12 +\frboxsep=0pt % encadrer au plus proche¤§*\frboxsep¤ +\def\foo#1{\frbox{\tt\strut#1} }% boite avec un strut et en fonte "tt" puis espace¤\idx*\strut§*\frbox¤ +\def\liste{ Programmer, en \TeX{} ,est ,{\bf facile}, et utile } +a) \runlist\liste\with\foo% encadrer les items¤§*\runlist¤ + +b) \sanitizelist\liste% supprimer les espaces inutiles des items¤§*\sanitizelist¤ + \runlist\liste\with\foo/\idx*[|)]{liste d'éléments} + +Il est toujours possible d'éviter ces fastidieux ponts d'\verb|\expandafter|. Nous aurions pu mettre à contribution nos macros §\expsecond et §\eaddtomacro pour alléger le code. Ces \verb|\expandafter| ont été laissés en l'état pour montrer que la gestion du développement est parfois assez pénible. + +\begin{centrage} +$\star$\par\nobreak +$\star$\quad$\star$ +\end{centrage} + +Après avoir compris dans cette partie comment construire des macros récursives et donc, comment s'y prendre pour bâtir des boucles, nous allons poursuivre notre chemin. + +Il s'agit désormais de lire non pas des arguments comme le font les macros, mais descendre à un niveau inférieur, celui des tokens. Pour prendre une analogie chimique, les arguments seraient les molécules tandis que les tokens seraient les atomes; l'un étant fait avec l'autre. Il nous faut apprendre à programmer des macros qui lisent des tokens, un peu comme le fait \TeX{} lorsqu'il lit du code, mais en gardant la main après chaque token de façon à être capable de \emph{contrôler} chaque token lu et d'agir en conséquence. +%| | +%| Fin partie 3 | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Partie 4 | +%| | +\defpartcomment{\lettrine[lines=3,nindent=0pt,slope=2pt]{\libertineInitialGlyph{M}}{\kern -2pt aintenant} que les arguments des macros n'ont plus de secrets, il faut apprendre à intervenir à un niveau plus bas, celui des tokens. En effet, pour certains besoins spécifiques, la lecture d'arguments par des macros est trop grossière et ne permet pas de les résoudre de façon satisfaisante.} + +\part{Au niveau des tokens} +\chapter{Mise en évidence du problème} +Supposons que nous souhaitions effectuer un traitement sur chaque caractère d'une phrase. Pour rendre les choses visuelles, choisissons d'encadrer chaque caractère. L'idée est de lire chaque caractère de la phrase par une macro à argument puis d'encadrer ce caractère à l'aide de la macro §\frbox vue dans la partie précédente. La récursivité sera classique, une macro chapeau §\boxsentence se chargera de transmettre la phrase suivie d'un quark à une macro récursive. La macro récursive \verb|\boxsentence@i| lira donc un argument, le stockera dans une macro brouillon pour le comparer au \idx{quark} et tant que l'égalité n'aura pas lieu, bouclera sur elle-même pour lire les arguments les uns après les autres. + +\showcode/\catcode`\@11 +\def\boxsentence#1{%¤§*\boxsentence¤ + \leavevmode% se mettre en mode horizontal¤\idx*\leavevmode\idx*{mode!horizontal}¤ + \boxsentence@i#1\quark% transmet "#1+\quark" à boxsentence@i¤§*\quark¤ +} +\def\boxsentence@i#1{% #1= argument lu + \def\current@arg{#1}% stocke l'argument dans une macro temporaire + \unless\ifx\quark\current@arg% si la fin n'est pas atteinte¤\tidx*{unless}§*\quark¤ + \frbox{#1}% encadrer cet argument¤§*\frbox¤ + \expandafter\boxsentence@i% lire l'argument suivant + \fi +} +\catcode`@12 +\frboxsep=1pt \frboxrule=0.2pt ¤§*\frboxsep§*\frboxrule¤ +\boxsentence{Programmer en \TeX\ est facile}¤§*\boxsentence¤/ + +Les espaces sont ignorés puisque la règle dit que lorsque les espaces ne sont pas enveloppés entre accolades, ils sont ignorés lorsque lus par une macro en tant qu'arguments. Si l'on avait voulu encadrer les espaces de cette phrase, il aurait fallu les écrire entre accolades ou bien utiliser la primitive \idx\ ou utiliser la macro \idx\space. Ce sont des artifices que l'on n'utilise jamais dans une phrase tapée naturellement, sauf éventuellement après une séquence de contrôle, comme ici après \verb|\TeX|. +\grandsaut + +Voici un autre exemple. On pourrait aussi vouloir écrire une macro et pouvoir l'appeler avec une \emph{variante}. Le moyen le plus communément utilisé pour signifier que l'on souhaite la variante est d'écrire une étoile juste après le nom de la macro. À charge pour la macro de tester si l'étoile est présente ou pas et d'adopter deux comportements différents selon le cas. En l'état actuel de nos connaissances, nous sommes obligés de lire l'argument qui suit afin de le tester pour déterminer si cet argument est une étoile. + +Appelons §\expo une macro sans argument qui, appelée seule, produit l'exposant «${}^{\dag}$» et, appelée étoilée (\verb|\expo*|), affiche l'exposant «${}^{\ddag}$»\idx*{macro étoilée}. Le signe «$\dag$» s'obtient avec la macro \idx\dag tandis que «$\ddag$» s'obtient avec \idx\ddag, ces macros devant opérer en mode mathématique\idx*{mode!mathématique}. + +Nous allons donc définir §\expo comme une macro admettant un argument : + +\begin{itemize} + \item si c'est une étoile, afficher «\verb|$^\dag$|»; + \item si ce n'est pas une étoile, afficher «\verb|$^\ddag$|» et \emph{réécrire l'argument lu}. +\end{itemize} + +\showcode|\catcode`\@11 +\edef\star@macro{\string *}% stocke une étoile de catcode 12 +\def\expo#1{%¤§*\expo¤ + \def\temp@arg{#1}% stocke l'argument lu + \ifx\star@macro\temp@arg\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si l'argument est une étoile de catcode 12¤\idx*{catcode!12\space(autre)}¤ + $^\dag$% affiche une dague + }% sinon + {$^\ddag$% affiche une double dague + {#1}% puis ré-écrit l'argument {#1}¤\defline\aaa¤ + }% +} +A\expo B\expo*C¤§*\expo¤| + +Bien que cela \emph{semble} fonctionner, on va tout droit à la catastrophe si on écrit : + +\centrecode/A\expo \def\foo{BAR}/ + +\noindent En effet, à la ligne \no\aaa, en réécrivant l'argument \verb|#1|, \emph{on le met entre accolades}. Et donc ici, §\expo prendrait \verb|\def| comme argument. Ce \verb|\def| se retrouverait \emph{seul} entre accolades ce qui, lors de son exécution, déclencherait une erreur du type «\texttt{Missing control sequence.}». Rappelons-nous que \verb|\def| \emph{doit} être suivie d'une séquence de contrôle ou d'un caractère actif. On pourrait penser s'en sortir en réécrivant \verb|#1| à la ligne \no\aaa{}, c'est-à-dire sans entre accolades. Mais avec cette modification, si on écrit + +\centrecode-A\expo {<code dans le groupe>}- + +\noindent alors, le \verb|<code dans le groupe>| ne sera justement plus dans un groupe puisque dépouillé des accolades par la macro §\expo lorsqu'elle le réécrit. Avec les conséquences que l'on sait : toutes les assignations que l'utilisateur pensait confinées dans le groupe seraient désormais de portée globale ! + +Toute la difficulté vient du fait qu'une macro qui lit un argument ne \emph{peut pas savoir} si cet argument est enveloppé dans des accolades ou pas, notamment lorsque l'argument est un token unique. Ainsi, que l'on écrive «\verb|\foo A|» ou «\verb|\foo{A}|», la macro \verb|\foo| lit dans les deux cas l'argument «\verb|A|» et ne peut pas déterminer s'il était entre accolades ou pas. +\grandsaut + +Pour résumer, la lecture d'arguments est défectueuse sur deux points essentiels : +\begin{itemize} + \item les espaces ne sont pas vus comme des arguments et sont ignorés (sauf si l'utilisateur les a délibérément mis entre accolades); + \item il est impossible pour une macro de faire la différence entre l'argument «\verb|{x}|» et l'argument «\verb|x|» où \verb|x| est un token quelconque (de catcode civilisé tout de même) autre qu'un espace. +\end{itemize} +\medbreak + +Ces deux défauts rendent bien trop imprécise la lecture d'un texte quelconque \emph{argument par argument} en vue d'effectuer une action pour chaque argument lu. Heureusement, on peut lire du code \emph{token par token}. Voyons comment\ldots + +\chapter{Lire du code token par token} +Un petit rappel s'impose concernant la primitive \idx\let vue à partir de la page~\pageref{let}. Nous savons qu'elle agit au niveau des tokens. Sa syntaxe est la suivante : + +\centrecode-\let\<macro>= <token>- + +\noindent Et à la suite de cette assignation, la \verb|\<macro>| (qui peut être un caractère actif) devient un alias pour le \verb|<token>|. Rappelons également que le test \verb|\ifx| se fait modulo la \idx\let-égalité. + +\section{Reprendre la main après un \ttfamily \textbackslash let} +Comment construire une macro §\readtok qui lit le \verb|<token>| qui la suit et qui l'assigne avec \verb|\let| à \verb|\nxttok| ? + +La chose n'a rien de compliqué, il suffit de commencer l'assignation avec \idx\let à l'intérieur du texte de remplacement de la macro pour que le \verb|<token>| soit lu à l'extérieur. Il est important d'écrire la syntaxe \emph{complète} de \idx\let dans le texte de remplacement, c'est-à-dire faire suivre \verb|\let\nxttok| de «\verb*|= |». Si par exemple, le \verb|<token>| est «\verb|=|», cette man\oe uvre nous assure que ce signe \verb|=| n'est pas compris comme faisant partie prenante de la syntaxe de \idx\let et soit perdu pour l'assignation. Voici comment on pourrait programmer la macro §\readtok : + +\centrecodespc|\def\readtok{\let\nxttok= }|§*\readtok + +En procédant de cette façon, même si la macro est suivie d'un signe égal comme dans «\verb|\readtok=|», ce signe égal sera bien assigné à \verb|\nxttok|. Le problème est cependant évident : la macro lit bien un \verb|<token>| et l'assigne à \verb|\nxttok|, mais \emph{c'est tout} ! Après cette action, la macro s'achève. Pour reprendre la main après l'assignation afin d'appeler une autre macro \verb|\cmptok| chargée d'analyser le \verb|<token>| lu, nous sommes bloqués. En effet, si nous voulons appeler une macro \verb|\cmptok| après l'assignation, il serait absurde d'écrire + +\centrecodespc-\def\readtok{\let\nxttok= \cmptok}-§*\readtok + +\noindent car cela reviendrait à rendre \verb|\nxttok| égal à \verb|\cmptok| et pour le coup, la macro §\readtok ne lirait rien du tout. On peut tourner le problème dans tous les sens, «\verb*|\let\nxttok= |» \emph{doit} se trouver à la toute fin du texte de remplacement de la macro. + +\subsection{La primitive \texttt{\char`\\afterassignment}}\label{afterassignment} +Ce cas de figure a été prévu et la primitive \idx\afterassignment permet de s'en sortir. + +\begin{regle} +La primitive \idx\afterassignment dont la syntaxe est + +\centrecode-\afterassignment<token>- + +stocke le \verb|<token>| dans un endroit spécial de la mémoire de \TeX{} pour le placer sur la \idx{pile d'entrée} immédiatement après la prochaine assignation. Cette assignation peut résulter de l'utilisation de primitives déjà vues \verb|\def|, \verb|\gdef|, \verb|\edef|, \verb|\xdef|, \idx\let, \idx\chardef, \idx\mathchardef, ou bien d'une assignation à un registre (de boite, d'entier, de ressort, de dimension, de token), voire après une opération sur un registre d'entier ou de dimension par \idx\advance, \idx\multiply ou \idx\divide. + +Si plusieurs \verb|\afterassignment<token>| sont rencontrés avant une assignation, seul le dernier \verb|<token>| est pris en compte. + +Lorsque \verb|\afterassignment<token>| est appelé avant \idx\setbox (c'est-à-dire avant l'assignation à un registre de boite), le \verb|<token>| est placé en tout début de boite. +\end{regle} + +Tout ceci nous conduit donc à programmer la macro §\readtok ainsi : + +\centrecodespc-\def\readtok{\afterassignment\cmptok\let\nxttok= }-§*\readtok + +Dès lors, on peut programmer une macro §\boxsentence qui va encadrer chaque token d'une phrase. Il suffira pour la macro \verb|\cmptok|, appelée après chaque assignation, d'encadrer le token lu (dont l'alias est \verb|\nxttok|) et de stopper la récursivité si elle lit un token spécial (nous prendrons ici §\quark) que nous mettrons à la fin de l'argument de §\boxsentence. + +\showcode/\def\boxsentence#1{%¤§*\boxsentence¤ + \readtok#1\quark% met le token d'arrêt \quark à la fin de #1¤§*\quark§*\readtok¤ +} +\def\readtok{\afterassignment\cmptok\let\nxttok= }¤\idx*\afterassignment§*\readtok¤ +\def\cmptok{% + \unless\ifx\nxttok\quark% si la fin n'est pas atteinte¤\tidx*{unless} §*\quark¤ + \frbox{\nxttok}% encadrer le token lu¤§*\frbox¤ + \expandafter\readtok% puis aller lire le suivant¤§*\readtok¤ + \fi +} +\frboxsep=1pt \frboxrule=0.2pt ¤§*\frboxsep §*\frboxrule¤ +\leavevmode\boxsentence{Programmer en \TeX\ est facile}¤\idx*\leavevmode§*\boxsentence¤/ + +Nous avons déjà levé un défaut qui existait avec la lecture d'arguments : les espaces ne sont pas ignorés lorsque lus après \idx\let\verb|\<macro>|\verb*|= |. + +\subsection{Application : retour sur la permutation circulaire des voyelles} +Reprenons l'exercice de la deuxième partie où l'on effectuait une permutation circulaire sur les voyelles (voir page~\pageref{permutation.voyelles}). Appelons §\permutstart la macro qui marque le début de la zone où cette permutation sera faite et §\permutend la macro qui marque la fin de cette zone. + +Pour mener à bien cette permutation, il nous suffit de reprendre l'exemple précédent et tester dans \verb|\cmptok| si le token lu est successivement chacune des voyelles et dans chaque cas, afficher la voyelle suivante. La macro §\permutend sera un \idx{quark} : + +\showcode/\def\permutstart{\afterassignment\cmptok\let\nxttok= }¤\idx*\afterassignment§*\permutstart¤ +\def\permutend{\permutend}%¤§*\permutend¤ +\def\cmptok{% + \unless\ifx\permutend\nxttok% tant que la fin n'est pas atteinte :¤\tidx*{unless}§*\permutend¤ + \ifxcase\nxttok¤§*\ifxcase¤ + ae% si le token lu est "a", afficher un "e" + ei io ou uy ya% etc pour les autres lettres + \elseif¤§*\elseif¤ + \nxttok% si ce n'est aucune voyelle, afficher le token¤\defline\aaa¤ + \endif¤§*\endif¤ + \expandafter\permutstart% aller lire le token suivant + \fi +} +\permutstart Un "a" puis "e puis "i" ensuite, un "o", un "u" et "y".\permutend¤§*\permutend§*\permutstart¤/ + +Cela semble fonctionner comme nous l'attendons, mais ce n'est qu'une impression. L'algorithme présente un défaut majeur : à la ligne \no\aaa, il met le token lu tel quel dans le flux de lecture de \TeX. Tout se passe bien tant qu'il ne s'agit que de tokens \emph{indépendants} et n'ayant aucun besoin les uns des autres. Si l'on avait écrit «\verb|\'e|», le token «\idx\'» aurait été lu puis exécuté à la ligne \no\aaa. Comme ce token est une macro qui va lire l'argument suivant pour lui mettre un accent dessus, elle aurait donc capturé le premier \verb|\fi| de la ligne \no\number\numexpr\aaa+1{} avec deux conséquences aussi graves l'une que l'autre : + +\begin{itemize} + \item ce \verb|\fi| capturé par \idx\' n'aurait pas été disponible pour le \verb|\ifx| apparié et donc, l'équilibrage entre les \verb|\ifx| et les \verb|\fi| aurait été rompu en faveur des \verb|\ifx| qui auraient été excédentaires; + \item l'argument de \idx\' ayant été \verb|\fi| au lieu d'une simple lettre comme elle l'attend. Il est quasi certain que cet argument aurait provoqué une erreur de compilation puisque l'argument \verb|\fi| est un argument «explosif» dès lors qu'il se retrouve dans un test (ce qui est le cas pour la macro \idx\' avec le \idx[!\LaTeX]{format} \LaTeX.). +\end{itemize} + +Comme on l'a déjà vu à l'exercice qui mettait les premières lettres de chaque mot en majuscule (lire page~\pageref{exo.majuscules}), il faudrait accumuler dans une variable les tokens au fur et à mesure de leur lecture. Il suffirait ensuite d'appeler cette variable en toute fin du processus. Hélas, une fois que \verb|\nxttok| a été rendu \idx\let-égal au token lu, on ne peut pas\footnote{On pourrait éventuellement penser à \texttt{\string\meaning} mais les cas de figure à envisager sont tellement nombreux que cette man\oe uvre est irréaliste.} savoir \emph{a posteriori} à quel token il a été rendu \idx\let-égal. Certes, on peut le tester, le faire lire par \TeX{} pour l'afficher, mais on ne peut pas savoir ce à quoi il est égal ! L'affectation avec \idx\let est à sens unique\ldots{} C'est toute la différence entre une \emph{macro} dont le texte de remplacement est facilement accessible (il suffit de la développer) et entre une séquence de contrôle assignée avec \idx\let qui cache soigneusement ce à quoi elle est \idx\let-égale. Impossible donc de faire comme avec une macro, de « développer » la séquence de contrôle \verb|\nxttok| pour l'ajouter à une variable qui collecterait les tokens lus. Il faudra attendre le chapitre suivant pour apprendre comment, avec l'aide d'une autre primitive, procéder différemment. + +\subsection{Lecture d'accolades explicites} +Essayons maintenant de corser la difficulté et de parcourir avec \idx\let du code qui contient des accolades explicites (c'est-à-dire des tokens de catcode 1\idx*{catcode!1 (accolade)} et 2). Faisons l'expérience avec le code vu au-dessus, où chaque token est enfermé dans une boite encadrée par la macro §\frbox programmée dans la partie précédente : + +\errcode/\def\boxsentence#1{\readtok#1\boxsentence}¤§*\boxsentence§*\readtok¤ +\def\readtok{\afterassignment\cmptok\let\nxttok= } +\def\cmptok{% + %\show\nxttok% à décommenter pour débogage + \unless\ifx\nxttok\boxsentence + \frbox{\nxttok}%¤§*\frbox¤ + \expandafter\readtok + \fi¤§*\readtok¤ +} +\frboxsep=1pt \frboxrule=0.2pt ¤§*\frboxsep §*\frboxrule¤ +\leavevmode\boxsentence{Pro{gra}mmer en \TeX\ est facile}¤\idx*\leavevmode§*\boxsentence¤/{! You can't use `\string\hrule' here except with leaders.} + +Le message d'erreur n'est pas vraiment explicite, mais il est manifeste que quelque chose a mal tourné. Pour avoir un début d'explication, on peut chercher à déboguer ce code. Un simple «\idx\show\verb|\nxttok|» placé tout au début de la macro \verb|\cmptok| écrira dans le \idx[!log]{fichier} \verb|log| ce qu'est \verb|\nxttok| à chaque itération. Voici ce que l'on obtient :% TODO : voir si un saut de page a lieu ici + +\centrecode*|> \nxttok=the letter P. +> \nxttok=the letter r. +> \nxttok=the letter o. +> \nxttok=begin-group character {. +> \nxttok=the letter g. +> \nxttok=the letter r. +> \nxttok=the letter a. +> \nxttok=end-group character }. +! You can't use `\hrule' here except with leaders. +To put a horizontal rule in an hbox or an alignment, +you should use \leaders or \hrulefill (see The TeXbook). + +! Missing number, treated as zero. +<to be read again>| + +Les messages d'erreurs (qui commencent par «\verb|!|») continuent sur plusieurs lignes, seuls les deux premiers ont été retranscrits ici. + +Il est donc intéressant de voir que \verb|\nxttok|, accolade ouvrante devenue \emph{implicite} (comme l'est \idx\bgroup) par le truchement de \idx\let, ne provoque pas d'erreur lorsqu'elle seule dans §\frbox. Ainsi donc, §\frbox\verb|{\bgroup}| est valide\ldots{} Mais ce n'est qu'apparent, car l'équilibrage des accolades a été rompu et \TeX{} finirait tôt ou tard par se plaindre d'un «\texttt{Missing \} inserted}». Avant qu'il puisse en arriver là, l'erreur survient lorsque \idx\let enferme dans §\frbox l'accolade fermante explicite devenue implicite. Avant d'aller plus loin, il faut se souvenir que les boites de \TeX{} ont comme propriété que leurs accolades peuvent \emph{aussi} être implicites. À ce titre, les primitives de boites acceptent indifféremment accolades explicites et implicites qui sont donc interchangeables. Pour expliquer l'erreur constatée, voici la définition de §\frbox où son argument \verb|#1| a été remplacé par un \idx\egroup à la ligne \no10 : + +{\indencodenumtrue +\small +\indentcode-\def\frbox#1{%¤§*\frbox¤ + \hbox{% + \vrule width\frboxrule¤\idx*\vrule§*\frboxrule¤ + \vtop{%¤\idx*\vtop¤ + \vbox{%¤\idx*\vbox¤ + \hrule height\frboxrule¤\idx*\hrule¤ + \kern\frboxsep¤\idx*\kern§*\frboxsep¤ + \hbox{%¤\idx*\hbox¤ + \kern\frboxsep + \egroup% est l'argument #1 de la macro¤\idx*\egroup¤ + \kern\frboxsep + }% + }% + \kern\frboxsep¤\idx*\kern§*\frboxsep¤ + \hrule height\frboxrule¤\idx*\hrule¤ + }% + \vrule width\frboxrule¤\idx*\vrule§*\frboxrule¤ + }% +}- +} + +Le \idx\egroup de la ligne \no10 ferme prématurément la \idx\hbox la plus intérieure (ligne \no8), rejetant hors de celle-ci le \verb|\kern\frboxsep| de la ligne \no11. L'accolade explicite suivante (ligne \no12) ferme la \idx\vbox tandis que l'accolade explicite de la ligne \no13 ferme la \idx\vtop. Par conséquent, \verb|\kern\frboxsep| et \idx\hrule (lignes \no14-15) se trouvent rejetés hors de la \idx\vtop et prennent place dans la \idx\hbox chapeau de la ligne \no2, en mode horizontal interne\idx*{mode!horizontal interne} donc. C'est là que la première erreur survient car \idx\hrule ne peut être employé en mode horizontal\idx*{mode!horizontal}, sauf justement à l'utiliser avec \idx\leaders comme le suggère le message d'erreur. + +\grandsaut +Pour en revenir à la lecture d'un code avec \idx\let, on voit donc que les accolades explicites posent un vrai problème puisqu'elles sont derechef transformées par \idx\let en leur « alias » \idx\bgroup ou \idx\egroup qui ne leur est pas équivalent. Là encore, patientons jusqu'au chapitre suivant où une nouvelle primitive nous permettra d'agir \emph{avant} que le token ne soit lu. + +\subsection{Retour sur la macro \texttt{\char`\\litterate}}\idx*[|(]{verbatim} +Forts de nos connaissances, nous sommes en mesure d'écrire une variante §\Litterate de la macro §\litterate programmée à la page~\pageref{litterate}. Son action était de désactiver tous les tokens spéciaux de façon à écrire exactement le texte situé entre deux tokens identiques (par exemple «\verb-|-») servant de délimiteurs. + +Le défi va être de fabriquer un équivalent de la macro §\litterate mais sans modifier les catcodes. Pour y parvenir et pour rendre tous les tokens lus inoffensifs, il faudra faire en sorte que chaque token du texte lu soit précédé d'un \idx\string. + +La première chose à faire est de stocker le délimiteur qui suit §\Litterate. Pour être surs que son catcode est 12, nous allons le faire précéder d'un \idx\string que nous développerons : la séquence de contrôle \verb|\lim@tok| recevra ce token. Par la suite, lorsque l'on parcourra le texte, si le token lu après avoir été traité par \idx\string est égal à \verb|\lim@tok|, cela signifiera que le deuxième token délimiteur est atteint et qu'il faut cesser la lecture. + +\showcode/\catcode`\@11 +\def\Litterate{%¤§*\Litterate¤ + \begingroup% ouvrir un groupe + \tt% et adopter une fonte à chasse fixe¤\idx*\tt¤ + \afterassignment\Litterate@i% après l'assignation, aller à \Litterate@i¤\idx*\afterassignment¤ + \expandafter\let\expandafter\lim@tok\expandafter=\string% \lim@tok = token délimiteur¤\idx*\string¤ +} +\def\Litterate@i{% + \afterassignment\Litterate@ii%après avoir lu le prochain token, aller à \Litterate@ii¤\idx*\afterassignment¤ + \expandafter\let\expandafter\nxttok\expandafter=\string% lit le token suivant¤\defline\aaa\idx*\string¤ +} +\def\Litterate@ii{% + \ifx\nxttok\lim@tok% si le token suivant="token délimiteur" + \endgroup% fermer le groupe et finir + \else + \nxttok% sinon, afficher ce token¤\defline\bbb¤ + \expandafter\Litterate@i% et lire le token suivant + \fi +} +\catcode`\@12 +\Litterate|Programmer en \TeX {} est << facile >> !|¤§*\Litterate¤/ + +Cela ne fonctionne pas du tout comme on l'attendait : + +\begin{itemize} + \item les espaces sont ignorés ; + \item la séquence de contrôle \idx\TeX est exécutée. +\end{itemize} + +En fait, les deux dysfonctionnements proviennent d'un même mal. L'erreur se trouve à la ligne \no\aaa{} de la macro \verb|\Litterate@i| qui lit le prochain token en développant au préalable \idx\string. Si cette macro s'apprête à lire un espace, alors cet espace est précédé d'un \idx\string et donc, après développement, cet espace reste inchangé. Il va être considéré par \TeX{} comme l'espace facultatif qui vient après le signe «\verb|=|» dans la syntaxe de \idx\let ! Il sera donc ignoré et \verb|\nxttok| sera rendu \idx\let-égal au token suivant, mais sans que ce token ne soit soumis à \idx\string puisque celui-ci a déjà été utilisé par l'espace. Voilà pourquoi la macro \idx\TeX, qui se trouve juste après un espace, n'est pas passée par \idx\string : \verb|\nxttok| est donc devenu un alias pour \idx\TeX et à la ligne \no\bbb, \verb|\nxttok| est exécuté et provoque l'affichage du logo de \TeX. + +Pour corriger cette erreur de programmation, dans la macro \verb|\Litterate@i|, il \emph{faut} écrire l'espace après le signe «\verb|=|» et sauter cet espace avec un \idx\expandafter de plus. Mais on ne peut pas écrire : + +\centrecodespc|\expandafter\let\expandafter\nxttok\expandafter=\expandafter \string| + +\noindent car l'espace serait ignoré puisque précédé par une séquence de contrôle. Au lieu de cet espace \emph{explicite}, il va falloir écrire la macro \idx\space (dont le texte de remplacement est un espace) que nous allons 1-développer après avoir 1-développé \idx\string. Cela conduit à un pont d'\idx\expandafter. + +\showcode/\catcode`\@11 +\def\Litterate{%¤§*\Litterate¤ + \begingroup% ouvrir un groupe + \tt% et adopter une fonte à chasse fixe¤\idx*\tt¤ + \afterassignment\Litterate@i% après l'assignation, aller à \Litterate@i¤\idx*\afterassignment¤ + \expandafter\let\expandafter\lim@tok\expandafter=\string% \lim@tok = token délimiteur¤\idx*\string¤ +} +\def\Litterate@i{% + \afterassignment\Litterate@ii% après avoir lu un token, aller à \Litterate@ii + \expandafter\expandafter\expandafter% 1-développer \string + \let + \expandafter\expandafter\expandafter% puis 1-développer \space en " " + \nxttok + \expandafter\expandafter\expandafter + =\expandafter\space\string% et lire le token obtenu¤\idx*\space\idx*\string¤ +} +\def\Litterate@ii{% + \ifx\nxttok\lim@tok% si le token suivant="token délimiteur" + \endgroup% fermer le groupe et finir + \else + \nxttok% sinon, afficher ce token¤\defline\bbb¤ + \expandafter\Litterate@i% et lire le token suivant + \fi +} +\catcode`\@12 +\Litterate|Programmer en \TeX {} est << facile >> : $~$^_#|¤§*\Litterate¤/ + +Dernière observation : le résultat n'est pas \emph{exactement} ce qui est entre les délimiteurs. Deux règles de lecture du code s'appliquent toujours : +\begin{itemize} + \item plusieurs espaces consécutifs sont lus comme un seul; + \item un espace qui suit une séquence de contrôle est ignoré. Ici, l'espace qui suit la macro \idx\TeX est ignoré. +\end{itemize} + +Ces deux règles concernent les espaces, c'est-à-dire les tokens de catcode 10\idx*{catcode!10 (espace)}. Pour les contourner, nous allons devoir légèrement enfreindre la contrainte que nous nous étions donnée : changer le catcode de l'espace à 12 pour que les règles ne s'appliquent plus. Il suffit donc d'écrire + +\centrecodespc|\catcode`\ =12 | + +\noindent juste après le \idx\begingroup pour que les espaces soient tous affichés. + +La conclusion est que §\Litterate fonctionne bien, mais de par l'assignation et le test qu'elle effectue pour chaque token, elle est \emph{beaucoup plus lente} que son homologue §\litterate. Cette dernière ne s'encombrait pas de tant précautions. Une fois les catcodes changés et les \idx{ligature}s désactivées, elle laissait \TeX{} librement lire le texte jusqu'à ce qu'il rencontre le délimiteur de fin qui, rendu actif et \idx\let-égal à \idx\endgroup, redonnait aux tokens spéciaux leur catcode normal et mettait fin au comportement initié par §\litterate\idx*[|)]{verbatim}. + +\begin{exercice} +Créer une macro §\letterspace de syntaxe + +\centrecode-\letterspace{<dimension de ressort>}{<texte>}- + +où le \verb|<texte>| est constitué de tokens de catcode 10, 11 ou 12. L'action de cette macro est d'afficher entre chaque caractère du \verb|<texte>| un ressort dont la dimension est spécifiée dans le premier argument. + +\solution +Puisque l'on sait lire un argument token par token, on va largement s'inspirer de ce qui a été fait avec la macro §\Litterate. + +\showcode/\catcode`@11 +\newskip\ltr@spc¤\idx*\newskip¤ +\def\letterspace#1#2{%¤§*\letterspace¤ + \ltr@spc=#1\relax % assigne le ressort + \letterspace@i#2\letterspace@i% appelle la macro \letterspace@i +} +\def\letterspace@i{% + \afterassignment\letterspace@ii¤\idx*\afterassignment¤ + \let\nxttok= +} +\def\letterspace@ii{% + \ifx\nxttok\letterspace@i% si la fin est atteinte + \unskip% supprimer le dernier ressort qui est de trop¤\idx*\unskip¤ + \else + \nxttok% afficher le token + \hskip\ltr@spc\relax% insère le ressort¤\idx*\hskip¤ + \expandafter\letterspace@i% va lire le token suivant + \fi +} +\catcode`@12 +Voici "\letterspace{0.3em}{des lettres espacées}" +et voici "\letterspace{-0.075em}{de la compression}".¤§*\letterspace¤/ + +Mais attention, si cela fonctionne ici, c'est parce que l'\idx{encodage} du code source de ce livre est \latin et donc, le caractère «\verb|é|» est vu comme un seul token. Si l'\idx{encodage} avait été \utf et le moteur 8 bits, le caractère «\verb|é|», codé sur 2 \idx{octet}s, aurait été vu comme \emph{deux} tokens. Ceux-ci sont inséparables pour former la lettre \verb|é| (le premier est actif et prend le second comme argument). Comme l'algorithme les aurait séparés, on aurait certainement eu une erreur de compilation. Le même problème serait apparu si l'on avait écrit «\idx\'\verb|e|». +\end{exercice} + +\begin{exercice} +Inventer un procédé qui permet à une macro §\ifinteger de tester si son argument est un nombre entier. La syntaxe sera : +\centrecode-\ifinterger{<caractères>}{<vrai>}{<faux>}- + +\solution +L'idée directrice est de définir un compteur, de faire précéder l'argument de cette macro d'un «\verb|0|» et d'assigner \verb|0#1| au compteur : + +\centrecode-<compteur>=0#1\relax- + +Le «\verb|0|» nous assure qu'au moins un caractère servira à former un \verb|<nombre>|. Après cette assignation, il faut aller voir ce qu'il reste jusqu'au \verb|\relax| et qui ne sert pas à la formation du \verb|<nombre>|. S'il ne reste rien, alors la totalité de l'argument était constituée de caractères pouvant entrer dans la composition d'un \verb|<nombre>| et peut donc être assimilée à un entier. + +On va rencontrer un petit grain de sable lorsque \verb|#1| commence par le caractère «\verb|-|» car + +\centrecode|0-<caractères>| + +\noindent sera compris comme le nombre \verb|0| suivi d'autres tokens ne pouvant entrer dans la composition d'un nombre à cause du «\verb|-|». Il faut donc prévoir de tester si \verb|#1| commence par «\verb|-|» à l'aide de la macro §\ifstart vue à la page~\pageref{ifstart} de la troisième partie. Si le test est positif, on se contentera de manger ce signe «\verb|-|» et recommencer avec ce qui reste dans \verb|#1|. Pour éviter qu'un argument vide donne un test §\ifinteger positif, il est préférable de tester si \verb|#1| est vide auquel cas il faut lire l'argument \verb|<faux>|. + +\showcode|\catcode`@11 +\newcount\test@cnt¤\idx*\newcount¤ +\def\ifinteger#1{%¤§*\ifinteger¤ + \ifstart{#1}{-}% si "-" est au début de #1¤§*\ifstart¤ + {\exparg\ifinteger{\gobone#1}% l'enlever et recommencer¤§*\ifinteger§*\exparg¤ + } + {\ifempty{#1}% sinon, si #1 est vide¤§*\ifempty¤ + \secondoftwo% lire l'argument <faux> + {\afterassignment\after@number% sinon, après l'assignation, aller à \after@number¤\idx*\afterassignment¤ + \test@cnt=0#1\relax% faire l'assignation + %(\relax termine le nombre et n'est pas mangé) + }% + }% +} +\def\after@number#1\relax{% #1 est ce qui reste après l'assignation + \ifempty{#1}% teste si #1 est vide et lit l'argument <vrai> ou <faux> qui suit¤§*\ifempty¤ +} + +\catcode`@12 +1) \ifinteger{5644}{oui}{non}\qquad +2) \ifinteger{-987}{oui}{non}\qquad +3) \ifinteger{6a87}{oui}{non}\qquad +4) \ifinteger{abcd}{oui}{non}\qquad +5) \ifinteger{-a}{oui}{non}\qquad +6) \ifinteger{-}{oui}{non}\qquad +7) \ifinteger{3.14}{oui}{non}¤§*\ifinteger¤| + +Ajoutons que la macro §\ifinteger n'est pas développable, car elle contient trois choses qui ne le sont pas : +\begin{enumerate} + \item la macro §\ifstart; + \item l'assignation à un compteur; + \item la primitive \idx\afterassignment. +\end{enumerate} +\end{exercice} + +\subsection{En résumé...} +Prenons le temps de faire le point sur la lecture token par token. Du côté des avantages : + +\begin{itemize} + \item les espaces sont bien lus ; + \item il est possible de faire un test sur chaque token après qu'il ait été lu. +\end{itemize} + +Et du côté des défauts: + +\begin{itemize} + \item lorsqu'un token a été lu et assigné à une séquence de contrôle par \idx\let, on ne peut plus savoir quel était le token lu. Par conséquent, il est impossible d'ajouter ce token à une variable cumulative que l'on pourrait développer à la fin, hors de toute récursivité. Si l'on veut afficher le token lu, on doit mettre la séquence de contrôle qui lui est \idx\let-égale dans le flux de lecture pendant la boucle récursive ce qui, on l'a vu, peut entrainer des problèmes lorsque des tokens sont des macros nécessitant un ou plusieurs arguments ; + \item les tokens de catcode 1\idx*{catcode!1 (accolade)} et 2 («\verb|{|» et «\verb|}|») sont dénaturés par une assignation via \idx\let et deviennent \idx\bgroup et \idx\egroup. +\end{itemize} + +La conclusion est sans appel : la lecture token par token souffre de défauts qui la rendent bien peu commode. La suite va certainement adoucir cette conclusion\ldots{} + +\section{Lire un token sans avancer la tête de lecture} +Le titre ci-dessus a de quoi surprendre puisqu'on a considéré jusqu'à présent que toute lecture entrainait l'inévitable avancée de la tête de lecture de \TeX{} ! Il y a eu là un petit mensonge par omission puisqu'il y a une seule exception à cette règle. + +\begin{regle} +La primitive \idx\futurelet a la syntaxe suivante : + +\centrecode-\futurelet\<macro><token1><token2>- + +L'effet est le suivant : + +\begin{enumerate} + \item \TeX{} effectue l'action «\verb*|\let\<macro>= <token2>|» (en figeant le catcode de \verb|<token2>|); + \item puis la tête de lecture avance jusqu'à \verb|<token 1><token2>| qui restent intacts, comme s'ils n'avaient pas été lus. +\end{enumerate} + +Il y a comme un air de ressemblance avec \idx\expandafter sauf qu'il n'est pas question de développement, mais d'assignation via \idx\let : il y a bien le «saut» de \verb|<token1>| pour faire une assignation sans que la tête de lecture ne bouge. Tout se passe comme si \TeX{} allait lire le \verb|<token2>| pour le \emph{copier} juste après \verb|\<macro>| pour effectuer une assignation avec un \idx\let. Et donc, le code + +\centrecode-\futurelet\<macro><token1><token2>-\idx*\futurelet + +\noindent est équivalent à + +\centrecode-\let\<macro>= <token2><token1><token2>- +\end{regle} + +Assurément, \idx\futurelet est une primitive formidable car elle va régler tous nos problèmes. En effet, si le texte de remplacement d'une \verb|\<macro>| se termine par + +\centrecode-\futurelet\nxttok\testtok-\idx*\futurelet + +\noindent alors, le token se trouvant juste après cette \verb|\<macro>| sera lu et stocké dans \verb|\nxttok|, mais la tête de lecture n'aura pas bougé et ce token qui se trouve hors de la macro restera intact\footnote{En particulier, les accolades explicites restent explicites et ne sont pas transformées en leurs alias \texttt{\string\bgroup} ou \texttt{\string\egroup}.}. Il suffira que la macro \verb|\testtok| teste ce token pour décider l'action à faire. Cette primitive permet donc de prévoir (d'un token) le futur, c'est sans doute pourquoi elle s'appelle \idx\futurelet et non pas \verb|\letafter|. +\grandsaut + +Voici comment programmer une macro §\teststar dont le comportement va changer selon qu'elle est immédiatement suivie d'une étoile ou non : la version \emph{normale} composera l'argument en italique et la version \emph{étoilée}\idx*{macro étoilée} en gras. Clairement, un \idx\futurelet sera utilisé pour savoir si le token qui suit immédiatement §\teststar est une étoile; la macro qui sera chargée de tester ce token est \verb|\teststar@i| : + +\showcode|\catcode`\@11 +\def\teststar{%¤§*\teststar¤ + \futurelet\nxttok\teststar@i% % pioche le token suivant puis aller à \teststar@i¤\idx*\futurelet¤ +} +\def\teststar@i{% + \ifx *\nxttok\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si le token suivant est une étoile + \afterassignment\teststar@bf% après avoir lu "*", aller à \teststar@bf¤\idx*\afterassignment¤ + \let\nxttok= % lire l'étoile + }% si le token n'est pas une étoile + {\teststar@it% aller à \teststar@it + }% +} +\def\teststar@bf#1{{\bf#1}}% lit l'argument et le compose en gras dans un groupe¤\idx*\bf¤ +\def\teststar@it#1{{\it#1\/}}% lit l'argument et le compose en italique¤\idx*\it\idx*[ (correction d'italique)]\/¤ +\catcode`\@12 +Un essai \teststar{réussi} et un essai \teststar*{étoilé} réussi aussi.¤§*\teststar¤| + +Il serait plus intéressant de programmer une macro à caractère généraliste «§\ifnexttok» dont la syntaxe serait : + +\centrecode-\ifnexttok<token>{<code vrai>}{<code faux>}- + +\noindent que l'on placerait à la fin du texte de remplacement d'une macro et qui irait tester le prochain token extérieur à la macro non égal à un espace. Le \verb|<code vrai>| serait exécuté s'il est égal à \verb|<token>| et \verb|<code faux>| sinon. + +Pour savoir si le token lu est un espace, nous le comparerons à §\sptoken défini à la page~\pageref{sptoken}. Profitons-en pour découvrir une autre façon (parmi beaucoup d'autres) de définir §\sptoken avec une macro §\deftok : + +\showcode|\catcode`@11 +\def\deftok#1#2{\let#1= #2\empty}% définit le token #1 (\empty au cas où #2 est vide)¤§*\deftok¤ +\deftok\sptoken{ }¤§*\sptoken¤ +\def\ifnexttok#1#2#3{% lit les 3 arguments : #1=token #2=code vrai #3=code faux¤§*\ifnexttok¤ + \deftok\test@tok{#1}% stocke le token cherch餧*\deftok¤ + \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter + \ifnexttok@i% aller à la macro récursive +} +\def\ifnexttok@i{% + \futurelet\nxttok\ifnexttok@ii% piocher le token d'après et aller à \ifnexttok@ii¤\idx*\futurelet¤ +} +\def\ifnexttok@ii{% + \ifx\nxttok\sptoken% si le prochain token est un espace¤§*\sptoken¤ + \def\donext{% + \afterassignment\ifnexttok@i% recommencer après¤\idx*\afterassignment¤ + \let\nxttok= % après avoir absorbé cet espace + }% + \else + \ifx\nxttok\test@tok% si le prochain token est celui cherché + \let\donext\true@code% exécuter le code vrai + \else + \let\donext\false@code% sinon code faux + \fi + \fi + \donext% faire l'action décidée ci-dessus +} +\catcode`@12 +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }a.\par +\ifnexttok *{je vais lire une étoile : }{je ne vais pas lire une étoile : } *.\par +\ifnexttok *{je vais lire une étoile : }{je ne vais pas lire une étoile : }a.¤§*\ifnexttok¤| + +Cela fonctionne bien comme on l'attend notamment à l'avant-dernière ligne où l'étoile est séparée de l'accolade fermante par un espace. Le test est pourtant positif ce qui montre que les espaces sont bien ignorés. + +\begin{exercice} +Comment pourrait-on modifier la macro §\ifnexttok pour lui donner une option afin de choisir d'ignorer les espaces ou pas ? +\solution +Le plus simple est de définir un booléen \verb|\iftestspace| que l'on mettrait à faux par défaut et à vrai si l'on se trouve dans des conditions où l'on veut prendre en compte les espaces. + +\showcode|\catcode`@11 +\newif\iftestspace \testspacefalse¤\idx*\newif¤ +\def\deftok#1#2{\let#1= #2}\deftok\sptoken{ }¤§*\deftok §*\sptoken¤ +\def\ifnexttok#1#2#3{% #1=token #2=code vrai #3=code faux¤§*\ifnexttok¤ + \let\test@tok= #1% stocke le token à tester + \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter + \iftestspace \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@ii}%¤\idx*\futurelet¤ + \else \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@iii}%¤\idx*\futurelet¤ + \fi% après avoir défini la macro récursive selon le booléen, + \ifnexttok@i% l'exécuter +} +\def\ifnexttok@ii{% macro "normale" qui ne teste pas les espaces + \ifx\nxttok\test@tok \expandafter\true@code% exécuter le code vrai + \else \expandafter\false@code% sinon code faux + \fi +} +\def\ifnexttok@iii{% macro qui ignore les espaces + \ifx\nxttok\sptoken% si le prochain token est un espace¤§*\sptoken¤ + \def\donext{% + \afterassignment\ifnexttok@i% lire le token d'après¤\idx*\afterassignment¤ + \let\nxttok= % après avoir absorbé l'espace + }% + \else + \let\donext\ifnexttok@ii% sinon, faire le test "normal" + \fi + \donext% faire l'action décidée ci-dessus +} +\catcode`@12 +\testspacefalse +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : } W.\medbreak +\testspacetrue +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par +\ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : } W.¤§*\ifnexttok¤| +\end{exercice} + +Élaborons à présent une macro §\ifstarred qui permettrait de tester, lorsque placée à la fin du texte de remplacement d'une macro, si cette dernière est étoilée ou pas et agir en conséquence\idx*{macro étoilée}. La syntaxe serait : + +\centrecode-\ifstarred{<code vrai>}{<code faux>}- + +Gardons en mémoire qu'en cas de test positif avec §\ifnexttok, l'étoile n'est pas mangée. Il faut donc trouver un moyen de manger cette étoile et le plus simple est d'utiliser \verb|\firstoftwo| où le premier argument sera \verb|<code vrai>| et le second l'étoile, non encore lue. + +\centrecode/\def\ifstarred#1#2{% #1=code vrai #2=code faux¤§*\ifstarred¤ + \ifnexttok *% si le prochain token est une étoile¤§*\ifnexttok¤ + {\firstoftwo{#1}}% le 2e argument de \firstoftwo est "*" + {#2}% sinon, exécuter le code faux +}/ + +Comme l'argument \verb|{#2}| figure une seule fois dans la macro en dernière position, nous pouvons nous permettre de ne pas l'écrire dans le texte de remplacement de §\ifstarred. Au risque d'être bien moins compréhensible, le code peut donc se simplifier en : + +\centrecode-\def\ifstarred#1{\ifnexttok*{\firstoftwo{#1}}}- + +\showcode|\def\ifstarred#1{\ifnexttok*{\firstoftwo{#1}}}¤§*\ifstarred§*\ifnexttok¤ +\catcode`\@11 +\def\teststar{\ifstarred{\teststar@bf}{\teststar@it}}¤§*\ifstarred§*\teststar¤ +\def\teststar@bf#1{{\bf#1}}¤\idx*\bf¤ +\def\teststar@it#1{{\it#1\/}}¤\idx*\it\idx*[ (correction d'italique)]\/¤ +\catcode`\@12 + +Un essai \teststar{réussi} et un essai \teststar*{étoilé} réussi aussi.¤§*\teststar¤| + +\section{Parser du code}\label{parser.code}\idx*[|(]{parser du code} +\subsection{La macro \texttt{\char`\\parse}}§*\parse[|(] +L'utilisation conjointe de \idx\futurelet et de la lecture d'arguments est la méthode qu'il nous faut pour parcourir et analyser du code (l'anglicisme \emph{parser du code} est souvent employé). Inventons une syntaxe simple :§*\parse + +\centrecode-\parse<code à parser>\parsestop-§*\parsestop + +\noindent Le bond en avant que permet \idx\futurelet est de savoir, avant de l'avoir absorbé, si le token qui suit dans le code est un token « à problème » comme le sont l'espace et l'accolade ouvrante. Le principe va être de parcourir le code et selon ce que l'on rencontre, ajouter des tokens au fur et à mesure dans un collecteur (qui sera un registre de tokens \verb|\code@toks|). + +Nous allons nous fixer comme objectif que si le \verb|<code à parser>| contient un texte entre accolades, celui-ci sera ajouté tel quel au collecteur. En revanche, pour les autres tokens y compris l'espace, un test sera fait et selon le token rencontré, on pourra choisir ce que l'on ajoute au collecteur. Cela implique qu'un test sera effectué à chaque itération sur le prochain token non encore absorbé. + +Voici l'algorithme qui va parser le code : + +\begin{algo} + \item initialiser \verb|\code@toks| à vide; + \item lire le prochain token \verb|\nxttok| avec \idx\futurelet. + \item si \verb|\nxttok| est §\parsestop, manger §\parsestop et afficher le collecteur \verb|\code@toks|; + \item si \verb|\nxttok| est différent de §\parsestop + \begin{algo} + \item si \verb|\nxttok| est un espace, manger l'espace et aller en 5 en transmettant «\verb*| |» ; + \item si \verb|\nxttok| est une accolade ouvrante (qui est vue comme un \idx\bgroup par \idx\futurelet), lire l'argument formé par le texte entre accolades et l'ajouter à \verb|\code@toks| puis retourner en 2; + \item dans les autres cas, aller en 5 pour lire ce prochain token; + \end{algo} + \item tester le token transmis en argument et, selon l'issue du test, ajouter ce que l'on souhaite à \verb|\code@toks| puis, retourner en 2. +\end{algo} + +Du point de vue des macros à écrire, le point \no1 est effectué par la macro chapeau §\parse, le point \no2 sera l'affaire d'une macro auxiliaire \verb|\parse@i| et les points \nos3 et 4 seront effectués par la macro \verb|\parse@ii|. Le point \no5 sera une macro \emph{publique} «§\testtoken» à un argument, écrite par l'utilisateur pour y effectuer les tests qu'il souhaite sur l'argument et selon l'issue de ceux-ci, ajouter ce qu'il souhaite au collecteur de tokens. + +\label{parse}\showcode|\catcode`\@11 +\def\parsestop{\parsestop}% définit la macro-quark se trouvant en fin de code¤§*\parsestop¤ +\newtoks\code@toks% registre contenant le code lu¤\idx*\newtoks¤ +\def\parseadd{\addtotoks\code@toks}¤§*\addtotoks¤ +\def\parse{%¤§*\parse¤ + \code@toks={}% initialise \code@toks à vide + \parse@i% et passe la main à \parse@i +} +\def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token et va à \parse@ii¤\idx*\futurelet¤ +\def\parse@ii{% + \ifxcase\nxttok¤§*\ifxcase¤ + \parsestop\parsestop@i% si la fin va être atteinte, aller à \parsestop@i¤§*\parsestop¤ + \sptoken\read@space% si un espace va être lu, aller à \read@space¤§*\sptoken¤ + \bgroup\read@bracearg% si une accolade ouvrante aller à \read@bracearg¤\idx*\bgroup¤ + \elseif¤§*\elseif¤ + \testtoken% dans les autres cas, aller à \testtoken¤§*\testtoken¤ + \endif¤§*\endif¤ +} + +\def\parsestop@i\parsestop{% la fin est atteinte : manger \parsestop + \the\code@toks% afficher le registre de tokens¤\idx*\the¤ +} +\expandafter\def\expandafter\read@space\space{% manger un espace dans le code¤\idx*\space¤ + \testtoken{ }% et aller à \testtoken¤§*\testtoken¤ +} +\def\read@bracearg#1{% l'argument entre accolades est lu + \parseadd{{#1}}% puis ajouté (entre accolades) tel quel à \code@toks + \parse@i% ensuite, lire le prochain token +} +\catcode`@12 + +{\bf Exemple 1 :} +\catcode`@11 +\def\testtoken#1{% macro qui teste le token¤§*\testtoken¤ + \ifxcase{#1}¤§*\ifxcase¤ + a{\parseadd{e}}% remplacer a par e + e{\parseadd{i}}% e par i + i{\parseadd{o}}% i par o + o{\parseadd{u}}% o par u + u{\parseadd{y}}% u par y + y{\parseadd{a}}% y par a + \elseif¤§*\elseif¤ + \parseadd{#1}% sinon, ajouter le token tel quel + \endif¤§*\endif¤ + \parse@i% aller lire le token suivant +}% +\catcode`@12 +\parse +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, corses ou grecques} assez inattendues.¤\defline\aaa\idx*\bf¤ +\parsestop\medbreak¤\idx*\medbreak§*\parsestop¤ + +{\bf Exemple 2 :}¤\idx*\bf¤ +\leavevmode \frboxsep=1pt ¤\idx*\leavevmode§*\frboxsep¤ +\catcode`@11 +\def\testtoken#1{%¤§*\testtoken¤ + \ifxcase{#1}% si #1 est un espace¤§*\ifxcase¤ + { }{\parseadd{\hskip 0.75em }}% ajouter un espace + \ {\parseadd{\hskip 0.75em }} + \elseif¤§*\elseif¤ + \parseadd{\frbox{#1}}% sinon, l'encadrer¤§*\frbox¤ + \endif¤§*\endif¤ + \parse@i}% +\catcode`@12 +\parse Programmer en \TeX\ est facile\parsestop\medbreak¤\idx*\medbreak§*\parsestop¤ + +{\bf Exemple 3 :} +\catcode`@11 +\def\testtoken#1{%¤§*\testtoken¤ + \ifx a#1\parseadd{X}\else\parseadd{#1}\fi% remplace les "a" par des "X" + \parse@i +} +\catcode`@12 +\parse a\bgroup\bf braca\egroup dabra\parsestop¤§*\parse\idx*\bgroup\idx*\egroup§*\parsestop¤| + +On le voit, le code entre accolades (ligne \no\aaa) n'a pas été modifié, c'est ce que l'on s'était fixé dans le cahier des charges. + +\begin{exercice} +Une zone d'ombre subsiste quant à la séquence de contrôle \idx\bgroup. En effet, une accolade ouvrante explicite sera vue par \idx\futurelet comme \idx\bgroup. Par conséquent, la macro §\parse \emph{ne sait pas distinguer} une accolade ouvrante de la séquence de contrôle \idx\bgroup. + +\begin{enumerate} + \item Décrire ce que contient le registre de tokens \verb|\code@toks| lors de l'exemple \no3. + \item \Qu elle difficulté se présente si on souhaite parser «\verb|\hbox\bgroup XY\egroup|» ? + \item Comment peut-on résoudre cette difficulté ? +\end{enumerate} +\solution +Lorsque le token qui doit être lu est \idx\bgroup, la façon dont est programmée \verb|\parse@ii| lui fait croire à tort que ce qui suit est une accolade ouvrante. La macro \verb|\read@bracearg| lit donc l'argument (qui est ici \idx\bgroup) et le met entre accolades. Le registre de tokens contient donc + +\centrecode-X{\bgroup}\bf brXcX\egroup dXbrX- +\grandsaut + +Pour la question \no2, la réponse est donnée à la question précédente. En effet, si l'on parse + +\centrecode|\hbox\bgroup XY\egroup| + +le registre de tokens va contenir + +\centrecode|\hbox{\bgroup}XY\egroup| + +La \idx\hbox va donc contenir un simple \idx\bgroup au lieu du \verb|XY|. +\grandsaut + +Répondre à la question \no3 est difficile. Pour résoudre le problème, nous allons tout d'abord construire une macro §\ifbracefirst de syntaxe + +\centrecode-\ifbracefirst{<argument>}{<code vrai>}{<code faux>}-§*\ifbracefirst + +\noindent qui exécute \verb|<code vrai>| si son \verb|<argument>| commence par une accolade ouvrante (ou tout autre token de catcode 1) et \verb|<code faux>| sinon. + +Le simple fait que §\ifbracefirst admette un argument et non un token implique de renoncer à la lecture \emph{au fur et à mesure} du \verb|<texte>| entre §\parse et §\parsestop. Nous sommes contraints de lire la \emph{totalité} de ce qui reste avant §\parsestop et de transmettre cet \verb|<argument>| à §\ifbracefirst. + +Pour mener à bien sa mission, cette macro va détokéniser son \verb|<argument>|, n'en garder que le premier token et, avec \idx\catcode, regarder s'il s'agit d'un token ayant un catcode égal à 1. Comme \idx\catcode attend de lire un nombre, un développement maximal est lancé et un pont d'\idx\expandafter détokénise d'abord l'argument \verb|#1| puis en isole le premier token : + +\showcode/\catcode`\@11 +\def\ifbracefirst#1{%¤§*\ifbracefirst¤ + \ifnum\catcode\expandafter\expandafter\expandafter + `\expandafter\firstto@nil\detokenize{#1W}\@nil=1 % tester si son catcode est 1¤§*\firstto@nil¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi +} +\catcode`\@12 + +a) \ifbracefirst{123 456}{vrai}{faux}\qquad +b) \ifbracefirst{\bgroup12\egroup3 456}{vrai}{faux}\qquad +c) \ifbracefirst{{12}3 456}{vrai}{faux}\qquad +d) \ifbracefirst{1{2}3 456}{vrai}{faux}\qquad +\begingroup +\catcode`[=1 \catcode`]=2 % les crochets deviennent des accolades +e) \ifbracefirst[[]123 456][vrai][faux]¤§*\ifbracefirst¤ +\endgroup/ + +Le «\verb|W|», écrit dans l'argument de \idx\detokenize, nous met à l'abri d'une erreur de compilation en cas d'\verb|<argument>| vide. Par ailleurs, comme il est assez improbable que «\verb|W|» ait un catcode 1 lorsque la macro est appelée, un \verb|<argument>| vide donnera un test négatif. + +Malgré cela, ce code comporte une erreur de programmation : si l'\verb|<argument>| commence par un espace, cet espace sera ignoré par §\firstto@nil et donc, le test «§\ifbracefirst\verb*|{ {}}|» sera positif. Pour se prémunir de ce bug, il suffit au préalable d'effectuer le test §\ifspacefirst et s'il est positif, agir en conséquence. + +\showcode/\catcode`\@11 +\def\ifbracefirst#1{% teste si #1 commence par un token de catcode 1¤§*\ifbracefirst¤ + \ifspacefirst{#1}% si #1 commence par un espace¤§*\ifspacefirst¤ + {\secondoftwo}% renvoyer faux + {\ifnum\catcode\expandafter\expandafter\expandafter + `\expandafter\firstto@nil\detokenize{#1W}\@nil=1 % tester si son catcode est 1¤§*\firstto@nil¤ + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi + }% +} + +\catcode`\@12 +a) \ifbracefirst{}{vrai}{faux}\qquad b) \ifbracefirst{ }{vrai}{faux}\qquad +c) \ifbracefirst{ {}}{vrai}{faux}¤§*\ifbracefirst¤/ + +Revenons maintenant à la macro \verb|\read@bracearg|. Voici son fonctionnement : tout d'abord, elle rajoute un token (ici \verb|\relax|) avant ce qui reste à parser. Cette précaution évite que ce qui reste ne soit dépouillé des accolades s'il s'agit d'un unique texte entre accolades. Ensuite, elle passe la main à \verb|\read@bracearg@i| dont l'argument, délimité par §\parsestop, est tout ce qui reste à parser. Après avoir ôté le \verb|\relax|, cet argument est testé avec §\ifbracefirst. \label{parsestop.obligatoire}Remarquons que faire de §\parsestop un délimiteur d'argument le rend \emph{obligatoire} dans la syntaxe de §\parse et introduit une contrainte assez forte. Par exemple, il ne sera pas possible de définir une autre séquence de contrôle \idx\let-égale à §\parsestop et utiliser cette macro en lieu et place de §\parsestop. + +La macro §\testtoken est réduite à sa plus simple expression : elle ne fait que rajouter le token lu au collecteur de tokens \verb|\code@toks| sans le modifier. + +Pour s'assurer que le code est parsé correctement, au lieu d'écrire \verb|\the\code@toks| à la fin du processus, on a écrit «\idx\detokenize\verb-\expandafter{\the\code@toks}-» de façon à afficher le contenu du registre de tokens. On aurait également pu écrire «\idx\showthe\verb|\code@toks|» pour écrire le contenu de ce registre dans le \idx[!log]{fichier} \verb|log|. + +Le code ci-dessous ne montre que la macro \verb|\read@bracearg| puisqu'elle seule a été modifiée. + +\showcode/\catcode`\@11 +\def\parsestop@i\parsestop{% la fin va être atteinte + \detokenize\expandafter{\the\code@toks}% afficher le contenu du registre¤\idx*[|etc]\the\forbidindex\the¤ +} +\def\read@bracearg{% + \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i +} +\def\read@bracearg@i#1\parsestop{% #1 = tout jusqu'à \parsestop¤§*\parsestop¤ + \exparg\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{"¤§*\exparg§*\ifbracefirst¤ + {\expandafter\read@bracearg@ii\gobone#1\parsestop}% si oui, aller à \readbrace@ii¤§*\parsestop¤ + {\expandafter\testtoken\gobone#1\parsestop}% sinon, aller à \testtoken¤§*\parsestop§*\testtoken¤ +} +\def\read@bracearg@ii#1{% lit l'argument entre accolades + \parseadd{{#1}}% ajoute cet argument entre accolades + \parse@i% aller lire le token suivant +} + +\def\testtoken#1{%¤§*\testtoken¤ + \parseadd{#1}% ajouter le token tel quel + \parse@i% aller lire le token suivant +} +\catcode`\@12 +\parse a\hbox\bgroup\bf braca\egroup {da}{}b{ra}\parsestop¤\idx*\hbox\idx*\bgroup\idx*\bf\idx*\egroup§*\parsestop¤/% + +Comme nous obtenons exactement ce qui est parsé, nous avons l'assurance que la macro \verb|\read@brace| fait désormais correctement son travail. +\end{exercice} + +\subsection{Parser aussi l'intérieur des groupes} +Le fait que la macro §\parse n'aille pas voir à l'intérieur des accolades est certes une règle que l'on s'était fixée, mais on pourrait trouver utile d'ajouter une option pour que cette macro pénètre aussi dans les groupes entre accolades. En guise d'option, une étoile sera mise juste après la macro. Pour que l'option soit stockée quelque part, nous créerons un nouveau booléen \verb|\ifparse@groups| que l'on mettra à vrai dans la version étoilée\idx*{macro étoilée} et à faux sinon. Ensuite, on sent bien que le n\oe ud du problème se trouve dans la macro \verb|\read@bracearg|, seul endroit où le parseur s'attend à lire un argument entre accolades. + +La macro \verb|\read@bracearg| doit donc être réécrite. Elle testera le booléen et s'il est faux, il faudra qu'elle agisse comme précédemment. S'il est vrai, \verb|\read@bracearg| doit aller parser l'argument \verb|#1| qu'elle a lu. Pour ce faire, elle exécutera ce code \emph{dans un groupe semi-simple} : + +\centrecode-\parse*#1\parsestop-§*\parsestop + +Une fois que le quark §\parsestop est atteint, il ne faut pas afficher \verb|\code@toks| comme le faisait \verb|\parsestop@i|. La macro \verb|\parsestop@i| doit donc être redéfinie localement afin de lui faire exécuter la chose suivante : ajouter le contenu de \verb|\code@toks| collecté dans le groupe (sans oublier de l'envelopper d'accolades) au contenu de \verb|\code@toks| tel qu'il était à l'extérieur du groupe. Le \verb|\endgroup| fait donc partie prenant de \verb|\parsestop@i|. Afin que cet ajout soit correctement mené à bien, un pont d'\idx\expandafter sera mis en place pour retarder la sortie du groupe et développer au préalable «\verb|\the\code@toks|» qui est l'argument de \verb|\parseadd| : + +\centrecode|\expandafter\endgroup% ne fermer le groupe qu'après avoir +\expandafter\parseadd% 1-développé à l'extérieur du groupe +\expandafter{\expandafter{\the\code@toks}}% "\the\code@toks"| + +Une fois cette action effectuée, il appartient à \verb|\parsestop@i| de poursuivre le parsing en appelant la macro \verb|\parse@i| qui lira le token suivant. + +Voici le code complet : + +\label{parse.a}\showcode|\catcode`\@11 +\def\parsestop{\parsestop}% définit le quark se trouvant en fin de code¤§*\parsestop¤ +\newtoks\code@toks% alloue le registre contenant le code lu¤\idx*\newtoks¤ +\def\parseadd#1{\code@toks\expandafter{\the\code@toks#1}} +\newif\ifparse@group¤\idx*\newif¤ +\def\parse{% + \code@toks{}% initialise le collecteur de tokens + \ifstarred% teste si la macro est étoilée¤§*\ifstarred¤ + {\parse@grouptrue\parse@i}% mettre le booléen à vrai + {\parse@groupfalse\parse@i}% sinon à faux +} +\def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token + % et va à \parse@ii¤\idx*\futurelet¤ +\def\parse@ii{% + \ifxcase\nxttok¤§*\ifxcase¤ + \parsestop\parsestop@i% si la fin va être atteinte, aller à \parsestop@i¤§*\parsestop¤ + \sptoken\read@space% si un espace va être lu, aller à \read@space¤§*\sptoken¤ + \bgroup\read@bracearg% si une accolade ouvrante aller à \read@bracearg¤\idx*\bgroup¤ + \elseif¤§*\elseif¤ + \testtoken% dans les autres cas, aller à \testtoken¤§*\testtoken¤ + \endif¤§*\endif¤ +} +\def\parsestop@i\parsestop{% la fin est atteinte + \the\code@toks% afficher le registre de tokens +} +\expandafter\def\expandafter\read@space\space{% \read@space mange un espace dans le code¤\idx*\space¤ + \testtoken{ }% et va à \testtoken¤§*\testtoken¤ +} +\def\read@bracearg{% + \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i +} +\def\read@bracearg@i#1\parsestop{% l'argument tout jusqu'à \parsestop¤§*\parsestop¤ + \expsecond\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{"¤§*\ifbracefirst§*\expsecond¤ + {\expandafter\read@bracearg@ii\gobone#1\parsestop}% lire l'argument entre accolades + {\expandafter\testtoken\gobone#1\parsestop}% sinon, tester le token¤§*\parsestop§*\testtoken¤ +} +\def\read@bracearg@ii#1{% l'argument entre accolades est lu + \ifparse@group\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% si macro étoilée + {\begingroup% ouvre un groupe pour parser l'intérieur de l'accolade + \def\parsestop@i\parsestop{% redéfinir localement \parsestop@i pour + \expandafter\endgroup% ne fermer le groupe qu'après avoir + \expandafter\parseadd% 1-développé à l'extérieur du groupe + \expandafter{\expandafter{\the\code@toks}}%¤\idx\the¤ + \parse@i% puis va lire le token suivant + }% + \parse*#1\parsestop% <- le \parsestop@i fermera le groupe semi-simple¤§*\parsestop¤ + } + {\parseadd{{#1}}% macro non étoilée, on ajoute #1 tel quel entre accolades + \parse@i% puis va lire le token suivant + }% +} +\def\testtoken#1{% macro qui teste le token¤§*\testtoken¤ + \ifxcase{#1}¤§*\ifxcase¤ + a{\parseadd{e}} + e{\parseadd{i}} + i{\parseadd{o}} + o{\parseadd{u}} + u{\parseadd{y}} + y{\parseadd{a}} + \elseif¤§*\elseif¤ + \parseadd{#1}% + \endif¤§*\endif¤ + \parse@i% aller lire le token suivant +} +\catcode`@12 +\frboxsep=1pt +a) \parse +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues.¤§*\frbox¤ +\parsestop\medbreak¤\idx*\medbreak§*\parsestop¤ + +b) \parse* +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues.¤\idx*\bf§*\frbox¤ +\parsestop¤§*\parsestop¤| + +\begin{exercice} +Expliquer d'où vient l'espace indésirable devant «Ci» que l'on constate lorsqu'on appelle la \idx{macro étoilée} et proposer un remède pour le supprimer. +\solution + +La présence de l'étoile fait que le retour à la ligne qui la suit est interprété comme un espace et n'est pas ignoré puisque dans ce cas, l'espace ne suit pas une séquence de contrôle. + +Il va donc falloir tester si un espace suit l'étoile et dans l'affirmative, manger cet espace avant de commencer le processus. Un simple «§\ifnexttok\verb*|{ }|» tester si un espace suit l'étoile et dans l'affirmative, cet espace sera mangé en mettant à la fin du texte de remplacement de la macro : + +\centrecodespc-\afterassignment\parse@i\let\nxttok= - + +Cette macro devient donc : + +\showcode/\catcode`\@11 +\def\parse{% + \code@toks{}% initialise le collecteur de tokens + \ifstarred¤§*\ifstarred¤ + {\parse@grouptrue + \ifnexttok{ }% si un espace suit l'étoile¤§*\ifnexttok¤ + {\afterassignment\parse@i% aller à \parse@i¤\idx*\afterassignment¤ + \let\nxttok= }% après l'avoir mangé + {\parse@i}% sinon, aller à \parse@i + } + {\parse@groupfalse + \parse@i + }% +} +\def\testtoken#1{% macro qui teste le token¤§*\testtoken¤ + \ifxcase{#1}¤§*\ifxcase¤ + a{\parseadd{e}}e{\parseadd{i}}i{\parseadd{o}}o{\parseadd{u}} + u{\parseadd{y}}y{\parseadd{a}} + \elseif¤§*\elseif¤ + \parseadd{#1}% + \endif¤§*\endif¤ + \parse@i% aller lire le token suivant +} +\catcode`@12 +\frboxsep=1pt +a) \parse +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues. +\parsestop\medbreak¤\idx*\medbreak§*\parsestop¤ +b) \parse* +Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es +{\bf catalanes, \frbox{corses} ou grecques} assez inattendues.¤\idx*\bf§*\frbox¤ +\parsestop¤§*\parsestop¤/ +\end{exercice}\idx*[|)]{parser du code}§*\parse[|)] + +\section{Rechercher, remplacer un ensemble de tokens} +Nous avions déjà écrit la macro §\ifin à la page~\pageref{ifin} qui permettait de savoir si un texte contenait un ensemble de tokens. Nous utilisions des arguments délimités et la limitation qui en découlait était que le motif cherché ne pouvait pas contenir d'accolades. Heureusement, nous allons pouvoir contourner cette difficulté en parcourant le code token par token. + +Pour parvenir à nos fins, il nous suffit de programmer une macro capable de tester si un \verb|<texet>| commence par un \verb|<motif>|. + +\subsection{Un ensemble de tokens commence-t-il par un motif?} +La fabrication de la macro §\ifstartwith, dont le but est de tester si un \verb|<texte>| commence par un \verb|<motif>| va donc devenir le seul enjeu pour l'instant. En voici la syntaxe : + +\centrecode-\ifstartwith{<texte>}{<motif>}{<vrai>}{<faux>}- + +Avant d'en arriver à cette macro, la difficulté se concentre dans l'écriture d'une macro auxiliaire qui ampute le premier argument du texte du remplacement d'une \verb|<macro 1>| et le place dans le texte de remplacement de \verb|<macro 2>|. Le mot «argument» doit ici être compris dans un sens \emph{élargi} puisqu'il peut être un texte entre accolades, un unique token ou \emph{un espace}. + +Cette macro sera baptisée \verb|\grab@first| et admettra deux arguments qui seront des séquences de contrôle + +\centrecode-\grab@first\<macro1>\<macro2>- + +\noindent Le principe va consister à tester si le texte de remplacement de \verb|\<macro1>| commence par une accolade et sinon, utiliser \idx\futurelet pour déterminer s'il commence par un espace ou par autre chose. Selon les trois cas envisagés, on s'orientera vers une macro qui lira convenablement le premier argument de \verb|<macro1>| et le déplacera dans \verb|\<macro2>| tout en assignant ce qu'il reste à \verb|\<macro1>|. + +\showcode/\catcode`\@11 +\def\grab@first#1#2{% + \ifx#1\empty\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\let#2\empty% si #1 est vide, ne rien faire et assigner <vide> à #2 + }% si #1 n'est pas vide + {\def\arg@b{#2}% stocke la macro #2 dans \arg@b + \exparg\ifbracefirst#1% si le 1er token de #1 est "{"¤\defline\aaa §*\ifbracefirst§*\exparg¤ + {\expandafter\grab@arg#1\@nil#1% aller lire l'argument avec \grab@arg + } + {% sinon, développer #1 avant de le regarder avec \futurelet : + \expandafter\futurelet\expandafter\nxttok\expandafter\test@nxttok#1\@nil#1%¤\defline\bbb\idx*\futurelet¤ + % puis aller à \test@nxttok + }% + }% +} +\def\test@nxttok{% si le premier token de l'arg #1 de \grab@first est + \ifx\nxttok\sptoken% un espace¤§*\sptoken¤ + \expandafter\grab@spc% aller le lire avec \grab@spc + \else + \expandafter\grab@tok% sinon, lire le token avec \grab@tok + \fi +} +\def\grab@arg#1{% assigne l'argument de \grab@first (mis entre accolades) + \expandafter\def\arg@b{{#1}}% à #2 + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +\expandafter\def\expandafter\grab@spc\space{%¤\idx*\space¤ + \expandafter\def\arg@b{ }% assigne un espace à #2 de \grab@first + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +\def\grab@tok#1{%% assigne le premier token de l'arg #1 de \grab@first + \expandafter\def\arg@b{#1}% à la macro #2 de \grab@first + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +% assigne tout ce qui reste à lire (moins le "\relax") à la macro +% #1 de \grab@first +\def\assign@tonil#1\@nil#2{\expsecond{\def#2}{\gobone#1}}¤§*\expsecond¤ +\tt +a) \def\foo{1 {2 3} 4}\grab@first\foo\bar + "\meaning\bar"\qquad"\meaning\foo" + +b) \def\foo{ 1 {2 3} 4}\grab@first\foo\bar + "\meaning\bar"\qquad"\meaning\foo"" + +c) \def\foo{{1 2} 3 4}\grab@first\foo\bar + "\meaning\bar"\qquad"\meaning\foo"/ + +Revenons sur la macro \verb|\grab@first| et ses macros auxiliaires\ldots{} On constate dans son texte de remplacement que, en plus du cas trivial où \verb|#1| est vide, l'aiguillage se fait en deux temps selon ce par quoi commence le texte de remplacement de la macro \verb|#1|. Le premier test fait concerne l'accolade ouvrante (ligne \no\aaa). Afin d'envisager tous les cas, supposons que le test est vrai : le texte de remplacement de \verb|#1| est de la forme «\verb|{<code a>}<code b>|». L'appel suivant est alors effectué : + +\centrecode-\expandafter\grab@arg#1\@nil#1- + +\noindent L'\idx\expandafter fait apparaitre ce texte de remplacement puis \verb|\grab@arg| en capture l'argument \verb|<code a>| pour le mettre dans le texte de remplacement de la macro \verb|#2|, après l'avoir rhabillé d'accolades. Une fois ceci fait, il reste à lire le code suivant : + +\centrecode-<code b>\@nil#1- + +Rappelons-nous de ce code restant et passons maintenant à l'autre éventualité, celle où le texte de remplacement de \verb|#1| ne commence pas par une accolade. Le texte de remplacement de \verb|#1| est de la forme «\verb|<token a><code b>|». Dans ce cas, on passe la main à cette ligne : + +\centrecode-\expandafter\futurelet\expandafter\nxttok\expandafter\test@nxttok#1\@nil#1-\idx*\futurelet + +\noindent Tout d'abord, un pont d'\idx\expandafter développe la macro \verb|#1| pour faire apparaitre son texte de remplacement ce qui donne + +\centrecode-\futurelet\nxttok\test@nxttok<token a><code b>\@nil#1-\idx*\futurelet + +Ensuite, le \idx\futurelet va rendre \verb|\nxttok| \idx\let-égal à \verb|<token a>|. La macro \verb|\test@nxttok| est appelée par le \idx\futurelet pour tester ce qu'est \verb|<token a>| et selon qu'il est un espace ou pas, appelle \verb|\grab@spc| ou \verb|\grab@tok|. Ces deux macros capturent le \verb|<token a>| pour le mettre dans le texte de remplacement de \verb|#2| et le code qui reste à lire après cette opération est ici encore : + +\centrecode-<code b>\@nil#1- + +\Qu e le texte de remplacement de \verb|#1| commence par un argument entre accolades, un espace ou un token, il reste dans tous les cas la même chose à lire + +\centrecode|<code b>\@nil#1| + +\noindent Le code «\verb|\assign@tonil\relax|» est exécuté à la toute fin de chacune des trois macros \verb|\grab@arg|, \verb|\grab@spc| et \verb|\grab@tok| devant ce code restant de telle sorte que l'on a formé l'appel + +\centrecode-\assign@tonil\relax<code b>\@nil#1- + +\noindent La macro \verb|\assign@tonil| mange ce \verb|\relax| avec \verb|\assign@tonil| puis assigne à la \verb|\<macro1>| (qui est l'argument \verb|#1|) le \verb|<code b>| et ceci termine le processus. Ouf, nous avons fait le plus dur ! +\grandsaut + +Ayant construit \verb|\grab@first|, il suffit ensuite pour §\ifstartwith d'extraire le premier argument élargi de \verb|<texte>| et de \verb|<motif>|, de les comparer et renvoyer \verb|<faux>| s'il ne sont pas égaux et dans le cas contraire, recommencer jusqu'à ce que le \verb|<motif>| soit vide. À ce moment-là, comme aucune comparaison n'a été fausse, il faut renvoyer \verb|<vrai>|. Le cas où le \verb|<texte>| devient vide \emph{avant} le \verb|<motif>| doit être envisagé, car cela signifie que le \verb|<texte>| est plus court que le \verb|<motif>| et dans cette éventualité, il faut renvoyer \verb|<faux>|. + +\showcode|\catcode`\@11 +\def\ifstartwith#1#2{% #1=<texte> #2=<motif>¤§*\ifstartwith¤ + \ifempty{#2}¤§*\ifempty¤ + \firstoftwo% si <motif> est vide, renvoyer vrai + {\ifempty{#1}% si <code> est vide et <motif> non vide¤§*\ifempty¤ + \secondoftwo% renvoyer faux + {\def\startwith@code{#1}\def\startwith@pattern{#2}% + \ifstartwith@i% dans les autres cas, aller à \ifstartwith@i + }% + }% +} +\def\ifstartwith@i{% + \grab@first\startwith@code\first@code% extrait le premier "argument" de <texte> + \grab@first\startwith@pattern\first@pattern% et celui de <motif> + \ifx\first@code\first@pattern\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {% si les deux arguments élargis sont égaux + \exparg\ifempty\startwith@pattern¤§*\exparg§*\ifempty¤ + \firstoftwo% et que <motif> ne contient plus rien => vrai + {\exparg\ifempty\startwith@code¤§*\exparg§*\ifempty¤ + \secondoftwo% si <texte> ne contient plus rien => faux + \ifstartwith@i% sinon poursuivre les tests + }% + } + \secondoftwo% si les deux argument élargis sont différents, renvoyer faux +} +\catcode`\@12 + +1) \ifstartwith{a b c}{a b}{oui}{non}\quad 2) \ifstartwith{a b}{a b c}{oui}{non}\quad +3) \ifstartwith{ 123 }{ }{oui}{non}\quad 4) \ifstartwith{ 123 }{1}{oui}{non}\quad +5) \ifstartwith{1{2} 3}{12}{oui}{non}\quad 6) \ifstartwith{1{2} 3}{1{2} }{oui}{non}\quad +7) \ifstartwith{{} {}}{ }{oui}{non}\quad 8) \ifstartwith{{} {}}{{} }{oui}{non}\quad +9) \ifstartwith{ {12} a}{ }{oui}{non}\quad10) \ifstartwith{ {12} a}{ {1}}{oui}{non}¤§*\ifstartwith¤| + +\subsection{Un code contient-il un motif ?} +Puisque nous avons à notre disposition la macro §\ifstartwith qui sera la brique essentielle pour la suite, poursuivons avec la macro §\ifcontain dont la syntaxe est + +\centrecode-\ifcontain{<texte>}{<motif>}{<vrai>}{<faux>}- + +\noindent et qui renverra \verb|<vrai>| si le \verb|<texte>| contient le \verb|<motif>| et \verb|<faux>| sinon. + +L'algorithme le plus naïf, que nous allons utiliser, consiste à tester si \verb|<motif>| est au début du \verb|<texte>|. Dans l'affirmative, il faut renvoyer \verb|<vrai>| et dans le cas contraire, manger le premier «argument élargi» de \verb|<texte>| et recommencer jusqu'à ce que \verb|<texte>| soit vide auquel cas, on renvoie \verb|<faux>|. + +\showcode|\catcode`\@11 +\def\ifcontain#1#2{% #1 contient-il #2?¤§*\ifcontain¤ + \def\main@arg{#1}\def\pattern@arg{#2}% stocke le <code> et le <motif> + \ifempty{#2}¤§*\ifempty¤ + \firstoftwo% si #2 est vide => vrai + {\ifempty{#1} + \secondoftwo% si #1 est vide et pas #2 => faux + \ifcontain@i% sinon, aller faire les tests + }% +} +\def\ifcontain@i{% + \exptwoargs\ifstartwith\main@arg\pattern@arg¤§*\exptwoargs§*\ifstartwith¤ + \firstoftwo% si motif est au début de code => vrai + {\exparg\ifempty\main@arg¤§*\exparg§*\ifempty¤ + \secondoftwo% sinon, si code est vide => faux + {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code + \ifcontain@i% et recommencer + }% + }% +} +\catcode`\@12 +1) \ifcontain{abc def}{c }{oui}{non}\quad 2) \ifcontain{abc def}{cd}{oui}{non}\quad +3) \ifcontain{12 34 5}{1 }{oui}{non}\quad 4) \ifcontain{12 34 5}{ }{oui}{non}\quad +5) \ifcontain{a{b c}d}{b c}{oui}{non}\quad 6) \ifcontain{a{b c}d}{{b c}}{oui}{non}\quad +7) \ifcontain{{} {}}{ }{oui}{non}\quad 8) \ifcontain{{} {}}{{}}{oui}{non}¤§*\ifcontain¤| + +On le voit au cas \no5, la macro §\ifcontain n'inspecte pas l'intérieur des groupes et donc, elle considère que le motif «\verb*|b c|» n'est pas contenu dans «\verb*|a{b c}d|». On pourrait souhaiter que la macro §\ifcontain aille aussi voir à l'intérieur des groupes lorsqu'elle est étoilée\idx*{macro étoilée}. Pour ajouter cette fonctionnalité, il va falloir modifier la macro \verb|\ifcontain@i| pour qu'elle teste si \verb|\main@arg| commence par une accolade ouvrante et dans ce cas, agir différemment. + +Voici l'algorithme que l'on pourrait suivre : + +\begin{algo} + \item si \verb|<motif>| est vide, renvoyer \verb|<vrai>|, sinon, si \verb|<texte>| est vide, renvoyer \verb|<faux>|. Si aucun n'est vide, aller au point \no2; + \item initialiser le booléen \verb|\ifin@group| à faux; + \item si \verb|<texte>| commence par «\verb|{|» + \begin{algo} + \item enlever le premier argument de \verb|<texte>| et le mettre dans une macro temporaire \verb|\aux@arg|; + \item ouvrir un groupe semi-simple; + \item initialiser le booléen \verb|\ifin@group| à \verb|vrai|; + \item dans ce groupe, prendre \verb|<texte>| égal à \verb|\aux@arg|; + \item aller au point 3. + \end{algo} + \item sinon + \begin{algo} + \item si \verb|<motif>| est au début de \verb|<texte>| sortir de tous les groupes et renvoyer \verb|<vrai>|\label{sortir.tous.les.groupes}; + \item sinon + \begin{algo} + \item si \verb|<texte>| est vide + \begin{algo} + \item si \verb|\ifin@group| est \verb|vrai| sortir du groupe semi-simple et aller en 3; + \item sinon renvoyer \verb|<faux>|; + \end{algo} + \item sinon retirer le premier argument élargi de \verb|<texte>| et aller en 3. + \end{algo} + \end{algo} +\end{algo} + +Distribuons les tâches. Les points 1 et 2 seront traités par une macro chapeau comme auparavant. Les points 3 et 4 seront écrits dans une macro récursive \verb|\ifcontain@star| alors que la macro récursive \verb|\ifcontain@nostar|, vue précédemment sous le nom de \verb|\ifcontain@i| sera employée dans le cas ou §\ifcontain n'est pas étoilé\idx*{macro étoilée}. + +Pour le point \ref{sortir.tous.les.groupes}, nous devons également écrire une macro \verb|\return@true| qui ferme tous les groupes et qui renvoie \verb|<vrai>|. Le booléen \verb|\ifin@group| rend la tâche facile puisqu'il est localement \verb|vrai| dans tous les groupes et faux à l'extérieur. Une simple macro récursive qui ferme successivement les groupes jusqu'à ce que le booléen soit faux est suffisante : + +\centrecode-\def\return@true{% + \ifin@group% si on est dans un groupe + \endgroup% le fermer + \expandafter\return@true% et recommencer + \else + \expandafter\firstoftwo% sinon, renvoyer vrai + \fi +}- + +Voici le code complet qui en résulte : + +\showcode|\catcode`@11 +\newif\ifin@group¤\idx*\newif¤ +\def\ifcontain{%¤§*\ifcontain¤ + \ifstarred¤§*\ifstarred¤ + {\in@groupfalse\ifcontain@i\ifcontain@star}% + {\ifcontain@i\ifcontain@nostar}} + +\def\ifcontain@i#1#2#3{% #1 = macro à appeler selon étoile ou pas. #2 = code. #3 = motif + \def\main@arg{#2}\def\pattern@arg{#3}% + \ifempty{#3}¤§*\ifempty¤ + \firstoftwo + {\ifempty{#2}¤§*\ifempty¤ + \secondoftwo + #1% aller à \ifcontain@star ou \ifcontain@nostar + }% +} +\def\ifcontain@nostar{% + \exptwoargs\ifstartwith\main@arg\pattern@arg¤§*\exptwoargs§*\ifstartwith¤ + \firstoftwo% si motif est au début de code => vrai + {\exparg\ifempty\main@arg¤§*\ifempty§*\exparg¤ + \secondoftwo% sinon, si code est vide => faux + {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code + \ifcontain@nostar% et recommencer + }% + }% +} +\def\ifcontain@star{% + \expandafter\ifbracefirst\expandafter{\main@arg}% si code commence par "{"¤§*\ifbracefirst¤ + {\grab@first\main@arg\aux@arg% enlever {<argument>} de main@arg + \begingroup% ouvrir un groupe + \in@grouptrue% mettre le booléen à vrai + \expandafter\def\expandafter\main@arg\aux@arg% assigner "argument" à \main@arg + \ifcontain@star% et recommencer avec ce nouveau \main@arg + }% si code ne commence pas par "{" + {\exptwoargs\ifstartwith\main@arg\pattern@arg% si motif est au début de code¤§*\exptwoargs§*\ifstartwith¤ + \return@true% renvoyer vrai + {\expandafter\ifempty\expandafter{\main@arg}% si code est vide¤§*\ifempty¤ + {\ifin@group% et que l'on est dans un groupe + \endgroup \expandafter\ifcontain@star% en sortir et recommencer + \else% si on n'est pas dans un groupe, le code a été parcouru + \expandafter\secondoftwo% sans trouver <motif> => renvoyer faux + \fi + }% si code n'est pas vide + {\grab@first\main@arg\startwith@code% manger le 1er "argument" de code + \ifcontain@star% et recommencer + }% + }% + }% +} +\def\return@true{% + \ifin@group% tant qu'on est dans un groupe + \endgroup \expandafter\return@true% en sortir et recommencer + \else + \expandafter\firstoftwo% sinon, renvoyer vrai + \fi +} +\catcode`@12 +1) \ifcontain{ab {c d}ef}{c}{oui}{non}\quad 2) \ifcontain*{ab {c d}ef}{c}{oui}{non}¤§*\ifcontain¤| + +\subsection{Remplacer un motif par un autre}\label{substitute}§*\substitute[|(] +Il nous reste une dernière étape, celle de remplacer dans \verb|<texte>| un \verb|<motif a>| par un \verb|<motif b>|. La syntaxe naturelle pour une telle macro est : + +\centrecode-\substitute{<texte>}{<motif a>}{<motif b>}- + +La recette est connue; il nous suffit de reprendre une méthode similaire à celle de la macro §\ifcontain (en version étoilée\idx*{macro étoilée} ou pas) et si le \verb|<texte>| restant à lire commence par le \verb|<motif>|, on ajoutera à un registre de tokens tenant lieu de collecteur le \verb|<motif b>|. Dans le cas contraire, il faudra ajouter à ce même registre le premier argument élargi du \verb|<texte>|. + +Comme il n'est pas question de fermer tous les groupes d'un coup, nous allons nous passer du booléen \verb|\ifin@group| en définissant une macro de fin de processus \verb|\substitute@end| qui sera appelée lorsque \verb|<texte>| sera vide. Ce \verb|<texte>| est vide dans deux situations; soit la totalité du texte initial a été parsée, soit nous sommes dans un groupe et le \verb|<texte>| local est parsé. En dehors de tout groupe ou pour la macro non étoilée\idx*{macro étoilée}, la macro \verb|\substitute@end| se contentera d'afficher le contenu du registre de tokens \verb|\subst@toks| : + +\centrecode-\def\substitute@end{\the\subst@toks}- + +En revanche, lorsque la macro est étoilée\idx*{macro étoilée} et que l'on inspecte un groupe, cette macro sera modifiée de la même façon qu'était modifiée la macro \verb|\parsestop@i| : \verb|\substitute@end| devra ajouter au collecteur \verb|\subst@toks| hors du groupe ce qu'il était dans le groupe, après l'avoir enveloppé d'accolades. + +\showcode|\catcode`@11 +\newtoks\subst@toks% registre de tokens pour stocker le <texte> modifié +\def\substitute{%¤§*\substitute¤ + \def\substitute@end{\the\subst@toks}% macro exécutée à la fin + \ifstarred¤§*\ifstarred¤ + {\let\recurse@macro\substitute@star \substitute@i}% + {\let\recurse@macro\substitute@nostar\substitute@i}% +} +\def\substitute@i#1#2#3#4{% #1=macro à appeler selon étoile ou pas. + % #2=<texte> #3=<motif> #4=<motif de substi> + \def\code@arg{#2}\def\pattern@arg{#3}\def\subst@arg{#4}% + \subst@toks={}% initialiser le collecteur à vide + #1% aller à \substitute@star ou \substitute@nostar +} +\def\substitute@nostar{% + \exptwoargs\ifstartwith\code@arg\pattern@arg% si le <texte> commence par <motif>¤\defline\aaa§*\exptwoargs§*\ifstartwith¤ + {\eaddtotoks\subst@toks\subst@arg% ajouter <motif subst>¤§*\eaddtotoks¤ + \grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \substitute@nostar% et recommencer + } + {\expandafter\ifempty\expandafter{\code@arg}%¤§*\ifempty¤ + {\substitute@end% sinon, si <texte> est vide => afficher le registre de tokens + } + {\grab@first\code@arg\aux@arg% autrement, manger le 1er "argument" de <texte> + \eaddtotoks\subst@toks\aux@arg% et l'ajouter au registre de tokens¤§*\eaddtotoks¤ + \substitute@nostar% et recommencer + }% + }%¤\defline\bbb¤ +} +\def\substitute@star{% + \expandafter\ifbracefirst\expandafter{\code@arg}% si <texte> commence par "{"¤§*\ifbracefirst¤ + {\grab@first\code@arg\aux@arg% enlever {<argument>} de \code@arg + \begingroup% ouvrir un groupe + \def\substitute@end{% modifier localement la macro exécutée à la fin + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\subst@toks\expandafter% ajouter au registre hors du groupe¤§*\addtotoks¤ + {\expandafter{\the\subst@toks}}% ce qui est collecté localement, mis entre {} + \substitute@star% puis recommencer + }% + \subst@toks{}% initialiser à vide + \expandafter\def\expandafter\code@arg\aux@arg% % assigner "argument" au <texte> + \substitute@star% et recommencer avec ce nouveau \code@arg + }% si code ne commence pas par "{" + {\exptwoargs\ifstartwith\code@arg\pattern@arg% si <motif> est au début de <texte>¤\defline\ccc§*\exptwoargs§*\ifstartwith¤ + {\eaddtotoks\subst@toks\subst@arg% ajouter <motif subst>¤§*\eaddtotoks¤ + \grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \substitute@star% et recommencer + } + {\expandafter\ifempty\expandafter{\code@arg}% si <texte> est vide¤§*\ifempty¤ + {\substitute@end% aller à la macro de fin + }% si <texte> n'est pas vide + {\grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \eaddtotoks\subst@toks\aux@code% et l'ajouter au registre¤§*\eaddtotoks¤ + \substitute@star% et recommencer + }% + }%¤\defline\ddd¤ + }% +} +\catcode`@12 +1) \substitute{ab{\bf racada}bra}{a}{W}\qquad +2) \substitute*{ab{\bf racada}bra}{a}{W}\qquad +3) \substitute{12\frbox{\bf 34}56}{\bf}{\it}\qquad +4) \substitute*{12\frbox{\bf 34}56}{\bf}{\it}¤§*\frbox¤| + +\begin{exercice} +Dans le code proposé, les lignes \nos\aaa-\bbb{} sont identiques aux lignes \nos\ccc-\ddd{}, exception faite des macros \verb|\substitute@star| et \verb|\substitute@nostar|. Proposer une amélioration afin de ne pas écrire deux fois cette portion de code. + +Définir une macro §\substitutetocs de syntaxe + +\centrecode-\substitutetocs{<texte>}{<motif a>}{<motif b>}\<macro>- + +\noindent qui assigne à \verb|\<macro>| le résultat de la substitution. + +\solution +On peut définir une macro auxiliaire \verb|\recurse@macro| qui, selon que la version est étoilée\idx*{macro étoilée} ou non, sera \idx\let-égale à \verb|\substitute@star| ou \verb|\substitute@nostar|. Il suffit dès lors de remplacer les occurrences de \verb|\substitute@star| et celles de \verb|\substitute@nostar| par \verb|\recurse@macro| et remplacer les lignes \no\ccc-\ddd{} par \verb|\substitute@nostar|. +\grandsaut + +Pour créer §\substitutetocs, nous allons nous mettre à profit la macro \verb|\substitute@end| qui est en réalité un \idx[ (méthode de programmation)]{hook}. Il sera redéfini pour que la \verb|\<macro>| (argument \verb|#4|) reçoive le contenu du registre de tokens \verb|\subst@toks|. Un \verb|\edef| peut être utilisé ici, car lorsque \idx\the\verb|<registre de tokens>| est placé dans un \verb|\edef|, le contenu de ce registre \emph{non développé} apparait dans le texte de remplacement de la macro ainsi définie (voir page~\pageref{bloquer.developpement.maximum}). + +Une fois le hook correctement défini, le déroulement de la macro \verb|\subtitute| se poursuit. + +\showcode|\catcode`@11 +\newtoks\subst@toks% registre de tokens pour stocker le <texte> modifié¤\idx\newtoks¤ +\def\substitute{%¤§*\substitute¤ + \def\substitute@end{\the\subst@toks}% macro exécutée à la fin + \ifstarred¤§*\ifstarred¤ + {\let\recurse@macro\substitute@star \substitute@i}% + {\let\recurse@macro\substitute@nostar\substitute@i}% +} +\def\substitute@i#1#2#3{% #1=<texte> #2=<motif> #3=<motif de substi> + \def\code@arg{#1}\def\pattern@arg{#2}\def\subst@arg{#3}% + \subst@toks={}% initialiser à vide + \recurse@macro% aller à \substitute@star ou \substitute@nostar +} +\def\substitute@nostar{% + \exptwoargs\ifstartwith\code@arg\pattern@arg% si le <texte> commence par <motif>¤§*\exptwoargs §*\ifstartwith¤ + {\eaddtotoks\subst@toks\subst@arg% ajouter <motif subst>¤§\eaddtotoks¤ + \grab@first\code@arg\aux@code% manger le 1er "argument" de <texte> + \recurse@macro% et recommencer + } + {\expandafter\ifempty\expandafter{\code@arg}%¤§*\ifempty¤ + \substitute@end% sinon, si <texte> est vide => afficher le registre de tokens + {\grab@first\code@arg\aux@arg% autrement, manger le 1er "argument" de <texte> + \eaddtotoks\subst@toks\aux@arg% et l'ajouter au registre de tokens¤§*\eaddtotoks¤ + \recurse@macro% et recommencer + }% + }% +} +\def\substitute@star{% + \expandafter\ifbracefirst\expandafter{\code@arg}% si <texte> commence par "{"¤§*\ifbracefirst¤ + {\grab@first\code@arg\aux@arg% enlever {<argument>} de \code@arg + \begingroup% ouvrir un groupe + \def\substitute@end{% modifier localement la macro exécutée à la fin + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\subst@toks\expandafter% ajouter au registre hors du groupe¤§*\addtotoks¤ + {\expandafter{\the\subst@toks}}% ce qui est collecté localement, mis entre {}¤\idx*\the¤ + \recurse@macro% puis recommencer + }% + \subst@toks{}% initialiser à vide + \expandafter\def\expandafter\code@arg\aux@arg% % assigner "argument" au <texte> + \recurse@macro% et recommencer avec ce nouveau \code@arg + }% si <texte> ne commence pas par "{" : + \substitute@nostar% exécuter macro non étoilée pour une seule itération +} +\def\substitutetocs{%¤§*\substitutetocs¤ + \ifstarred¤§*\ifstarred¤ + {\let\recurse@macro\substitute@star \substitutetocs@i}% + {\let\recurse@macro\substitute@nostar\substitutetocs@i}% +} +\def\substitutetocs@i#1#2#3#4{% #1=<texte> #2=<motif> #3=<motif de substi> #4=\<macro> + \def\substitute@end{\edef#4{\the\subst@toks}}% macro exécutée à la fin + \substitute@i{#1}{#2}{#3}% continuer comme avec \substitute +} +\catcode`@12 +\frboxsep=1pt +1) \substitute{ab{\bf racada}bra}{a}{W}\qquad +2) \substitute*{ab{\bf racada}bra}{a}{W}\qquad +3) \substitute{12\frbox{\bf 34}56}{\bf}{\it}\qquad +4) \substitute*{12\frbox{\bf 34}56}{\bf}{\it}¤§*\substitute\idx*\bf\idx*\it§*\frbox¤ +\medbreak + +1) \substitutetocs{ab{\bf racada}bra}{a}{W}\foo \meaning\foo\par +2) \substitutetocs*{ab{\bf racada}bra}{a}{W}\foo \meaning\foo\par +3) \substitutetocs{12\frbox{\bf 34}56}{\bf}{\it}\foo \meaning\foo\par +4) \substitutetocs*{12\frbox{\bf 34}56}{\bf}{\it}\foo \meaning\foo¤§*\substitutetocs\idx*\bf\idx*\it§*\frbox¤| +\end{exercice}§*\substitute[|)] + +\chapter{Des macros sur mesure} +\section{Macros à arguments optionnels}§*\vecteur[|(] +L'utilisation de §\ifnexttok va nous permettre de bâtir des macros acceptant des arguments \emph{optionnels}. Les arguments optionnels de ces macros ont vocation à se trouver entre des délimiteurs que l'on peut choisir comme on le souhaite (parenthèses, crochets\footnote{C'est la choix fait par \LaTeX{} et Con\TeX t.} ou autre), tout en rendant ces arguments et leurs délimiteurs \emph{facultatifs}. En cas d'absence d'argument optionnel, un argument \emph{par défaut}, fixé lors de la définition de la macro sera utilisé. Pour nous placer dans la continuité de ce qui se fait, nous prendrons les crochets pour délimiter les arguments optionnels. + +La méthode consiste, pour une macro chapeau, à tester avec §\ifnexttok si le prochain token est un délimiteur ouvrant. Si tel est le cas, il faut appeler une macro auxiliaire à argument délimité qui va lire l'argument optionnel entre délimiteurs. Dans le cas contraire, il faut transmettre à la macro à arguments délimités un argument optionnel entre délimiteurs que l'on choisit par défaut. + +Imaginons une macro \verb|\vecteur{<lettre>}| qui admet un argument obligatoire et qui se comporte de la façon suivante : + +\begin{center} + \begin{tabular}{c@{ donne }c} + \verb|\vecteur{n}|&$(x_1,\ldots,x_n)$\\ + \verb|\vecteur{k}|&$(x_1,\ldots,x_k)$\\ + \verb|\vecteur{i}|&$(x_1,\ldots,x_i)$ + \end{tabular} +\end{center} + +L'argument obligatoire \verb|<lettre>| est l'indice du dernier terme. On pourrait vouloir spécifier un argument \emph{optionnel} entre crochets qui viendrait juste après la macro et qui serait l'indice du premier terme. La valeur par défaut de cet argument serait 1. Voici comment procéder : + +\showcode/\catcode`@11 +\def\vecteur{% + \ifnexttok[% si la macro est suivie d'un crochet¤§*\ifnexttok¤ + \vecteur@i% aller à la macro à arguments délimités + {\vecteur@i[1]}% sinon, ajouter l'argument optionnel par défaut entre crochets +} +\def\vecteur@i[#1]#2{% #1=arg optionnel #2=arg obligatoire + $(x_{#1},\ldots,x_{#2})$¤\idx*\ldots¤ +} +\catcode`@12 +1) \vecteur{n}\qquad 2) \vecteur[0]{k}\qquad 3) \vecteur[i]j \qquad4) \vecteur[]n/ + +Bien que fonctionnant bien, on peut imaginer ce qu'il va se passer si cette macro est appelée alors que l'on est \emph{déjà} en mode mathématique\idx*{mode!mathématique} : le premier \verb|$| rencontré dans la macro va faire quitter ce mode et provoquer des erreurs de compilation avec le token «\verb|_|» qui n'opère qu'en mode mathématique. Il faut donc programmer une macro §\forcemath qui compose son argument en mode mathématique\idx*{mode!mathématique}, quel que soit le mode en cours. Pour cela, on va utiliser le test \tidx{ifmmode} qui est un test de \TeX{} : + +\centrecode-\ifmmode<code vrai>\else<code faux>\fi- + +Ce test est vrai si le mode mathématique\idx*{mode!mathématique} est en vigueur. C'est lorsqu'il est faux que la macro §\forcemath doit composer son argument entre délimiteurs mathématiques «\verb|$|». + +Une autre amélioration serait de programmer un deuxième argument optionnel pour spécifier la lettre des coordonnées du vecteur, en choisissant $x$ par défaut. Bien qu'il nous soit possible de le mettre entre crochets, nous allons, par souci de changement, mettre cet argument optionnel entre parenthèses : + +\showcode/\catcode`@11 +\def\forcemath#1{% compose #1 en mode math, quel que soit le mode en cours¤§*\forcemath\idx*{mode!mathématique}¤ + \ifmmode\expandafter\firstoftwo\else\expandafter\secondoftwo\fi¤\tidx*{ifmmode}¤ + {#1}{$#1$}% +} +\def\vecteur{% + \ifnexttok[% si la macro est suivie d'un crochet¤§*\ifnexttok¤ + \vecteur@i% aller à la macro à arguments délimités + {\vecteur@i[1]}% sinon, ajouter l'argument optionnel par défaut entre crochets +} +\def\vecteur@i[#1]{% + \ifnexttok(% si une parenthèse vient ensuite¤§*\ifnexttok¤ + {\vecteur@ii[#1]}% aller à la macro à argument délimités + {\vecteur@ii[#1](x)}% sinon, rajouter "x" comme argument optionnel +} +\def\vecteur@ii[#1](#2)#3{% #1 et #2=arg optionnel #3=arg obligatoire + \forcemath{({#2}_{#1},\ldots,{#2}_{#3})}%¤§*\forcemath¤ +} +\catcode`@12 +1) \vecteur{n}\qquad 2) \vecteur[0]{k}\qquad +3) \vecteur[i](y)j \qquad4) \vecteur[](\alpha)n/ + +\begin{exercice} +La macro \verb|\vecteur| ci-dessus admet deux arguments optionnels, mais ceux-ci doivent se trouver dans le bon ordre : celui entre crochets d'abord puis celui entre parenthèses et enfin l'argument obligatoire. On peut écrire \verb|\vecteur[i](y){j}|, mais pas \verb|\vecteur(y)[i]{j}|. + +Comment faudrait-il modifier la macro pour que les arguments optionnels puissent être tapés dans n'importe quel ordre ? +\solution +Le plus simple est d'aiguiller l'algorithme vers deux «branches» selon que la macro \verb|\vecteur| est suivie d'un crochet ou d'une parenthèse. Ces deux branches seront contenues dans les macros \verb|\vecteur@bracket| et \verb|\vecteur@paren|. + +\showcode/\catcode`@11 +\def\vecteur{% + \ifnexttok[% si la macro est suivie d'un crochet¤§*\ifnexttok¤ + \vecteur@bracket% lire cet argument entre crochet + {\ifnexttok(% sinon, si elle est suivie d'une parenthèse¤§*\ifnexttok¤ + \vecteur@paren % lire cet argument entre parenthèses + {\vecteur@i[1](x)}% sinon, transmettre les arguments par défaut + }% +} +\def\vecteur@bracket[#1]{% + \ifnexttok(% s'il y a une parenthèse après¤§*\ifnexttok¤ + {\vecteur@i[#1]}% lire la parenthèse + {\vecteur@i[#1](x)}% sinon, donne "x" comme argument par défaut +} +\def\vecteur@paren(#1){% + \ifnexttok[% si le caractère suivant est un crochet¤§*\ifnexttok¤ + {\vecteur@ii(#1)}% lire l'argument entre crochets + {\vecteur@ii(#1)[1]}% sinon, donner "1" comme argument par défaut +} +\def\vecteur@i[#1](#2)#3{\forcemath{({#2}_{#1},\ldots,{#2}_{#3})}}¤§*\forcemath¤ +\def\vecteur@ii(#1)[#2]{\vecteur@i[#2](#1)}% met les arg optionnels dans l'ordre +\catcode`@12 +1) \vecteur{n}\qquad 2) \vecteur[i](y){j}\qquad 3) \vecteur(y)[i]{j}\qquad +4) \vecteur[0]{k}\qquad 5) \vecteur(\alpha){n}/ +\end{exercice}§*\vecteur[|)] + +\section{Définir une macro à plusieurs arguments optionnels} +La tentation est grande de créer une macro qui étendrait les possibilités de la primitive \verb|\def| et qui permettrait de définir des macros avec un ou plusieurs arguments optionnels\footnote{Comme le fait la macro \texttt{\string\newcommand} de \LaTeX, même si elle ne permet de définir qu'un seul argument optionnel.}. Afin de rester simple, nous allons devoir faire quelques sacrifices : les macros ainsi définies ne pourront pas être à arguments délimités et les arguments optionnels seront entre crochets. Bien entendu, le nombre total d'arguments sera inférieur ou égal à 9. + +Voici la syntaxe que nous pouvons nous fixer pour la macro §\newmacro[|(] : + +\centrecode-\newmacro\<macro><paramètres>{<code>}- + +\noindent Les \verb|<paramètres>| sont composés soit de chiffres, soit de textes entre crochets. Les chiffres désignent le nombre d'arguments obligatoires requis à cette position et les crochets un argument optionnel dont la valeur par défaut est le code qui se trouve entre les crochets. Par exemple, si on écrit : + +\centrecode-\newmacro\foo1[xxx]2[y]1{<code>}- + +\noindent alors, les paramètres «\verb|1[xxx]2[y]1|» spécifient que la macro \verb|\foo| admet 6 arguments qui seront dans l'ordre : +\begin{itemize} + \item un argument obligatoire; + \item un argument optionnel dont la valeur par défaut est «\verb|xxx|»; + \item deux argument obligatoires; + \item un argument optionnel dont la valeur par défaut est «\verb|y|»; + \item un argument obligatoire. +\end{itemize} + +Le \verb|<code>| de la macro pourra contenir les arguments sous la forme habituelle \verb|#1|, \verb|#2|, etc. + +\subsection{Cas particulier} +Écrire d'emblée la macro \verb|\newmacro| est un défi vraiment difficile. Essayons plutôt de construire à la main la macro \verb|\foo| pour bien en comprendre le fonctionnement et ensuite, nous écrirons une macro généraliste \verb|\newmacro| qui bâtira automatiquement des macros admettant plusieurs arguments optionnels. + +Il est bien évident que notre macro \verb|\foo| ne va pas tenir en une seule macro. En réalité, à chaque fois que nous devons lui faire lire un argument optionnel, il va falloir s'assurer que le token suivant est un crochet ou pas. Par conséquent, à ces occasions, la macro \verb|\foo| va passer la main à d'autres macros auxiliaires \verb|\foo@i|, \verb|\foo@ii|, etc. + +Voici le code qui permet de construire la macro \verb|\foo| : + +\showcode/\catcode`\@11 +\def\foo#1{% #1 -> lit le premier argument obligatoire + \ifnexttok[% si le token suivant est un crochet¤§*\ifnexttok¤ + {\foo@i{#1}}% aller à \foo@i qui va lire cet argument¤\defline\aaa¤ + {\foo@i{#1}[xxx]}% sinon, transmettre [xxx] à foo@i¤\defline\bbb¤ +} + +\def\foo@i#1[#2]#3#4{% lit l'arg obligatoire + arg optionnel + 2 arg obligatoires + \ifnexttok[¤§*\ifnexttok¤ + {\foo@ii{#1}[#2]{#3}{#4}}%¤\defline\ccc¤ + {\foo@ii{#1}[#2]{#3}{#4}[y]}%¤\defline\ddd¤ +} + +\def\foo@ii#1[#2]#3#4[#5]#6{% lit tous les arguments + 1="#1" 2="#2" 3="#3" 4="#4" 5="#5" 6="#6"% +} + +\catcode`\@12 +1) \foo{arg1}{arg2}{arg3}{arg4}\par +2) \foo{arg1}[OPT\_A]{arg2}{arg3}{arg4}\par +3) \foo{arg1}{arg2}{arg3}[OPT\_B]{arg4}\par +4) \foo{arg1}[OPT\_A]{arg2}{arg3}[OPT\_B]{arg4}\medbreak¤\idx*\medbreak¤/ + +On voit donc qu'à chaque fois que l'on attend un argument optionnel, il s'agit de tester la présence d'un crochet. Pour lire l'argument optionnel entre crochet, une nouvelle macro auxiliaire \verb|\foo@<n>| à argument délimité doit être créée. On remarque aussi que lorsque cette macro auxiliaire est appelée (lignes \nos\aaa-\bbb{} et lignes \nos\ccc-\ddd), la \emph{totalité} des arguments lus jusqu'à présent lui est transmise. À charge pour cette macro de les lire, de lire aussi l'argument optionnel pour lequel elle est créée, ainsi que d'éventuels arguments obligatoires à suivre. + +Il est également très important de remarquer que si une macro créée par le truchement de §\newmacro admet un ou plusieurs arguments optionnels, non seulement elle ne peut pas être purement développable à cause de l'emploi de \idx\futurelet, mais on ne peut pas faire apparaitre son texte de remplacement avec des \verb|\expandafter|. C'est la contrepartie qu'il faut concéder pour profiter de la souplesse des arguments optionnels. + +\subsection{Cas général} +Pour construire la macro \verb|\newmacro|, nous allons d'abord nous mettre au clair avec les noms des macros auxiliaires. Dans le cas particulier précédent, la macro \verb|\foo| avait deux macros auxiliaires \verb|\foo@i| et \verb|\foo@ii|. Ces macros, si elles sont automatiquement créées par §\newmacro, posent un problème si l'utilisateur veut en disposer pour créer de lui même d'autres macros auxiliaires. Nous allons donc prendre le parti que si la macro à définir est \verb|\<macro>|, alors les macros auxiliaires seront de la forme \verb|\<macro>@[<chiffre romains>]|. Pour ce faire, une macro purement développable \verb|\macro@name{<nombre>}| se chargera de donner sous forme de tokens de catcode 12, le nom de la macro (sans caractère d'échappement) sous la forme \verb|<macro>@[<chiffre romains>]|. + +Venons-en à la méthode proprement dite. Elle va consister à définir la \idx{macro fille} \verb|\macro@name| puis à parser les \verb|<paramètres>| jusqu'à rencontrer une accolade ouvrante. Comme on l'a vu avec la macro \verb|\foo| précédemment, chaque macro auxiliaire doit relire la totalité des arguments rencontrés jusqu'à présent en y ajoutant d'autres arguments. Le \idx{texte de paramètre} de ces macros va donc s'allonger au fur et à mesure que les \verb|<paramètres>| seront parsés. Rappelons-nous qu'avec la macro \verb|\foo|, ce texte de paramètre était «\verb|#1|» pour la macro principale puis «\verb|#1[#2]#3#4|» pour \verb|\foo@i| et enfin «\verb|#1[#2]#3#4[#5]#6|» pour la troisième macro \verb|\foo@ii|. Dans le cas général, nous allons stocker ce texte de paramètre dans le registre de tokens \verb|\param@text|. + +Lorsque les \verb|<paramètres>| sont parsés, trois cas sont possibles selon le prochain token vu par \idx\futurelet : + +\begin{enumerate} + \item si c'est une accolade ouvrante (ou plus exactement un \idx\bgroup comme le voit \idx\futurelet), alors, c'est que le \idx{texte de paramètre} a été entièrement lu; + \item si c'est un crochet, il faut créer une sous-macro supplémentaire et ajouter à \verb|\param@text| «\hbox{\verb|[#<x>]|}» où \hbox{\verb|<x>|} est le numéro du prochain paramètre; + \item dans les autres cas, il s'agit donc d'un chiffre (un unique token de 1 à 9) et il faut ajouter à \verb|\param@text| «\hbox{\verb|#<x>|}», «\hbox{\verb|#<x+1>|}», etc., autant de fois que le spécifie le chiffre lu. +\end{enumerate} + +La dernière difficulté est d'appeler les macros auxiliaires tout en leur transmettant les arguments déjà lus sous la forme «\hbox{\verb|{#<x>}|}» si c'est un argument obligatoire et sous la forme «\hbox{\verb|[#<x>]|}» si c'est un argument optionnel. Comme les arguments obligatoires doivent être transmis entre accolades, le registre de tokens \verb|\param@text| ne peut pas être utilisé. Il faut donc utiliser un autre registre de tokens \verb|\arg@text|, mis à jour en même temps que \verb|\param@text| et qui enveloppera d'accolades les arguments obligatoires. + +Voici donc le code, plutôt difficile, de la macro \verb|\newmacro| : + +\showcode7\catcode`\@11 +\newcount\macro@cnt% numéro à mettre dans le nom des sous macros¤\idx*\newcount¤ +\newcount\arg@cnt% compte le nombre d'arguments +\newtoks\param@text% texte de paramètre des macros sous forme "#x" et/ou "[#x]"¤\idx*\newtoks¤ +\newtoks\arg@text% arguments sous forme "{#x}" et/ou "[#x]" + +\def\newmacro#1{% + % \macro@name construit le <nom> de la macro et éventuellement "@[<chiffre romain>]" + \def\macro@name##1{\expandafter\gobone\string#1\ifnum##1>0 @[\romannumeral##1]\fi}%¤\idx*\romannumeral\idx*\string¤ + \macro@cnt=0 \arg@cnt=0 % initialise les compteurs + \param@text{}\arg@text{}% vide les registres de texte de paramètre et d'argument + \newmacro@i% va voir le prochain token +} + +\def\newmacro@i{\futurelet\nxttok\newmacro@ii}% met le prochain token dans \nxttok...¤\idx*\futurelet¤ +% ...puis va à la macro : +\def\newmacro@ii{% + \ifxcase\nxttok¤§*\ifxcase¤ + [\newmacro@optarg% si le prochain token est un crochet aller à \newmacro@optarg + \bgroup% si c'est un accolade ouvrante¤\idx*\bgroup¤ + % le texte de paramètre est fini et il faut définir la macro + {\defname{\macro@name\macro@cnt\expandafter}%¤§*\defname¤ + \the\param@text}% <- le {<code>} est juste après, il n'est pas encore lu¤\idx*\the¤ + \elseif% sinon, c'est donc un chiffre¤§*\elseif¤ + \newmacro@arg% aller à \newmacro@arg + \endif¤§*\endif¤ +} + +\def\newmacro@optarg[#1]{% lit la valeur par défaut de l'argument optionnel + % Définit la macro \<nom>@[<nbre>] qui lit tous les arguments (optionnels ou pas) + % jusqu'alors définis à l'aide de \param@text. Puis, cette macro testera si le prochain + % token est un crochet + \expandafter\edef\csname\macro@name\macro@cnt\expandafter\endcsname\the\param@text{% + \noexpand\ifnexttok[%¤\idx*\noexpand§*\ifnexttok\idx*\noexpand¤ + % si oui : la macro \<nom>@<nbr+1> le lira + {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname + \the\arg@text}% + % si non : transmettre à \<nom>@<nbr+1> l'argument optionnel par défaut lu + {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname + \the\arg@text[\unexpanded{#1}]}%¤\idx*\unexpanded¤ + }% + \advance\arg@cnt 1 % incrémenter le numéro d'argument¤\idx*\advance¤ + % pour ajouter "[#<x>]" à \param@text et à \arg@text + \eaddtotoks\param@text{\expandafter[\expandafter##\number\arg@cnt]}%¤§*\eaddtotoks¤ + \eaddtotoks\arg@text {\expandafter[\expandafter##\number\arg@cnt]}%¤§*\eaddtotoks¤ + \advance\macro@cnt 1 % incrémenter le numéro de nom de macro¤\idx*\advance¤ + \newmacro@i% va voir le token suivant +} + +\def\newmacro@arg#1{% #1=nombre d'arguments obligatoires à ajouter + % boucle qui ajoute "#<x>#<x+1>etc" dans \param@text + % et "{#<x>}{#<x+1>}etc" dans \arg@text + \ifnum#1>\z@ % tant qu'on n'a pas ajouté le nombre de #x nécessaire¤\idx*\z@¤ + \advance\arg@cnt 1 % incrémenter le numéro d'argument¤\idx*\advance¤ + % pour ajouter #x à \param@text et {#x} à \arg@text + \eaddtotoks\param@text{\expandafter##\number\arg@cnt}% + \eaddtotoks\arg@text {\expandafter{\expandafter##\number\arg@cnt}}%¤§*\eaddtotoks¤ + \expandafter\newmacro@arg\expandafter{\number\numexpr#1-1\expandafter}% boucler + \else% si les arguments sont tous ajoutés + \expandafter\newmacro@i% lire le token suivant + \fi +} +\catcode`\@12 +\newmacro\foo 1[xxx]2[y]1{1="#1" 2="#2" 3="#3" 4="#4" 5="#5" 6="#6"} +a) \foo{arg1}{arg2}{arg3}{arg4}\par +b) \foo{arg1}[OPT\_A]{arg2}{arg3}{arg4}\par +c) \foo{arg1}{arg2}{arg3}[OPT\_B]{arg4}\par +d) \foo{arg1}[OPT\_A]{arg2}{arg3}[OPT\_B]{arg4}\medbreak¤\idx*\medbreak¤ + +\meaning\foo\par +\expandafter\meaning\csname foo@[i]\endcsname\par +\expandafter\meaning\csname foo@[ii]\endcsname¤\idx*\meaning¤7 + +Il est peut être utile de donner quelques explications sur le code : + +\begin{itemize} + \item au chapitre des variables, le compteur \verb|\macro@cnt| sert à \verb|\macro@name| pour nommer les macros auxiliaires via \idx\romannumeral. Ces numéros entre crochets viennent après «\verb|@|» et sont «\texttt i», «\texttt{ii}», etc. + + Ensuite, le compteur \verb|\arg@cnt| sert à compter les arguments. Il est utilisé dans la macro \verb|\newmacro@arg| pour insérer dans les registres à tokens \verb|\param@text| et \verb|arg@text|, soit \verb|[#<i>]|, soit \verb|#<i>|, soit \verb|{#<i>}|; + \item la macro \verb|\newmacro| n'est qu'une macro chapeau qui se charge des initialisations; + \item ensuite, \verb|\newmacro@i| va voir le prochain token avec un \idx\futurelet; + \item ce token est testé par la macro \verb|\newmacro@ii| qui décide ce qu'il faut faire selon les cas envisagés plus haut : + \begin{itemize} + \item si ce token est un crochet, \verb|\newmacro@optarg| se charge de construire une macro auxiliaire supplémentaire avec un \verb|\edef| en prenant soin de ne pas développer toutes les séquences de contrôle qui ne doivent pas l'être. La primitive \idx\unexpanded est employée pour l'argument entre crochets puisqu'il est susceptible de contenir un nombre inconnu de tokens; + \item si ce token est un chiffre, \verb|\newmacro@arg| lit ce chiffre et met en place une récursivité pour ajouter \verb|{#<x>}| et \verb|#<x>| autant de fois qu'il le faut aux registres de tokens \verb|\arg@text| et \verb|\param@text|; + \item enfin, si ce token est une accolade ouvrante, la dernière macro avec le texte de paramètre complet est définie avec le \verb|<code>| qui suit les \verb|<paramètres>|. + \end{itemize} +\end{itemize} + +Afin que les choses soient le plus claires possible et même si c'est fastidieux, essayons de faire mentalement fonctionner \verb|\newmacro| pour l'exemple + +\centrecode|\newmacro\foo 1[xxx]2[y]1| + +\noindent Par souci de clarté, les crochets autour du chiffre romain figurant dans les noms des macros auxiliaires seront omis : + +\begin{enumerate} + \item le chiffre 1 est détecté. Il est lu par \verb|\newmacro@arg| qui ajoute «\verb|{#1}|» et «\verb|#1|» à \verb|\arg@text| et \verb|\param@text| qui, étant initialisés à vide par \verb|\newmacro| contiennent donc «\verb|{#1}|» et «\verb|#1|»; + \item un crochet est détecté et l'argument «\verb|[xxx]|» est lu par \verb|\newmacro@optarg| : + \begin{itemize} + \item elle définit la macro \verb|\foo| de cette façon : +\centrecode-\def\foo#1{% + \ifnexttok[%¤§*\ifnexttok¤ + {\foo@i{#1}} + {\foo@i{#1}[xxx]}% +}- + \item les registres \verb|\arg@text| et \verb|\param@text| sont mis à jour et contiennent «\verb|{#1}[#2]|» et «\verb|#1[#2]|» + \end{itemize} + \item le chiffre 2 est détecté. Il est lu par \verb|\newmacro@arg| qui ajoute 2 arguments «\verb|{#3}{#4}|» et «\verb|#3#4|» à \verb|\arg@text| et \verb|\param@text| qui contiennent désormais «\verb|{#1}[#2]{#3}{#4}|» et «\verb|#1[#2]#3#4|»; + \item un crochet est détecté et l'argument «\verb|[y]|» est lu par \verb|\newmacro@optarg| : + \begin{itemize} + \item elle définit la macro \verb|\foo@i| de cette façon : +\centrecode-\def\foo@i#1[#2]#3#4{% + \ifnexttok[%¤§*\ifnexttok¤ + {\foo@ii{#1}[#2]{#3}{#4}} + {\foo@ii{#1}[#2]{#3}{#4}[y]}% +}- + \item les registres \verb|\arg@text| et \verb|\param@text| sont mis à jour et contiennent «\verb|{#1}[#2]{#3}{#4}[#5]|» et «\verb|#1[#2]#3#4#5|» + \end{itemize} + \item le chiffre 1 est détecté. Il est lu par \verb|\newmacro@arg| qui ajoute «\verb|{#6}|» et «\verb|#6|» à \verb|\arg@text| et \verb|\param@text|; + \item une accolade ouvrante est lue par \idx\futurelet ce qui signe la fin du processus. Le code + + \centrecode-\defname{\macro@name\macro@cnt\expandafter}\the\param@text}- + + se développe en + + \centrecode-\def\foo@ii#1[#2]#3#4#5#6- + + Ceci est suivi du \verb|<code>| entre accolades «\verb-{|#1|#2|#3|#4|#5|#6|}-» toujours non lu ce qui signifie que la macro \verb|\foo@ii| aura ce \verb|<code>| comme texte de remplacement. +\end{enumerate} + +Cet algorithme ne définit donc une macro \emph{que} lorsqu'un crochet est détecté dans les \verb|<paramètres>| ou lorsque l'accolade ouvrante marquant la fin des \verb|<paramètres>| est atteinte. + +\begin{exercice} +Créer une macro §\framebox\label{framebox} dont la syntaxe est + +\centrecode-\framebox[<lettres>]{<texte}- + +\noindent et qui agit comme §\frbox mais ne trace les réglures que selon les lettres qui figurent dans l'argument optionnel. La lettre «U» (comme «\emph{up}») demandera à ce que la réglure supérieure soit tracée. De la même façon «D», «L» et «R» spécifieront respectivement que les réglures inférieures, gauche et droite soient tracées. Par défaut, l'argument optionnel vaut «ULRD». +\solution +Il faut reprendre la code de §\frbox et tester avec §\ifin s'il y a lieu de tracer une réglure et l'espace qui la sépare du \verb|<texte>|. Afin que les \verb|<lettres>| de l'argument optionnel puisse être minuscules ou majuscules, la primitive \idx\uppercase sera chargée de mettre en majuscule + +\centrecode-\ifin{#1}<lettre>- + +Comme cette primitive est sans effet sur les séquences de contrôle, seuls l'argument optionnel \verb|#1| et la \verb|<lettre>| seront mis en majuscule. + +\showcode/\newmacro\framebox[ULRD]1{% #1 = ULRD (Up, Down, Right, Left)¤§*\framebox¤ +% ne pas changer le mode H ou V en cours + \hbox{% enferme dans une \hbox¤\idx*\hbox¤ + \uppercase{\ifin{#1}L}{\vrule width\frboxrule}{}% réglure gauche¤\idx*\uppercase\idx*\vrule§*\frboxrule§*\ifin¤ + \vtop{%¤\idx*\vtop¤ + \vbox{% 1er élément de la \vtop¤\idx*\vbox¤ + \uppercase{\ifin{#1}U}{% si la réglure sup doit être tracée¤\idx*\uppercase §*\ifin¤ + \hrule height\frboxrule% réglure supérieure¤\idx*\hrule¤ + \kern\frboxsep% espace haut¤\idx*\kern¤ + } + {}% + \hbox{%¤\idx*\hbox¤ + \uppercase{\ifin{#1}L}{\kern\frboxsep}{}% espace gauche + #2% contenu + \uppercase{\ifin{#1}R}{\kern\frboxsep}{}% espace droite¤\idx*\uppercase¤ + }% + }% puis autres éléments de la \vtop, sous la ligne de base + \uppercase{\ifin{#1}D}{%¤\idx*\uppercase §*\ifin¤ + \kern\frboxsep% espace bas¤§*\frboxsep¤ + \hrule height\frboxrule% réglure inférieure¤\idx*\hrule§*\frboxrule¤ + }% + {}% + }% + \uppercase{\ifin{#1}R}{\vrule width\frboxrule}{}% réglure droite¤\idx*\uppercase\idx*\vrule§*\ifin¤ + }% +} +\frboxsep=1pt ¤§*\frboxsep¤ +Boite \framebox{entière}, \framebox[ud]{Up down}, \framebox[LR]{Left Right}, +\framebox[LU]{Left Up} et \framebox[rd]{Right Down}.¤§*\framebox¤/ +\end{exercice}§*\newmacro[|)] + +\section{Développer les arguments d'une macro}\label{eargs} +Pour rendre l'emploi des macros encore plus agréable et souple, nous devons maintenant chercher, lorsqu'elles sont exécutées, à développer comme on le souhaite leurs arguments. Nous nous limiterons aux \emph{vrais} arguments et laisserons de côté les arguments optionnels. + +Certes, nous avons déjà à notre disposition de §\exparg (et §\expsecond qui lui est \idx\let-égale) ainsi que §\exptwoargs, mais ces macros ne peut 1-développer qu'un seul ou deux arguments. Imaginons qu'une \verb|\<macro>| admette 5 arguments et qu'un appel à cette macro nécessite de lui passer le premier tel quel, de 1-développer le premier token du 2\ieme{} argument, de 2-développer celui du 3\ieme, de 3-développer celui du 4\ieme{} et de développer au maximum le dernier argument. Nos macros §\exparg et §\expsecond seraient bien faibles pour y parvenir. Bâtissons une macro §\eargs[|(], admettant un argument obligatoire entre crochets contenant, pour chaque argument, le nombre de développements souhaités. Si un développement maximal est voulu, un «\verb|+|» sera spécifié. Pour notre macro \verb|\<macro>|, voici la syntaxe que l'appel à cette macro prendrait : + +\centrecode-\eargs[0123+]\<macro>{<arg1>}{<arg2>}{<arg3>}{<arg4>}{<arg5>}- + +Du côté de la \TeX nique, §\eargs lira son argument entre crochet et la \verb|\<macro>|. Les autres argument seront lus un par un par une macro récursive. Cette macro se chargera de développer correctement l'argument courant et d'ajouter le résultat à un registre de tokens \verb|\eargs@toks| qui jouera le rôle d'un accumulateur. Ce registre étant initialisé avec \verb|\<macro>| par §\eargs, il contiendra à la fin cette \verb|\<macro>| et les arguments correctement développés. + +Voici l'algorithme que nous mettrons en \oe uvre : + +\begingroup +\numalgofalse +\def\algoleftskip{.08\hsize} +\def\algorightskip{.08\hsize} +\algorithm[\#]{Macro \texttt{\string\eargs}}/ +macro ~eargs~[#1]#2% #1= liste des n-développements #2=macro à exécuter + eargs@toks:=#2% mettre la macro dans le collecteur de tokens + ~eargs@i~#1nil% appeler eargs@i avec la liste de n-développements +~fin~\medskip +macro ~eargs@i~#1nil% #1=liste des n-développements + ~si~ #1 est vide + exécuter eargs@toks + ~sinon~ + ~eargs@ii~#1nil% appeler eargs@ii avec la liste de n-développements + ~finsi~ +~fin~\medskip +macro ~eargs@ii~#1#2nil#3 +%#1=n-développement actuel %#2=n-développements restants #3=argument courant + ~si~ #1="+"% si un développement maximal est demandé + macro@temp:=développement maximal de #3 + ~sinon~ + macro@temp:=#3% mettre l'argument dans une macro + ~pour~ i=1 to #1 ~faire~% 1-développer #1 fois macro_temp + macro@temp:=1-développement du contenu de macro@temp + ~finpour~ + ~finsi~ + eargs@toks:=eargs@toks+\{macro@temp\}% ajouter le résultat au registre + ~eargs@i~#2nil% recommencer avec la liste des n-développements restants +~fin~ +/ +\endgroup + +Pour nous assurer que \verb|\eargs| fonctionne bien, la macro \verb|\foo| se contente ici d'afficher les arguments qu'elle reçoit tels quels avec \idx\detokenize. + +\showcode/\catcode`\@11 +\newtoks\eargs@toks¤\idx*\newtoks¤ +\newtoks\eargs@temptoks +\def\eargs[#1]#2{% #1=liste des développements #2=macro + \eargs@toks{#2}% mettre la macro dans le collecteur de tokens + \expandafter\eargs@i\detokenize{#1}\@nil% appeler \eargs@i avec + % la liste des developpements¤\idx*\detokenize¤ +} + +\def\eargs@i#1\@nil{% #1=liste des n-développements restant + \ifempty{#1}% s'il n' y plus de n-développements¤§*\ifempty¤ + {\the\eargs@toks}% exécuter la macro et ses arguments développés¤\idx*\the¤ + {\eargs@ii#1\@nil}% sinon appeller la macro qui lit un argument +} + +% #1=n-développement actuel #2=liste des n-développements restants #3=argument lu +\def\eargs@ii#1#2\@nil#3{% + \if+#1% si #1="+", un \edef est demandé pour cet argument¤\tidx*{if}¤ + \edef\eargs@tempmacro{{#3}}% le stocker dans une macro temporaire + \else% sinon + \eargs@temptoks={#3}% stocker l'argument dans un registre temporaire + \for\eargs@loop = 1 to #1\do 1 % faire #1 fois :¤§*\for¤ + {\eargs@temptoks=% 1-développer le 1er token du registre temporaire + \expandafter\expandafter\expandafter{\the\eargs@temptoks}% + }% puis le stocker dans la macro temporaire + \edef\eargs@tempmacro{{\the\eargs@temptoks}}%¤\defline\aaa\idx*\the¤ + \fi + \eaddtotoks\eargs@toks\eargs@tempmacro% ajouter le contenu de la macro au collecteur¤§*\eaddtotoks¤ + \eargs@i#2\@nil% appeler \eargs@i avec les n-développements restants +} +\catcode`\@12 +\def\foo#1#2#3#4#5{\detokenize{1="#1" 2="#2" 3="#3" 4="#4" 5="#5"}} +\def\aaa{\bbb}\def\bbb{\ccc}\def\ccc{Bonjour} + +\eargs[0123+]\foo{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}./ + +La ligne \no\aaa{} rhabille d'accolades l'argument dument développé et, comme cela a été expliqué à la page~\pageref{bloquer.developpement.maximum}, le \idx\edef ne \emph{développe pas} le contenu du registre.§*\eargs[|)] +%| | +%| Fin partie 4 | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Partie 5 | +%| | +\defpartcomment{\lettrine[lines=3,nindent=0pt]{\libertineInitialGlyph{L}}{e moment} est venu de mettre en application toutes les connaissances et les méthodes de programmation vues dans les parties précédentes. Un effort a été fait pour rendre certains de ces exemples aussi proches que possibles de ceux que l'on pourrait rencontrer dans une vie d'utilisateur de \TeX{}, même si leur complexité peut pour certains sembler rebutante, mais il en est ainsi, plus on s'approche de \emph{vrais} exemples, plus le niveau tend à s'élever.} + +\part{Aller plus loin} + +\chapter{Du nouveau dans les arguments des macros} + +\section{Détecter des marqueurs dans l'argument d'une macro} +\subsection{Créer un effet entre deux marqueurs} +\Qu i n'a jamais souhaité mettre des «marqueurs» dans l'argument\idx*{argument!marqueur} d'une macro spécialement conçue, de telle sorte qu'entre deux de ces marqueurs, un effet spécial soit obtenu ? Si l'on appelle §\detectmark cette macro, on pourrait par exemple écrire + +\centrecode-\detectmark{+}{Un +argument+ où les +marqueurs+ sont détectés}-§*\detectmark + +\noindent pour que ce qui est entre deux «\verb|+|» subisse un traitement que l'on peut choisir. On obtiendrait par exemple + +\begin{centrage}\small +Un {\bf argument} où les {\bf marqueurs} sont détectés + +{\normalsize ou}\hfill\null + +Un \frboxsep=1pt \frbox{\strut argument} où les \frbox{\strut marqueurs} sont détectés +\end{centrage} + +Le but est bien sûr de créer un raccourci pour passer de \verb|+<code>+| à \verb|{\bf<code>}| ou à \verb|\frbox{<code>}| ou à tout autre effet. +\grandsaut + +Pour programmer la macro §\detectmark, l'idée est d'agir dans un groupe, de rendre actif son premier argument qui est le caractère «marqueur», et de programmer ce caractère pour obtenir l'effet souhaité. Nous utiliserons ici l'astuce du \idx\lccode et \idx\lowercase, sous réserve que \cidx\~ soit actif au moment de la définition. Ceci fait, il suffira de faire lire le deuxième argument et ensuite, fermer le groupe. + +Pour davantage de souplesse, la programmation de l'effet sera confiée à une macro externe §\markeffect qui recevra dans son argument ce qui se trouve entre les deux marqueurs. + +\showcode/\catcode`\@11 +\def\detectmark#1{% #1 est le marqueur¤§*\detectmark¤ + \begingroup + \catcode`#1=13 % rendra #1 actif après la macro + \begingroup% pour les besoins du \lccode + \lccode`\~=`#1 % transforme "~" en " #1 actif"¤\idx*\lccode¤ + \lowercase{\endgroup\def~##1~}{\markeffect{##1}}%¤\idx*\lowercase\cidx*\~§*\markeffect¤ + \detectmark@i +} +\def\detectmark@i#1{% + #1% exécute le code + \endgroup% ferme le groupe, le marqueur perd son catcode actif +} +\catcode`\@12 +a) \def\markeffect#1{{\bf #1}}% met en gras¤§*\markeffect¤ +\detectmark+{Un +argument+ où les +marqueurs+ sont détectés}¤§*\detectmark¤ +\medskip¤\idx*\medskip¤ + +b) \def\markeffect#1{% met dans une boite¤§*\markeffect¤ + \begingroup + \frboxsep=1pt % modifie l'espacement entre texte et encadrement¤§*\frboxsep¤ + \frbox{\strut#1}% encadre¤\idx*\strut§*\frbox¤ + \endgroup +} +\detectmark|{Un |argument| où les |marqueurs| sont détectés}¤§*\detectmark¤ +\medskip¤\idx*\medskip¤ + +c) \def\markeffect#1{$\vcenter{\hbox{#1}\hbox{#1}}$}% superpose 2 fois¤§*\markeffect\idx*\vcenter\idx*[|etc]\hbox\forbidindex\hbox¤ +\detectmark`{Un `argument` où les `marqueurs` sont détectés}¤§*\detectmark¤/ + +La macro §\detectmark est certes bien pratique, mais elle doit cependant être utilisée avec précaution. Tout d'abord parce qu'en l'état, elle ne fonctionnera pas dans l'argument d'une macro : + +\showcode/\catcode`\@11 +\def\detectmark#1{%¤§*\detectmark¤ + \begingroup + \catcode`#1=13 % rendra #1 actif après la macro + \begingroup% pour les besoins du \lccode + \lccode`\~=`#1 % transforme "~" en " #1 actif"¤\idx*\lccode¤ + \lowercase{\endgroup\def~##1~}{\markeffect{##1}}%¤\idx*\lowercase\cidx*\~§*\markeffect¤% + \detectmark@i +} +\def\detectmark@i#1{% + #1% lit le code + \endgroup% ferme le groupe, le marqueur perd son catcode actif +} +\catcode`\@12 +\def\markeffect#1{{\bf #1}}% met en gras¤\idx*\bf§*\markeffect¤ +\frbox{\detectmark+{Un +argument+ où les +marqueurs+ sont détectés}}¤§*\detectmark§*\frbox¤/ + +La raison est toujours la même, mais il faut la rappeler une fois de plus : la macro §\frbox \emph{lit} son argument (pour le traiter par la suite) et dès lors, les catcodes de tous les tokens qui le composent sont figés\idx*{argument!catcode figé}. Par conséquent, la macro interne §\detectmark, qui agit \emph{après} cette congélation des catcodes, ne peut plus modifier celui de \verb|+| pour le rendre actif. + +Mais il existe une autre limitation : nous aurions aussi pu rêver que la macro §\markeffect agisse comme §\litterate de la page~\pageref{litterate} c'est-à-dire qu'elle affiche le code tel qu'il est écrit. Là aussi, l'échec est assuré, et toujours pour la même raison. En effet, le marqueur dans la macro §\detectmark, après être rendu actif, a été programmé \emph{lire} ce qui se trouve jusqu'au prochain marqueur. Ce marqueur transmet tout ce qu'il a lu à la macro §\markeffect qui, à son tour, le \emph{lit} avant de le traiter. Ce qui est entre deux marqueurs est donc lu \emph{deux fois} avant d'être traité. La conséquence est la même que s'il y avait eu une seule lecture : les catcodes des tokens se trouvant entre deux marqueurs sont figés et il n'est plus possible de les neutraliser avec \idx\dospecials. + +\subsection{Autoriser du verbatim dans l'argument d'une macro}\idx*[|(]{verbatim} +Est-il possible de trouver une méthode permettant de faire lire une portion de texte en «\idx{verbatim}» dans l'argument d'une macro ? C'est-à-dire que les tokens faisant partie de cette portion seraient lus par la macro comme étant tous de catcode 12. La réponse est oui, mais le procédé va être assez \TeX nique et de toute façon, la solution aura des limitations. + +Décidons pour cela de créer une macro §\alter dont la syntaxe serait la suivante : + +\centrecode-\alter<délimiteur>\<macro>{<argument>}-§*\alter + +\noindent La \verb|\<macro>| serait altérée en ce sens que si des \verb|<délimiteurs>| sont présents par paires dans son \verb|<argument>|, alors tout ce qui est entre deux délimiteurs consécutifs serait compris comme étant en mode « \idx{verbatim} ». Par exemple, écrire + +\centrecode-\alter{|}\frbox{texte normal |$\ver {# batim$| texte normal}-§*\alter + +\noindent produirait à l'affichage + +\begin{centrage} +\leavevmode\small\frbox{texte normal {\ttfamily\string$\string\ver\ \{\#\ batim\string$} texte normal} +\end{centrage} +\grandsaut + +Pour parvenir à nos fins, le principe général est de collecter dans un registre de tokens \verb|\alter@toks| tous les tokens de l'\verb|<argument>| en prenant soin de modifier à 12 le catcode de tous les tokens qui sont entre deux occurrences du \verb|<délimiteur>|. Une fois ceci fait, il ne reste plus qu'à donner à la \verb|\<macro>| le contenu du registre de tokens. + +La macro §\alter va effectuer les actions suivantes : elle va lire deux arguments qui sont le délimiteur et la \verb|\<macro>| à altérer et va les stocker pour les utiliser ultérieurement. C'est ensuite qu'elle va effectuer une action que nous n'avons jusqu'ici jamais faite : elle va manger l'accolade ouvrante de l'\verb|<argument>| avec un \idx\let\verb*|\next= | précédé d'un \idx\afterassignment qui spécifiera où aller après avoir mangé cette accolade. Dès lors, le problème qui surgit est que l'accolade qui ferme l'\verb|<argument>| se retrouve orpheline et il faudra bien prendre en compte et gérer cette rupture d'équilibre à un moment. Mais poursuivons notre chemin : puisque §\alter est entré à l'intérieur de l'\verb|<argument>|, elle va le parcourir token par token (comme le faisait la macro §\parse) et décider l'action à faire en fonction du prochain token à lire (obtenu avec \idx\futurelet). Voici les cas à envisager : + +\begin{itemize} + \item accolade fermante; + \item accolade ouvrante; + \item espace; + \item un \verb|<délimiteur>|; + \item un autre token. +\end{itemize} + +Précisons que la macro §\ifbracefirst ne doit pas être utilisée pour tester si une accolade ouvrante est à lire, car cette macro lit la totalité de son argument pour savoir si celui-ci commence par une accolade. Cette lecture provoque des pertes d'informations (lire page~\pageref{perte.information.lecture}) et le gel des catcodes de tous les tokens, ce qui l'exact contraire de ce qui est recherché ici. + +Comme l'\verb|<argument>| est parcouru avec \idx\futurelet, les limitations de la commande §\alter apparaissent donc : il faut que l'\verb|<argument>| ne contienne ni \idx\bgroup ni \idx\egroup, car cela provoquerait des faux positifs par le test \verb|\ifx| avec les accolades ouvrantes et fermantes. + +La macro §\alter va être construite sur le modèle de la macro §\parse de la page~\pageref{parse} et, comme elle doit aussi effectuer son travail à l'intérieur de groupes entre accolades, elle va emprunter la méthode de la \idx{macro étoilée} \verb|\parse*| de la page~\pageref{parse.a}. Une macro de fin de processus va être programmée et localement modifiée lorsqu'un groupe entre accolades est rencontré. + +Ceci donne l'algorithme suivant : + +\begin{algo} + \item définir la macro de fin \verb|\alter@stop| + \begin {algo} + \item lire l'accolade fermante orpheline «\verb|}|»; + \item passer à l'argument de \verb|\<macro>| le contenu de \verb|\alter@toks| + \end {algo} + \item lire les deux arguments \verb|<délimiteur>| et \verb|\<macro>| + \item manger l'accolade ouvrante qui suit + \item initialiser \verb|\alter@toks| à vide + \item lire avec \idx\futurelet le prochain token \verb|\alter@nxttok| + \item tester \verb|\alter@nxttok| + \begin {algo} + \item si c'est une accolade fermante, la fin de l'argument est donc atteinte, aller à la macro de fin \verb|\alter@stop| + \item si c'est un espace : absorber cet espace et ajouter un espace à \verb|\alter@toks| + \item si c'est une accolade ouvrante + \begin{algo} + \item ouvrir un groupe + \item modifier localement la macro de fin \verb|\alter@stop| pour qu'elle rajoute à \verb|\alter@toks| le contenu local de \verb|\alter@toks| enveloppé d'accolades puis qu'elle ferme le groupe et aille au point \no5 + \item aller au point \no3 + \end{algo} + \item si c'est \verb|<délimiteur>| : ouvrir un groupe semi-simple, changer les catcodes de tous les octets pour 12, ajouter tous les tokens jusqu'à la prochaine occurrence de ce délimiteur après avoir fermé le groupe (toutes ces opérations sont faites par une macro à arguments délimités) + \item dans les autres cas, le token à lire ne demande aucune action particulière : le manger et l'ajouter à \verb|\alter@toks| + \end{algo} + \item dans tous les cas ci-dessous sauf le c, aller au point \no5 +\end{algo} + +Les cas \no b, c et e ont déjà été vus avec la macro §\parse. Ici, il n'y a donc que deux cas supplémentaires à traiter, les cas \no a et d. + +\label{alter}\showcode/\catcode`\@11 +\newtoks\alter@toks% collecteur de tokens + +\def\alter#1#2{% #1= délimiteur #2 = macro à altérer¤§*\alter¤ + \let\alter@macro#2% sauvegarde la macro + \edef\alter@restorecatcode{% restaurera le catcode de #1 + \catcode`\noexpand#1=\the\catcode`#1 }%¤\idx*\noexpand\idx*\the¤ + \edef\alter@tmp{\let\noexpand\alter@markertoks= \string#1}%¤\idx*\string¤ + \alter@tmp% et sauvegarder le délimiteur après avoir mis son catcode à 12 + \edef\alter@tmp{\def\noexpand\alter@readlitterate@i\string#1####1\string#1}%¤\idx*\string\idx*\noexpand¤ + % développe les \string#1 pour que les arguments délimités aient + % des délimiteurs de catcode 12¤\idx*{catcode!12\space(autre)}¤ + \alter@tmp{% <- comme si on écrivait "\def\alter@readlitterate@i#1##1#1" + \endgroup% après avoir lu ##1 (tokens rendus inoffensifs), fermer le groupe + \addtotoks\alter@toks{{\tt##1}}% ajouter ces tokens¤§*\addtotoks¤ + \alter@i% et aller lire le prochain token + }% + \alter@toks{}% initialise le collecteur de tokens + \afterassignment\alter@i% aller lire le premier token après avoir¤\idx*\afterassignment¤ + \let\alter@tmptok= % mangé l'accolade ouvrante de l'<argument> qui suit +} + +\def\alter@i{% lit le prochain token et va à \alter@ii + \futurelet\alter@nxttok\alter@ii}%¤\idx*\futurelet¤ + +\def\alter@ii{% teste le token qui doit être lu + \ifxcase\alter@nxttok% si le token à lire est¤§*\ifxcase¤ + \egroup \alter@stop% "}" : aller à \alterstop@i¤\idx*\egroup¤ + \sptoken \alter@readspc% " " : aller à \alter@readspc¤§*\sptoken¤ + \bgroup \alter@readarg% "{" : aller à \alter@readarg¤\idx*\bgroup¤ + \alter@markertoks \alter@readlitterate% "<delimiteur>" : aller à \alter@readlitterate + \elseif¤§*\elseif¤ + \alter@readtok% dans les autres cas, aller à \alter@readtok + \endif¤§*\endif¤ +} + +\def\alter@readlitterate{% le prochain token est le délimiteur + \begingroup% ouvrir un groupe + \for\alter@tmp=0to255\do{\catcode\alter@tmp=12 }%¤§*\for¤ + % mettre tous les catcodes à 12 + \defactive{ }{\ }% sauf l'espace rendu actif¤§*\defactive¤ + \doforeach\alter@tmp\in{<,>,-,`,{,},'}% pour chaque motif de ligature¤§*\doforeach\idx{ligature}¤ + {\unless\if\alter@tmp\alter@markertoks% s'il est différent du délimiteur¤\tidx*{unless}¤ + % le rendre actif pour éviter la ligature + \expandafter\alter@defligchar\alter@tmp + \fi + }% + \alter@readlitterate@i% puis aller à \alter@readlitterate@i... + % ...qui a été définie dans \alter +} + +\def\alter@defligchar#1{% définit le caractère pour ne pas provoquer de ligature¤\idx*{ligature}¤ + \defactive#1{\string#1{}}%¤§*\defactive\idx*\string¤ +} + +\expandafter\def\expandafter\alter@readspc\space{% mange un espace dans le code¤\idx*\space¤ + \addtotoks\alter@toks{ }% ajoute l'espace¤§*\addtotoks¤ + \alter@i% puis lire le token suivant +} + +\def\alter@readarg{% le token qui suit est "{" + \begingroup% ouvrir un groupe + \def\alter@stop@ii{% et modifier localement la macro appelée à la toute fin, + % après que l'accolade fermante ait été mangée (par \alterstop@i) + \expandafter\endgroup% retarder la fermeture de groupe ouvert ci-dessus + \expandafter\addtotoks\expandafter\alter@toks\expandafter¤§*\addtotoks¤ + {\expandafter{\the\alter@toks}}%¤\idx*\the¤ + % pour ajouter hors du groupe ce qui a été collecté à l'intérieur, + % le tout mis entre accolades + \alter@i% puis, lire le token suivant + }% + \alter@toks{}% au début du groupe, initialiser le collecteur + \afterassignment\alter@i% aller lire le prochain token après¤\idx*\afterassignment¤ + \let\alter@tmptok= % avoir mangé l'accolade ouvrante +} + +\def\alter@readtok#1{% le prochain token ne demande pas une action spéciale + \addtotoks\alter@toks{#1}% l'ajouter au collecteur¤§*\addtotoks¤ + \alter@i% et aller lire le token suivant +} + +\def\alter@stop{% le token à lire est "}" + \afterassignment\alter@stop@ii% aller à \alter@stop@ii après¤\idx*\afterassignment¤ + \let\alter@tmptok= % avoir mangé l'accolade fermante +} + +\def\alter@stop@ii{% donner à la \<macro> tout ce qui a été récolté + \expandafter\alter@macro\expandafter{\the\alter@toks}%¤\idx*\the¤ + \alter@restorecatcode% puis restaure le catcode du délimiteur +} +\catcode`@12 + +\frboxsep=1pt ¤§*\frboxsep¤ +\alter|\frbox{Texte normal - |#& }| - texte normal - |_^ ##| - texte normal}¤§*\frbox¤ +\alter=\frbox{La macro =\alter= autorise du verbatim dans +des commandes imbriquées \frbox{comme ici =\alter=}.}¤§*\alter¤/ + +Nous avons considéré que la \verb|\<macro>| à altérer n'avait qu'un seul argument. Or, il serait commode de pouvoir altérer les macros à plusieurs arguments de façon à mettre du \idx{verbatim} dans n'importe lequel de leurs arguments. C'est ici que la macro §\identity (voir page~\pageref{identity}) peut résoudre ce problème. Il suffira de placer la macro à altérer ainsi que ses arguments dans l'argument de §\identity : + +\showcode/\def\hello#1#2{Bonjour #1 et #2 !} +\hello{foo}{bar}\par +\alter|\identity{\hello{macro |{{{\foo|}{macro |\bar}|}}¤§*\alter §*\identity¤/\idx*[|)]{verbatim} + +\section{Modifier les catcodes des arguments déjà lus} +La règle de la page~\pageref{catcode.inalterable} était pourtant claire : dès que \TeX{} lit un token, il lui affecte de façon inaltérable un code de catégorie\idx*{argument!catcode figé}. Il était même précisé que ce mécanisme est incontournable. Le titre de cette section pose donc des interrogations quant au moyen utilisé. + +\subsection{Technique de «écriture-lecture»} +Ce moyen consiste à enregistrer des tokens dans un fichier auxiliaire puis lire ce fichier. En effet, lorsqu'on enregistre du code dans un fichier, celui-ci, après éventuel développement, est « détokénisé », c'est-à-dire que n'en subsistent que les caractères (au sens d'octet) qui le formaient. Les catcodes, caractéristiques de \TeX, sont perdus à l'enregistrement. Par la suite, lors de la lecture d'un fichier, les octets qui le composent sont lus par \TeX{} et transformés en tokens selon le régime de catcode en cours à ce moment. + +Cette astuce redonne à n'importe quel ensemble de tokens une sorte de virginité puisqu'on peut les relire à n'importe quel moment --~y compris dans le texte de remplacement d'une macro~-- pour qu'ils se colorent des catcodes en vigueur. + +On peut l'observer en utilisant la macro §\frbox en mettant dans son argument macro §\retokenize qui enregistre les tokens de son argument (déjà lus par la macro §\frbox) dans un fichier puis les relit : + +\showcode/\def\retokenize#1{%¤§*\retokenize¤ + \immediate\openout\wtest=retokenize.tex % ouvre le fichier¤\idx*\immediate\idx*\openout¤ + \immediate\write\wtest{\unexpanded{#1}}% y écrit l'argument¤\idx*\unexpanded¤ + \immediate\closeout\wtest% ferme le fichier¤\idx*\immediate\idx*\closeout¤ + \input retokenize.tex % lit le fichier selon les catcodes en vigueur¤\idx*\input¤ + \unskip% mange l'espace précédemment ajouté qui provient de la fin du fichier¤\idx*\unskip¤ +} +\frboxsep=1pt +1) \frbox{Programmer en \catcode`\~=12 \TeX{} est~facile et~utile.}\par¤§*\frbox¤ +2) \frbox{Programmer en \retokenize{\catcode`\~=12 \TeX{} est~facile} et~utile.}\par +3) \frbox{Programmer en \catcode`\~=12 \retokenize{\TeX{} est~facile} et~utile.}¤§*\frbox§*\retokenize¤/ + +Le cas \no1 montre bien que «\cidx\~» ne change pas de catcode dans l'argument de la macro §\frbox puisqu'il ne prend jamais le catcode 12\idx*{catcode!12 (autre)} pour donner l'affichage «\cidx\~». Il ne le prend pas non plus à l'extérieur du texte de remplacement de cette macro, car §\frbox enveloppe son argument dans des boites qui jouent le rôle de groupe. + + +Au contraire, les cas \nos2 et 3 mettent en évidence que la relecture du fichier «\texttt{retokenize.tex}» se fait avec les catcodes en cours. Le fichier est lu avec \cidx\~ de catcode 12\idx*{catcode!12 (autre)} pour le cas \no2 tandis que pour le cas \no3, le changement de catcode est inclus dans le fichier ce qui ne change rien au résultat final. + +Il faut bien avoir conscience que la relecture du fichier se fait \emph{après} que l'argument de §\frbox ait été lu. Cette lecture d'argument s'accompagne d'irrémédiables pertes d'information (par exemple des espaces consécutifs lus comme un seul espace). De plus, la lecture d'un fichier provoque une altération qui lui est propre : si ce n'est pas déjà le cas, un espace est inséré après toute séquence de contrôle lue dans le fichier. Cet espace ajouté n'a aucune incidence si le code est \emph{exécuté} car les espaces sont ignorés lorsqu'ils suivent une séquence de contrôle. Le seul cas où cet espace peut devenir gênant est lorsqu'on compose du « \idx{verbatim} » en provenance d'un fichier. L'exemple montre avec §\litterate la double altération que subit le code : la première rend plusieurs espaces consécutifs égaux à un seul espace et l'autre insère un espace après la macro \idx\TeX. Les deux arguments des cas \no1 et 2, bien que et présentant plusieurs différences, sont rendus de la même façon : + +\showcode/\frboxsep=1pt ¤§*\frboxsep¤ +1) \frbox{Programmer \retokenize{\litterate|en \TeX {} est |}facile et utile.}\par¤§*\frbox§*\litterate§*\retokenize¤ +2) \frbox{Programmer \retokenize{\litterate|en \TeX{} est |}facile et utile.}¤§*\frbox§*\litterate§*\retokenize¤/ + +\subsection{La primitive \texttt{\char`\\scantokens}}\idx*[|(]{\scantokens} +Le moteur \eTeX{}\idx*{moteur!etex} dispose de la primitive \verb|\scantokens{<texte>}| qui agit comme la macro §\retokenize avec quelques petites différences : + +\begin{itemize} + \item l'écriture a lieu dans un fichier \emph{virtuel}, c'est-à-dire qu'aucun fichier n'est créé sur le disque dur et en réalité, tout se passe dans la mémoire de \eTeX{}; + \item la primitive \verb|\scantokens| est développable et son 1-développement est l'ensemble des tokens qui sont dans son argument, remis au gout du régime de catcode en cours; + \item comme toutes les primitives devant être suivies d'une accolade, \verb|\scantokens| procède à un développement maximal jusqu'à rencontrer l'accolade ouvrante. +\end{itemize} + +\showcode/\frboxsep=1pt ¤§*\frboxsep¤ +1) \frbox{Programmer en \catcode`\~=12 \TeX{} est~facile et~utile.}\par¤§*\frbox¤ +2) \frbox{Programmer en \scantokens{\catcode`\~=12 \TeX{} est~facile} et~utile.}\par¤\idx*\scantokens¤ +3) \frbox{Programmer en \catcode`\~=12 \scantokens{\TeX{} est~facile} et~utile.}\par +4) \frbox{Programmer \scantokens{\litterate|en \TeX {} est facile|} et utile.}\par¤§*\litterate¤ +5) \frbox{Programmer \scantokens{\litterate|en \TeX{} est facile|} et utile.}¤§*\frbox\idx*\scantokens§*\litterate¤/ + +\begin{exercice} +Expliquer d'où vient l'espace en trop entre les mots «facile» et «et» des cas \no2-5 de l'exemple précédent ainsi que l'espace parasite entre lettres «a» et «b» ci-dessous : + +\showcode/\scantokens{a}b/ +\solution +Il provient du \idx{caractère de fin de ligne}\idx*{fin de ligne} qui est inséré à la fin de chaque ligne. Ici, le fichier virtuel est constitué d'une seule ligne contenant «a» et le caractère de code \idx\endlinechar est inséré à la fin de cette ligne. Par défaut, ce caractère est «\verbidx*[ (retour charriot)]{^^M}\verb|^^M|», de catcode 5\idx*{catcode!5 (retour charriot)}, qui est lu comme un espace. On peut donc définir localement \idx\endlinechar comme entier négatif pour le neutraliser : + +\showcode/\begingroup\endlinechar=-1 \scantokens{a}b\endgroup¤\idx*\scantokens\idx*\endlinechar¤/ +\end{exercice} + +\subsection{\texttt{\char`\\scantokens} : erreurs en vue} +Le fonctionnement est clair, mais une erreur surprenante est émise lorsque l'on tente de développer \verb|\scantokens| dans le texte de remplacement d'une macro : + +\errcode/\edef\foo{\scantokens{Bonjour le monde}}% produit une erreur/{! File ended while scanning definition of \string\foo.} + +\noindent Si l'on réitère l'expérience en ne la développant qu'une seule fois, la même erreur survient : + +\errcode/\expandafter\def\expandafter\foo\expandafter + {\scantokens{Bonjour le monde}}% produit une erreur/{! File ended while scanning definition of \string\foo.} + +Il arriverait une erreur similaire si l'on écrivait : + +\centrecode-\toks0=\expandafter{\scantokens{Bonjour le monde}}-\idx*\toks +\noindent et on obtiendrait «\texttt{! File ended while scanning text of \string\toks.}» + +Comme \verb|\scantokens| agit un peu comme \idx\input, il en partage aussi les secrets (voir page~\pageref{input.secrets}). Le lecteur qui avait sauté la lecture de cette section ou qui n'en a plus qu'un vague souvenir est invité à s'y replonger. L'essentiel à retenir est que la fin d'un fichier --~virtuel ou pas~-- est «\idx\outer». Ici, \TeX{} se plaint, car cette fin de fichier se trouve à des endroits qui lui sont interdits par son statut d'\idx\outer. + +Le remède pour la fin d'un fichier virtuel \idx\outer est le même que pour la fin d'un fichier réel présent dans le texte de remplacement d'une macro : il suffit de placer un \idx\noexpand juste avant la fin de ce fichier et de s'assurer qu'il sera développé \emph{avant} que la définition de la macro ne se fasse. Ceci implique donc qu'il \emph{faut} utiliser la macro \verb|\edef| : + +\begingroup\edef\foo{Bonjour le monde} +\showcode/¤\litterate|\edef\foo{\scantokens{Bonjour le monde\noexpand}}|§*\litterate\idx*\noexpand¤ +Voici la macro \string\foo\ : \foo.¤\idx*\string¤/ +\endgroup + +Utiliser \verb|\edef| comporte l'inconvénient de développer la totalité de l'argument alors que finalement, seul le \idx\noexpand final aurait besoin d'être développé. Comme nous l'avions fait avec \idx\input, il est possible de construire une macro §\scandef ayant la syntaxe suivante + +\centrecode-\scandef\<macro>{<texte>}- + +\noindent et qui met le 1-développement de \verb|\scantokens{<texte>}| dans le texte de remplacement de \verb|\<macro>|. Nous allons utiliser le même artifice qu'avec \idx\input (voir page~\pageref{input.dans.macro}) et nous servir de \idx\everyeof pour placer un \verb|\@nil| à la fin du fichier virtuel. Ce \verb|\@nil| sera utilisé par une macro auxiliaire comme délimiteur d'argument: + +\showcode/\catcode`\@11 +\def\scandef#1#2{% #1=\<macro> #2=<texte>¤§*\scandef¤ + \begingroup + \endlinechar=-1 % pas de caractère de fin de ligne¤\idx*\endlinechar\idx*{caractère de fin de ligne}¤ + \everyeof{\@nil#1\noexpand}% ajoute "\@nil\<macro>\noexpand" avant la fin du fichier¤\idx*\everyeof\idx*\noexpand¤ + \expandafter\scandef@i\expandafter\relax\scantokens{#2}%¤\idx*\scantokens¤ +} +\def\scandef@i#1\@nil#2{% "\@nil#2" ont été ajouté par \everyeof + \endgroup% ferme le groupe + \expandafter\def\expandafter#2\expandafter{\gobone#1}% et définit la \<macro> +} +\catcode`@12 +\def\foo{% + Dans tout l'argument de \string\foo, <<~>> est actif¤\idx*\string¤ + sauf + \catcode`\~12 + \scandef\bar{dans celui de \string\bar : <<~>>}%¤\idx*\string§*\scandef¤ + \catcode`~13 + \bar +} +\foo/ + +\noindent Toute l'astuce réside dans «\idx\everyeof\verb|{\@nil#1\noexpand}|» qui demande que soit ajouté avant la fin du fichier un \verb|\@nil| et la \verb|\<macro>| (qui est \verb|#1|), le tout suivi de \idx\noexpand. Par conséquent, la ligne + +\centrecode-\expandafter\scandef@i\scantokens{<texte>}- + +\noindent se développe en + +\centrecode-\scandef@i<texte relu par \scantokens>\@nil\<macro>\noexpand<EOF>- + +\noindent et la macro à argument délimité \verb|\scandef@i| n'a plus qu'à saisir les arguments délimités pour, une fois sorti du groupe, définir la \verb|\<macro>| sans jamais avoir à lire la fin du fichier \verb|<EOF>| qui de toute façon, est neutralisée par \idx\noexpand. +\grandsaut + +La chronologie qui se met en place lorsque \verb|\scantokens|\linebreak[1]\verb|{<code>}| est exécuté est la suivante : + +\begin{itemize} + \item insertion du caractère de code \idx\endlinechar après tous les retours à la ligne, y compris après la dernière; + \item transformation du code qui en résulte en tokens; + \item insertion du code contenu dans \idx\everyeof à la fin de ce qui va être lu par \verb|\scantokens|. +\end{itemize} + +On peut constater que le point \no1 est bien celui qui est exécuté en premier avec ce code assez amusant où la lettre «\verb|d|» de \idx\noexpand est ajoutée par \idx\endlinechar : + +\begingroup +\edef\foo{Bonjour le monde} +\showcode/{% +\endlinechar=`\d% insère la lettre "d" à chaque fin de ligne¤\idx*\endlinechar¤ +¤\litterate|\edef\foo{\scantokens{Bonjour le monde\noexpan}}|¤% le \noexpan(d) est incomplet +Voici la macro \string\foo\ : \foo.% fins de ligne...¤\idx*\string¤ +}% ...commentées pour éviter le "d"/ +\endgroup + +\begin{exercice} +Utiliser \verb|\scantokens| pour programmer une macro §\cprotect (le «\verb|c|» signifiant «catcode»), dont la syntaxe est + +\centrecode-\cprotect\<macro>{<argument>}- + +\noindent et qui appelle la \verb|\<macro>| de cette façon : + +\centrecode-\<macro>{\scantokens{<argument>\noexpand}}- + +Comme l'\verb|<argument>| est relu par \idx\scantokens, l'intérêt de cette macro est qu'elle permet d'utiliser §\litterate dans l'\verb|<argument>|. De façon plus générale, §\cprotect permet de changer des catcodes dans l'\verb|<argument>| de la \verb|\<macro>|. +\solution +La primitive \verb|\scantokens| va évidemment considérablement simplifier la méthode, mais va aussi changer les limites de la macro comme nous le verrons, de telle sorte que §\cprotect ne peut pas être qualifié d'équivalent de la macro §\alter vue précédemment. + +Tout d'abord, nous allons lire l'\verb|<argument>| après avoir rendu les catcodes de tous les octets égaux à 12, sauf celui de l'accolade ouvrante et de l'accolade fermante qui gardent leurs catcodes de 1 et 2 afin justement de pouvoir délimiter l'argument et de rendre possible sa lecture. La suite est simple : l'argument ainsi lu sera retokénisé par \verb|\scantokens| et donné comme argument à la \verb|\<macro>|. + +\showcode/\catcode`\@11 +\def\cprotect#1{% #1 est la \<macro>¤§*\cprotect¤ + \def\cprotect@i##1{% ##1 est l'<argument> + \endgroup% ferme le groupe précédemment ouvert + #1{\scantokens{##1\noexpand}}%¤\idx*\scantokens\idx*\noexpand¤ + }% + \begingroup% rend tous les octets de catcode12 + \for\cprotect@temp=0to255\do{\catcode\cprotect@temp=12 }%¤§*\for¤ + \catcode`\{=1 \catcode`\}=2 % sauf "{" et "}" + \cprotect@i% puis, lit l'argument +} +\catcode`@12 +\frboxsep=1.5pt ¤§*\frboxsep¤ +1) \cprotect\frbox{foo \litterate-&# #^ ^_%- bar}\par¤§*\litterate§*\frbox¤ +2) \cprotect\frbox{\catcode`\~=12 a~b~c~d}\par +3) \cprotect\frbox{foo \litterate-\bar- \cprotect\frbox{\litterate-&# #-} fin}¤§*\cprotect§*\frbox§*\litterate¤/ + +La macro §\cprotect, malgré sa grande simplicité, présente trois limitations principales : + +\begin{enumerate} + \item l'\verb|<argument>| ne doit pas contenir d'accolade ouvrante ou fermante non équilibrée, et donc le texte traité par §\litterate doit également obéir à cette contrainte. Y contrevenir tromperait \TeX{} sur la portée de l'\verb|<argument>| car lors de la lecture de cet \verb|<argument>|, les accolades ont leurs catcodes normaux. + \item §\cprotect ne peut agir que sur une macro admettant un seul argument puisque \verb|\cprotect@i| a été définie comme lisant et traitant un unique argument. Il n'est pas possible de contourner cette limitation comme on le faisait avec §\identity pour la macro §\alter. En revanche, il aurait été possible de programmer la macro §\cprotect différemment afin de mettre entre les accolades la liste des arguments de la \verb|\<macro>| : + +\centrecode-\cprotect\<macro>{{argument 1}{argument 2}...}-§*\cprotect + + \item on a considéré que l'\verb|<argument>| était entre accolades, sans envisager le cas d'autres caractères de catcode 1\idx*{catcode!1 (accolade)} ou 2. Pour bien faire, on n'aurait dû changer le catcode d'un caractère que s'il est différent de 1 ou 2. La boucle §\for aurait donc dû être programmée ainsi : + +\centrecode-\for\cprotect@temp=0to255\do{% + \unless\ifnum\catcode\cprotect@temp=1 + \unless\ifnum\catcode\cprotect@temp=2 + \catcode\cprotect@temp=12 + \fi + \fi}- +\end{enumerate} + +Selon les besoins et les limitations dont on veut s'affranchir, il convient donc de s'orienter vers §\cprotect ou §\alter. +\end{exercice} + +\begin{exercice} +Comment modifier le code de §\cprotect pour permettre à la \verb|\<macro>| d'avoir plusieurs arguments et offrir la syntaxe qui est donnée au point \no2 de la solution précédente? +\solution +Appelons §\Cprotect cette macro. La stratégie consiste à parcourir la liste des arguments, et les stocker sous la forme + +\centrecode-{\scantokens{<argument>\noexpand}}- + +\noindent dans un registre de tokens qui accumulera ces arguments de telle sorte qu'à la fin du processus, ce registre contienne + +\centrecode-\<macro>{\scantokens{#1\noexpand}}{\scantokens{#2\noexpand}}etc...- + +\noexpand Une fois tout ceci fait, le contenu du registre sera exécuté avec \idx\the. + +La principale difficulté est de parcourir tous les arguments et gérer d'une façon ou d'une autre la fin de la liste. Ici, le \idx{quark} §\quark est mis comme dernier argument. Une macro récursive lira un argument à la fois et avant d'agir, fera un test pour déterminer si tous les arguments ont été traités (cas où l'argument lu est \verb|\ifx|-égal à §\quark). Il faut également tenir compte du fait que chaque argument est dépouillé de ses éventuelles accolades lorsqu'il est lu par une macro. + +Le registre de token \no0 tiendra lieu d'accumulateur. + +\showcode/\catcode`@11 +\def\Cprotect#1{% #1 est la \<macro>¤§*\Cprotect¤ + \def\Cprotect@i##1{% ##1 est la liste des arguments + \endgroup% ferme le groupe précédemment ouvert + \toks0={#1}% met la \<macro> dans le registre de tokens¤\idx*\toks¤ + \Cprotect@ii##1\quark% \quark est mis à la fin des arguments¤§*\quark¤ + \the\toks0 % exécute le registre¤\idx*\the\idx*\toks¤ + }% + \begingroup% rend tous les octets de catcode12 + \for\temp@arg= 0 to 255 \do{% changer à 12 tous les catcodes¤§*\for¤ + \unless\ifnum\catcode\temp@arg=1 % sauf si catcode=1¤\tidx*{unless}¤ + \unless\ifnum\catcode\temp@arg=2 % ou 2 + \catcode\temp@arg=12 + \fi + \fi}% + \Cprotect@i% puis, lit l'argument +} +\def\Cprotect@ii#1{% #1 est l'argument courant (dépouillé de ses accolades) + \def\temp@arg{#1}% stocke l'argument pour le tester ci dessous : + \unless\ifx\quark\temp@arg% si la fin n'est pas atteinte¤\tidx*{unless} §*\quark¤ + % ajouter "{\scantokens{#1\noexpand}}" au registre + \addtotoks{\toks0}{{\scantokens{#1\noexpand}}}%¤§*\addtotoks\idx*\toks\idx*\noexpand¤ + \expandafter\Cprotect@ii% et recommencer + \fi +} +\catcode`@12 + +\def\test#1#2{Bonjour #1 et #2} +\Cprotect\test{{argument 1 : \litterate-\foo-}{argument 2 : \litterate-\bar-}}¤§*\Cprotect §*\litterate¤/ +\end{exercice}\idx*[|)]{\scantokens} + +\chapter{Aller plus loin avec des réglures} +Les réglures, qui ne sont par définition que de simples rectangles noirs peuvent, moyennant un peu d'imagination, être utilisées pour donner lieu à des effets plutôt originaux\ldots + +\section{Créer une police Impact}\idx*[|(]{police Impact} +\subsection{Analyse du problème} +Le but ici est de créer de toutes pièces une « police » de type \impact[1pt] Impact\impactend{} comme il était courant d'en rencontrer du temps où les imprimantes à aiguilles (ou matricielles) étaient encore largement majoritaires. Leur fonctionnement est d'une grande simplicité : la tête d'écriture est constituée d'une ligne de fins picots --~les aiguilles~--, tous jointifs, alignés verticalement et actionnables indépendamment les uns des autres. Au fur et à mesure que la tête d'écriture se déplace par rapport à la page, les aiguilles sont brièvement actionnées, chacune imprimant un pixel lorsqu'elle exerce une pression sur le ruban encreur qui se trouve entre elle et la page. Voici l'effet que l'on obtient en rendant visibles les espaces entre les aiguilles : + +\begin{centrage} +\impact[1.5pt][.5pt]La police Impact\impactend +\end{centrage} + +La tête de lecture a deux caractéristiques qui lui sont données lors de sa fabrication : sa hauteur et le nombre d'aiguilles. Plus les aiguilles sont en grand nombre par unité de longueur, plus la résolution est importante et meilleure est la qualité d'impression. + +Précisons tout de suite que nous allons \emph{dessiner} des caractères de toutes pièces, mais que \emph{stricto sensu}, nous n'allons pas créer une « \idx{police} » de caractères. En effet, toutes les fonctionnalités avancées des \emph{vraies} \idx{police}s ne seront pas présentes ici : + +\begin{itemize} + \item \idx{crénage}\footnote{C'est la possibilité de modifier (souvent réduire) l'espacement entre deux caractères en fonction de leur forme de telle sorte que l'ensemble soit plus esthétique. Ainsi «\hbox{AVATAR}» avec crénage est bien plus esthétique que «\hbox{A{}V{}A{}T{}A{}R}» sans crénage. Le mot avec crénage a une longueur de \setbox0\hbox{AVATAR}\the\wd0 {} alors que le mot sans crénage mesure \setbox0\hbox{A{}V{}A{}T{}A{}R}\the\wd0.} entre caractères; + \item \idx{ligature}s; + \item \idx*{coupure de mot}coupures des mots, même si les coupures n'ont rien à voir avec les polices, mais sont faites par \TeX{} en fonction de la position de certains groupes de lettres rencontrés dans le mot; + \item différentes formes (italique, petites majuscules, etc.), graisse. +\end{itemize} + +\noindent Il s'agit donc d'un abus de langage, certes volontaire, mais pas réellement malhonnête puisqu'on pourra dessiner les caractères que l'on veut et écrire de vraies phrases avec les dessins créés. + +\subsection{Empilement de pixels} +Avec \TeX, ce qui va nous tenir lieu de point élémentaire et que nous dénommerons «\emph{pixel}, est une simple réglure carrée. Contrairement aux imprimantes, nous n'imposerons aucune limite sur le nombre de pixels qui composent un caractère, pas plus en hauteur, profondeur qu'en largeur, la seule limite étant finalement la patience de l'utilisateur à créer un caractère fait d'un grand nombre de pixels. Pour dessiner un pixel et correctement le positionner par rapport aux autres, il nous faut définir deux dimensions, §\pixelsize qui est le côté du carré et §\pixelsep qui est l'espacement entre deux pixels adjacents, aussi bien horizontalement ou verticalement. + +Autant commencer par le plus facile et définir la macro §\pixel qui imprime un pixel carré en mode horizontal\idx*{mode!horizontal}, de telle sorte que le bord inférieur du carré coïncide avec la \idx{ligne de base} : + +\centrecode-\def\pixel{\vrule height\pixelsize width\pixelsize depth0pt }-\idx*\vrule§*\pixelsep§*\pixelsize + +Pour corser un peu plus l'exemple qui va suivre, on peut essayer d'empiler verticalement deux lignes de pixels au-dessus de la \idx{ligne de base}. Puisqu'on est au-dessus de cette \idx{ligne de base}, on va naturellement le faire dans une \idx\vbox et enfermer dans une \idx\hbox chaque ligne de pixels après l'avoir convenablement séparée de la précédente. Pour respecter l'espacement vertical entre les deux lignes de pixels, il suffira d'annuler le ressort d'interligne avec \idx\offinterlineskip puis de régler ce ressort d'interligne à §\pixelsep : + +\showcode/\newdimen\pixelsize \newdimen\pixelsep¤\idx*\newdimen§*\pixelsep§*\pixelsize¤ +\def\pixel{\vrule height\pixelsize width\pixelsize depth0pt }¤§*\pixel¤ +\def\vblankpixel{\vrule height\pixelsize width0pt depth0pt } +\def\blankpixel{\vblankpixel \vrule height0pt width\pixelsize depth0pt }¤\idx*\vrule¤ +\def\gap{\kern\pixelsep} +\pixelsize=3pt \pixelsep=1pt ¤§*\pixelsize§*\pixelsep¤ +Essai : +\vbox{% aligne verticalement¤\idx*\vbox¤ + \offinterlineskip% annule le ressort d'interligne¤\idx*\offinterlineskip¤ + \lineskip=\pixelsep\relax% pour le mettre à \pixelsep¤\idx*\lineskip§*\pixelsep¤ + \hbox{\pixel\gap\blankpixel\gap\blankpixel\gap\blankpixel\gap\pixel}% 1re ligne¤\idx*\hbox¤ + \hbox{\pixel\gap\pixel \gap\blankpixel\gap\pixel \gap\pixel}% 2e ligne¤§*\pixel¤ +}/ + +Les deux autres macros \verb|\blankpixel| et \verb|\vblankpixel| nous seront utiles par la suite et laissent un pixel blanc pour la première et un strut de la hauteur d'un pixel pour la seconde. La macro \verb|\gap| insère une espace insécable de la valeur de §\pixelsep et a vocation à être insérée horizontalement entre deux pixels consécutifs, quelle que soit leurs couleurs. + +Le code est clair et on comprend vite le principe. On voit ici que les pixels dessinés pourraient former le haut de la lettre + +\begin{centrage} +\impact[3pt][1pt]M\impactend +\end{centrage} + +\noindent Et l'on pourrait facilement écrire les lignes supplémentaires nécessaires pour afficher cette lettre en entier. + +Remarquons que l'ordre d'affichage des pixels n'est pas celui des imprimantes à aiguilles : le caractère n'est pas imprimé par une succession de lignes de pixels verticales. Bien sûr, on pourrait procéder ainsi, mais les boites de \TeX{} nous facilitent la tâche si l'on s'y prend autrement. On va plutôt imprimer de haut en bas toutes les lignes de pixels (elles-mêmes imprimées de gauche à droite) nécessaires à la formation d'un caractère. + +\subsection{Prise en compte de la ligne de base} +Il faut maintenant s'intéresser au respect de la \idx{ligne de base} si nous voulons imprimer un caractère comme \impact[1pt][0.4pt]g\impactend : il faut que le bas des pixels formant la partie inférieure de la boucle de la lettre «\impact[1pt][0.4pt]g\impactend» coïncide exactement avec la \idx{ligne de base} : + +\begin{centrage} + \quitvmode\rlap{\vrule width4cm height0.15pt depth0.15pt }% + \kern2cm \clap{\impact[3pt][1pt]g\impactend}\kern 2cm +\end{centrage} + +Pour cela, la même méthode que celle de §\frbox (voir page~\pageref{frbox.ligne.de.base}) va être déployée : une \idx\vbox sera insérée au sommet d'une \idx\vtop, ce qui revient à utiliser ce schéma : + +\centrecode|\vtop{%¤\idx*\vtop¤ + \offinterlineskip \lineskip=\pixelsep ¤\idx*\offinterlineskip\idx*\lineskip§*\pixelsep¤ + \vbox{<lignes de pixels au-dessus de la ligne de base>}%¤\idx*\vbox¤ + <lignes de pixels au-dessous de la ligne de base>% + }| + +Voici un exemple où nous appliquons ce schéma pour construire la lettre «\impact[1pt][0.4pt]g\impactend» : + +\showcode/\pixelsize=3pt \pixelsep=1pt ¤§*\pixelsize §*\pixelsep¤ +Essai : +\vtop{%¤\idx*\vtop¤ + \offinterlineskip \lineskip=\pixelsep\relax¤\idx*\lineskip\idx*\offinterlineskip§*\pixelsep¤ + \vbox{%¤\idx*\vbox¤ + \hbox{\blankpixel\gap\pixel \gap\pixel \gap\pixel}% ***¤\idx*\hbox §*\pixel¤ + \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * + \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * + \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * + \hbox{\blankpixel\gap\pixel \gap\pixel \gap\pixel}% *** + }% ----ligne de base + \hbox {\blankpixel\gap\blankpixel\gap\blankpixel\gap\pixel}% * + \hbox {\blankpixel\gap\pixel \gap\pixel }% **¤§*\pixel¤ +}/ + +La constatation qui s'impose est que définir un caractère avec cette méthode est extrêmement fastidieux et il n'est pas acceptable de l'utiliser pour définir toutes les lettres de l'alphabet. + +\subsection{Créer une macro dont la syntaxe est facile} +\subsubsection{La macro \ttfamily\char`\\makecar} +Syntaxiquement, l'idéal serait d'inventer un moyen pour écrire les lignes de pixels « visuellement » les unes à la suite des autres. Il faudrait donc bâtir une macro §\makecar qui ressemblerait à ceci + +\centrecode/\makecar\<macro>{%¤§*\makecar¤ + *** +* * +* * +* * + ***_ + * + **,}/ + +\noindent et qui stockerait dans \verb|\<macro>| le code exposé dans l'exemple ci-dessus qui permet de tracer la lettre «\impact[1pt][0.4pt]g\impactend». Le caractère «\verb|_|» signifie, s'il est présent, que la ligne de pixels en cours s'achève et que les lignes suivantes doivent se trouver sous la \idx{ligne de base}. + +Pour s'offrir cette syntaxe, la première chose à faire est de forcer \TeX{} à faire prendre en compte les espaces lorsqu'ils sont consécutifs ou placés en début de ligne. Ceci nous contraindra à changer localement le code de catégorie de l'espace à 12 pour qu'il échappe à la règle qui lui est fixée lorsque ce catcode est 10. Le retour charriot sera rendu actif et se développera en une virgule pour pouvoir utiliser par la suite la macro §\doforeach. + +Enfin, il faut déterminer si «\verb|_|» est présent ou pas et si c'est le cas, scinder l'argument \verb|#2| en deux parties, chacune représentant les lignes de pixels au-dessus et au-dessous de la ligne de base. + +\subsubsection{La macro \ttfamily\char`\\makecar@i} +Une macro privée \verb|\makecar@i| traitera une de ces parties en transformant «\verb|*|» en §\pixel, «\verb*| |» en §\blankpixel et en ajoutant \idx\kern§\pixelsep après chacune de ces macros, tout en insérant à la toute fin \idx\unkern pour annuler le dernier espace interpixel. + +Par le truchement de §\doforeach, cette macro \verb|\makecar@i| appellera une autre macro auxiliaire \verb|\makecar@ii| qui sera chargée de transformer chaque ligne de pixels (une suite de \verb|*| et \verb*| |) en une série de §\pixel, §\blankpixel et §\gap. Elle enfermera le tout dans une \idx\hbox et ajoutera le code ainsi obtenu à une macro \verb|\pixabove| qui jouera le rôle de collecteur et qui sera affichée dans la \idx\vtop chapeau à la toute fin. L'écriture du code de cette macro privée ne présente pas de difficulté mais fait entrer en jeu quelques astuces. + +\showcode/\catcode`\@11 +\begingroup% dans ce groupe :¤\defline\aaa¤ +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\catcode`\^^M=13\relax% le retour à la ligne est actif¤\verbidx*[ (retour chariot)]{^^M}¤ +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\global\deftok\otherspc{ }% définit un espace de catcode 12¤\idx*\global\idx*{catcode!12\space(autre)}§*\otherspc§*\deftok¤ +\xdef\letter@code{% macro contenant le dessin de la lettre "e"¤\idx*\gdef¤ + ** +* * +*** +* + ***}% +\endgroup¤\defline\bbb¤ + +\def\makecar@i#1{% #1 = dessin de la lettre avec les caractères "," "*" et " " + \doforeach\current@line\in{#1}% pour chaque ligne dans #1 :¤§*\doforeach¤ + {\ifx\empty\current@line% si la ligne est vide¤\idx*\empty¤ + \addtomacro\pixabove{\hbox{\vblankpixel}}% ajouter une fausse ligne¤§*\addtomacro¤ + \else% sinon + \let\pix@line\empty% initialiser le code de la ligne à vide¤\idx*\empty¤ + \expandafter\makecar@ii\current@line\quark% et la construire¤§*\quark¤ + \fi + }% +} + +\def\makecar@ii#1{% #1=caractère de dessin de la ligne en cours + \ifxcase#1% si le caractère est¤§*\ifxcase¤ + * {\addtomacro\pix@line\pixel}%¤§*\addtomacro§*\pixel¤ + \otherspc{\addtomacro\pix@line\blankpixel}%¤§*\addtomacro§*\otherspc§*\blankpixel¤ + \endif + \ifx#1\quark% si la fin est atteinte ¤§*\quark¤ + \addtomacro\pix@line\unkern% annuler le dernier espace interpixel¤\idx*\unkern¤ + \eaddtomacro\pixabove{% et encapsuler \pix@line dans une \hbox¤§*\eaddtomacro¤ + \expandafter\hbox\expandafter{\pix@line}}%¤\idx*\hbox¤ + \else% si la fin n'est pas atteinte, ajouter l'espace interpixel + \addtomacro\pix@line\gap¤§*\addtomacro§*\gap¤ + \expandafter\makecar@ii% recommencer avec le caractère suivant + \fi +} +\pixelsize=3pt \pixelsep=1pt ¤§*\pixelsize §*\pixelsep¤ +\let\pixabove\empty% initialisation de la macro finale à vide +\exparg\makecar@i\letter@code% appelle la macro qui construit \pixabove¤\defline\ccc¤ +La lettre +\vtop{% enferme le tout dans une \vtop :¤\idx*\vtop¤ + \offinterlineskip\lineskip=\pixelsep% ajuste l'espace interligne¤\idx*\offinterlineskip\idx*\lineskip§*\pixelsep¤ + \vbox{\pixabove}% affiche le caractère créé¤\idx*\vbox¤ +}./ + +La partie la plus intéressante se situe dans le groupe semi-simple au début (lignes \nos\aaa{} à \bbb) où le code de catégorie de l'espace est changé à 12 et le celui du retour charriot est changé à 13 pour qu'il devienne actif. Cette altération de \verb|^^M| suppose que toutes les fins de ligne où l'on ne souhaite pas voir ce \verb|^^M| actif soient commentées, même celles que l'on ne commente habituellement pas (après une séquence de contrôle par exemple). Ces modifications locales faites, on peut donc tranquillement définir globalement la macro \verb|\letter@code| qui contient le code qui dessine la lettre «\verb|e|». On profite de ce groupe pour définir avec §\deftok la séquence de contrôle §\otherspc qui sera \verb|\let|-égale à un espace de catcode 12\idx*{catcode!12 (autre)} et qui servira dans un test \verb|\ifx| plus tard. + +\subsubsection{Retour sur la macro \ttfamily\char`\\makecar} +On voit maintenant comment procéder lorsqu'il y a le marqueur «\verb|_|» qui indique que des pixels se trouvent au-dessous de la \idx{ligne de base}. Il faut séparer le code définissant le caractère en deux parties et agir de la même façon avec \verb|\makecar@i| sur chacune. Le code résultant de la première est voué à se retrouver dans la \idx\vbox tandis que celui résultant de la seconde est simplement ajouté dans la \idx\vtop chapeau. + +Si une \verb|\<macro de dessin>| contient les éléments qui dessinent un caractère à l'aide de «\verb|*|», «\verb*| |» et «\verb|^^M|» de catcode 12 et éventuellement «\verb|_|»), la macro §\makecar, de syntaxe + +\centrecode-\makecar\<macro>\<macro de dessin>-§*\makecar + +\noindent se chargera de stocker dans la \verb|\<macro>| le code résultant de l'analyse du texte de remplacement de \verb|\<macro de dessin>|. Le texte de remplacement de \verb|\<macro>| sera constitué de §\pixel, §\blankpixel et §\gap enfermés dans des \idx\hbox, elles-mêmes incluses dans une \idx\vtop. + +\showcode/\catcode`\@11 +\begingroup% dans ce groupe :¤\defline\aaa¤ +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\catcode`\^^M=13\relax% le retour à la ligne est actif¤\verbidx*[ (retour charriot)]{^^M}¤ +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\global\deftok\otherspc{ }% définit un espace de catcode 12¤\idx*\global\idx*{catcode!12\space(autre)}§*\otherspc§*\deftok¤ +\xdef\letter@code{% macro contenant le dessin de la lettre "g"¤\idx*\gdef¤ + *** +* * +* * +* * + ***_ + * + **}% +\endgroup¤\defline\bbb¤ + +\def\makecar#1#2{% #1=nom recevant le code final #2=macro contenant le dessin¤§*\makecar¤ + \let\pixabove\empty \let\pixbelow\empty \let\pix@line\empty% initialise à vide¤\idx*\empty¤ + \exparg\ifin{#2}_% si le code contient _¤§*\ifin¤ + {\expandafter\makecar@iii#2\@nil}% aller à \makecar@iii + {\exparg\makecar@i{#2}}% sinon, à \makecar@i + \edef#1{% définit la macro #1 comme + \vtop{% une \vtop contenant :¤\idx*\vtop¤ + \unexpanded{\offinterlineskip\lineskip\pixelsep}% réglage d'espace inter ligne¤\idx*\unexpanded\idx*\offinterlineskip\idx*\lineskip §*\pixelsep¤ + \vbox{\unexpanded\expandafter{\pixabove}}% \vbox des pixels au-dessus + % de la ligne de base¤\idx*\vbox\idx*\unexpanded¤ + \unless\ifx\pixbelow\empty% s'il y a des pixels au-dessous de la baseline¤\tidx*{unless}¤ + \unexpanded\expandafter{\pixbelow}% les ajouter dans la \vtop¤\idx*\unexpanded¤ + \fi + }% + }% +} + +\def\makecar@iii#1_,#2\@nil{% + \makecar@i{#2}% construit la partie au-dessous de la baseline + \let\pixbelow\pixabove% et affecte le code à \pixbelow + \let\pixabove\empty \let\pix@line\empty% ré-initialise¤\idx*\empty¤ + \makecar@i{#1}% construit la partie au-dessus de la baseline +} + +\makecar\lettreg\letter@code¤§*\makecar¤ +\catcode`\@12 +\pixelsize=3pt \pixelsep=1pt ¤§*\pixelsize §*\pixelsep¤ + +Essai : \lettreg/ + +On voit clairement ce que fait la macro \verb|\makecar@iii|; elle construit séparément les lignes de pixels se trouvant au-dessous puis au-dessus de la \idx{ligne de base} et stocke les codes obtenus dans les séquences de contrôle \verb|\pixbelow| et \verb|\pixabove|. La macro §\makecar peut ensuite définir \verb|#1| en incluant \verb|\pixbelow| si elle n'est pas vide (cas où \verb|_| n'est pas présent). Cette définition se fait avec un \verb|\edef| en prenant soin de protéger avec \verb|\unexpanded| les endroits qui ne doivent pas être développés. + +\subsubsection{Définir toutes les lettres} +L'étape suivante va être de finaliser le tout. Il faut d'abord trouver un moyen simple de définir toutes les lettres de l'alphabet. La macro \verb|\impact@alphabet|, contenant le code de toutes les lettres, pourrait être définie ainsi : + +\centrecode|\def\impact@alphabet{¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +a/¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +<dessin de la lettre a>,¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +b/¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +<dessin de la lettre b>,¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +etc¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +Z/¤\bioLegacyKeyGlyph{E_n_t_e_r}¤ +<dessin de la lettre Z>}| + +Pour que les choses soient parfaitement claires, les retours charriots ne faisant pas partie du dessin des lettres sont explicitement écrits «\bioLegacyKeyGlyph{E_n_t_e_r}». Il est important de noter que dans le texte de remplacement de \verb|\impact@alphabet|, tous les \verb|^^M| sont actifs, \emph{mais non développés} en une virgule. Imaginons maintenant que l'on parcoure ce texte de remplacement avec une boucle de type §\doforeach : + +\centrecode-\doforeach\letter@name/\letter@code\in + {<texte de remplacement de \code@lettre>}- + +\noindent À chaque itération, \verb|\letter@name| va contenir + +\centrecode-¤\bioLegacyKeyGlyph{E_n_t_e_r}¤<lettre>- + +\noindent et \verb|\letter@code| + +\centrecode-¤\bioLegacyKeyGlyph{E_n_t_e_r}¤<dessin de la lettre>- + +\noindent où les \bioLegacyKeyGlyph{E_n_t_e_r} sont ceux qui ne font pas partie du dessin de la lettre. Si l'on développe au maximum \verb|\letter@name| et \verb|\letter@code|, tous les retours charriots vont céder leur place à une virgule, et les textes de remplacement de \verb|\letter@name| et \verb|\letter@code| seront : + +\begin{centrage} + \verb|,<lettre>|\qquad et\qquad\verb|,<dessin de la lettre>| +\end{centrage} + +Il faudra donc tester si ces textes de remplacement commencent par une virgule et dans l'affirmative, la supprimer. + +\showcode|\catcode`\@11 +\begingroup +\expandafter\gdef\csname impact@" "\endcsname{% définit la lettre "espace" + \hskip 4\pixelsize plus.5\pixelsize minus.5\pixelsize\relax}% +\catcode`\^^M=13\relax% le retour à la ligne est actif¤\verbidx*[ (retour charriot)]{^^M}¤ +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\gdef\impact@alphabet{ +a/ +*** + * + *** +* * +****, +b/ +* +* +*** +* * +* * +* * +***, +% beaucoup de caractères omis +9/ + *** +* * +* * + **** + * + * + ***, +0/ + *** +* * +* * +* * * +* * +* * + ***, +error/ +* * * + * * +* * * + * * +* * * + * * +* * *}% <- commenter la fin de ligne +\endgroup% +% On parcourt le texte de remplacement de \impact@alphabet +\edef\saved@crcatcode{\catcode13=\the\catcode13\relax}% +\catcode`\^^M=13\relax% le retour à la ligne est actif¤\verbidx*[ (retour charriot)]{^^M}¤ +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\expsecond{\doforeach\letter@name/\letter@code\in}\impact@alphabet%¤§*\expsecond §*\doforeach¤ + {\edef\letter@name{\letter@name}% développe la lettre (^^M devient ",") + \edef\letter@code{\letter@code}% développe le code (^^M devient ",") + \exparg\ifstart\letter@name,% si la lettre commence par "," + {\edef\letter@name{\expandafter\gobone\letter@name}}% la retirer + {}% + \exparg\ifstart\letter@code,% si le code commence par "," + {\edef\letter@code{\expandafter\gobone\letter@code}}% la retirer + {}% + \expandafter\makecar\csname impact@"\letter@name"\endcsname\letter@code%¤§*\makecar\defline\aaa¤ + }% +\saved@crcatcode% redonne le catcode de ^^M +% % définit l'espace +\pixelsize=3pt \pixelsep=1pt ¤§*\pixelsize §*\pixelsep¤ +% puis, on affiche ce qui a été défini : +a = \csname impact@"a"\endcsname\qquad b = \csname impact@"b"\endcsname\qquad +9 = \csname impact@"9"\endcsname \qquad 0 = \csname impact@"0"\endcsname\qquad +error = \csname impact@"error"\endcsname| + +La méthode fonctionne correctement pour les caractères définis : bien évidemment, pour éviter un très long listing, seuls quelques uns sont définis ici. + +Dans le code itéré de la boucle §\doforeach, remarquons à la ligne \no\aaa{} que \verb|\makecar| est invoqué et la macro contenant le code qui dessine le caractère «\verb|<car>|» est \verb|\impact@"<car>"|. Notons également que l'espace est défini comme un ressort horizontal de dimension fixe égale à 4 fois celle de §\pixelsize mais contenant aussi des composantes étirables égales à la moitié de §\pixelsize. Celles-ci peuvent entrer en jeu pour justifier le texte à composer à la largeur disponible en comblant la différence entre la largeur des caractères affichés sur la ligne et la largeur de composition. + +Un caractère «\verb|error|» a également été dessiné au cas où l'on rencontrerait une lettre non définie. + +\subsubsection{La macro \ttfamily\char`\\impact} +Tout cela va permettre de définir deux macros §\impact et \verb|\impactend| qui vont composer en police \impact Impact\impactend{} le \verb|<texte>| se trouvant entre elles selon cette syntaxe : + +\centrecode/\impact[1.2pt][0.2pt]Programmer en {TEX} est +facile et tr{e`}s utile !\impactend/ + +Tout d'abord, pour composer ce livre, un caractère de la police impact dont le nom est «TEX» a été défini et le fait qu'il soit écrit entre accolades assure que l'ensemble est bien lu comme un argument et compris comme le nom d'un caractère. Le même principe s'applique aux caractères accentués «\verb|e`|», «\verb|e'|» et d'autres. Ensuite, la macro §\impact a deux arguments optionnels, le premier étant la dimension qui sera assignée à §\pixelsize et le second celle qui sera assignée à §\pixelsep. La macro §\newmacro que nous avons vue à la partie précédente sera utilisée afin que ces arguments optionnels soient facilement accessibles. + +En reprenant la procédure mise en \oe uvre pour la macro §\parse, nous allons parcourir ce qui se trouve après la macro §\impact et, à l'aide de \idx\futurelet, nous déciderons quelle action entreprendre selon le type de token à venir : +\begin{enumerate} + \item le \idx{quark} \verb|\impactend|; + \item un espace; + \item un argument commençant par une accolade. +\end{enumerate} + +Le premier cas stoppera la lecture du texte. Pour les deux derniers cas, on insèrera après chaque caractère une espace qui est en réalité le ressort \verb|\letter@skip| défini par + +\centrecode-\letter@skip=\pixelsep plus.1\pixelsize minus.1\pixelsize-§*\pixelsep + +\noindent Les composantes de compression et d'étirement de l'espace interlettre, cumulées avec celles du caractère intermot «\verb*|\letter@" "|», assurent que les avertissements\idx*{message d'avertissement} de type \verb|Overful|/\linebreak[1]\verb|Underful hbox| qui caractérisent les lignes trop longues ou trop courtes seront le plus rare possible. + +La façon de coder ce genre de macro a été vue à la partie précédente et il suffit de reproduire le schéma déjà étudié : + +\showcode|\catcode`\@11 +\newskip\letter@skip% ressort mis entre chaque lettre¤\idx*\newskip¤ +\def\impactend{\impactend}% définit le quark de fin + +\newmacro\impact[0.2ex][0pt]{% définit la macro à arguments optionnels¤§*\impact§*\newmacro¤ + \leavevmode% passer en mode horizontal¤\idx*\leavevmode¤ + \begingroup% dans un groupe semi-simple : + \pixelsize=#1\relax \pixelsep=#2\relax% définir ces deux dimensions¤§*\pixelsize §*\pixelsep¤ + \letter@skip=#1 plus.1\pixelsize minus.1\pixelsize\relax% définir espace inter-lettre + \baselineskip=% définir la distance entre les lignes de base¤\idx*\baselineskip¤ + \dimexpr12\pixelsize+7\pixelsep\relax% à 12\pixelsize+7\pixelsep + \lineskiplimit=0pt % si lignes trop serrées¤\idx*\lineskiplimit¤ + \lineskip=2\pixelsize\relax% les espacer de 2\pixelsize¤\idx*\lineskip¤ + \impact@i% puis aller voir le prochain token +} + +% va voir le prochain token puis va le tester à \impact@ii +\def\impact@i{\futurelet\nxtletter\impact@ii}¤\idx*\futurelet¤ + +\def\impact@ii{% + \ifx\nxtletter\impactend% si le token est \let égal à \impactend + \let\donext\impact@endprocess% aller à al macro de fin + \else + \ifx\nxtletter\sptoken% si c'est un espace¤§*\sptoken¤ + \let\donext\impact@spc% aller à \impact@spc + \else + \let\donext\impact@arg% sinon, aller à \impact@arg + \fi + \fi + \donext% faire l'action décidée ci-dessus +} + +% mange un espace (argument délimité) et affiche "\letter@<spc>" +\expandafter\def\expandafter\impact@spc\space{%¤\idx*\space¤ + \csname impact@" "\endcsname + \impact@i% puis va voir le prochain token +} +% +% lit l'argument suivant +\def\impact@arg#1{% + \csname impact@% affiche + \ifcsname impact@"#1"\endcsname¤\tidx{ifcsname}¤ + "#1"% le caractère \impact@"#1" s'il est défini + \else + "error"% sinon \impact@"error" + \fi + \endcsname + \hskip\letter@skip% insérer le ressort inter-lettre¤\idx*\hskip¤ + \impact@i% puis, aller voir le prochain token +} +\def\impact@endprocess\impactend{% macro exécuté lorsqque le quark \impactend va être lu + \unskip% annuler le dernier ressort¤\idx*\unskip¤ + \par% composer le paragraphe pour prendre en compte + % \baselineskip, \lineskip et \lineskiplimit + \endgroup% et fermer le groupe +} +\catcode`\!=12 % rend le point d'exclamation "gentil" +\impact Programmer en {TEX} est facile et tr{e`}s utile !\impactend\par +\impact[2pt][0.5pt]Programmer en {TEX} est facile et tr{e`}s utile !\impactend¤§*\impact¤| + +La valeur de \idx\baselineskip s'explique par le fait que tels qu'ils sont dessinés, les caractères alphanumériques de la police impact ont au maximum 9 pixels de \emph{hauteur totale}, où la hauteur totale est la somme de la hauteur et de la profondeur. L'encombrement vertical maximal d'un caractère est donc + +\[9\times\verb|\pixelsize|+7\times\verb|\pixelsep|\] + +Pour augmenter un peu l'espacement entre les lignes, on rajoute à cette dimension $3\times\verb|\pixelsize|$, ce qui donne la valeur de \idx\baselineskip choisie pour composer le paragraphe composé en police impact. + +\idx*[|)]{police Impact} + +\section{Tracer des graphiques} +Pourquoi vouloir tracer un graphique avec \TeX{} alors que des extensions très puissantes\footnote{Les deux plus connues sont PStricks et Ti\textit kz.} aux capacités quasi illimitées existent pour cela ? Il s'agit ici non pas de les concurrencer, mais de relever le défi et exploiter les maigres possibilités qu'offre \TeX{}, c'est-à-dire les réglures. Le but est de comprendre comment les placer précisément dans une zone. Voici par exemple ce qu'il est possible de faire : + +\begin{centrage} + \xunit=1cm \yunit=0.75cm + \def\fonction#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% + \decmul{#1}{#1}pt-#1pt*3+2pt\relax}% + \graphzone{-2}[0.5]{2.5}[5]{-5}{5}[2]{% + \let\plotstuff\cross% définit ce qu'il faut afficher comme point + \FOR\xx=-2 to 2.5 \do 0.1{% + \plot(\xx,\fonction{\xx})% afficher les points de coordonnées (x ; f(x)) + }% + \putat{5pt}{\dimexpr\graphboxht-10pt\relax}% afficher + {$\scriptstyle f(x)=x^3-x^2-3x+2$}% la fonction tracée + \showaxis% et les axes + }% +\end{centrage} + +Le nombre de réglures est assez important et nous allons découper le problème en problèmes élémentaires avant de rassembler le tout à la fin. + +La méthode qui va être notre fil directeur est de tracer \emph{toutes} les réglures et \emph{tous} les caractères après avoir rendu leurs dimensions nulles. Ainsi, pendant tout le tracé, le \idx{point de référence} reste le point inférieur gauche qui a pour coordonnées dans cet exemple \hbox{ $(-2{,}5\;\string;\;-5)$}. C'est seulement à la fin que l'on forcera une boite à prendre celle du graphique afin que la boite englobante\idx*{boite!englobante} du tout coïncide avec le rectangle représentant la zone graphique. + +\subsection{Dessiner des axes gradués}\idx*[|(]{axe gradué} +Le premier problème est de tracer les axes gradués situés en bas et à gauche de la zone. \Qu els paramètres faut-il spécifier pour tracer un axe horizontal tel que celui-ci ? + +\begin{centrage} +\leavevmode\xaxis[2cm]{-2}[2]{8}[4] +\end{centrage} + +Une simple observation permet de répondre. Il faut : + +\begin{enumerate} + \item l'abscisse de départ et celle d'arrivée «\verb|xmin|» et «\verb|xmax|» (ici $-2$ et $8$); + \item l'incrément «\verb|inc|» entre 2 graduations principales (ici $2$); + \item le nombre de subdivisions «\verb|nb_subdiv|» (ici $4$); + \item la distance «\verb|dist|» entre deux graduations principales (ici \numprint[cm]2); + \item enfin, les dimensions des réglures proprement dites :\label{graphzone.non.arg} + \begin{itemize} + \item les dimensions verticales des traits représentant une division principale et une subdivision ; + \item les épaisseurs des traits représentant une division principale et une subdivision ; + \item l'épaisseur du trait horizontal représentant l'axe. + \end{itemize} +\end{enumerate} + +Nous allons décider que la macro §\xaxis va tracer un axe horizontal et que tous les arguments (sauf ceux du point \no\ref{graphzone.non.arg}) seront passés à la macro. Celle-ci aura 5 arguments (2 obligatoires et 3 optionnels) disposés selon cette syntaxe : + +\centrecode-\xaxis[dist]{xmin}[inc]{xmax}[nb_subdiv]-§*\xaxis + +Les 5 autres dimensions graphiques du point \no\ref{graphzone.non.arg} seront contenues dans des registres de dimension : + +\begin{itemize} + \item \verb|\maingraddim| et \verb|\subgraddim| pour les hauteurs des traits ; + \item \verb|\maingradwd| et \verb|\subgradwd| pour leurs épaisseurs; + \item \verb|\axiswd| pour l'épaisseur de l'axe. +\end{itemize} + +Maintenant que la syntaxe est définie, passons au mécanisme de tracé proprement dit. Voyons d'abord comment tracer une graduation principale avec une abscisse située au-dessous. Pour le trait, nous utiliserons un \idx\rlap pour que sa dimension soit nulle. Pour le nombre affiché au-dessous, il sera l'argument de la macro et sera également affiché avec la macro §\clap, et abaissé de \verb|1.5ex| à l'aide de la primitive \idx\lower : + +\showcode/\newdimen\maingraddim \maingraddim=4pt % hauteur des graduations principales¤\idx*\newdimen¤ +\newdimen\maingradwd \maingradwd=0.5pt % épaisseur des graduations principales +\def\maingradx#1{% + \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous¤\idx*\lower\idx*\scriptscriptstyle§*\clap¤ + \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure¤\idx*\rlap\idx*\vrule¤ +} +Début\maingradx{1}suite\maingradx{2}conclusion\maingradx{3}fin./ + +La longueur horizontale de l'axe sera + +\[\text{\small\tt dist}\times\frac{\text{\small\tt xmax}-\text{\small\tt xmin}}{\text{\small\tt inc}}\] + +Voici donc une ébauche de la macro §\xaxis : + +\showcode|\maingraddim=4pt \maingradwd=0.5pt +\newdimen\axiswd \axiswd=0.5pt ¤\idx*\newdimen¤ +\newmacro\xaxis[1cm]1[1]1[4]{%% #1= dist #2=xmin #3=inc #4=xmax #5=subdiv¤§*\xaxis§*\newmacro¤ + \hbox{% mettre le tout dans une \hbox¤\idx*\hbox¤ + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale¤§*\FOR¤ + \maingradx{\xx}% tracer la graduation et écrire l'abscisse + \kern#1\relax% puis se déplacer vers la droite¤\idx*\kern¤ + }% + }% + \vrule% tracer l'axe des abscisses¤\idx*\vrule\defline\aaa¤ + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax¤§*\decdiv§*\dimtodec¤ + depth 0pt\relax % et de profondeur nulle¤\defline\bbb¤ + }% +} +a) \xaxis{-2}{5}suite + +b) \frboxsep=0pt\frbox{\xaxis[1.25cm]{-1}[0.25]{1}}suite¤§*\axis§*\frboxsep§*\frbox§*\xaxis¤| + +Dans le deuxième cas, on constate en l'encadrant avec §\frbox que l'encombrement de l'axe est bon. C'est ce qui était recherché puisque la seule chose tracée ayant une dimension horizontale non nulle est l'axe lui-même, c'est-à-dire la \idx\vrule des lignes \nos\aaa-\bbb{}. + +Il nous reste à insérer dans chaque boucle les graduations secondaires qui sont au nombre de \hbox{$\verb|#|5-1$}. Elles seront affichées en débordement à droite après chaque graduation principale, sauf pour la dernière. Préalablement à la boucle, elles seront stockées dans une \idx\hbox via le registre de boite \no0. Dans cette \idx\hbox, il doit y avoir $\verb|#5|-1$ fois ceci : + +\begin{itemize} + \item une espace de dimension \hbox{$\verb|#|1\div\verb|#|5$}; + \item une réglure de dimension nulle correspondant à la marque de subdivision. +\end{itemize} + +Nous avons le choix pour remplir cette \idx\hbox avec ces $\verb|#5|-1$ répétitions. Soit nous faisons appel à \idx\leaders, soit à une boucle §\for. C'est cette deuxième alternative qui sera choisie ici : + +\showcode|\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\newdimen\subgraddim \subgraddim=2.5pt ¤\idx*\newdimen¤ +\newdimen\subgradwd \subgradwd=0.2pt +% trace un trait de subdivision +\def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }}¤\idx*\rlap\idx*\vrule¤ + +\newmacro\xaxis[1cm]1[1]1[4]{% #1= dist #2=xmin #3=inc #4=xmax #5=subdiv¤§*\xaxis§*\newmacro¤ + \hbox{% tout mettre dans une \hbox¤\idx*\hbox¤ + \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités¤\idx*\setbox¤ + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions¤\idx*\the¤ + \for\xx=1 to #5-1 \do{% insérer #5-1 fois¤§*\for¤ + \kern\dimsubgrad% une espace secondaire¤\idx*\kern¤ + \subgradx% une graduation secondaire + }% + }% + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale¤§*\FOR¤ + \maingradx{\xx}% imprimer l'abscisse + \ifdim\xx pt<#4pt % et en débordement à droite, + \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière¤\idx*\rlap\idx*\copy¤ + \fi + \kern#1\relax% se déplacer vers la droite¤\idx*\kern¤ + }% + }% + \vrule% tracer l'axe des abscisses¤\idx*\vrule\defline\aaa¤ + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax¤§*\decdiv§*\dimtodec¤ + depth 0pt\relax % et de profondeur nulle¤\defline\bbb¤ + }% +} +a) \xaxis{-2}{5}[2] + +)b \xaxis[1.25cm]{-1}[0.25]{1}[5]¤\idx*\leavevmode§*\xaxis¤| + +Retenons que la largeur de la \idx\hbox chapeau est la largeur de la zone graphique. Notons également que l'on \emph{ne peut pas} écrire + +\centrecode|\setbox0\xaxis{-2}{5}[2]|§*\axis + +\noindent car, contrairement aux apparences, la macro §\xaxis ne se développe pas en une \idx\hbox. Cela est dû à la façon dont §\newmacro construit les macros qui ont des arguments optionnels : des sous-macros sont créées et l'emploi de \idx\futurelet annule toute possibilité de développement maximal. + +Passons maintenant aux axes verticaux. La méthode va être la même, sauf que toutes les primitives typées en mode horizontal\idx*{mode!horizontal} passeront cette fois en mode vertical\idx*{mode!vertical} : nous mettrons le tout dans une \idx\vbox et puisqu'elle sera remplie du haut vers le bas, il faudra commencer avec l'ordonnée de fin et construire une boucle §\FOR avec un incrément négatif. + +La méthode utilisée avec §\yaxis pour l'affichage des ordonnées ne sera pas identiques à celles des abscisses : les dimensions des nombres représentant les ordonnées seront rendues nulles grâce à une \idx\llap. En effet, la dimension horizontale d'un nombre est variable alors que sa dimension verticale est constante. Réserver suffisamment d'espace à gauche de l'axe pour y placer les ordonnées aurait nécessité de calculer la plus grande dimension horizontale des ordonnées ce qui implique une boucle préalable. Il s'avère donc plus facile et plus économique d'ignorer cette dimension horizontale, quitte à rejeter hors de la boite englobante toutes les ordonnées. + +\showcode|\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\subgraddim=2.5pt \subgradwd=0.2pt +\def\maingrady#1{% affiche... + \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée...¤§*\vlap \idx*\llap\idx*\scriptscriptstyle¤ + \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure¤\idx*\vbox\idx*\vss\idx*\hrule¤ +} + +% affiche une subdiv +\def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }}¤§*\vlap\idx*\hrule¤ +\newmacro\yaxis[1cm]1[1]1[4]{% +% #1= dist #2=ymin #3=inc #4=ymax #5=subdiv¤§*\yaxis§*\newmacro¤ + \vbox{%¤\idx*\vbox¤ + \offinterlineskip% désactiver le ressort d'interligne¤\idx*\offinterlineskip¤ + \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités¤\idx*\setbox¤ + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions¤\idx*\the¤ + \for\xx=1 to #5-1 \do{% insérer #5-1 fois¤§*\for¤ + \kern\dimsubgrad% une espace secondaire¤\idx*\kern¤ + \subgrady% une graduation secondaire + }% + }% + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions¤\idx*\the\idx*\dimexpr¤ + \vbox to 0pt{% en débordement vers le bas + \FOR\xx = #4to#2\do-#3{%¤§*\FOR¤ + \maingrady{\xx}% imprimer l'abscisse + \ifdim\xx pt>#2pt % et en débordement à droite, + \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière¤\idx*\copy\idx*\vss¤ + \fi + \kern#1\relax% se déplacer vers la droite¤\idx*\kern¤ + }% + \vss% assure le débordement vers le bas¤\idx*\vss¤ + }% + \clap{\vrule% tracer l'axe des ordonnées + width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 + height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax¤§*\decdiv§*\dimtodec¤ + depth 0pt\relax % profondeur nulle + }% + }% +} + +Essai : \yaxis{-2}{5}[2]\qquad \frboxsep=0pt \frbox{\yaxis[0.75cm]{-1}[0.5]{3}[5]}¤§*\frboxsep§*\frbox§*\yaxis¤|\idx*[|)]{axe gradué} + +\subsection{Créer une zone pour le graphique} +Tout est maintenant prêt pour tracer notre zone graphique. Deux registres de dimension supplémentaires vont être créés, \verb|\xunit| et \verb|\yunit|, chacun spécifiant la distance entre deux graduations principales sur chacun des axes. La syntaxe de la macro va être proche des syntaxes de §\xaxis ou §\yaxis: + +\centrecode-\graphzone{xmin}[xinc]{xmax}[nbsubdivx]{ymin}[yinc]{ymax}[nbsubdivy]{<code>}-§*\graphzone + +\noindent où le \verb|<code>| contient les instructions graphiques que nous verrons plus tard et qui permettent de dessiner dans la zone. + +\showcode|\newdimen\xunit \xunit=1cm ¤\idx*\newdimen¤ +\newdimen\yunit \yunit=0.75cm +\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\subgraddim=2.5pt \subgradwd=0.2pt +\catcode`@11 +\newmacro\graphzone1[1]1[4]1[1]1[4]{%¤§*\graphzone§*\newmacro¤ + \leavevmode% quitter le mode vertical¤\idx*\leavevmode\idx*{mode!vertical}¤ + \begingroup% travailler dans un groupe semi-simple + \def\graphxmin{#1}\def\graphxmax{#3}% sauvegarder + \def\graphymin{#5}\def\graphymax{#7}% les + \def\xincrement{#2}\def\yincrement{#6}% arguments + \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% axe "y" dans boite 0¤\idx*\setbox§*\yaxis¤ + \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% axe "x" dans boite 1¤\idx*\setbox§*\xaxis¤ + \edef\graphboxht{\the\ht0 }% \graphboxh est la hauteur de la zone¤\idx*\the\idx*\ht¤ + \edef\graphboxwd{\the\wd1 }% \graphboxw est la largeur de la zone¤\idx*\wd¤ + \rlap{% annuler la dimension horizontale, et...¤\idx*\rlap¤ + \box1 % ...afficher l'axe (Ox) puis¤\idx*\box¤ + % le trait vertical à l'extrême droite de la zone + \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }%¤\idx*\vrule\idx*\dimexpr¤ + }% + \rlap{\box0 }% afficher l'axe (Oy) en annulant sa dimension horizontale¤\idx*\rlap\idx*\box¤ + \raise\graphboxht% puis monter tout en haut de la zone¤\idx*\raise¤ + % pour tracer le trait horizontal en annulant sa longueur + \rlap{\kern-0.5\axiswd% et en rattrapant le centrage de l'axe des ordonnées + \vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}%¤\idx*\rlap\idx*\vrule¤ + \begingroup% pour lire l'argument "<code>" : + \catcode`\^^M=9\relax % ignorer les retours à la ligne¤\verbidx*[ (retour charriot)]{^^M}¤ + \graphzone@i% appeler \graphzone@i +} +\def\graphzone@i#1{% lire le <code>... + \endgroup% et fermer le groupe précédemment ouvert + \setbox0\hbox{#1}% mettre les instructions graphique dans la boite 0¤\idx*\setbox¤ + \wd0=\dimexpr\graphboxwd+\axiswd\relax% forcer sa dim horizontale¤\idx*\wd\idx*\dimexpr¤ + \ht0=\dimexpr\graphboxht+\axiswd\relax% et verticale¤\idx*\ht¤ + % pour correspondre aux dimension de la zone graphique + \box0 % l'afficher¤\idx*\box\defline\aaa¤ + \endgroup% sortir du groupe initial +} +\catcode`@12 +essai\graphzone{-1}[0.5]{1.5}[5]{-20}[10]{10}[2]{\relax}fin¤§*\graphzone¤| + +Le code ne présente pas beaucoup de difficulté. Signalons tout de même les quelques points suivants: +\begin{itemize} + \item les réglures correspondant au trait vertical à droite et au trait horizontal en haut sont tracées à coup de \idx\vrule. Celle de droite est tracée à la suite de \idx\box\verb|1| qui est l'axe horizontal et celle du haut en est correctement placée en s'aidant de \idx\raise qui élève la boite \idx\rlap de la hauteur de la zone graphique ; + \item on a modifié le code de catégorie du retour à la ligne dans l'argument final \verb|<code>| de §\graphzone afin que les instructions graphiques puissent être entrées le plus « naturellement » possible, notamment sans avoir besoin de commenter les fins de ligne ; + \item le \idx{point de référence} demeure en bas à gauche de la zone graphique jusqu'au \idx\box\verb|0| de la macro \verb|\graphzone@i| (ligne \no\aaa{}) puisque jusque là, on n'a affiché que des \idx\rlap. C'est justement en forçant les dimensions de cette boite \no0 aux dimensions de la zone graphique qu'en l'affichant, on obtient le bon encombrement final. +\end{itemize} + +\subsection{Remplir la zone graphique} +Il nous reste à écrire des instructions graphiques pour remplir la zone\ldots{} Tout d'abord, il va falloir construire une macro §\putat qui place n'importe quel contenu relativement au \idx{point de référence} courant : + +\centrecode-\putat{dimx}{dimy}{<contenu>}-§*\putat + +Il est bien entendu que cette macro va placer le point de référence du \verb|<contenu>| à des coordonnées relatives par rapport au point de référence courant, mais \emph{sans changer ce \idx{point de référence} courant}. Autrement dit, la boite englobante\idx*{boite!englobante} de cette macro a toutes ses dimensions nulles et donc, son exécution n'a aucune influence sur le point de référence courant. + +En jouant sur les boites et imbriquant des boites horizontales et verticales en débordement, on peut facilement effectuer cette action : + +\showcode|\def\putat#1#2#3{%¤§*\putat¤ + \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}%¤\idx*\leavevmode\idx*\rlap\idx*\kern\idx*\vbox\idx*\hss¤ +} +Essai\putat{0.5cm}{0.3cm}{A}\putat{-0.2cm}{-0.1cm}{B}\putat{0pt}{0.2cm}{C}suite¤§*\putat¤| + +Cette macro §\putat va nous permettre de placer n'importe quel matériel dans la zone graphique, mais l'idéal serait que le matériel placé le soit avec les coordonnées affichées sur les bords de la zone graphique (que nous appelons $X$ et $Y$) et non pas les coordonnées par rapport au coin inférieur gauche ($x$ et $y$) qui est le \idx{point de référence}. Il est bien entendu facile de passer d'un système de coordonnées à l'autre avec une simple formule de changement de repère : + +\[\begin{cases} + X=(x-x_{\text{min}})\div x_{\text{inc}}\\[5pt] + Y=(y-y_{\text{min}})\div y_{\text{inc}} +\end{cases}\] + +Pour éviter que les divisions ne soient faites à chaque placement d'un objet, nous diviserons une bonne fois pour toutes les dimensions \verb|\xunit| et \verb|\yunit| par les incréments $x_{\text{inc}}$ et $y_{\text{inc}}$ à l'intérieur de la zone graphique. + +Pour placer les axes, il faut d'abord tester si le nombre 0 est contenu dans l'intervalle représenté sur l'axe horizontal dans la zone graphique et faire de même pour l'axe vertical. Le plus simple est de modifier la macro §\ifinside, programmée à la page~\pageref{ifinside}, mais qui avait le défaut de n'agir que sur les entiers : + +\showcode|\def\ifinside#1[#2,#3]{%¤§*\ifinside¤ + \ifnum\sgn{\dimexpr#1pt-#2pt\relax}\sgn{\dimexpr#1pt-#3pt}1=1 ¤§*\sgn¤ + \expandafter\secondoftwo + \else + \expandafter\firstoftwo + \fi +} +1) \ifinside3.5[0.5,4]{vrai}{faux}\qquad +2) \ifinside-0.2[-0.4,-0.3]{vrai}{faux}\qquad +3) \ifinside-0.999[-1,-0.5]{vrai}{faux}¤§*\ifinside¤| + +Puis, il faut définir la fonction dont on veut afficher la courbe représentative, sachant que les seules opérations dont nous disposons sont les quatre opérations arithmétiques sur des décimaux. On ne peut donc représenter que des fonctions rationnelles\footnote{Il est bien évident qu'avec une extension spécialisée dans le calcul comme l'est «\texttt{fp}», toutes les fonctions, y compris transcendantes, sont très facilement accessibles.}. + +Supposons que l'on veuille représenter la fonction $f(x)=x^3-x^2-3x+2$. Nous allons devoir faire le petit effort de traduire cet enchainement d'opérations en instructions compréhensibles par \eTeX{} muni des macros rendant possibles la multiplication et la division par un décimal que sont §\decmul et §\decdiv, vues à partir de la page~\pageref{decmul} : + +\showcode|\def\fonction#1{\dimtodec\dimexpr¤§*\dimtodec¤ + \decmul{#1}{\decmul{#1}{#1}}pt% x^3¤§*\decmul¤ + -\decmul{#1}{#1}pt% -x^2¤§*\decmul¤ + -#1pt*3% -3x + +2pt\relax}% +2 +1) \fonction{-4}\qquad% doit afficher -66 +2) \fonction{-1.7}\qquad % doit afficher -0.703 +3) \fonction{1}\qquad% doit afficher -1 +4) \fonction{1.35}% doit afficher -1.412125| + +On voit ici que l'on a ajouté les dimensions en \idx*{unité!pt}\idx*{pt (unité)}\verb|pt| pour permettre à \idx\dimexpr d'effectuer le calcul avant que §\dimtodec n'enlève cette unité pour que cette macro, purement développable, renvoie un nombre décimal sans unité. Bien sûr, certaines erreurs d'arrondis rendent les résultats légèrement faux ce qui n'a aucune importance ici puisqu'ils seront des coordonnées de points dans un repère où l'erreur commise est insignifiante en regard de ce qui est visible à l'\oe il nu. + +Il faut ensuite définir la forme du point qui sera affiché. Pourquoi pas une croix ? Il faut bien sûr qu'elle soit de dimensions nulles : + +\showcode|\newmacro\cross[2pt][0.2pt]{%¤§*\cross§*\newmacro¤ + % #1=dimensions de traits depuis le centre de la croix + % #2=épaisseur des traits + \leavevmode¤\idx*\leavevmode¤ + \vlap{%¤§*\vlap¤ + \clap{%¤§*\clap¤ + \vrule height#2 depth0pt width#1 % 1/2 trait horizontal gauche + \vrule height#1 depth#1 width#2 % trait vertical + \vrule height#2 depth0pt width#1 % 1/2 trait horizontal droit + }%¤\idx*\vrule¤ + }% +} +Une croix : \cross{}puis une autre\cross[8pt][0.8pt]¤§*\cross¤| + +Voici maintenant le code reprenant toutes les macros vues précédemment où deux courbes représentatives sont construites : + +\showcode|\maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt +\subgraddim=2.5pt \subgradwd=0.2pt +\xunit=1.25cm \yunit=0.75cm + +\def\maingradx#1{% + \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous¤\idx*\lower\idx*\scriptscriptstyle§*\clap¤ + \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure¤\idx*\rlap\idx*\vrule¤ +} + +\def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }}¤\idx*\rlap\idx*\vrule¤ + +\newmacro\xaxis[1cm]1[1]1[4]{%¤§*\xaxis§*\newmacro¤ + \hbox{% tout mettre dans une \hbox¤\idx*\hbox¤ + \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités¤\idx*\setbox¤ + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions¤\idx*\the¤ + \for\xx=1 to #5-1 \do{% insérer #5-1 fois¤§*\for¤ + \kern\dimsubgrad% une espace secondaire¤\idx*\kern¤ + \subgradx% une graduation secondaire + }% + }% + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale¤§*\FOR¤ + \maingradx{\xx}% imprimer l'abscisse + \ifdim\xx pt<#4pt % et en débordement à droite, + \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière¤\idx*\rlap\idx*\copy¤ + \fi + \kern#1\relax% se déplacer vers la droite¤\idx*\kern¤ + }% + }% + \vrule% tracer l'axe des abscisses¤\idx*\vrule\defline\aaa¤ + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax¤§*\decdiv§*\dimtodec¤ + depth 0pt\relax % et de profondeur nulle¤\defline\bbb¤ + }% +} + +\def\maingrady#1{% affiche... + \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée...¤§*\vlap \idx*\llap\idx*\scriptscriptstyle¤ + \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure¤\idx*\vbox\idx*\vss\idx*\hrule¤ +} + +% affiche une subdiv +\def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }}¤§*\vlap\idx*\hrule¤ +% #1= dim entre 2 grad principales #2=abscisse départ #3=incrément +% #4=abscisse arrivée #5=nb intervalles secondaires +\newmacro\yaxis[1cm]1[1]1[4]{%¤§*\yaxis§*\newmacro¤ + \vbox{%¤\idx*\vbox¤ + \offinterlineskip% désactiver le ressort d'interligne¤\idx*\offinterlineskip¤ + \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités¤\idx*\setbox¤ + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions¤\idx*\the¤ + \for\xx=1 to #5-1 \do{% insérer #5-1 fois¤§*\for¤ + \kern\dimsubgrad% une espace secondaire¤\idx*\kern¤ + \subgrady% une graduation secondaire + }% + }% + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions¤\idx*\the\idx*\dimexpr¤ + \vbox to 0pt{% en débordement vers le bas + \FOR\xx = #4to#2\do-#3{%¤§*\FOR¤ + \maingrady{\xx}% imprimer l'abscisse + \ifdim\xx pt>#2pt % et en débordement à droite, + \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière¤\idx*\copy\idx*\vss¤ + \fi + \kern#1\relax% se déplacer vers la droite¤\idx*\kern¤ + }% + \vss% assure le débordement vers le bas¤\idx*\vss¤ + }% + \clap{\vrule% tracer l'axe des ordonnées + width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 + height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax¤§*\decdiv§*\dimtodec¤ + depth 0pt\relax % profondeur nulle + }% + }% +} + +\catcode`@11 +\newmacro\graphzone1[1]1[4]1[1]1[4]{%¤§*\graphzone§*\newmacro¤ + \leavevmode% quitter le mode vertical¤\idx*\leavevmode\idx*{mode!vertical}¤ + \begingroup% travailler dans un groupe semi-simple + \def\graphxmin{#1}\def\graphxmax{#3}% sauvegarder + \def\graphymin{#5}\def\graphymax{#7}% les + \def\xincrement{#2}\def\yincrement{#6}% arguments + \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% axe "y" dans boite 0¤\idx*\setbox§*\yaxis¤ + \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% axe "x" dans boite 1¤\idx*\setbox§*\xaxis¤ + \edef\graphboxht{\the\ht0 }% \graphboxh est la hauteur de la zone¤\idx*\the\idx*\ht¤ + \edef\graphboxwd{\the\wd1 }% \graphboxw est la largeur de la zone¤\idx*\wd¤ + \rlap{% annuler la dimension horizontale, et...¤\idx*\rlap¤ + \box1 % ...afficher l'axe (Ox) puis¤\idx*\box¤ + % le trait vertical à l'extrême droite de la zone + \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }%¤\idx*\vrule\idx*\dimexpr¤ + }% + \rlap{\box0 }% afficher l'axe (Oy) en annulant sa dimension horizontale¤\idx*\rlap\idx*\box¤ + \raise\graphboxht% puis monter tout en haut de la zone¤\idx*\raise¤ + % pour tracer le trait horizontal en annulant sa longueur + \rlap{\kern-0.5\axiswd% et en rattrapant le centrage de l'axe des ordonnées + \vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}%¤\idx*\rlap\idx*\vrule¤ + \begingroup% pour lire l'argument "<code>" : + \catcode`\^^M=9\relax % ignorer les retours à la ligne¤\verbidx*[ (retour charriot)]{^^M}¤ + \graphzone@i% appeler \graphzone@i +} +\def\graphzone@i#1{% lire le <code>... + \endgroup% et fermer le groupe précédemment ouvert + \xunit=\decdiv1\xincrement\xunit% divise les unités par l'incrément + \yunit=\decdiv1\yincrement\yunit + \setbox0\hbox{#1}% mettre les instructions graphique dans la boite 0¤\idx*\setbox¤ + \wd0=\dimexpr\graphboxwd+\axiswd\relax% forcer sa dim horizontale¤\idx*\wd\idx*\dimexpr¤ + \ht0=\dimexpr\graphboxht+\axiswd\relax% et verticale¤\idx*\ht¤ + % pour correspondre aux dimension de la zone graphique + \box0 % l'afficher¤\idx*\box\defline\aaa¤ + \endgroup% sortir du groupe initial +} + +\def\putat#1#2#3{%¤§*\putat¤ + \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}%¤\idx*\leavevmode\idx*\rlap\idx*\kern\idx*\vbox\idx*\hss¤ +} + +\newmacro\cross[2pt][0.2pt]{%¤§*\cross§*\newmacro¤ + \leavevmode¤\idx*\leavevmode¤ + \vlap{%¤§*\vlap¤ + \clap{%¤§*\clap¤ + \vrule height#2 depth0pt width#1 % 1/2 terait horizontal gauche + \vrule height#1 depth#1 width#2 % trait vertical + \vrule height#2 depth0pt width#1 % 1/2 trait horizontal droit + }%¤\idx*\vrule¤ + }% +} + +\def\plot(#1,#2){% place "\plotstuff" aux coordonnées #1,#2¤§*\plot¤ + \edef\x@plot{#1}\edef\y@plot{#2}% développer au cas où + \ifinside\x@plot[\graphxmin,\graphxmax]% si #1 est dans les limites¤§*\ifinside¤ + {\ifinside\y@plot[\graphymin,\graphymax]% et si #2 l'est aussi¤§*\ifinside¤ + {\putat% placer aux coordonnées¤§*\putat¤ + {\dimexpr\x@plot\xunit-\graphxmin\xunit\relax}% X=(x-xmin)/xinc¤\idx*\dimexpr¤ + {\dimexpr\y@plot\yunit-\graphymin\yunit\relax}% Y=(y-ymin)/yinc¤\idx*\dimexpr¤ + \plotstuff% le contenu de \plotstuff + }% + \relax + } + \relax +} + +\def\showxaxis{% affiche l'axe (Ox) + \ifinside0[\graphymin,\graphymax]%¤§*\ifinside¤ + {\putat\z@{-\graphymin\yunit}{\vlap{\vrule width\graphboxwd height\axiswd}}}%¤\idx*\vrule §*\putat\idx*\z@¤ + \relax +} + +\def\showyaxis{% affiche l'axe (Oy) + \ifinside0[\graphxmin,\graphxmax]%¤§*\ifinside¤ + {\putat{-\graphxmin\xunit}\z@{\clap{\vrule width\axiswd height\graphboxht}}}%¤\idx*\vrule §*\putat\idx*\z@¤ + \relax +} + +\def\showaxis{\showxaxis\showyaxis}% affiche les deux axes +\catcode`@12 + +%%%%%%%%%%%%%%% 1er exemple : +\xunit=1cm \yunit=0.5cm +\def\fonction#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% + \decmul{#1}{#1}pt-#1pt*3+2pt\relax}¤§*\dimtodec §*\decmul\idx*\dimexpr¤ +Exemple 1 :\qquad +\graphzone{-2}[0.5]{2.5}[5]{-5}{5}[2]{%¤§*\graphzone¤ + \let\plotstuff\cross% définit ce qu'il faut afficher comme point¤§*\cross¤ + \FOR\xx=-2 to 2.5 \do 0.1{%¤§*\FOR¤ + \plot(\xx,\fonction{\xx})% afficher les points de coordonnées (x ; f(x))¤§*\plot¤ + }% + \putat{5pt}{\dimexpr\graphboxht-10pt\relax}% afficher¤§*\putat¤ + {$f(x)=x^3-x^2-3x+2$}% la fonction tracée + \showaxis% et les axes +}\par\medskip¤\idx*\medskip¤ +%%%%%%%%%%%%% 2e exemple +\xunit=0.7cm \yunit=\xunit +\def\foncx#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% + \decmul{4}{\decmul{#1}{#1}}pt-#1pt+4pt}¤§*\dimtodec §*\decmul\idx*\dimexpr¤ +\def\foncy#1{\dimtodec\dimexpr\decmul{#1}{#1}pt-#1pt*2-6pt}¤§*\dimtodec §*\decmul\idx*\dimexpr¤ +Exemple 2 :\qquad +\graphzone{-12}[2]{12}[2]{-8}[2]{8}[2]{%¤§*\graphzone¤ + \def\plotstuff{\cross[1.25pt]}¤§*\cross¤ + \FOR\tt = -5 to 5 \do 0.1 {%¤§*\FOR¤ + \plot(\foncx\tt,\foncy\tt)%¤§*\plot¤ + }% + \putat{5pt}{\dimexpr\graphboxht-20pt\relax}% afficher la fonction paramétrique tracée¤§*\putat¤ + {$\left\{\vcenter{\hbox{$x(t)=t^3-4t^2-t-4$}\hbox{$y(t)=t^2-2t-6$}}\right.$}%¤\idx*\vcenter¤ + \showaxis +}| + +\subsection{Construire l'ensemble de Mandelbrot}\idx*{Mandelbrot} +L'\idx{ensemble de Mandelbrot}\footnote{Du nom de son inventeur, Benoît \textsc{Mandelbrot} (1924--2010), mathématicien franco-arménien.}, noté $\mathcal M$, est une figure fractale définie comme l'ensemble des points $c$ du plan complexe pour lesquels la suite définie par récurrence par + +\[\left\{ +\begin{tabular}{@{}r@{}l} +$z_0$&${}=0$\\[2ex] +$z_{n+1}$&${}=z_n^2+c$ +\end{tabular} +\right.\] + +\noindent est bornée en module. En posant $c=a+\mathrm ib$, on passe aux coordonnées cartésiennes et l'on obtient + +\[\left\{ +\begin{tabular}{@{}r@{}l} +$x_0$&${}=0$\\[2ex] +$x_{n+1}$&${}=x_n^2-y_n^2+a$ +\end{tabular}\right.\kern5em +\left\{\begin{tabular}{@{}r@{}l} +$y_0$&${}=0$\\[2ex] +$y_{n+1}$&${}=2x_n y_n+b$ +\end{tabular} +\right.\] + +Cet ensemble a été beaucoup étudié et on a démontré les choses suivantes qui vont nous servir pour le construire : + +\begin{itemize} + \item $\mathcal M$ est symétrique par rapport à l'axe des abscisses et donc, on peut se limiter au calcul des points dont la partie imaginaire est positive et tracer leurs symétriques par rapport à $(Ox)$; + \item si à un certain indice $i$, on a $|z_i|>2$, alors la suite diverge et donc, le point d'affixe $c$ n'appartient pas à $\mathcal M$. Il est dès lors inutile de poursuivre les calculs aux indices supérieurs à $i$. +\end{itemize} + +Pour dessiner l'ensemble, nous nous limiterons à la portion de plan complexe comprise entre $-2$ et $1$ pour la partie réelle et entre $-1$ et $1$ pour la partie imaginaire. L'essentiel est d'écrire une macro §\mandeltest qui admet deux arguments décimaux qui sont les coordonnées du point à tester. Cette macro mettra en place une boucle qui calculera les termes de la suite en fonction des deux arguments qui représentent $Re(c)$ et $Im(c)$. Bien évidemment, si la suite ne diverge pas, il n'est pas possible de calculer un nombre illimité de termes de la série. Nous nous limiterons à un entier, contenu dans la macro \verb|\maxiter|. + +L'algorithme de §\mandeltest est très simple : les itérations doivent se poursuivre tant que le nombre d'itérations est inférieur à \verb|\maxiter| et que le module du terme $z_i$ actuellement calculé est inférieur à 2. Une fois le test terminé et l'issue connue, le test §\mandeltest définira la macro \verb|\mandelresult| : elle contiendra 1 si le test est positif et 0 sinon. + +Pour chaque terme $z_i$, les macros \verb|\zx| et \verb|\zy| contiennent $Re(z_i)$ et $Im(z_i)$. Par souci d'optimisation et pour ne pas les calculer deux fois, nous stockerons les carrés de \verb|\zx| et \verb|\zy| dans \verb|\zxx| et \verb|\zyy|. + +\begingroup +\numalgofalse +\def\algoleftskip{.1\hsize} +\def\algorightskip{.1\hsize}\label{algo3} +\algorithm[\#]{Tester si un point appartient à M}/ +macro ~mandeltest~ #1#2 % #1 et #2 = coordonnées du point à tester + mandelresult:=1 % on considère a priori que le point apprtient à M + zx:=0 \qquad zy:=0 % partie réelle et imaginaire de $z_0$ + zxx:=0\qquad zyy:=0 % les carrés de zx et zy + ~pour~ i=1 to maxiter + zy:=2*zx*zy + #2 % $y_{n+1}=2x_n y_n + Im(c)$ + zx:=zxx-zyy + #1 % $x_{n+1}=x_n^2-y_n^2+Re(c)$ + zxx:=zx*zx\qquad zyy:=zy*zy % calcul des carrés + ~si~ $\mathrm{zxx}+\mathrm{zyy}>4$ % si $|z_n|^2>4$ (càd si le module est > à 2) + mandelresult:=0 % le point n'est pas dans M + i:=maxiter % sortir de la boucle prématurément + ~finsi~ + ~finpour~ +~finmacro~/ +\endgroup + +Dans le domaine $[-2\;\string;\;1]\times[-1\;\string;\;1]$ nous allons choisir la \emph{résolution} du dessin, c'est-à-dire combien de pixels doivent être contenus dans une unité, aussi bien horizontalement que verticalement. Ce nombre sera le premier argument de la macro §\mandel chargée de tracer $\mathcal M$. Le second argument sera le nombre maximal d'itérations. + +Pour parcourir tous les points du rectangle, nous allons utiliser deux boucles §\FOR imbriquées, l'une pilotant les abscisses \verb|\xxx| et l'autre les ordonnées \verb|\yyy|. L'incrément sera égal à $\frac{1}{\string#1}$. Dans le c\oe ur de la boucle, c'est-à-dire pour chaque point de coordonnées (\verb|\xxx|\;\string;\;\verb|\yyy|), le test §\mandeltest sera effectué et si la macro \verb|\mandelresult| vaut 1, le pixel et son symétrique par rapport à l'axe $(Ox)$ seront affichés. + +Un pixel sera une réglure carrée ayant pour côté l'incrément multiplié par la longueur \verb|\xunit|. + +\errcode|\def\mandeltest#1#2{%¤§\mandeltest¤ + \def\zx{0}\def\zy{0}% zn=0 + i*0 + \def\zxx{0}\def\zyy{0}% carrés de \zx et \zy + \def\mandelresult{1}% le point appartient à M a priori + \for\ii=1to\maxiter\do1{%¤§*\for¤ + \advance\count255 by1¤\idx*\advance¤ + \edef\zy{\dimtodec\dimexpr\decmul{\decmul2\zx}\zy pt+#2pt\relax}%¤§*\dimtodec\idx*\dimexpr §*\decmul¤ + \edef\zx{\dimtodec\dimexpr\zxx pt-\zyy pt+#1pt\relax}%%¤§*\dimtodec\idx*\dimexpr¤ + \edef\zxx{\decmul\zx\zx}%¤§*\decmul¤ + \edef\zyy{\decmul\zy\zy}%¤§*\decmul¤ + \ifdim\dimexpr\zxx pt+\zyy pt\relax>4pt¤\tidx*{ifdim}¤ + \def\mandelresult{0}% + \exitfor\ii¤§*\exitfor¤ + \fi + }% +} +\def\mandel#1#2{% #1=points par unité #2=nombre maximal d'itérations¤§\mandel¤ + \graphzone{-2}[1]{1}[2]{-1}[1]{1}[2]{%¤§*\graphzone¤ + \def\maxiter{#2}% + \edef\plotstuff{\the\dimexpr\xunit/#1\relax}% taille d'un pixel¤\idx*\the\idx*\dimexpr¤ + \edef\plotstuff{\vrule height\plotstuff width\plotstuff}%¤\idx*\vrule¤ + \edef\increment{\decdiv{1}{#1}}% incrément¤§*\decdiv¤ + \count255=0 % compteur des itérations¤\idx*\count¤ + \FOR\xxx = -2 to 1 \do \increment{% pour chaque¤§*\FOR¤ + \FOR\yyy = 0 to 1 \do \increment{% pixel du domaine¤§*\FOR¤ + \mandeltest\xxx\yyy% tester s'il est dans M¤§*\mandeltest¤ + \ifnum\mandelresult=1 % si oui, + \plot(\xxx,\yyy)\plot(\xxx,-\yyy)% afficher les 2 points¤§*\plot¤ + \fi + }% + }% + \edef\plotstuff{$\scriptstyle\number\count255 $}% affiche la valeur du compteur¤\idx*\scriptstyle\idx*\number\idx*\count¤ + \plot(-1.99,0.92)% aux coordonnées (-1.99 ; 0.92)¤§*\plot¤ + }% +} +\xunit=3cm \yunit=3cm \mandel{400}{500}¤§\mandel¤|{\includegraphics{mandelbrot.pdf}} + +Afin d'obtenir un tracé précis et assez fidèle à ce qu'est $\mathcal M$, nous avons demandé un grand nombre de pixels par unité (400) et un nombre élevé d'itérations (500). Nous avons donc $3\times400$ pixels horizontalement et 400 pixels verticalement à tester c'est-à-dire \numprint{480000} tests à faire. Chacun exécute une boucle supplémentaire pouvant aller jusqu'à 500 itérations. Même si tous les tests ne procèdent pas à ces 500 itérations, il s'agit là sans nul doute du plus grand nombre d'itérations qu'il m'ait été donné de programmer en \TeX{} : il y en a très exactement \numprint{63430877} ! Compte tenu de la grandeur des arguments de §\mandel conduisant à cet incroyable nombre d'itérations, la compilation de ce seul code demande de longues minutes. En outre, le nombre de pixels (c'est-à-dire de réglures) à placer sur la page est extrêmement important ce qui requiert une augmentation de la mémoire de \TeX{}. Pour ce faire, les paramètres suivants ont été modifiés : + +\centrecode/main_memory = 10000000 +extra_mem_bot = 20000000/ + +Ce dernier code, en raison du temps de compilation qu'il requiert, déroge à la règle qui veut que chaque code génère l'affichage qui se trouve dans la partie basse du cadre. Ce code a donc été compilé à part avec une version modifiée de \texttt{tex} et le fichier pdf obtenu a été incorporé sous le code. + +\chapter{Aller plus loin dans la mise en forme} +\section{Formater un nombre}\idx*[|(]{formatter un nombre} +\Qu elle différence y a t-il entre «\hbox{9876543,21012}» et «\numprint{9876543,21012}» ? Mathématiquement aucune, mais typographiquement, la différence est énorme : elle se situe au niveau de ces petites espaces insécables qui séparent les groupes de chiffres en les groupant trois par trois et qui font que, du premier coup d'\oe il, on sait que 9 est le chiffre des unités de millions. Il en va ainsi de la typographie française qui, par souci de lisibilité, fixe comme règle d'insérer une espace fine insécable entre chaque groupe de trois chiffres. + +Le but que nous nous fixons e de pouvoir formater n'importe quel nombre, on ne va s'imposer aucune limite sur le nombre de chiffres, pas plus dans la partie entière que dans la partie décimale. Par conséquent, nous ne pourrons à aucun moment utiliser la primitive \idx\number puisque l'entier qui la suit doit être inférieur à $2^{31}-1$. + +\subsection{Partie décimale} +La partie facile est de traiter la partie décimale puisqu'elle doit être parcourue de gauche à droite --~le sens de lecture de \TeX~-- afin d'insérer une espace fine (créée par la macro \verb|\numsep|) chaque fois que l'on a parcouru 3 chiffres. Pour cela, nous allons reprendre la macro §\reverse vue à la page~\pageref{reverse} en la modifiant légèrement. Cette macro offre la particularité intéressante de fournir du matériel purement développable ne produisant aucune sortie vers l'affichage jusqu'à ce que l'argument soit inversé en totalité, moment il est dirigé vers l'affichage. + +Sur ce modèle, nous allons bâtir une macro récursive \verb|\formatdecpart@i| qui admet trois arguments délimités. Le premier est l'entier qui représente le nombre de chiffres parcourus et qu'il faudra tester par rapport à 3. Les deux autres sont les « réservoirs » déjà vus avec la macro §\reverse qui stockent les tokens et qui, en se passant de séquence de contrôle définies par \verb|\def|, rendent la macro purement développable. + +\showcode|\catcode`\@11 +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres¤\idx*\protected¤ + +\def\formatdecpart#1{% #1=série de chiffres¤§*\formatdecpart¤ + \ifempty{#1}% si la partie décimale est vide¤§*\ifempty¤ + {}% ne rien afficher + {{,}\formatdecpart@i 1.#1..}% sinon, afficher la virgule et mettre en forme +} + +% #1=compteur de caractères #2= chiffre courant +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatdecpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre¤§*\ifempty¤ + {#4#2}% le mettre en dernière position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatdecpart@i \number\numexpr#1+1.#3.#4#2.}%¤\idx*\numexpr¤ + }% +} +a) \formatdecpart{1234567}\qquad +b) \formatdecpart{987125}\qquad +c) \formatdecpart{56}\qquad +d) \edef\foo{\formatdecpart{2014}}\meaning\foo¤§*\formatdecpart¤| + +La virgule, qui joue le rôle de séparateur décimal est insérée entre accolades pour que cette virgule ne soit pas prise comme un séparateur de liste si la macro est exécutée en mode mathématique\idx*{mode!mathématique} car dans ce cas, elle serait suivie d'une espace. + +\subsection{Partie entière} +La partie entière est plus difficile. La première étape sera de légèrement modifier la macro §\formatdecpart en la macro §\formatintpart pour que cette dernière affiche le nombre qu'elle a traité, mais à l'envers. Pour cela, il suffit de changer l'ordre des arguments \verb|#2| et \verb|#4| dans le texte de remplacement de \verb|\formatdecpart@i| : + +\showcode|\catcode`\@11 +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres¤\idx*\protected¤ +\def\formatintpart#1{% #1=série de chiffres¤§*\formatintpart¤ + \formatintpart@i 1.#1..% appelle la macro récursive +} + +% #1=compteur de caractères #2= chiffre courant +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatintpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre¤§*\ifempty¤ + {#2#4}% le mettre en première position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo + \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatintpart@i 1.#3.\numsep#2#4.}% mettre "\numsep#2" en premier et recommencer + % sinon, mettre #2 en première position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatintpart@i \number\numexpr#1+1.#3.#2#4.}%¤\idx*\numexpr¤ + }% +} +\catcode`\@12 +a) \formatintpart{1234567}\qquad b) \formatintpart{987125}\qquad +c) \formatintpart{56}\qquad +d) \edef\foo{\formatintpart{2014}}\meaning\foo¤§*\formatintpart¤| + +Comme deux inversions s'annulent, le résultat serait celui que l'on attend si au lieu du nombre passé comme argument à §\formatintpart, on passait le nombre inversé. On va donc utiliser la macro §\reverse et faire l'appel à la macro récursive de cette façon : + +\centrecode-\expandafter\formatintpart@i\expandafter1\expandafter.\reverse{#1}..- + +\noindent Le seul ennui est que §\reverse ne donne pas son résultat en un seul développement ! Mais, du fait de sa structure, l'affichage est fait à la toute fin, après des instructions purement développables ne donnant aucun affichage. La primitive \idx\romannumeral va nous servir pour tout développer en un coup jusqu'à l'affichage final. Pour stopper \idx\romannumeral \emph{avant} qu'elle ne voit les chiffres et ne s'en empare pour former un nombre romain, nous mettrons \idx\z@ dans l'argument de §\reverse telle sorte qu'il stoppe l'expansion et l'action de \idx\romannumeral. Comme §\reverse inverse l'ordre, il faut écrire \idx\z@ en dernier pour qu'il soit en premier à la fin du traitement : + +\showcode|\catcode`\@11 +\def\formatintpart#1{% #1=série de chiffres¤§*\formatintpart¤ + \expandafter\formatintpart@i\expandafter1\expandafter.\romannumeral\reverse{#1\z@}..%¤\idx*\romannumeral\idx*\z@¤ +} + +\catcode`\@12 +a) \formatintpart{1234567}\qquad b) \formatintpart{987125}\qquad +c) \formatintpart{56}\qquad +d) \edef\foo{\formatintpart{2014}}\meaning\foo¤§*\formatintpart¤| + +Mettre le tout en cohérence pour former une macro §\formatnum est maintenant aisé : + +\showcode|\catcode`\@11 +\def\ifnodecpart#1{\if@nodecpart#1.\@nil}% teste si #1 est un entier¤§*\ifnodecpart¤ +\def\if@nodecpart#1.#2\@nil{\ifempty{#2}}¤§*\ifempty¤ + +\def\formatnum#1{%¤§*\formatnum¤ + \ifnodecpart{#1}% s'il n'y a pas de partie décimale¤§*\ifnodecpart¤ + {\formatintpart{#1}}% formatter la partie entière + {\formatnum@i#1\@nil}% sinon, formatter les deux parties +} + +\def\formatnum@i#1.#2\@nil{% + \formatintpart{#1}% formatte la partie entière¤§*\formatintpart¤ + \formatdecpart{#2}% et la partie décimale¤§*\formatdecpart¤ +} + +\catcode`\@12 +a) \formatnum{3.1415926}\qquad +b) \formatnum{1987654.12301}\qquad +c) \edef\foo{\formatnum{0987654.12300}}$\foo$¤§*\formatnum¤| + +Un défaut de notre macro saute aux yeux avec le dernier cas; elle ne supprime pas les zéros inutiles. Et comme il est impossible d'utiliser \idx\number qui l'aurait fait, il va donc falloir écrire deux macros, purement développables, qui suppriment les zéros inutiles d'un nombre entier lorsqu'ils sont au début pour l'une et à la fin pour l'autre. + +\subsection{Supprimer les 0 inutiles de gauche} +Voici la macro §\removefirstzeros, purement développable, qui supprime les 0 au début de son argument : + +\showcode|\catcode`\@11 +\def\removefirstzeros#1{%¤§*\removefirstzeros¤ + \removefirstzeros@i#1\quark% ajoute "\quark" en dernier¤§*\quark¤ +} +\def\removefirstzeros@i#1{% #1=chiffre courant + \ifx\quark#1% fin atteinte donc nombre = 0 + \expandafter0% laisser un zéro + \else + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer + \else% sinon remettre le chiffre #1 et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removefirstzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removefirstzeros@ii#1\quark{#1}¤§*\quark¤ +\catcode`\@12 +a) \removefirstzeros{000325478}\qquad +b) \removefirstzeros{00000}\qquad +c) \edef\foo{\removefirstzeros{001000}}\meaning\foo\qquad +d) \long\def\>#1<{\detokenize{#1}} + \expandafter\>\romannumeral\removefirstzeros{0123}<¤§*\removefirstzeros¤| + +La macro fonctionne bien sauf qu'au dernier cas, la forcer à donner le résultat en un développement avec \idx\romannumeral est un échec puisque justement, les caractères que laisse la macro sont des chiffres qui sont derechef convertis en un nombre romain. L'idéal serait de trouver une astuce pour rendre cette macro développable avec \idx\romannumeral sans rien changer à la macro. La réponse, non encore vue jusqu'à présent tient dans le dernier point de la règle de la page~\pageref{definition.entier}. + +Rappelons-nous que cette règle stipule que si \TeX{} s'attend à lire un nombre, les caractères + +\centrecode|-`\@| + +\noindent sont lus comme la représentation interne de l'entier \number-`\@{} car le code de caractère de \verb|@| est 64\footnote{On aurait tout aussi bien pu écrire «\texttt{-`\string\a}» qui représente \number-`\a\relax{} ou encore tout nombre négatif ou nul de la forme «\texttt{-`\char`\\\codeelement{car}}».}. Mais le point intéressant de cette règle est que \TeX{} \emph{continue} le développement maximal après ces caractères jusqu'à trouver un espace (qu'il absorbe) ou tout autre token (qu'il laisse tel quel). On comprend tout le bénéfice que l'on peut tirer de cette règle ici. En effet, §\removefirstzeros ne dirige aucun token vers l'affichage tant qu'il n'a pas trouvé autre chose que 0. Par conséquent, si + +\centrecode/\romannumeral-`\@/ + +\noindent est placé devant §\removefirstzeros, le développement maximal sera en route jusqu'à ce que quelque chose soit affiché, c'est-à-dire après avoir mangé les 0 inutiles. De plus, \idx\romannumeral évaluant le nombre négatif \verb|-`\@|, aucun chiffre romain ne sera dirigé vers l'affichage. + +\begin{regle} +Lorsqu'il est 1-développé, le code suivant + +\centrecode|\romannumeral-`\<caractère>| + +\noindent effectue l'action « tout développer au maximum jusqu'à trouver un token (absorbé si c'est un espace et laissé tel quel sinon) qui stoppera le développement ». +\end{regle} + +\showcode|\long\def\>#1<{\detokenize{#1}} +\expandafter\>\romannumeral-`\@\removefirstzeros{000123}<¤\idx*\romannumeral§*\removefirstzeros¤| + +\subsection{Supprimer les 0 inutiles de droite} +Maintenant, il nous faut écrire la partie plus difficile, celle d'enlever les zéros inutiles finaux. Pour cela, inutile de tout réinventer, nous avons déjà tout ce qu'il nous faut : il suffit d'inverser l'argument, d'enlever tous les zéros de gauche puis d'inverser encore le résultat obtenu pour arriver à nos fins. Rien n'empêche d'utiliser plusieurs \idx\romannumeral imbriqués qui se passent la main afin de provoquer le développement maximal dans le bon ordre. + +On souhaiterait écrire + +\centrecode-\reverse{\removefirstzeros{\reverse{<nombre>}}}-§*\reverse + +\noindent mais ça serait oublier que \TeX{} n'évalue pas ses arguments avant que la macro ne les lise. Il nous incombe donc de forcer le développement dans le bon ordre, c'est-à-dire développer §\reverse\verb|{<nombre>}| en premier, puis §\removefirstzeros pour que le §\reverse chapeau agisse sur le nombre purgé de ses 0 de gauche. De plus, §\removefirstzeros doit être ré-écrit, car cette macro laissait un 0 si le nombre était nul, et ce comportement n'est pas souhaitable ici. La macro \verb|\removelastzeros@i| rétablit le comportement souhaité. + +Toute la \TeX nicité réside dans la macro chapeau §\removelastzeros où, par le truchement de \idx\romannumeral, les arguments sont développés dans le bon ordre. + +\showcode|\catcode`\@11 +\def\removelastzeros#1{%¤§*\removelastzeros¤ + \exparg\reverse% inverser après¤§*\reverse¤ + {\romannumeral-`\.% tout développer¤\idx*\romannumeral¤ + \expandafter\removelastzeros@i% enlever les 0 de gauche après + \romannumeral\reverse{#1\z@}\quark% avoir inversé #1¤§*\reverse\idx*\z@\idx*\romannumeral¤ + }% +} +\def\removelastzeros@i#1{% enlève tous les 0 de gauche + \unless\ifx\quark#1% si la fin n'est pas atteinte¤\tidx*{unless}§*\quark¤ + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removelastzeros@i% recommencer + \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removelastzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removelastzeros@ii#1\quark{#1}¤§*\quark¤ +\catcode`\@12 +a) \removelastzeros{0003254780}\qquad +b) \removelastzeros{00000}\qquad +c) \edef\foo{\removelastzeros{001000}}\foo\qquad +\long\def\>#1<{\detokenize{#1}} +d) \expandafter\>\romannumeral-`\.\removelastzeros{012300}<¤§*\removelastzeros\idx*\romannumeral¤| + +\subsection{Gérer le signe du nombre} +Nous voyons bientôt le bout du tunnel, il nous reste à parler du signe du nombre. Pour rester cohérent avec \TeX{}, nous allons admettre qu'un nombre peut commencer par autant de signes \verb|+| et \verb|-|, mais que le signe mathématique du nombre affiché sera négatif si les signes «\verb|-|» sont en nombre impair. Pour cela, la macro §\formatnum va devoir transmettre son argument à une macro \verb|\formmatnum@i|, à arguments délimités de type « réservoirs communicants». Ici, le délimiteur sera «\verb|!|» puisque le «\verb|.|» est susceptible d'être contenu dans le nombre. La macro chapeau effectuera cet appel initial à la macro récursive : + +\centrecode-\def\formatnum#1{\formatnum@i 1!#1!}- + +Le premier argument délimité, qui tient lieu de réservoir \no1, vaut 1 lors de l'appel initial. Ce réservoir accumulera en première position tous les signes rencontrés au début du réservoir \no2 qui situé entre les deux points d'exclamation, dont la valeur initiale est le \verb|<nombre>| à formater. La macro \verb|\formatnum@i| va donc avoir un texte de paramètre défini ainsi + +\centrecode-\def\formatnum@i#1!#2#3!{...}- + +\noindent de façon à ce que \verb|#2| soit le premier caractère du nombre à examiner et \verb|#3| soit les caractères non encore traités. Il suffit de tester si ce caractère \verb|#2| est \verb|+| ou \verb|-| et dans ce cas, le mettre en première position dans le réservoir \no1 et recommencer avec les arguments qui seront devenus «\verb|#2#1|» et \verb|#3|». Si ce n'est pas le cas, cela veut dire que l'on a épuisé les signes situés au début du nombre. Il est alors temps de tester si le nombre dans le réservoir \no1, constitué de tous les signes rencontrés suivis de 1, est lu par \TeX{} comme égal à $-1$ et dans ce cas, afficher un «\verb|-|» puis passer la main à la mise en forme du nombre proprement dite. + +On remarquera que si le nombre n'est constitué que de signes \verb|+| ou \verb|-|, $+1$ ou $-1$ sera lu dans le premier réservoir selon la parité du nombre de signes \verb|-|. Enfin, dernier raffinement, une macro se chargera de remplacer dans l'argument de §\formatnum le séparateur décimaux «virgule» en «point» avant de traiter le nombre. Ainsi, la virgule ou le point peuvent indifféremment être utilisés comme séparateur décimal. + +Voici le code complet : + +\showcode|\catcode`\@11 +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres¤\idx*\protected¤ + +\def\formatdecpart#1{% #1=série de chiffres¤§*\formatdecpart¤ + \ifempty{#1}% si la partie décimale est vide¤§*\ifempty¤ + {}% ne rien afficher + {{,}\formatdecpart@i 1.#1..}% sinon, afficher la virgule et mettre en forme +} + +% #1=compteur de caractères #2= chiffre courant +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatdecpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre¤§*\ifempty¤ + {#4#2}% le mettre en dernière position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatdecpart@i \number\numexpr#1+1.#3.#4#2.}%¤\idx*\numexpr¤ + }% +} + +\def\formatintpart#1{% #1=série de chiffres¤§*\formatintpart¤ + \expandafter\formatintpart@i\expandafter1\expandafter.% + \romannumeral\reverse{#1\z@}..% appelle la macro récursive¤\idx*\romannumeral\idx*\z@¤ +} + +% #1=compteur de caractères #2= chiffre à déplacer +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatintpart@i#1.#2#3.#4.{% + \ifempty{#3}% si #2 est le dernier chiffre à traiter¤§*\ifempty¤ + {#2#4}% le mettre en première position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatintpart@i 1.#3.\numsep#2#4.}% mettre \numsep#2 en premier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatintpart@i\number\numexpr#1+1.#3.#2#4.}% + }% +} +\def\removefirstzeros#1{%¤§*\removefirstzeros¤ + \removefirstzeros@i#1\quark% ajoute "\quark" en dernier¤§*\quark¤ +} +\def\removefirstzeros@i#1{% #1=chiffre courant + \ifx\quark#1% fin atteinte donc nombre = 0 + \expandafter0% laisser un zéro + \else + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer + \else% sinon remettre le chiffre #1 et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removefirstzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removefirstzeros@ii#1\quark{#1}¤§*\quark¤ + +\def\removelastzeros#1{%¤§*\removelastzeros¤ + \exparg\reverse% inverser après¤§*\reverse¤ + {\romannumeral-`\.% tout développer¤\idx*\romannumeral¤ + \expandafter\removelastzeros@i% enlever les 0 de gauche après + \romannumeral\reverse{#1\z@}\quark% avoir inversé #1¤§*\reverse\idx*\z@\idx*\romannumeral¤ + }% +} +\def\removelastzeros@i#1{% enlève tous les 0 de gauche + \unless\ifx\quark#1% si la fin n'est pas atteinte¤\tidx*{unless}§*\quark¤ + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removelastzeros@i% recommencer + \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removelastzeros@ii + \expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removelastzeros@ii#1\quark{#1}¤§*\quark¤ + +% renvoie vrai s'il n'y a pas de partie décimale +\def\if@nodecpart#1.#2\@nil{\ifempty{#2}}¤§*\ifempty¤ + +\def\formatnum#1{\formatnum@i1!#1!}¤§*\formatnum¤ + +% #1 = <série de signes> suivie de "1" +% #2 = caractère courant +% #3 = caractères non traités +\def\formatnum@i#1!#2#3!{% + \ifx+#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\ifempty{#3}% si #2=+ et #3 est vide¤§*\ifempty¤ + {\number#1 }% afficher "+1" ou "-1" (il n'y avait que des signes)¤\idx*\number¤ + {\formatnum@i#2#1!#3!}% sinon, mettre le signe #2 devant #1 + } + {\ifx-#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\ifempty{#3}% si #2=- et #3 est vide¤§*\ifempty¤ + {\number#1 }% afficher "+1" ou "-1" (il n'y avait que des signes)¤\idx*\number¤ + {\formatnum@i#2#1!#3!}% sinon, mettre le signe #2 devant #1 + }% #2 est le dernier caractère qui n'est pas un signe + ou - + {\ifnum#1<0 -\fi% si "<signes>1" est <0 afficher un "-"¤\defline\aaa¤ + \formatnum@ii{#2#3}% formatter le nombre formé avec "#2#3"" + }% + }% +} + +\def\formatnum@ii#1{% + \if@comma{#1}% si #1 comporte une virgule + {\formatnum@iii#1\@nil}% la remplacer par un "." et recommencer + {\if@nodecpart#1.\@nil% puis si les chiffres restants sont un entier + {\formatintpart{#1}}% formatter l'entier + {\formatnum@iv#1\@nil}% sinon, formatter le nombre + }% +} + +\def\formatnum@iii#1,#2\@nil{\formatnum@ii{#1.#2}} + +\def\formatnum@iv#1.#2\@nil{% formatte le nombre décimal #1.#2 + \exparg\formatintpart{\romannumeral-`\.\removefirstzeros{#1}}%¤\idx*\romannumeral§*\removefirstzeros§*\exparg¤ + \exparg\formatdecpart{\romannumeral-`\.\removelastzeros{#2}}%¤§*\formatdecpart§*\removelastzeros§*\exparg¤ +} + +\def\if@comma#1{\if@comma@i#1,\@nil}% teste la présence d'un virgule dans #1 +\def\if@comma@i#1,#2\@nil{\ifempty{#2}\secondoftwo\firstoftwo}¤§*\ifempty¤ +\catcode`\@12 + +a) \formatnum{3,141592653589793238462643383279502884197169399375105820974944592}\par +b) \formatnum{---+69874}\qquad +c) \formatnum{0032100,98000}\qquad +d) \formatnum{+++010.01100}\qquad +e) \formatnum{-+--+}\qquad +f) \formatnum{---00.0000}\qquad +g) \formatnum{+99,0000}\qquad +h) \formatnum{.123456}\par +i) \edef\foo{\formatnum{-+-+-010500,090900}}\meaning\foo¤§*\formatnum¤|\idx*[|)]{formatter un nombre} + +\section{Permettre à une boite encadrée de franchir des pages} +\subsection{Couper une boite verticale}\idx*[|(]{couper une boite verticale} +Cela avait été clairement annoncé à la page~\pageref{boite.insecable} : toute boite construite avec les primitives \idx\hbox, \idx\vbox ou \idx\vtop est \emph{insécable}. C'est effectivement le cas à un petit mensonge près\ldots{} Si une boite \emph{verticale} est stockée dans un registre de boite, il est possible de couper la boite ainsi stockée en deux boites verticales. La primitive qui permet cette opération est \idx\vsplit\footnote{En revanche, les boites horizontales \texttt{\string\hbox} sont \emph{vraiment} insécables, il n'existe hélas pas de primitive \texttt{\string\hsplit}.}. + +\begin{regle} +Si \verb|<x>| est un registre de boite auquel on a assigné une boite verticale (construite avec \idx\vbox ou \idx\vtop) et si \verb|<y>| est un numéro de registre de boite, alors + +\centrecode-\setbox <y>=\vsplit <x> to <dimension>-\idx*\vsplit + +\noindent va commander à \TeX{} de parcourir la boite contenue dans le registre \no\verb|<x>| et opérer la meilleure coupure possible pour générer une boite de hauteur \verb|<dimension>|. Le registre \no\verb|<y>| contiendra cette nouvelle boite tandis que le registre \verb|<x>| contiendra la boite initiale amputée d'autant. Si la \verb|<dimension>| est suffisamment grande pour que la coupure englobe la totalité de la boite initiale \verb|<x>|, le registre \verb|<x>| devient vide, c'est-à-dire positif au test \tidx{ifvoid}. + +Le mécanisme de coupure de boite verticale est similaire à celui des coupures de pages. En particulier, les objets « volatils » sont supprimés à la coupure (\idx{pénalité}s et espaces verticaux de type \idx[|etc]\kern\forbidindex\kern{} ou \idx\vskip). + +Enfin, un ressort appelé \idx\splittopskip est inséré au sommet de la boite restante \verb|<x>|. +\end{regle} + +Le code ci-dessous illustre cette règle, où la boite initiale contient 4 lignes, chacune enfermée dans une \idx\hbox : + +\showcode/\setbox0=\vbox{%¤\idx*[|etc]\setbox\forbidindex\setbox¤ + \hbox{Première ligne}¤\idx*\hbox¤ + \hbox{Deuxième ligne} + \hbox{Avant-dernière ligne} + \hbox{Dernière ligne} +} + +\frboxsep=0pt % aucun espace entre le contenu et l'encadrement¤§*\frboxsep¤ +Boite initiale de hauteur \the\ht0 {} : \frbox{\copy0 }¤§*\frbox¤ +\splittopskip0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ +\setbox1=\vsplit0 to 22pt % couper la boite à 22pt de hauteur¤\idx*\setbox\idx*\vsplit¤ + +Boite 1 de hauteur \the\ht1 {} : \frbox{\box1 } + +Boite 0 de hauteur \the\ht0 {} : \frbox{\box0 }¤\idx*\box\idx*\the\idx*\ht §*\frbox¤/ + +Il faut noter qu'un message d'avertissement\idx*{message d'avertissement} est émis lorsque la coupure est faite : «\texttt{Underfull \string\vbox{} (badness 10000) detected}». Ce message indique que la boite \no1 est insuffisamment remplie\idx*{boite insuffisamment remplie}, ce que l'on peut constater visuellement. L'explication tient au fait que seules deux lignes ont pu être logées verticalement dans 22 points. Mais ces deux lignes ne remplissent pas la totalité de ces 22 points qui sont imposés. Pour éviter de garder une boite insuffisamment pleine, on peut utiliser + +\centrecode-\setbox1=\vbox{\unvbox1 }-\idx*\unvbox + +\noindent afin que la boite \no1 soit « extraite » de la \idx\vbox de hauteur imposée et, ayant repris sa dimension naturelle, soit à nouveau enfermée dans une \idx\vbox. + +Pour empêcher \TeX{} d'émettre un message d'avertissement\idx*{message d'avertissement} lors de la coupe de la boite \no0, il faut agir sur l'entier \idx\vbadness qui représente le seuil de médiocrité au-delà duquel un message d'avertissement\idx*{message d'avertissement} n'est plus émis concernant une boite verticale (\idx\hbadness est le pendant pour les boites horizontales). Nous allons donc sauvegarder cet entier puis lui assigner l'entier \numprint{10000} qui représente la médiocrité maximale, et après avoir coupé la boite avec \idx\vsplit, restaurer \idx\vbadness à la valeur initiale. + +\showcode/\setbox0=\vbox{%¤\idx*\setbox¤ + \hbox{Première ligne} + \hbox{Deuxième ligne} + \hbox{Avant-dernière ligne} + \hbox{Dernière ligne} +} + +\edef\restorevbadness{\vbadness=\the\vbadness\relax}% restaurera le \vbadness¤\idx*\the\idx*\vbadness¤ +\vbadness=10000 % plus d'avertissement pour boite verticale¤\idx*{message d'avertissement}\idx*\vbadness¤ +\frboxsep=0pt % aucun espace entre le contenu et l'encadrement¤§*\frboxsep¤ +Boite initiale de hauteur \the\ht0 {} : \frbox{\copy0 }¤\idx*\the\idx*\copy §*\frbox¤ +\splittopskip0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ +\setbox1=\vsplit0 to 22pt % couper la boite à 22pt de hauteur¤\idx*\setbox\idx*\vsplit¤ +\setbox1=\vbox{\unvbox1 }% la boite 1 prend la hauteur naturelle¤\idx*\unvbox\idx*\vbox¤ +\restorevbadness\relax% restaure le \vbadness + +Boite 1 de hauteur \the\ht1 {} : \frbox{\box1 } + +Boite 0 de hauteur \the\ht0 {} : \frbox{\box0 }¤\idx*\box\idx*\the\idx*\ht §*\frbox¤/ + +On constate bien que la boite \no1 a repris la hauteur naturelle et devient donc la boite contenant le plus de lignes possible dans \verb|22pt| de hauteur. + +Il est en revanche assez inexplicable que la somme des deux hauteurs ne soit pas égale à la hauteur de la boite initiale. Il manque + +\[34.024\text{\ttfamily pt}-2\times15.024\text{\ttfamily pt}\approx 4\text{\ttfamily pt}\] + +Cette hauteur manquante est celle qui sépare la frontière inférieure de la boite \no1 de la frontière supérieure de la boite \no0. Ce \idx{ressort d'interligne} a disparu à la coupure, comme le font tous les ressorts aux coupures de page ou de ligne.\idx*[|)]{couper une boite verticale} + +\subsection{Couper à la bonne hauteur} +Avant de bâtir un algorithme permettant de composer un texte dans un cadre qui franchit des pages, il reste tout de même une question essentielle : comment connaitre la hauteur qui reste à composer dans la page en cours ? Deux nouvelles primitives de \TeX{} vont nous permettre de répondre à cette question : + +\begin{itemize} + \item \idx\pagetotal est la dimension accumulée verticalement jusqu'à la fin du précédent paragraphe entièrement composé; + \item \idx\pagegoal est la dimension verticale du texte à composer dans la page en cours. +\end{itemize} + +La dimension \idx\pagetotal n'est pas actualisée en permanence, mais seulement après chaque paragraphe composé. Par ailleurs, au tout début de la construction d'une page, lorsqu'elle est vide, \idx\pagetotal vaut \numprint[pt]0 alors que \idx\pagegoal vaut \numprint[pt]{16383,99999}\footnote{Lire le \TeX book page~133 et plus généralement le chapitre 15 en entier.}, soit \idx\maxdimen. Par conséquent, notre futur algorithme devra tenir compte de cette spécificité au début des pages. Afin que \idx\pagegoal prenne une valeur reflétant la hauteur de la page, il faudra donc, avant de mesurer quoi que ce soit, faire en sorte que la nouvelle page ne soit pas vide . Insérer le matériel vertical «\idx\hbox\verb-{}-\idx\nointerlineskip» teindra lieu de remplissage de haut de page, tout en ne prenant aucune place verticalement. Ainsi, après cette opération, \idx\pagegoal sera égal à la hauteur de la zone de texte en haut d'une page. Nous sommes maintenant assurés que, quelles que soient les circonstances, la différence entre \idx\pagegoal et \idx\pagetotal sera la hauteur de l'espace vertical disponible restant dans la page en cours. + +Ces deux primitives étaient ce qui nous manquait pour construire l'algorithme permettent de couper $n$ fois une boite verticale pour la loger sur $n+1$ pages. + +Nous allons d'abord décider que le contenu à afficher dans un cadre sera compris entre les macros §\breakpar et\verb|\endbreakpar|. La première macro sera chargée d'assigner à un registre de boite verticale tout ce qui se trouvera jusqu'à \verb|\endbreakpar|. Pour cela, cette macro utilisera \idx\bgroup au lieu de «\verb|{|» pour délimiter le début de la boite alors que \verb|\endbreakpar| utilisera \idx\egroup pour marquer la fin de la boite verticale. La macro §\breakpar aura également pris soin, en début de boite, de diminuer la dimension horizontale \idx\hsize de cette boite. En effet, la largeur du texte à encadrer doit tenir compte de l'épaisseur de la réglure d'encadrement §\frboxrule et de l'espace entre le texte et cette réglure §\frboxsep, chacune de ces dimensions devant être comptée deux fois, une fois pour la réglure de gauche et une fois pour celle de droite. + +En ce qui concerne le cadre proprement dit, il faut bien garder à l'esprit que selon les cas, les encadrements pourront être de 4 types différents : +\begin{enumerate} + \item cadre plein (\kern1pt {\frboxsep=0pt \framebox{\phantom M}}\kern1pt ) dans le cas où l'espace restant dans la page en cours permet de loger le cadre dans sa totalité; +\end{enumerate} + +\noindent Si le cadre ne peut loger sur la page en cours, 3 autres types sont possibles : +\begin{enumerate}[start=2] + \item cadre de début (\kern1pt {\frboxsep=0pt \framebox[ULR]{\phantom M}}\kern1pt ); + \item cadre intermédiaire (\kern1pt {\frboxsep=0pt \framebox[LR]{\phantom M}}\kern1pt ) dans le cas où le texte à encadrer est tellement long qu'il faut une ou plusieurs pages intermédiaires entre le début du cadre et la fin; + \item cadre de fin (\kern1pt {\frboxsep=0pt \framebox[DLR]{\phantom M}}\kern1pt ). +\end{enumerate} + +% rappeler où a été créée la macro \framebox +Pour tracer ces cadres, la macro §\framebox vue à la page~\pageref{framebox} va nous être d'un grand secours, il suffira de spécifier le bon argument optionnel pour avoir le cadre du type souhaité. + +Dans les grandes lignes, l'algorithme va fonctionner de cette manière : + +\begin{algo} + \item capturer et composer dans une \idx\vbox de largeur appropriée tout ce qui se trouve entre §\breakpar et \verb|\endbreakpar|, stocker cette boite dans le registre \verb|\remainbox| et aller au point \no2; + \item macro §\splitbox + \begin{algo} + \item si \verb|\remainbox| est vide : fin du processus + \item sinon, aller au point \no3. + \end{algo} + \item macro \verb|\splitbox@i| + \begin{algo} + \item si \verb|\remainbox|, une fois encadré, loge sur l'espace restant dans la page, l'encadrer avec le cadre \no1 ou \no4, et fin du processus; + \item sinon couper \verb|\remainbox| à une hauteur correspondant à celle de l'espace restant sur la page; encadrer le registre de boite obtenu (\verb|\partialbox|) avec le cadre \no2 ou \no3, composer la page en cours et aller au point \no2 avec la boite restante \verb|\remainbox|. + \end{algo} +\end{algo} + +Pour mener à bien cet algorithme, nous avons à écrire 5 macros différentes : §\breakpar, \verb|\endbreakpar|, §\splitbox, \verb|\splitbox@i| ainsi qu'une macro qui affiche la boite à encadrer \verb|\printpartialbox|. Nous devrons correctement gérer l'argument optionnel de §\framebox (où les lettres «UDRL» spécifient les réglures à afficher) pour choisir selon les cas, le bon encadrement. Pour ce faire, nous stockerons dans la macro \verb|\rule@arg| les lettres devant être passées à cet argument optionnel. +\grandsaut + +Avant d'entrer dans le vif du sujet, il est nécessaire d'examiner ce qui se passe en haut d'une page : afin que dans la mesure du possible, les lignes de base des premières lignes de chaque page se situent toutes à la même position verticale, le ressort \idx\topskip est inséré en haut de chaque page. Sa valeur est diminuée de la hauteur de la première ligne et si le résultat est négatif, le ressort \verb|0pt| est inséré. Plain-\TeX{} prend \idx\topskip égal à \verb|10pt| ce qui assure que toutes les premières lignes dont la hauteur est inférieure ou égale à \verb|10pt| commenceront à la même position verticale dans les pages. + +Pour notre problème, si une boite doit être coupée, il ne faut pas qu'un ressort en haut de page soit inséré avant le cadre restant (\kern1pt {\frboxsep=0pt \framebox[LR]{\phantom M}} ou {\frboxsep=0pt \framebox[DLR]{\phantom M}}\kern1pt ) : le haut des réglures du cadre doit exactement coïncider avec le bord supérieur de la zone de texte. Par conséquent, \idx\topskip sera pris égal à \verb|0pt|. Avant de procéder à cette assignation, la valeur de \idx\topskip sera copiée dans \idx\splittopskip qui joue le même rôle que \idx\topskip dans les boites résiduelles après une coupure par \idx\vsplit. Ce faisant, à chaque début de page, on s'assure que la première ligne dans le cadre se situe à la même position verticale que les premières lignes des pages « normales ». +\grandsaut + +Venons-en au c\oe ur du problème : à quelle hauteur allons-nous demander la coupure de la boite \verb|\remainbox| ? Au premier abord, on pourrait être tenté de répondre \idx*\pagegoal\idx*\pagetotal{}\verb|\pagegoal-\pagetotal| mais ce serait ignorer l'encombrement vertical de \verb|\frboxsep+\frboxrule|, requis par les éventuelles réglures (supérieures ou inférieures) d'épaisseur §\frboxrule et les espaces §\frboxsep qui les suivent ou les précèdent. Il va de soi que selon les cas, ces réglures sont présentes soit toutes les deux (cas du cadre {\frboxsep=0pt \framebox{\phantom M}}\kern1pt ), soit seulement une des deux (cadre {\frboxsep=0pt \framebox[ULR]{\phantom M}} ou {\frboxsep=0pt \framebox[DLR]{\phantom M}}\kern1pt ), soit aucune (cadre \kern1pt {\frboxsep=0pt \framebox[LR]{\phantom M}}\kern 1pt ). Nous devons être rigoureux pour ne pas demander trop ou trop peu de hauteur de coupure. + +Pour nous faciliter la tâche, nous allons stocker dans la macro \verb|\coeff@rule| le nombre de réglures horizontales qui restent à afficher. Dans la macro chapeau, il sera initialisé à 2 puisque les deux réglures sont encore à afficher. Ce coefficient sera ensuite pris égal à 1 dès que la première coupure sera faite puisque la réglure supérieure ne sera plus à afficher. + +Du côté du calcul, il faut décider si une coupure de \verb|\remainbox| doit avoir lieu ou pas. La réponse est positive si sa dimension verticale totale (hauteur + profondeur) est supérieure à l'espace disponible sur la page en cours auquel on a soustrait l'espace requis pour les réglures horizontales restantes. Mathématiquement, une coupure doit être faite si $\verb|\ht\remainbox|+\verb|\dp\remainbox|$ est strictement supérieur à $\verb|\pagegoal|-\verb|\pagetotal|-(\verb|\frboxsep|+\verb|\frboxrule|)\times\verb|\coeff@rule|$. Nous appellerons $D_v$ cette dernière quantité et la stockerons dans un registre de dimension appelé \verb|\cut@ht|. + +Si la coupure doit être faite, à quelle hauteur doit être coupée \verb|\remainbox| ? Comme une coupure est faite, la réglure horizontale de fin de cadre ne se trouvera pas sur la page recevant la boite coupée. Par conséquent, la hauteur de coupure est $D_v$ augmentée de $\verb|\frboxsep|+\verb|\frboxrule|$. + +Voici comment va opérer la macro \verb|\splitbox@i| : + +\begin{itemize} + \item le registre de dimension \verb|\cut@ht| reçoit la hauteur $D_v$; + \item si une coupure doit être faite, la dimension $\verb|\frboxsep|+\verb|\frboxrule|$ est ajoutée à \verb|\cut@ht|; + \item \verb|\remainbox| est coupée à la hauteur \verb|\cut@ht| et la boite obtenue est stockée dans le registre \verb|\partialbox|; + \item on rend \verb|\coeff@rule| égal à 1 puisque la réglure supérieure ne peut plus être présente; + \item si \verb|\splitbox@i| détecte que la coupure n'est pas nécessaire, \verb|\remainbox| sera copiée dans \verb|\partialbox| et \verb|\cut@ht| reçoit la hauteur verticale totale de \verb|\partialbox|; +\end{itemize} + +Nous sommes bientôt au bout du tunnel, mais il y a un dernier raffinement auquel nous devons penser. Comment s'assurer que les réglures verticales des encadrements «{\frboxsep=0pt \framebox[ULR]{\phantom M}}» et «{\frboxsep=0pt \framebox[LR]{\phantom M}}» iront jusqu'en bas de la page ? En effet, si nous encadrons avec + +\centrecode-\expandafter\framebox\expandafter[\rule@arg]{\vbox{\unvbox\partialbox}}-\idx*\unvbox§*\framebox + +\noindent le «\verb|\vbox{\unvbox\partialbox}|» va redonner à \verb|\partialbox| sa hauteur naturelle (plus petite que \verb|\cut@ht|) et l'encadrement n'atteindra pas exactement le bas de la page. Pour nous prémunir de cette petite perte de hauteur, nous devons imposer un cadre de hauteur \verb|\cut@ht|, car ce registre contient la hauteur totale verticale du texte à encadrer. On écrira donc dans l'argument de §\framebox + +\centrecode-\vbox to\cut@ht{\unvbox\partialbox\vss}-\idx*\unvbox\idx*\vss + +Le ressort \idx\vss se charge de rattraper la différence entre \verb|\cut@ht| et la hauteur naturelle de \verb|\partialbox|. +\grandsaut + +Le code ci-dessous est un de ceux qui dérogent à ce qui est la règle dans ce livre. Le résultat du code n'est pas présenté celui qui découle de la compilation de ce code, car pour des raisons de place, les pages obtenues ont été réduites et présentées les unes à côté des autres. + +\errcode/\catcode`@11 +\newbox\remainbox¤\idx*\newbox¤ +\newbox\partialbox¤\idx*\newbox¤ +\newdimen\cut@ht¤\idx*\newdimen¤ + +\def\breakpar{%¤§*\breakpar¤ + \par\nointerlineskip% termine le paragraphe précédent¤\idx*\nointerlineskip¤ + \vskip\frboxsep\relax% et saute une petite espace verticale¤\idx*\vskip§*\frboxsep¤ + \begingroup + \splittopskip\topskip% \topskip en haut des boites coupées¤\idx*\splittopskip\idx*\topskip¤ + \topskip=0pt % neutraliser le \topskip¤\idx*\topskip¤ + % nbre de réglures horizontales contribuant à l'encadrement restant (2 au début) + \def\coeff@rule{2}% + \setbox\remainbox=\vbox\bgroup% compose la boite après avoir...¤\idx*\setbox\idx*\vbox\idx*\bgroup¤ + \advance\hsize by -2\dimexpr\frboxrule+\frboxsep\relax% ajusté sa largeur¤\idx*\advance§*\frboxrule¤ +} + +\def\endbreakpar{% + \egroup% fin de la composition de la boite¤\idx*\egroup¤ + \def\rule@arg{ULR}% prendre l\cut@htes réglures d'encadrement haute, gauche et droite + \splitbox% puis, aller à l'algorithme de coupure¤§*\splitbox¤ + \endgroup% une fois fini, sortir du groupe semi-simple +} + +\def\splitbox{%¤§*\splitbox¤ + \ifvoid\remainbox% si la boite est vide, c'est la fin du processus¤\tidx*{ifvoid}¤ + \par\nointerlineskip% termine le paragraphe précédent¤\idx*\nointerlineskip¤ + \vskip\frboxsep\relax% et saute un petit espace vertical¤\idx*\vskip§*\frboxsep¤ + \else% sinon + \expandafter\splitbox@i% aller à \splitbox@i + \fi +} + +\def\splitbox@i{% + \hbox{}% composer un noeud en mode vertical¤\idx*\hbox\idx*{mode!vertical}¤ + \nointerlineskip% pas de ressort d'interligne¤\idx*\nointerlineskip¤ + % calculer la dimension verticale disponible dans la page pour le texte de la boite + \cut@ht=\dimexpr\pagegoal-\pagetotal-(\frboxsep+\frboxrule)*\coeff@rule\relax¤\idx*\dimexpr\idx*\pagegoal\idx*\pagetotal§*\frboxsep§*\frboxrule¤ + % si dimension totale du texte > dimension disponible pour le texte + \ifdim\dimexpr\ht\remainbox+\dp\remainbox>\cut@ht% si une coupure doit être faite¤\tidx*{ifdim}¤ + \advance\cut@ht\dimexpr% augmenter Dv de l'espace verticale libérée + +\frboxsep+\frboxrule% par la réglure inférieure qui n'est pas sur cette page + \relax + \edef\old@vbadness{\the\vbadness}% sauvegarder \badness¤\idx*\the\idx*\vbadness¤ + \vbadness=10000 % désactive les avertissement lors de la coupure¤\idx*\vbadness\idx*{message d'avertissement}¤ + \def\coeff@rule{1}% ne prendre en compte que réglure D + espace D + \setbox\partialbox=\vsplit\remainbox to\cut@ht% coupe à la hauteur calculée¤\idx*\setbox\idx*\vsplit¤ + % \partialbox retrouve sa hauteur naturelle + \setbox\partialbox=\vbox{\unvbox\partialbox}%¤\idx*\setbox\idx*\vbox\idx*\unvbox¤ + \vbadness=\old@vbadness\relax% restaure \vbadness¤\idx*\vbadness¤ + \printpartialbox% imprime la boite partielle + \vfill\eject% et compose la page en cours¤\idx*\vfill\idx*\eject¤ + \else% si une coupure n'est pas nécessaire : + \setbox\remainbox\vbox{\unvbox\remainbox}% reprendre la hauteur naturelle¤\idx*\setbox\idx*\vbox\idx*\unvbox¤ + \setbox\partialbox=\box\remainbox% \partialbox devient \remainbox + % et cette dernière devient vide¤\idx*\box¤ + \cut@ht=\dimexpr\ht\partialbox+\dp\partialbox\relax% hauteur à encadrer¤\idx*\the\idx*\ht¤ + \edef\rule@arg{\rule@arg D}% ajouter "D" aux réglures à tracer + \printpartialbox% afficher la boite restante + \fi + \splitbox¤§*\splitbox¤ +} + +\def\printpartialbox{% imprime \partialbox + \expandafter\framebox\expandafter[\rule@arg]{% + \vbox to\cut@ht{\unvbox\partialbox\vss}}%¤§*\framebox\idx*\vbox\idx*\unvbox\idx*\vss¤ + \def\rule@arg{LR}% ne mettre que les réglures d et g +} + +\def\dummytext#1{% + \for\xx=1to#1\do% composer #1 fois la phrase suivante :¤§*\for¤ + {Ceci est un texte sans aucun int\'er\^et dont le seul but est de meubler + la page artificiellement. + }% +} + +\catcode`@12 +\dummytext{5} +\frboxsep=5pt ¤§*\frboxsep¤ + +\breakpar + \dummytext{70} +\endbreakpar¤§*\breakpar¤ + +\dummytext{5}/{\frboxrule=1pt \frboxsep=0pt \leavevmode +\hfill\frbox{\includegraphics[scale=0.15]{pg0001.pdf}}\hfill +\frbox{\includegraphics[scale=0.15]{pg0002.pdf}}\hfill +\frbox{\includegraphics[scale=0.15]{pg0003.pdf}}\hfill\null +} + +\subsection{Couper un paragraphe en lignes} +Maintenant que le mécanisme de coupure à l'aide de \idx\vsplit est un peu maitrisé, nous allons le mettre à profit pour couper une boite verticale en lignes, chacune d'entre elles étant contenue dans une boite. L'énorme intérêt est qu'il devient alors possible d'effectuer une action pour chaque \emph{ligne} d'un ou plusieurs paragraphes. On pourra par exemple les numéroter ou afficher quelque chose dans la marge de gauche ou de droite. Il deviendra même possible d'agir sur la boite elle-même, pour l'encadrer par exemple. + +À quelle hauteur faudra-t-il couper la boite contenant le tout pour n'obtenir qu'une seule ligne ? La question mérite d'être posée car nous n'avons aucune indication sur la hauteur d'une ligne. La méthode consiste à demander une coupure à une hauteur nulle. Cela va évidemment provoquer une «\texttt{overful vbox}» puisque rien ne peut tenir dans 0pt de hauteur. Il va donc falloir, le temps de la coupure, désactiver le mécanisme qui guette si une boite est trop remplie. \TeX{} dispose de la primitive \idx\vfuzz qui contient une dimension, seuil au-delà duquel est émis un avertissement\idx*{message d'avertissement} lorsqu'une boite verticale déborde (\idx\hfuzz est l'équivalent pour les boites horizontales)\idx*{débordement de boite}. Il suffira donc de dire que \verb|\vfuzz=\maxdimen| pour s'assurer qu'aucun avertissement\idx*{message d'avertissement} n'est émis lors du débordement d'une boite verticale, quelle que soit la valeur de ce débordement. + +\showcode/\setbox0=\vbox{%¤\idx*\setbox¤ + \hsize=5cm ¤\idx*\hsize¤ + Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. +} + +Boite 0 : \copy0 % affiche la boite totale¤\idx*\copy¤ +\medbreak¤\idx*\medbreak¤ + +\edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% appelée après la coupure¤\idx*\the\idx*\vfuzz¤ +\vfuzz=\maxdimen% annule les avertissements pour débordement¤\idx*\maxdimen\idx*{message d'avertissement}\idx*\vfuzz¤ +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ +\setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur¤\idx*\setbox\idx*\vsplit¤ +\restorevfuzz% restaurer \vfuzz +\setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine¤\idx*\unvbox\idx*\vbox¤ + +Boites 1+0 : \vbox{% + \offinterlineskip% annule le ressort d'interligne¤\idx*\offinterlineskip¤ + \box1 % affiche la première ligne¤\idx*\box¤ + \box0 %affiche les lignes restantes +}/ + +Comme on l'a déjà constaté, les ressorts disparaissent lors des coupures et donc ici, nous avons perdu le ressort d'interligne entre la 1\iere{} et la 2\ieme{} ligne. Si l'on veut afficher les lignes une par une, telles qu'elles auraient été affichées lors d'une composition normale, on doit absolument récupérer les ressorts mangés lors des coupures pour les insérer entre chaque ligne. Si l'on appelle \verb|\htbefore| la hauteur de la boite \no0 avant la coupure, alors, la longueur verticale du matériel mangé à la coupure est + +\centrecode|\htbefore-(\ht0+\dp0+\ht1+\dp1)| + +On peut le vérifier : + +\label{recup.espace.vertical}\showcode/\def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1¤§*\vdim\idx*\dimexpr\idx*\ht\idx*\dp¤ +\setbox0=\vbox{%¤\idx*\vbox\idx*\setbox¤ + \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est¤\idx*\hsize¤ + de meubler la page de façon artificielle. +} +\edef\htbefore{\the\vdim0}% hauteur de la boite 0¤\idx*\the §*\vdim¤ + +Boite 0 : \copy0 % affiche la boite totale¤\idx*\copy¤ +\medbreak¤\idx*\medbreak¤ + +\edef\restoreparam{% + \vfuzz=\the\vfuzz\relax% sauvegarde le \vfuzz¤\idx*\the\idx*\vfuzz¤ + \splittopskip=\the\splittopskip% et \splittopskip¤\idx*\splittopskip¤ +}% +\vfuzz=\maxdimen% annule les avertissements pour débordement¤\idx*\vfuzz\idx*\maxdimen\idx*{message d'avertissement}¤ +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ +\setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur¤\idx*\setbox\idx*\vsplit¤ +\restoreparam +\setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine¤\idx*\vbox\idx*\unvbox¤ + +\edef\intersplitspace{\the\dimexpr\htbefore-(\vdim0+\vdim1)\relax}%¤\idx*\the\idx*\dimexpr§*\vdim¤ +Boites 1+0 : \vbox{%¤\idx*\vbox¤ + \offinterlineskip% annule le ressort d'interligne¤\idx*\offinterlineskip¤ + \box1 % affiche la première ligne¤\idx*\box¤ + \vskip\intersplitspace\relax% ajoute le ressort perdu à la coupure¤\idx*\vskip¤ + \box0 % affiche les lignes restantes¤\idx*\box¤ +}/ + +Ici encore, \eTeX{} offre des primitives précieuses qui vont rendre la tâche précédente plus simple\footnote{Lire le manuel de \eTeX, chapitre «3.11 Discarded Items».}. La primitive \idx\savingvdiscards a vocation à contenir un entier qui, s'il est positif, autorise la sauvegarde des éléments ignorés lors d'une coupure verticale (les \idx\kern, les ressorts verticaux et les \idx{pénalité}s), qu'elle soit une coupure de page ou une coupure de boite verticale par \idx\vsplit. On peut ensuite insérer ces éléments dans une liste verticale avec \idx\pagediscards pour les éléments ignorés lors d'une coupure de page et avec \idx\splitdiscards pour ceux ignorés lors d'une coupure par \idx\vsplit. Ces deux listes d'éléments sauvegardés sont vidées après la routine de sortie ou au début de l'opération \idx\vsplit. Elles sont également vidées après avoir été utilisées. + +Les macros \verb|\totalht| et \verb|\intersplitspace| du code précédent deviennent dès lors inutiles : + +\showcode/\setbox0=\vbox{%¤\idx*\vbox\idx*\setbox¤ + \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est¤\idx*\hsize¤ + de meubler la page de façon artificielle. +} + +Boite 0 : \copy0 % affiche la boite totale¤\idx*\copy¤ +\medbreak¤\idx*\medbreak¤ + +\edef\restoreparam{% + \vfuzz=\the\vfuzz\relax% sauvegarde le \vfuzz¤\idx*\the\idx*\vfuzz¤ + \splittopskip=\the\splittopskip\relax% , le \splittopskip¤\idx*\splittopskip¤ + \savingvdiscards=\the\savingvdiscards\relax% et le \savingvdiscards + }% +\vfuzz=\maxdimen% annule les avertissements pour débordement¤\idx*\vfuzz\idx*\maxdimen\idx*{message d'avertissement}¤ +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ +\savingvdiscards=1 % autorise la sauvagarde des éléments supprimés¤\idx*\savingvdiscards¤ +\setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur¤\idx*\setbox\idx*\vsplit¤ +\setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine¤\idx*\setbox\idx*\unvbox\idx*\vbox¤ +\restoreparam + +Boites 1+0 : \vbox{% + \offinterlineskip% annule le ressort d'interligne¤\idx*\offinterlineskip¤ + \box1 % affiche la première ligne¤\idx*\box¤ + \splitdiscards% affiche les éléments ignorés¤\idx*\splitdiscards¤ + \box0 % affiche les lignes restantes¤\idx*\box¤ +}/ + +Maintenant que la méthode pour couper \emph{une} ligne a été exposée, il est facile de construire une macro récursive qui coupera successivement les lignes jusqu'à ce qu'il n'y ait plus rien à couper. Ceci sera fait par la macro §\numlines : + +\centrecode-\numlines<texte à couper en lignes>\endnumlines- + +Nous voulons manipuler chaque ligne ainsi coupée, c'est-à-dire avoir la possibilité d'écrire quelque chose à droite et à gauche de chaque ligne, tout en ayant également le contrôle sur la ligne elle-même. Nous allons donc écrire trois macros : + +\begin{enumerate} + \item les macros §\leftline et §\rightline, de syntaxe + + \centrecode-\leftline[<dimension>]{<matériel>} +\rightline[<dimension>]{<matériel>}- + + \noindent qui stockent dans des macros privées le \verb|<matériel>| à écrire à gauche et à droite de chaque ligne. Ce \verb|<matériel>| sera composé dans une \idx\hbox de longueur \verb|<dimension>| et dont le ressort \idx\hss sera correctement positionné pour le contenu s'étende à gauche ou à droite en débordement; + \item une macro §\formatline dont l'argument \verb|#1| sera la boite contenant la ligne en cours. Par défaut, \verb|\formatline| sera rendue \verb|\let|-égale à §\identity pour afficher chaque ligne telle quelle. Il sera possible de personnaliser l'affichage en écrivant, par exemple, \verb|\let\formatline=\frbox| pour encadrer chaque ligne. +\end{enumerate} + +Chaque ligne sera enfermée dans une \idx\hbox afin que le mode vertical principal perdure. Après cette \idx\hbox, \idx\splitdiscards sera insérée. Ainsi, une ligne sera composée de la façon suivante : + +\centrecode-\hbox{% + \hbox to<dimension gauche>{\hss<matériel gauche>}% + \formatline{<ligne courante>}% + \hbox to <dimension droite>{<matériel droit>\hss}% +}% +\splitdiscards- + +Insérer \idx\splitdiscards dans la liste verticale principale nous empêche de savoir quelle est la dimension verticale totale de la ligne composée. Certes, nous aurions pu placer le tout dans une \idx\vbox et stocker cette boite un registre pour accéder à sa dimension verticale. Mais agir ainsi et enfermer \idx\splitdiscards aurait empêché le matériel contenu dans \idx\splitdiscards de disparaitre aux coupures de page. + +Pourtant, accéder à la dimension verticale totale de la ligne composée peut s'avérer nécessaire pour correctement dimensionner les matériels affichés à droite et à gauche de la ligne courante pour obtenir certains effets. Nous allons donc agir comme au code de la page~\pageref{recup.espace.vertical} et calculer \verb|\htbefore| pour obtenir la hauteur de \idx\splitdiscards dans \verb|\intersplitspace|. + +Comme le suggère son nom, la macro §\numlines a pour vocation première à numéroter les lignes du \verb|<texte>| qui s'étend jusqu'à \verb|\endnumlines|. Pour ce faire, un compteur \verb|\linecnt| est créé et incrémenté à chaque ligne. Il appartient à l'utilisateur d'afficher, s'il le souhaite, la valeur de ce compteur à l'aide de §\leftline ou §\rightline. + +\showcode|\catcode`@11 +\newbox\remainbox% boite contenant le texte total¤\idx*\newbox¤ +\newbox\currentline% boite contenant le ligne en cours¤\idx*\newbox¤ +\newcount\linecnt% compteur pour numéroter les lignes¤\idx*\newcount¤ + +\def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1¤§*\vdim\idx*\dimexpr\idx*\ht\idx*\dp¤ + +\newmacro\leftline[0pt]{% définit ce qui se trouve à gauche de chaque ligne¤§*\newmacro§*\leftline¤ + \def\wd@left{#1}% + \def\stuff@left +} + +\newmacro\rightline[0pt]{% définit ce qui se trouve à droite de chaque ligne¤§*\newmacro§*\rightline¤ + \def\wd@right{#1}% + \def\stuff@right +} + +\let\formatline=\identity% par défaut, afficher chaque ligne telle quelle¤§*\identity¤ + +% Par défaut : +\leftline[11pt]{$\scriptscriptstyle\number\linecnt$\kern3pt }% numérotation à gauche¤\idx*\scriptscriptstyle§*\leftline¤ +\rightline{}% rien à droite¤§*\rightline¤ + +\def\numlines{%¤§*\numlines¤ + \par\smallskip¤\idx*\smallskip¤ + \begingroup% dans un groupe semi-simple + \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ + \linecnt=0 % initialiser le compteur de lignes + \savingvdiscards=1 % autorise la sauvagarde des éléments supprimés¤\idx*\savingvdiscards¤ + \setbox\remainbox=\vbox\bgroup% compose la boite...¤\idx*\setbox\idx*\vbox\idx*\bgroup¤ + \advance\hsize by% diminuer la \hsize¤\idx*\advance\idx*\hsize¤ + -\dimexpr\wd@left+\wd@right\relax% de la largeur des contenus¤\idx*\dimexpr¤ +} + +\def\endnumlines{% + \egroup¤\idx*\egroup¤ + \offinterlineskip¤\idx*\offinterlineskip¤ + \split@line +} + +\def\split@line{% + \ifvoid\remainbox% si la boite est vide¤\tidx*{ifvoid}¤ + \par% fin du processus + \endgroup% fermer le groupe ouvert au début + \else% sinon + \advance\linecnt 1 % incrémente le compteur de lignes¤\idx*\advance¤ + \edef\htbefore{\the\vdim\remainbox}%¤\idx*\the §*\vdim¤ + \edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% sauvegarde le \vfuzz¤\idx*\vfuzz¤ + \vfuzz=\maxdimen% annule les avertissements pour débordement¤\idx*\maxdimen\idx*{message d'avertissement}\idx*\vfuzz¤ + \setbox\currentline=\vsplit\remainbox to 0pt % couper la boite à 0pt de hauteur¤\idx*\setbox\idx*\vsplit¤ + \restorevfuzz + \setbox\currentline=\vbox{\unvbox\currentline}% redonner à la boite sa hauteur¤\idx*\setbox\idx*\unvbox¤ + \edef\intersplitspace{% calcul de l'espace vertical perdu à la coupure + \the\dimexpr\htbefore-(\vdim\remainbox+\vdim\currentline)\relax¤\idx*\the\idx*\dimexpr §*\vdim¤ + }% + \hbox{% en mode vertical et dans une hbox, afficher :¤\idx*{mode!vertical}¤ + \hbox to\wd@left{\hss\stuff@left}% 1) ce qui est à gauche¤\idx*\hss\idx*\hbox¤ + \formatline{\box\currentline}% 2) la ligne courante¤\idx*\box¤ + \hbox to\wd@right{\stuff@right\hss}% 3) ce qui est à droite¤\idx*\hss¤ + }% + \splitdiscards% affiche ce qui a été ignoré à la coupure¤\idx*\splitdiscards¤ + \expandafter\split@line% recommencer + \fi +} + +\def\dummytext#1{% + \for\xx=1to#1\do% composer #1 fois la phrase suivante :¤§*\for¤ + {Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. }% +} + +\catcode`@12 +\parindent=2em ¤\idx*\parindent¤ + +ESSAI 1 : +¤§*\numlines¤\numlines + \dummytext{2}\par% 2 phrases + $$1+1=2$$\par% des maths + \dummytext{1}\par% une phrase + \hrulefill\par% un leaders¤\idx*\hrulefill¤ + \dummytext{2}% 2 phrases + \hrule height2pt depth 2pt %une \hrule¤\idx*\hrule¤ + \vskip10pt % saute 10pt verticalement¤\idx*\vskip¤ + \dummytext{1}% une phrase +\endnumlines\medbreak¤\idx*\medbreak¤ + +ESSAI 2 : +\leftline{}\rightline{}% rien à gauche et rien à droite¤§*\leftline¤ +\frboxsep=-\frboxrule% encadrer vers "l'intérieur"¤§*\frboxsep§*\frboxrule¤ +\let\formatline\frbox% lignes encadrées¤§*\frbox¤ +¤§*\numlines¤\numlines + \dummytext{4} +\endnumlines\medbreak¤\idx*\medbreak¤ + +ESSAI 3 : +\let\formatline\identity¤§*\identity¤ +\leftline[7pt]{%¤§*\leftline¤ + \for\xx= 1 to 2 \do{% insérer 2 fois¤\idx*\llap §*\for¤ + \setbox0=\hbox{% % mettre dans une \hbox une \vrule de "bonnes dimensions"¤\idx*\setbox\idx*\hbox¤ + \vrule height\ht\currentline depth\dimexpr\dp\currentline+\intersplitspace + width0.5pt }%¤\idx*\vrule\idx*\dimexpr\idx*\dp¤ + \dp0=\dp\currentline% réajuster la profondeur (c-à-d enlever \intersplitspace)¤\idx*\dp¤ + \box0 % afficher le boite¤\idx*\box¤ + \kern2pt % et insérer un espace horizontal de 2pt après chaque réglure verticale¤\idx*\kern¤ + }% + \kern2pt % ajouter 2pt de plus entre les lignes et le texte¤\idx*\kern¤ +} + +\rightline[10pt]{\kern5pt $\scriptscriptstyle\number\linecnt$}% numéroter à droite¤\idx*\scriptscriptstyle§*\rightline¤ + +¤§*\numlines¤\numlines + \dummytext{2} + + $$a^2+b^2=c^2$$ + + \dummytext{2} +\endnumlines| +À l'essai \no3, une petite cuisine interne est faite pour tracer deux traits verticaux à gauche du texte. Elle consiste à afficher deux fois un trait vertical de bonnes dimensions. Pour ce faire, le registre de boite \no0 reçoit une \idx\hbox contenant une réglure dont la hauteur coïncide exactement avec celle de la ligne en cours et dont la profondeur couvre aussi l'espace additionnel \verb|\intersplitspace| qui sépare deux lignes adjacentes. Cette \idx\hbox est ensuite redimensionnée pour qu'elle ait la dimension verticale de la ligne à afficher. En procédant ainsi, les réglures seront jointives d'une ligne à l'autre. + +Pour arriver au même résultat, nous aurions pu inclure une \idx\hrule de même dimensions que la \idx\vrule de l'essai \no3 dans une \idx\vtop dont la dimension verticale est imposée et égale à la hauteur totale de la ligne à afficher. Le débordement vers le bas, d'une valeur de \verb|\intersplitspace| serait rattrapé par un \idx\vss. Voici donc un code équivalent qui ne mobilise pas de registre de boite : + +\centrecode-\leftline[7pt]{%¤§*\leftline¤ + \for\xx= 1 to 2 \do{% insérer 2 fois¤§*\for¤ + \vtop to\dimexpr\ht\currentline+\dp\currentline{%¤\idx*\vtop\idx*\ht\idx*\dp¤ + \hrule height\ht\currentline + depth\dimexpr\dp\currentline+\intersplitspace¤\idx*\dp¤ + width0.5pt + \vss% rattrape la profondeur de trop \intersplitspace¤\idx*\vss¤ + }% + \kern2pt % et insérer un espace horizontal + % de 2pt après chaque réglure verticale¤\idx*\kern¤ + }% + \kern2pt % ajouter 2pt de plus entre les lignes et le texte¤\idx*\kern¤ +}- + +\section{Étirer horizontalement du texte}\idx*[|(]{étirer du texte} +Il tout d'abord faut être précis sur les termes. Étirer du texte va signifier ici que des espaces seront insérées entre chaque lettre de façon à ce que le texte ainsi modifié prenne davantage de place que s'il était composé normalement. + +Il faut également savoir qu'étirer du texte est un effet typographiquement discutable qui donne parfois des résultats malheureux. Il est donc recommandé de ne pas en abuser et de limiter sa portée à des portions de texte \emph{courtes}, par exemple pour mettre en évidence des titres, cas où son emploi est justifié. + +\subsection{Espace inter-lettre} +La méthode qui semble être la plus naturelle est d'insérer un ressort prédéfini que nous appellerons « inter-lettre » après chaque caractère dont le catcode est 11 ou 12 et en remplaçant l'espace par un ressort « inter-mot » dont la dimension fixe sera plus importante que celles des ressorts inter-lettres. La macro §\spreadtxt\verb|{<texte>}| s'acquittera de cette tâche. + +La macro §\parse écrite à la page~\pageref{parse} permet de lire le texte token par token et d'agir en conséquence via la macro §\testtoken. Nous nous contenterons donc de modifier localement §\testtoken pour lui faire insérer les espaces voulues. La macro §\spreadtxt se chargera de lire son argument, d'ouvrir un groupe semi-simple, de correctement définir §\testtoken et appellera §\parse : + +\showcode/% définition des ressorts "inter-lettre" et "inter-mot" +\newskip\interletterskip \interletterskip=0.25em plus0.05em minus0.05em ¤\idx*\newskip¤ +\newskip\interwordskip \interwordskip=3\interletterskip\catcode`\@11 ¤\idx*\newskip¤ +\catcode`@11 +\def\spreadtxt@testtoken#1{% macro qui teste le token¤§*\testtoken¤ + \ifcat\noexpand#1\sptoken% si le token est un espace¤\defline\aaa\tidx*{ifcat}\idx*\noexpand§*\sptoken¤ + \parseadd{% + \unskip% retirer le précédent ressort¤\idx*\unskip¤ + \hskip\interwordskip}% et ajouter le ressort inter-mot¤\idx*\hskip¤ + \else + \ifcat\noexpand#1a% si le token est une lettre¤\defline\bbb\tidx*{ifcat}¤ + \parseadd{#1\hskip\interletterskip}% ajouter le ressort inter-lettre¤\idx*\hskip¤ + \else + \ifcat\noexpand#1.% si le token est "autre", comme le "."¤\defline\ccc\tidx*{ifcat}\idx*\noexpand¤ + \parseadd{#1\hskip\interletterskip}% ajouter le ressort inter-lettre¤\idx*\hskip¤ + \else% sinon + \parseadd{#1}% ajouter le token lu¤\defline\ddd¤ + \fi + \fi + \fi + \parse@i +} +\def\spreadtxt{% + \ifstarred% si étoilée + {\spreadtxt@i{\parse*}}% appeler \parse*¤§*\parse¤ + {\spreadtxt@i{\parse}}% sinon, appeler \parse¤§*\parse¤ +} +\def\spreadtxt@i#1#2{% #1= appel "\parse*" ou "\parse" #2 = texte à espacer + \begingroup% dans un groupe + \let\testtoken=\spreadtxt@testtoken% modifier \testtoken + #1#2\parsestop% et appeler \parse + \endgroup +} +\catcode`@12 + +\spreadtxt{Comme on le voit sur cet exemple, une espace est insérée après +{\it chaque} caractère de catcode 10, 11 ou 12.}¤\idx*\it\idx*{catcode!10\space(espace)}¤ +\medbreak¤\idx*\medbreak¤ + +\spreadtxt*{Comme on le voit sur cet exemple, une espace est insérée après +{\it chaque} caractère de catcode 10, 11 ou 12.}¤\idx*\it¤/ + +Il y a un petit dysfonctionnement tout de même : les caractères accentués ne sont pas suivis d'une espace. Ceci s'explique par le fait que ce livre a été composé avec \LaTeX{} et l'extension \texttt{inputenc} chargée avec l'option \latin. La combinaison des deux rend les caractères accentués actifs et donc les tests des lignes \nos\aaa, \bbb{} et \ccc{} sont faux. Le token est donc simplement rajouté au registre de tokens à la ligne \no\ddd{} sans le ressort inter-lettre. Il y a plusieurs façons de se sortir de ce mauvais pas. On pourrait imaginer un test supplémentaire où l'on traiterait un caractère actif comme un caractère normal et insèrerait le ressort inter-lettre après lui. Mais cette méthode échouerait avec \utf et un moteur 8 bits\idx*{moteur!8 bits}, car si l'on rencontre un caractère codé sur plusieurs octets, le premier octet (actif) sera séparé des autres octets participant à la construction du caractère final. + +On touche du doigt une difficulté majeure, celle de lire un texte caractère par caractère, où le mot « caractère » est pris au sens d'entité typographique. La difficulté dépend de la combinaison entre l'\idx{encodage} du code source et le type de moteur utilisé : + +\begin{description} + \item[cas favorables] Les associations entre un moteur et un \idx{encodage} sur un nombre d'octets correspondant à ce que lit le moteur ne présentent aucune difficulté puisque les caractères typographiques sont les entités lues par le moteur. + \begin{itemize}[topsep=0pt] + \item \idx*{moteur!8 bits}moteur 8 bits (comme \verb|tex|, \verb|etex| et \verb|pdftex|) avec un \idx{encodage} 8 bits (comme \latin) : les caractères lus sont des \idx{octet}s; + \item \idx*{moteur!utf8}moteur \utf (comme \verb|xetex| et \verb|luatex|) avec un encodage \utf : les caractères lus sont des caractères \utf; + \end{itemize} + \item[cas défavorables] Les combinaisons entre un moteur 8 bits\idx*{moteur!8 bits} et un encodage multi octets comme \utf va faire surgir de grandes difficultés pour lire le code source caractère \utf par caractère \utf. +\end{description} + +\subsection{Liste de motifs insécables} +Comment s'en sortir sans laisser de côté le cas le plus défavorable qui est encore très utilisé\footnote{L'association de pdf\TeX{} (ou pdf\LaTeX) avec un encodage \utf est en effet très répandu. Cette combinaison ouvre la possibilité d'écrire dans le code source beaucoup plus de caractères que ceux accessibles avec un \idx{encodage} 8 bits et fonctionne très bien sauf lorsqu'on souhaite élaborer des \emph{programmes} devant lire le code source caractère par caractère.} ? L'idée est de créer une liste de motifs indivisibles qui seraient laissés tels quels : il faudrait donc tester à chaque itération si les prochains caractères à lire ne commencent pas par un des motifs. Dans l'affirmative, il faudrait laisser ce motif et insérer le ressort inter-lettre après ce motif. Si le test s'avérait négatif, il faudrait agir comme précédemment. + +Le problème est que pour déterminer si ce qui reste à lire commence par un motif, il faut lire la totalité du texte à espacer au préalable. La méthode change donc radicalement. Il ne s'agit plus de laisser §\parse faire le travail. Nous allons créer une nouvelle macro §\spacetxt qui reprendra en grande partie l'algorithme vu pour la macro §\substitute (voir page~\pageref{substitute}). Le principe consiste à stocker la totalité du texte initial dans la macro \verb|\spacetxt@code| et le texte espacé dans le registre de tokens \verb|\spacetxt@toks|. Au fur et à mesure des itérations, \verb|\spacetxt@code| sera peu à peu amputée des caractères lus et \verb|\spacetxt@toks| collectera le code produisant le texte espacé. + +La liste des motifs indivisibles sera stockée dans la macro \verb|\indivilist|, chacun étant séparé du suivant par une virgule. Pour tenir compte de ces motifs, il faudra à chaque itération et avant toute chose, tester si le texte de remplacement de \verb|\spacetxt@code| commence par un des motifs déclarés dans \verb|\indivilist|. Cette série de tests sera faite à l'aide d'une boucle de type §\doforeach. Il est clair que tous ces tests supplémentaires vont être exécutés à chaque itération et cela consommera du temps; ce n'est donc pas une panacée du point de vue de l'efficacité du programme. Heureusement, comme le texte à espacer est censé être assez court et le nombre de motifs ne doit normalement pas excéder une dizaine d'éléments, la perte de temps n'est pas très importante. Nous utiliserons la macro §\doforeachexit pour sortir prématurément de la série de tests si l'un est positif et le booléen \verb|\if@indivifound| sera pris égal à \verb|vrai| pour indiquer qu'un motif a été trouvé. Dans ce cas, nous retirerons ce motif à \verb|\spacetxt@code| pour l'ajouter à \verb|\spacetxt@toks| en le faisant suivre de l'espace inter-lettre. + +La fin du processus se produit lorsqu'il n'y a plus de texte à espacer, c'est-à-dire lorsque \verb|\spacetxt@code| est vide. + +Pour espacer du texte entre accolades, la \idx{macro étoilée} §\spacetxt\verb|*| devra être appelée. Les mêmes astuces de programmation que pour la macro §\substitute ont été employées.\label{spacetxt} + +\showcode/\newskip\interletterskip¤\idx*\newskip¤ +\newskip\interwordskip ¤\idx*\newskip¤ +\catcode`\@11 +\newtoks\spacetxt@toks% le registre qui contient le texte final¤\idx*\newtoks¤ + +\def\spacetxt{%¤§*\spacetxt¤ + \let\spacetxt@endprocess\spacetxt@endnormal + % définit la macro appelée en fin de processus -> a priori : fin normale + \ifstarred% si la macro est étoilée¤§*\ifstarred¤ + {\let\spacetxt@recurse\spacetxt@star% définir la macro récursive + \spacetxt@i% et aller à \spacetxt@i + }% sinon + {\let\spacetxt@recurse\spacetxt@nostar% définir la macro récursive + \spacetxt@i% et aller à \spacetxt@i + }% +} + +\newmacro\spacetxt@i[0.3em plus0.07em minus.07em][3\interletterskip]1{%¤§*\newmacro¤ +% arg optionnel #1 et #2 = ressorts inter-lettre et inter--mot +% #3 = texte à espacer + \interletterskip=#1\relax + \interwordskip=#2\relax + \def\spacetxt@code{#3}% met le texte à espacer dans \spacetxt@code + \spacetxt@toks{}% initialiser le registre contenant le texte final + \spacetxt@recurse% aller à la macro récursive précédemment définie +} + +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré¤\idx*\newif¤ + +\def\rightofsc#1#2{% + \exparg\ifin{#1}{#2}% si #1 contient le #2 + {\def\right@of##1#2##2\@nil{\def#1{##2}}% + \expandafter\right@of#1\@nil% appelle la macro auxiliaire + }% + {\let#1=\empty}% sinon, #1 est vide +} + +\def\spacetxt@nostar{% + \exparg\ifempty{\spacetxt@code}% si texte restant est vide¤§*\exparg §*\ifempty¤ + \spacetxt@endprocess% aller à la fin du processus + {\@indivifoundfalse% sinon, a priori, les motifs non réguliers ne sont pas trouvés + % pour chaque \indivi@tmp dans \indivilist + \expsecond{\doforeach\indivi@tmp\in}{\indivilist}% pour chaque motif indivisible¤§*\expsecond §*\doforeach¤ + {% si le code commence par le motif courant + \exptwoargs\ifstartwith\spacetxt@code\indivi@tmp¤§*\exptwoargs §*\ifstartwith¤ + {% l'ajouter dans le registre ainsi que l'espace inter-lettre + \eaddtotoks\spacetxt@toks{\indivi@tmp\hskip\interletterskip}%¤§*\eaddtotoks\idx*\hskip¤ + % et enlever le motif du texte restant à lire + \expsecond{\rightofsc\spacetxt@code}{\indivi@tmp}%¤§*\expsecond §*\rightofsc¤ + \@indivifoundtrue% marquer qu'un motif a été trouvé + \doforeachexit% et sortir prématurément de la boucle¤§*\doforeachexit¤ + }% + \relax% si le code ne commence pas le motif courant -> ne rien faire + }% + \unless\if@indivifound% si aucun motif n'a été trouvé¤\tidx*{unless}¤ + \grab@first\spacetxt@code\spacetxt@temp% retirer le 1er caractère du texte + \ifx\spacetxt@temp\space% si le 1er caractère est un espace¤\idx*\space¤ + \addtotoks\spacetxt@toks{%¤§\addtotoks¤ + \unskip% annuler le précédent ressort¤\idx*\unskip¤ + \hskip\interwordskip}% ajouter l'espace inter-mot au registre de token¤\idx*\hskip¤ + \else% si le 1er caractère n'est pas un espace + % ajouter ce caractère et l'espace inter-lettre au registre de token + \eaddtotoks\spacetxt@toks{\spacetxt@temp\hskip\interletterskip}%¤§*\eaddtotoks¤ + \fi + \fi + \spacetxt@recurse% enfin, continuer le processus + }% +} + +\def\spacetxt@star{% + \exparg\ifempty{\spacetxt@code}% si texte restant est vide¤§*\exparg §*\ifempty¤ + \spacetxt@endprocess% aller à la fin du processus + {% sinon, si texte commence par "{" + \exparg\ifbracefirst{\spacetxt@code}%¤§*\exparg §*\ifbracefirst¤ + {\grab@first\spacetxt@code\spacetxt@temp + % mettre {<argument} dans \spacetxt@temp + \begingroup% ouvrir un groupe + % mettre le contenu de l'argument dans \spacetxt@code + \expandafter\def\expandafter\spacetxt@code\spacetxt@temp + \let\spacetxt@endprocess\spacetxt@endingroup% changer le processus de fin + \spacetxt@toks{}% initialiser + \spacetxt@recurse% exécuter le processus avec ce nouveau texte + }% si le code ne commence pas par "{", aller à \spacetxt@nostar mais comme + \spacetxt@nostar% \spacetxt@recurse vaut \spacetxt@star, n'y faire qu'1 boucle + }% +} + +\def\spacetxt@endnormal{% fin de processus normal + \the\spacetxt@toks% afficher le registre à token¤\idx*\the¤ + \unskip% et supprimer le dernier ressort¤\idx*\unskip¤ +} + +\def\spacetxt@endingroup{% fin du processus dans un groupe : + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\spacetxt@toks\expandafter% ajouter au registre hors du groupe¤§*\addtotoks¤ + {\expandafter{\the\spacetxt@toks}}% ce qui est collecté localement mis entre {}¤\idx*\the¤ + \spacetxt@recurse% puis aller à la macro récursive +} + +\catcode`\@12 +\def\indivilist{é,è}% liste des motifs spéciaux +\spacetxt*{Comme on le voit sur cet exemple, une espace est insérée après +{\it chaque} caractère.}¤§*\spacetxt\idx*\it¤ +\medbreak¤\idx*\medbreak¤ + +\def\indivilist{é,es,au,<<,>>} +\spacetxt[4pt plus.7pt minus.7pt][12pt plus2pt minus2pt]{Ici, les motifs <<es>> et <<au>> +restent indivisibles et la ligature des guillemets devient possible en déclarant "<<" et +">>" comme motifs.}¤§*\spacetxt\idx*\it¤/ + +Nous avons utilisé les arguments optionnels de la macro \verb|\spacetxt@i| pour définir à l'intérieur de celle-ci les ressorts inter-lettre et inter-mot. Cette méthode est préférable à celle de §\spreadtxt où l'on avait défini ces registres de ressort en dehors de toute macro. En effet, si l'on définit ces ressorts avec des unités qui varient selon le contexte (comme l'\idx*{unité!ex}\verb|ex| ou l'\idx*{unité!em}\verb|em|), les dimensions des ressorts ainsi définis ne dépendront que de la valeur de ces unités \emph{lors de l'assignation}. + +Il est important de comprendre que l'\idx*{assignation!registre de ressort}assignation d'une dimension à un registre de ressort (ou de dimension) \emph{évalue} cette dimension pour la stocker dans le registre. La différence est fondamentale entre + +\centrecode-<ressort>=0.3em- + +\noindent et + +\centrecode-\def\<macro>{0.3em}- + +\noindent suivi plus tard de + +\centrecode-<ressort>=\<macro>- + +En effet, lorsqu'on écrit «\verb|<ressort>=0.3em|», la dimension \verb|3em| est évaluée et le résultat est stocké dans le registre qui dès lors, contient une dimension qui dépend de la valeur de l'\verb|em| lors de l'assignation. + +Au contraire, écrire «\verb|\def\<macro>{0.3em}|» stocke simplement dans le texte de remplacement de \verb|\<macro>| les caractères «\verb|0.3em|». C'est lors de l'assignation de ressort «\verb|<ressort>=\<macro>|» que l'évaluation de \verb|0.3em| est faite. Ce passage par une macro auxiliaire de stockage permet donc de garder le caractère variable de l'unité \verb|em| et choisir le moment où l'on évalue cette dimension. + +\subsection{Une alternative à \texttt{\char`\\litterate}} +La méthode précédente peut être adaptée pour insérer un court passage en fonte à chasse fixe à l'intérieur d'un paragraphe, pour écrire une adresse URL, par exemple. Nous allons écrire une macro §\ttcode, de syntaxe + +\centrecode-\ttcode<délimiteur><texte><délimiteur>- + +\noindent qui compose le \verb|<texte>| en police à chasse fixe où les tokens de catcode différents de 10, 11 et 12 sont neutralisés avec \idx\dospecials. En invoquant §\spacetxt, nous allons insérer un ressort de faible dimension après chaque token du \verb|<texte>| et donc, contrairement à §\litterate, §\ttcode permet qu'une coupure se produise après chaque token du \verb|<texte>|. + +\showcode|\catcode`\@11 +\def\ttcode#1{% lit #1, le <délimiteur> de début¤§*\ttcode¤ + \def\ttcode@i##1#1{% ##1 = <texte> entre délimiteurs + \tt% passe en fonte à chasse fixe¤\idx*\tt¤ + \setbox0=\hbox{ }%¤\idx*\setbox\idx*\hbox¤ + \edef\spc@wd{\the\wd0 }% longueur d'un espace¤\idx*\the\idx*\wd¤ + \spacetxt¤§*\spacetxt¤ + [.1pt plus0pt minus.1pt]% espace inter-lettre + [\glueexpr\wd0+.3pt plus.1pt minus.1pt\relax]% espace inter-mot¤\idx*\glueexpr\idx*\wd¤ + {##1}% le <texte> est composé par \spacetxt + \endgroup + }% + \begingroup + \def\indivilist{<<,>>,{,,},--}% (rajouter à, é, è, etc. en codage UTF8) + \def\do##1{\catcode`##1=12 }%¤\idx*\do¤ + \dospecials% rend inoffensifs tous les tokens spéciaux¤\idx*\dospecials¤ + \letactive\ =\space % rend l'espace actif¤§*\letactive¤ + \ttcode@i% va lire le <texte> et le délimiteur de fin +} +\catcode`\@12 + +\hfill\vrule\vbox{%¤\idx*\hfill\idx*\vrule\idx*\vbox¤ + \hsize=.75\hsize¤\idx*\hsize¤ + Avec \ttcode/\ttcode/, on peut insérer une adresse internet << + \ttcode-http://www.gutenberg.eu.org/Typographie- >> puis repasser en fonte normale + puis \ttcode+même composer un court passage en fonte à chasse fixe -- même si les + coupures de mots se font n'importe où -- et aussi afficher tous les caractères + spéciaux <<{$^ _$#}&+>>, et finir en fonte normale\ldots +}\vrule\hfill\null¤\idx*\hfill\idx*\vrule\idx*\null§*\ttcode¤|\idx*[|)]{étirer du texte} + +\section{Composition en fonte à chasse fixe}\idx*[|(]{composition en fonte à chasse fixe} +\subsection{Mise en évidence du problème} +Une \idx{fonte}\footnote{On peut souvent confondre police et fonte, mais une différence sémantique existe. Une \emph{\idx{police}} est un ensemble de glyphes qu'un auteur (bien souvent, c'est le même auteur qui dessine une police entière) a dessiné dans un même esprit en respectant le même style. Une \emph{\idx{fonte}} est un sous-ensemble d'une police ayant des caractéristiques bien déterminées : taille, graisse (léger, normal, demi-gras, gras, etc.), forme (italique, penché, petite capitale, etc.) + +Par exemple, le texte de ce livre est écrit avec la \emph{\idx{police}} Libertine, mais \textbf{ceci} et \textit{ceci} sont écrits en deux \emph{\idx{fonte}s} différentes de cette police.} est dite à « chasse fixe » lorsque tous ses caractères ont la même largeur, y compris l'espace qui est non étirable et non comprimable. La composition en fonte de ce type peut poser des problèmes, car à cause de cette particularité géométrique, il devient impossible de respecter \emph{en même temps} les trois contraintes suivantes\label{contraintes.tt} : + +\begin{enumerate} + \item caractères parfaitement les uns au-dessous des autres d'une ligne à l'autre; + \item composition justifiée, c'est-à-dire que la distance entre le bord gauche du premier caractère et le bord droit du dernier est toujours la même; + \item coupures des mots respectées ou interdites. +\end{enumerate} +\grandsaut + +Il faut savoir que le passage en \idx{fonte} à chasse fixe avec \LaTeX{} à l'aide des macros \idx\texttt ou \idx\ttfamily désactive la coupure des mots (nous verrons comment). Dès lors, il suffit qu'un mot assez long se trouve en \idx{fin de ligne} pour que le dépassement dans la marge soit tellement important que parfois, les caractères se trouvent en dehors de la page physique. Les utilisateurs de \LaTeX{} savent certainement de quoi je parle\ldots + +À titre d'exemple, voici un court paragraphe composé en fonte à chasse fixe (avec la macro \idx\ttfamily) comme le verrait un utilisateur de \LaTeX{} (alors que pour un utilisateur de \TeX{} avec la macro \idx\tt, les coupures de mots seraient effectuées) : + +\showcode[\hfuzz=\maxdimen]|\def\ttwide{0.7}% coefficient pour la largeur de composition +\def\ttindent{5}% nombre de caractères d'indentation +\hfill\vrule¤\idx*\hfill\idx*\vrule¤ +\vbox{% + \ttfamily% en TeX, on écrirait "\tt"¤\idx*\ttfamily¤ + \setbox0\hbox{0}% \wd0 est donc la largeur d'un caractère¤\idx*\setbox¤ + \parindent=\ttindent\wd0 % réglage de l'indentation¤\idx*\parindent\idx*\wd¤ + \hsize=\ttwide\hsize % compose sur 70% de la largeur¤\idx*\hsize¤ + Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauches + et droites du texte ont été tracées, on constate que les caractères sont + exactement les uns au-dessous d'une ligne à l'autre. Des débordements dans + la marge deviennent alors inévitables, car la largeur de composition (ici + \the\hsize) n'est pas un multiple de la largeur d'un caractère (\the\wd0 ),¤\idx*\the\idx*\hsize\idx*\wd¤ + le quotient des deux valant environ + \xdef\ttratio{\decdiv{\dimtodec\hsize}{\dimtodec\wd0 }}\ttratio.¤§*\decdiv§*\dimtodec\idx*\wd\idx*\xdef¤ +}% +\vrule\hfill\null¤\idx*\null¤| + +Bien sûr, le \idx[!log]{fichier} \verb|log| contient 6 avertissements\idx*{message d'avertissement}, chacun informant l'utilisateur que \TeX{} a dû composer une ligne trop longue\idx*{débordement de boite} : + +\centrecode|Overfull \hbox (4.97778pt too wide) in paragraph at lines 9--16 +Overfull \hbox (31.85786pt too wide) in paragraph at lines 9--16 +Overfull \hbox (20.33783pt too wide) in paragraph at lines 9--16 +Overfull \hbox (43.3779pt too wide) in paragraph at lines 9--16 +Overfull \hbox (1.13777pt too wide) in paragraph at lines 9--16 +Overfull \hbox (8.8178pt too wide) in paragraph at lines 9--16| + +Ici en effet, sur chaque ligne, il entre \def\intratio#1.#2\relax{\def\intratio{#1}}\expandafter\intratio\ttratio\relax\intratio{} caractères complets. Après ce \intratio\ieme{} caractère, comme il reste de la place, \TeX{} place le caractère suivant entrant dès lors dans un dépassement dans la marge. Les coupures de mots étant interdites, il continuera à placer des caractères dans la marge jusqu'au prochain espace qui provoquera, mais trop tard, une coupure de ligne. + +Livrons-nous à un petit calcul pour comprendre la valeur \verb|8.8178pt| donnée pour le dépassement de la sixième ligne. Cette ligne comporte 62 caractères : les \intratio{} complets plus les 3 dernières lettres du mot «\verb|quotient|». Le dépassement dans la marge vaut + +\[ +62\times3.84001-229.26292 +\] + +\noindent c'est-à-dire \verb|8.8177pt|. C'est bien, à une erreur d'arrondi près, la valeur qui est donnée par \TeX{} dans le message d'avertissement\idx*{message d'avertissement} du \idx[!log]{fichier} \verb|log|. + +\subsection{\Qu elques notions sur les fontes} +Avant d'envisager toute solution concernant les fontes de caractères, il nous faut entrer un peu plus profondément dans les entrailles de \TeX{} afin d'examiner quelques commandes spécifiques. Nous nous en tiendrons au strict minimum, car la manipulation des fontes en \TeX{} est un monde long et complexe à explorer. + +\subsubsection{Les primitives \texttt{\char`\\font} et \texttt{\char`\\fontname}}\idx*[|(]\fontname +\begin{regle} +La primitive \idx\fontname permet d'accéder au nom externe d'un fichier de \idx{fonte} selon la syntaxe + +\centrecode-\fontname<fonte>- + +\noindent où \verb|<fonte>| est soit une séquence de contrôle définie avec la primitive \idx\font, soit la primitive \idx\font elle-même auquel cas on fait référence à la fonte en cours d'utilisation. Le tout se développe en le nom du fichier externe correspondant à la \idx{fonte} spécifiée. +\end{regle} + +Ainsi, ce paragraphe est écrit avec une des fontes de la \idx{police} «Libertine» dont le nom du fichier externe est «\expandafter\texttt\expandafter{\fontname\font}». Lorsqu'on passe en fonte grasse, le nom devient «{\bfseries\expandafter}\expandafter\texttt\expandafter{\fontname\font}» tandis qu'avec la fonte italique, le nom est «{\itshape\expandafter}\expandafter\texttt\expandafter{\fontname\font}». + +Lorsqu'une \idx{fonte} est utilisée dans une autre taille que celle pour laquelle elle a été dessinée, le mot-clé «\verb|at|» suivi de la taille d'utilisation est ajouté après le nom obtenu : + +\begingroup +\showcode|Fonte normale : \fontname\font\par¤\idx*\fontname\idx*\font¤ +{\bf Fonte grasse : \fontname\font\par}¤\idx*\bf¤ +{\it Fonte italique : \fontname\font\par}¤\idx*\it¤ +{\sc Fonte petites majuscules : \fontname\font}¤\idx*\sc¤|\idx*[|)]\fontname +\endgroup + +Le «\verb|at 8.0pt|» exprime que les rendus des codes de ce livre sont composés avec une taille de \verb|8pt| imposée, ce qui revient à dire que les dessins des caractères ainsi que toutes leurs dimensions géométriques ont été redimensionnés pour atteindre cette taille. + +\begin{regle} +La primitive \idx\font permet de créer une séquence de contrôle qui, lorsqu'elle sera exécutée, effectuera un changement de fonte. On utilise la syntaxe + +\centrecode-\font\<macro>=<nom de fonte> at <dimension>- + +\noindent où «\verb|at <dimension>|» est facultatif. +\end{regle} + +Voici comment créer des macros (\verb|\gras|, \verb|\ital| et \verb|\itgras|) qui, lorsqu'elles sont appelées, sélectionnent respectivement la \idx{fonte} de taille \verb|8pt|, en gras, italique et gras italique : + +\showcode/\font\gras=LinLibertineTB-tlf-t1 at 8pt¤\idx*\font¤ +\font\ital= LinLibertineTI-tlf-t1 at 8pt +\font\itgras=LinLibertineTBI-tlf-t1 at 8pt +Du texte normal {\gras puis en gras, \ital en italique, +\itgras en italique gras} et retour à la normale./ + +\subsubsection{Caractère de coupure de mots} +Chaque \idx{fonte} se voit assigner un caractère de coupure qui sera inséré à la fin d'une ligne lorsqu'un mot est coupé. Par défaut, ce caractère est le tiret «\verb|-|». + +\begin{regle} +Le caractère inséré aux coupures de mots est stocké dans un registre interne de type entier appelé \idx*{caractère de coupure}\idx\hyphenchar. Ce registre \emph{doit} être suivi de la \idx{fonte} à laquelle on souhaite se référer : + +\centrecode-\hyphenchar<fonte>- + +On peut à tout moment choisir un autre caractère de coupure par son code de caractère en modifiant \idx*{caractère de coupure}\idx\hyphenchar : + +\centrecode-\hyphenchar<fonte>=<code de caractère>- + +Si le \verb|<code de caractère>| contenu dans \idx*{caractère de coupure}\idx\hyphenchar est négatif ou est supérieur à 255, aucun caractère n'est sélectionné et cela désactive les coupures de mots. + +L'assignation d'un entier à \idx*{caractère de coupure}\idx\hyphenchar est toujours globale\idx*{assignation!toujours globale}. +\end{regle} + +\showcode/Code du caractère de coupure = \number\hyphenchar\font\par¤\idx*\font¤ +Caractère de coupure : "\char\hyphenchar\font"/ + +Voici comment modifier le \idx*{caractère de coupure}\idx\hyphenchar : + +\showcode[\hfuzz=\maxdimen]/\def\longtext{Voici une phrase écrite avec des mots insignifiants mais terriblement, + épouvantablement, horriblement et indéniablement longs.} +% créér une macro restaurant le \hyphenchar +\edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}%¤\idx*\hyphenchar\idx*\font¤ + +%comportement normal, caractère de coupure "-" +1) \vrule\vbox{\hsize=5cm \longtext}\vrule\hfill¤\idx*\vrule\idx*\vbox\idx*\hsize\idx*\hfill¤ +% modification du caractère de coupure "W" +2) \vrule\vbox{\hsize=5cm \hyphenchar\font=`W \longtext}\vrule¤\idx*\hyphenchar¤ +\medbreak¤\idx*\medbreak¤ +% interdiction des coupures de mots +3) \vrule\vbox{\hsize=5cm \hyphenchar\font=-1 \longtext}\vrule¤\idx*\hyphenchar\idx*\font\idx*\vrule¤ +\restorehyphenchar/ + +Lorsque la largeur de la composition est faible, interdire les coupures peut conduire \TeX{} à composer des lignes trop ou pas assez remplies. La macro de \LaTeX{} \idx\sloppy relâche certains paramètres de composition de \TeX{} de telle sorte que les débordements dans les marges sont presque toujours évités, au prix d'espaces inter-mots parfois très (trop !) larges. C'est pourquoi elle ne doit être utilisée qu'en ultime recours. + +\showcode[\hbadness=10000 ]/\def\longtext{Voici une phrase écrite avec des mots insignifiants mais terriblement, + épouvantablement, horriblement et indéniablement longs.} + +\edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}%¤\idx*\hyphenchar\idx*\font¤ +\vrule\vbox{\hsize=5cm \hyphenchar\font=-1 \sloppy \longtext}\vrule¤\idx\hsize\idx*\hyphenchar\idx*\font\idx*\sloppy¤ +\restorehyphenchar/ + +Les utilisateurs de \LaTeX{}, à qui s'adresse le code ci-dessous, doivent donc savoir que lorsqu'ils passent en \idx{fonte} à chasse fixe (avec la macro \idx\ttfamily ou \idx\texttt), le code du \idx{caractère de coupure} vaut $-1$. Les coupures de mots sont dès lors interdites. + +\showcode/a) \texttt{\number\hyphenchar\font}\qquad¤\idx*\texttt\idx*\number\idx*\hyphenchar\idx*\font¤ +b) {\ttfamily\number\hyphenchar\font}¤\idx*\ttfamily¤/ + +\subsubsection{Dimensions de fonte}\label{espace.dimensions.fonte}\idx*[|(]{dimensions de fonte} +\begin{regle} +Pour composer du texte, chaque \idx{fonte} dispose de sept dimensions propres contenues dans des registres de dimension spéciaux accessibles via la primitive \idx\fontdimen. Chacun de ces registres est accessible par + +\centrecode-\fontdimen<entier><fonte>- + +où \verb|<entier>| est le numéro de la dimension (compris entre 1 et 7) et dont la signification est la suivante : + +\begin{enumerate}[label=\protect\no\arabic{*}] + \item pente par point (dimension utilisée pour placer les accents); + \item espace inter-mot (dimension naturelle de l'espace); + \item étirement inter-mot; + \item compression inter-mot; + \item \texttt x-height (valeur de \texttt{1ex}); + \item cadrat (valeur de \texttt{1em}); + \item espace supplémentaire (espace ajouté en fin des phrases). +\end{enumerate} +\end{regle} + +Les plus intéressants ici sont les \nos2, 3 et 4 qui permettent de régler la façon dont un espace se comporte lors de la composition. Pour les fontes à chasse fixe, les dimensions \nos3 et 4 sont nulles et la dimension \no2 est égale à largeur de tous les autres caractères. + +\showcode/\leavevmode¤\idx*\leavevmode¤ +\vbox{%¤\idx*\vbox¤ + \hsize=.4\hsize¤\idx\hsize¤ + \Souligne{Police à chasse variable}%¤§*\Souligne¤ + \vskip5pt ¤\idx*\vskip¤ + espace inter-mot = \the\fontdimen2\font\par¤\idx*\the\idx*\fontdimen\idx*\font¤ + étrirement inter-mot = \the\fontdimen3\font\par + compression inter-mot = \the\fontdimen4\font¤\idx*\the\idx*\fontdimen\idx*\font¤ +}\hfill¤\idx*\hfill¤ +\vbox{%¤\idx*\vbox¤ + \tt¤\idx*\tt¤ + \hsize=.4\hsize¤\idx*\hsize¤ + \Souligne{Police à chasse fixe}%¤§*\Souligne¤ + \vskip5pt ¤\idx*\vskip¤ + espace inter-mot = \the\fontdimen2\font\par + étrirement inter-mot = \the\fontdimen3\font\par + compression inter-mot = \the\fontdimen4\font¤\idx*\the\idx*\fontdimen\idx*\font¤ +}/\idx*[|)]{dimensions de fonte} + +\subsubsection{Coupures de mots} +Si l'on travaille avec \LaTeX, la première idée est de réactiver les coupures de mots pour composer un texte en fonte à chasse fixe. + +\showcode[\hfuzz=\maxdimen]/\def\ttwide{0.7}\def\ttindent{5}% +\hfill\vrule¤\idx*\vrule¤ +\vbox{% + \ttfamily¤\idx*\ttfamily¤ + \hyphenchar\font=`\- % change le caractère de coupure de la fonte en cours¤\idx*\hyphenchar\idx*\font¤ + \setbox0\hbox{0}\parindent=\ttindent\wd0 ¤\idx*\setbox\idx*\hbox\idx*\parindent\idx*\wd¤ + \hsize=\ttwide\hsize % compose sur 70% de la largeur¤\idx*\hsize¤ + Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche + et droite du texte ont été tracées, on constate que les caractères sont + exactement les uns au-dessous des autres d'une ligne à l'autre. Des débordements + dans la marge deviennent inévitables même si les coupures des mots sont + à nouveau rendues possibles.% +}% +\vrule\hfill\null¤\idx*\vrule\idx*\hfill\idx*\null¤/ + +Peut-on effectuer les coupures, mais ne pas insérer de caractère de coupure ? La réponse est non, mais il est parfois possible, selon la \idx{fonte} utilisée, de trouver un caractère de dimension horizontale nulle et ne se traduisant par aucun dessin. On peut le chercher en construisant une macro \verb|\printallchars| moyennant une boucle §\for : + +\showcode/\frboxsep=0.5pt ¤§*\frboxsep¤ +\def\printallchars{% + \leavevmode¤\idx*\leavevmode¤ + \for\xx=0 to255\do{%¤§*\for¤ + \vbox{% empiler verticalement¤\idx*\vbox¤ + \offinterlineskip% en désactivant le ressort d'interligne¤\idx*\offinterlineskip¤ + \setbox0\hbox{\frbox{\char\xx}}%¤\idx*\setbox\idx*\hbox §*\frbox\idx*\char¤ + \copy0 % la boite contenant le caractère encadré¤\idx*\copy¤ + \kern1pt% saute 1pt vertical¤\idx*\kern¤ + \hbox to\wd0{\hss$\scriptscriptstyle\xx$\hss}% le numéro¤\idx*\hbox\idx*\wd\idx*\hss\idx*\scriptscriptstyle¤ + }\hskip0.5em plus1pt minus1pt % saute 0.5em horizontalement¤\idx*\hskip¤ + }% +} +\tt¤\idx*\tt¤ +Nom de la fonte = \fontname\font\par¤\idx*\fontname\idx*\font¤ +\printallchars/ + +Par chance, il semblerait que dans la \idx{fonte} à chasse fixe utilisée dans ce livre (qui est « \texttt{LinLibertineMTC} »), le caractère \no23 de cette \idx{fonte} n'ait aucune dimension horizontale. Vérifions-le : + +\showcode/\setbox0=\hbox{\tt\char23}¤\idx*\setbox\idx*\hbox\idx*\tt\idx*\char¤ +Largeur = \the\wd0 \qquad Hauteur = \the\ht0 \qquad Profondeur = \the\dp0 ¤\idx*\the\idx*\wd\idx*\ht\idx*\dp¤/ + +Il est utile de signaler que \eTeX{} offre des primitives bien pratiques pour mesurer les dimensions d'un caractère dans une \idx{fonte} donnée : \idx\fontcharht, \idx\fontcharwd, \idx\fontchardp et \idx\fontcharic, suivies d'une spécification de \idx{fonte} et d'un code de caractères donnent des dimensions égales à la hauteur, largeur, profondeur et correction d'italique du caractère concerné. La dimension \verb|0pt| est renvoyée si le caractère n'existe pas. Le code précédent peut donc s'écrire : + +\showcode/{\tt\xdef\nfont{\the\font}}¤\idx*\the\idx*\font\idx*\tt\idx*\xdef¤ +Largeur = \the\fontcharwd\nfont23 \qquad Hauteur = \the\fontcharht\nfont23 ¤\idx*\fontcharwd\idx*\fontcharht\idx*\fontchardp¤ +\qquad Profondeur = \the\fontchardp\nfont23 / + +Concernant ce caractère \no23, il importe peu que la hauteur ne soit pas nulle puisque seule la largeur nulle revêt une importance ici. Elle signifie que si l'on choisit ce caractère comme caractère de coupure avec + +\centrecode-\hyphenchar\font=23 - + +\noindent tout se passera comme si aucun caractère de coupure n'était sélectionné. Cette man\oe uvre suppose bien entendu que la \idx{fonte} s'y prête et dispose d'un tel caractère. +\grandsaut + +Nous avons progressé, mais pas résolu le problème que pose la composition sur plusieurs lignes de texte en fonte à chasse fixe : les débordements sont toujours bien là, même s'ils sont moins importants. + +\subsection{Justifier et couper} +Nous allons maintenant supprimer la première contrainte vue à la page~\pageref{contraintes.tt} et nos contraintes seront donc : + +\begin{enumerate}[start=2] + \item composition justifiée, c'est-à-dire que la distance entre le bord gauche du premier caractère et le bord droit du dernier est toujours la même; + \item coupures des mots respectées ou carrément interdites. +\end{enumerate} + +\noindent Respecter ces contraintes revient à « casser » l'alignement vertical des caractères entre les lignes et en tirer parti pour supprimer --~ou au moins réduire au minimum~-- les débordements dans la marge. La méthode consiste à donner à l'espace le caractère étirable qu'il a dans les autres fontes, c'est-à-dire le rendre compressible et extensible dans des limites raisonnables à fixer, bien entendu. Pour cela, il faut rendre non nuls les \idx\fontdimen \nos3 et~4 de la \idx{fonte} à chasse fixe. Ici, nous allons donner une composante extensible égale à 30\% de l'espace inter-mot et une composante compressible égale à 20\% de cet espace. Un « environnement » sera créé pour l'occasion et s'étendra entre la macro §\ttstart et \verb|\ttstop|. + +Nous devons prendre nos précautions pour que les modifications des paramètres de la \idx{fonte} ne restent pas globales\idx*{assignation!toujours globale}\footnote{Les assignations \texttt{\char`\\fontdimen} et\texttt{\char`\\hyphenchar} sont toujours globales, lire le \TeX book page~322. }. Pour cela, la macro \verb|\restaurefontsettings| sera chargée de sauvegarder les paramètres de \idx{fonte} avant les modifications. Elle sera appelée par \verb|\ttstop|, juste avant la fermeture du groupe. + +\showcode/\newmacro\ttstart[5]{%¤§*\ttstart§*\newmacro¤ + \begingroup + \tt¤\idx*\tt¤ + \edef\restorefontsettings{% stocke les paramètres de fonte + \hyphenchar\font=\the\hyphenchar\font\relax% le \hyphenchar¤\idx*\the\idx*\hyphenchar\idx*\font¤ + \fontdimen2\font=\the\fontdimen2\font\relax% et les paramètres d'espacement¤\idx*\fontdimen¤ + \fontdimen3\font=\the\fontdimen3\font\relax + \fontdimen4\font=\the\fontdimen4\font\relax¤\idx*\the\idx*\fontdimen¤ + }% + \fontdimen3\font=0.30\fontdimen2\font% composante + = 30% de la dimension naturelle¤\idx*\fontdimen\idx*\font¤ + \fontdimen4\font=0.20\fontdimen2\font% composante - = 20% de la dimension naturelle + \hyphenchar\font=`\- % on autorise la coupure des mots (au cas où on utilise latex)¤\idx*\hyphenchar¤ + \setbox0\hbox{0}% largeur d'un caractère¤\idx*\setbox\idx*\hbox¤ + \parindent=#1\wd0 % indentation (en nombre de caractères)¤\idx*\parindent\idx*\wd¤ + \ignorespaces¤\idx*\ignorespaces¤ +} +\def\ttstop{% + \restorefontsettings% restaure les paramètres de fonte + \endgroup% et ferme le groupe +} +\hfill\vrule¤\idx*\hfill\idx*\vrule¤ +\def\ttwide{0.70}% +\vbox{% + \hsize=\ttwide\hsize % compose sur 70% de la largeur¤\idx*\hsize §*\ttstart¤ + \ttstart[5] + Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche + et droite du texte ont été tracées, on constate que les caractères ne sont pas + exactement les uns au-dessous des autres entre les lignes du paragraphe. + Puisque les espaces sont redevenus étirables, les débordements dans la marge + (même s'ils restent possibles), sont bien plus rares et ce d'autant plus que + le nombre d'espaces dans une ligne est grand.% + \ttstop +}% +\vrule\hfill\null¤\idx*\vrule\idx*\hfill\idx*\null¤/ + +Le but est atteint, aucun débordement dans la marge n'a lieu, mais comme attendu, on perd l'alignement vertical des lettres entre les lignes. Remarquons que les valeurs 30\% et 20\% devront être augmentées si le nombre de caractères par ligne diminue et réduites dans le cas contraire. +\grandsaut + +Une autre alternative aux \idx\fontdimen existe pour modifier l'espace inter-mot. Il s'agit du ressort-primitive \idx\spaceskip qui, s'il n'est pas nul, écrase les spécifications contenues dans \idx\fontdimen \nos2, 3 et~4. L'avantage est que sa modification tient compte des groupes et y reste locale, contrairement aux modifications de \idx\fontdimen. Ainsi, les lignes + +\centrecode-\fontdimen3\font=0.30\fontdimen2\font +\fontdimen4\font=0.20\fontdimen2\font-\idx*\font + +\noindent peuvent être remplacées par celles-ci\idx*\font + +\centrecode-\spaceskip=\fontdimen2\font ¤\idx*\spaceskip¤ + plus0.3\fontdimen2\font + minus0.2\fontdimen2\font- + +\subsection{Justifier et aligner} +Le but ici est de supprimer la 3\ieme{} contrainte de la page~\pageref{contraintes.tt} ce qui revient à se fixer les contraintes suivantes : + +\begin{enumerate} + \item caractères parfaitement les uns au-dessous des autres d'une ligne à l'autre; + \item composition justifiée, c'est-à-dire que la distance entre le bord gauche du premier caractère et le bord droit du dernier est toujours la même. +\end{enumerate} + +\noindent Ces deux contraintes impliquent que ces compromis doivent être concédés: + +\begin{itemize} + \item une espace inter-lettre de dimension invariable doit être insérée entre tous les caractères d'une même ligne; + \item les coupures peuvent se faire n'importe où, même entre deux caractères où elles n'auraient pas eu lieu si elles avaient été faites par l'algorithme de coupure\footnote{Cet algorithme est décrit à l'annexe H du \TeX book.} de \TeX. +\end{itemize} + +Puisqu'une espace inter-lettre est insérée entre chaque caractère, nous allons prendre le parti de laisser à l'utilisateur la possibilité de choisir cette espace. Pour ce faire, nous allons écrire une macro §\breaktt qui va insérer un \emph{faible} ressort fixe entre chaque lettre (rappelons-nous qu'un ressort est susceptible d'être un point de coupure). La syntaxe de cette macro sera : + +\centrecode|\breaktt[<dimension inter-lettre>][<largeur de composition>]{<texte>}| + +\noindent Les deux arguments optionnels permettent de spécifier la dimension de l'espace inter-lettre et la largeur de composition. La dimension inter-lettre sera insérée \emph{après} chaque caractère, sauf le dernier de chaque ligne. + +Si l'on veut que la largeur du texte soit égale à la largeur de composition passée en argument, la dimension inter-lettre doit être choisie avec soin. Elle dépend mathématiquement de la largeur des caractères et de la largeur de composition. Si l'utilisateur demande une dimension inter-lettre sans précaution, la largeur d'une ligne sera inférieure à la largeur de composition. Voici le schéma représentant la situation d'une ligne : + +\begin{centrage} +\begin{tikzpicture} +\def\wdchar{0.75}\def\wdskip{.15}\def\htchar{1} +\def\extrachar{0.95}\def\numchar{10} +\def\printnextchar{% + \let\currentabs\nextabs + \pgfmathparse{\currentabs+\wdchar+\wdskip}\let\nextabs\pgfmathresult + \filldraw[draw=black,fill=gray!66,line width=.5pt](\currentabs,0)rectangle(\currentabs+\wdskip,\htchar); + \draw[line width=.5pt](\currentabs+\wdskip,0) rectangle(\nextabs,\htchar); +} +\draw[line width=1pt](0,-\htchar/2)--(0,\htchar*1.5); +\node[inner sep=0pt,outer sep=0pt,minimum size=0pt](debut)at(0,\htchar*1.4){}; +\draw[line width=.5pt](0,0) rectangle ++(\wdchar,\htchar); +\let\nextabs\wdchar +\for\ii=1 to \numchar\do{\printnextchar}% +\draw[line width=1pt](\nextabs+\wdchar*\extrachar,-\htchar/2)--(\nextabs+\wdchar*\extrachar,\htchar*1.5); +\node[inner sep=0pt,outer sep=0pt,minimum size=0pt](fin)at(\nextabs+\wdchar*\extrachar,\htchar*1.4){}; +\draw[stealth-stealth](debut)--(fin) node[midway,above,inner sep=0pt,outer sep=2pt]{\footnotesize Largeur de composition}; +\end{tikzpicture} +\end{centrage} + +Les boites englobantes des caractères sont représentées par des rectangles vides et les espaces inter-lettres demandées par l'utilisateur par des zones grisées. + +Pour éviter que l'utilisateur ne calcule lui-même la valeur du premier argument, il devient évident qu'il doit être automatiquement ajusté de telle sorte que l'espace disponible à droite du dernier caractère soit également réparti dans chaque zone grisée. Par conséquent, la \verb|<dimension inter-lettre>| donnée en argument \no1 ne sera pas \emph{exactement} celle insérée entre chaque caractère, mais devra être légèrement augmentée. +\grandsaut + +Cherchons maintenant à formaliser le tout avec les notations suivantes : + +\begin{itemize} + \item $L$ est la largeur de composition (argument optionnel \verb|#2|); + \item $l$ est la largeur d'un caractère; + \item $\Delta$ la dimension inter-lettre demandée (argument optionnel \verb|#1|). +\end{itemize} + +Compte tenu du fait que le dernier caractère de la ligne n'est pas suivi d'une espace inter-lettre, nous allons dans un premier temps ignorer ce caractère et soustraire $l$ à la largeur de composition $L$ pour obtenir la place disponible pour les paires \verb|<caractère>| $+$ \verb|<dimension inter-lettre>|. Pour calculer le nombre de ces paires (de largeur $l+\Delta$), il faut prendre la partie entière du quotient + +\[ +\dfrac{L-l}{l+\Delta} +\] + +En ajoutant le dernier caractère de la ligne que nous avions ignoré, nous obtenons le nombre maximal de caractères $n$ qu'il est possible de loger sur une ligne. Cela donne (E représente l'opérateur «partie entière») : + +\[n=\text{E}\left(\dfrac{L-l}{l+\Delta}\right)+1\] + +Il est immédiat que le nombre de dimensions inter-lettre est égal à $n-1$ et donc, pour justifier le texte, il faut répartir également l'espace non occupé par les caractères entre chaque caractère. Par conséquent, il faut insérer après chaque caractère, non pas l'espace inter-lettre $\Delta$, mais + +\[\Delta'=\dfrac{L-nl}{n-1}\] + +Voici ce que devient le schéma précédent : + +\begin{centrage} +\begin{tikzpicture} +\def\wdchar{0.75}\def\wdskip{.15}\def\htchar{1} +\def\extrachar{.95}\def\numchar{10} +\def\printnextchar{% + \pgfmathparse{\nextabs+\wdchar*\extrachar/10}\let\currentabs\pgfmathresult + \pgfmathparse{\currentabs+\wdchar+\wdskip}\let\nextabs\pgfmathresult + \filldraw[draw=black,fill=gray!66,line width=.5pt](\currentabs,0)rectangle(\currentabs+\wdskip,\htchar); + \draw[line width=.5pt](\currentabs+\wdskip,0) rectangle(\nextabs,\htchar); +} +\draw[line width=1pt](0,-\htchar/2)--(0,\htchar*1.5); +\node[inner sep=0pt,outer sep=0pt,minimum size=0pt](debut)at(0,\htchar*1.4){}; +\draw[line width=.5pt](0,0) rectangle ++(\wdchar,\htchar); +\let\nextabs\wdchar +\for\ii=1 to \numchar\do{\printnextchar}% +\draw[line width=1pt](\nextabs,-\htchar/2)--(\nextabs,\htchar*1.5); +\node[inner sep=0pt,outer sep=0pt,minimum size=0pt](fin)at(\nextabs,\htchar*1.4){}; +\draw[stealth-stealth](debut)--(fin) node[midway,above,inner sep=0pt,outer sep=2pt]{\footnotesize Largeur de composition}; +\end{tikzpicture} +\end{centrage} + +\noindent L'espace grisé $\Delta$ doit être augmenté d'une petite quantité pour que la justification se fasse. Comme l'argument optionnel \verb|#1| n'est \emph{pas} exactement l'espace qui sera inséré entre chaque caractère, il doit être compris comme une dimension \emph{minimale} susceptible d'être un peu allongée pour justifier le texte à la dimension spécifiée. +\grandsaut + +Entrons dans le vif du sujet et définissons les variables principales dont nous avons besoin : + +\begin{itemize} + \item un compteur \verb|\brktt@cnt| qui va compter, pour chaque ligne, combien de caractères ont déjà été placés; + \item une macro \verb|\maxchar@num| qui contiendra $n$, le nombre de caractères par ligne; + \item un registre de dimension inter-lettre \verb|\brktt@interletter| pour recevoir $\Delta'$; + \item une macro \verb|\tt@remaintext| dont le texte de remplacement contient ce qu'il reste du texte à composer; + \item une liste de \idx{ligature}s \verb|\liglist| contenant des suites de caractères susceptibles de former un caractère \emph{unique}, soit par \idx{ligature} (par exemple «\verb|<<|», «\verb|--|», etc.) soit parce que ces caractères sont codés sur plusieurs \idx{octet}s en \utf avec un moteur 8 bits\idx*{moteur!8 bits}. +\end{itemize} +\grandsaut + +Le processus va se décomposer en deux. Tout d'abord, la macro §\breaktt va ouvrir un groupe semi-simple qui sera fermé à la fin du processus et passera en mode vertical. Elle va également procéder aux initialisations et aux calculs préalables (nombre de caractères par ligne $n$, espace inter-lettre $\Delta'$) ainsi qu'au passage en \idx{fonte} à chasse fixe. Cette macro chapeau appellera la macro récursive \verb|\breaktt@i| qui imprimera, en mode vertical et dans une \idx\hbox une ligne entière. Ensuite, si le texte restant est vide, cela signe la fin du processus. Dans le cas contraire, la \idx\hbox doit être fermée et une nouvelle doit être ouverte pour accueillir la ligne suivante qui sera imprimée par l'appel récursif \verb|\breaktt@i|. + +Le mécanisme de fermeture/ouverture de \idx\hbox sera effectué par la macro \verb|\restart@hbox| dont l'argument est le texte restant à composer. Après avoir fermé la \idx\hbox précédente avec \idx\egroup et avoir ouvert une nouvelle \idx\hbox avec \idx\bgroup, cette macro initialise le compteur de caractères \verb|\brktt@cnt| à 0 et, à l'aide de §\removefirstspaces, purge son argument des éventuels espaces qui se trouvent en première position. Le résultat de cette opération est stocké dans \verb|\tt@remaintext| : ceci évite que la prochaine ligne ne commence par une espace. + +Le n\oe ud du processus sera une macro auxiliaire \verb|\print@nchar{<n>}| qui est chargée d'afficher \verb|<n>| caractères pris dans \verb|\tt@remaintext|. Une boucle de type §\for sera mise en \oe uvre à cet effet. Au cours de la boucle : + +\begin{itemize} + \item si \verb|\tt@remaintext| est vide, une sortie prématurée de boucle doit être faite et la macro doit rendre la main; + \item si \verb|\tt@remaintext| commence par un des motifs figurant dans la liste des \idx{ligature}s, le caractère courant à afficher sera pris égal à ce motif et dans le cas contraire au premier caractère de \verb|\tt@remaintext|; + \item l'espace inter-lettre ne doit pas être inséré après le dernier caractère de la ligne. Autrement dit, il ne faut insérer cette espace que si $\verb|\brktt@cnt|<\verb|\maxchar@num|$; + \item si $\verb|\brktt@cnt|>\verb|\maxchar@num|$, une sortie prématurée de boucle doit être faite et la macro doit rendre la main. +\end{itemize} + +Le booléen \verb|\ifline@start|, inutile ici, nous servira dans d'autres versions de la macro §\breaktt. + +\showcode|\catcode`\@11 +\newcount\brktt@cnt¤\idx*\newcount¤ +\newdimen\brktt@interletter¤\idx*\newdimen¤ +\newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché¤\idx*\newif¤ +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré¤\idx*\newif¤ + +\def\insert@blankchar{%¤§*\ifstarred¤ + \ifstarred\insert@blankchar@ii\insert@blankchar@i +} + +\def\insert@blankchar@i#1{% insère une espace de largeur #1 caractères complets + \ifnum\numexpr#1\relax>0 + \kern\numexpr#1\relax\dimexpr\ttchar@width+\brktt@interletter\relax + \fi +} + +\def\insert@blankchar@ii#1{% insère #1-1 caractères complets + 1 largeur de caractère + \ifnum\numexpr#1\relax>0 + \insert@blankchar@i{#1-1}\kern\ttchar@width¤\idx*\kern¤ + \fi +} + +\def\restart@hbox#1{% + \egroup% feerme la \hbox précédente + \hbox\bgroup% ouvre la suivante¤\idx*\hbox\idx*\bgroup §*\expsecond¤ + \expsecond{\def\tt@remaintext} + {\romannumeral\removefirstspaces@i{#1}}% initialiser le code à composer¤\idx*\romannumeral§*\removefirstspaces¤ + \let\previous@char\space% initialise le caractère précédent¤\idx*\space¤ + \line@starttrue% aucun caractère n'a encore été imprimé + \brktt@cnt=0\relax% remettre le compteur à 0 +} + +\newmacro\breaktt[0.3em][\hsize]1{%¤§*\breaktt§*\newmacro¤ +% arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte +% #3 = texte à espacer + \begingroup% ouvrir un groupe et le fermer à la toute fin + \par% commencer un nouveau paragraphe -> passage en mode vertical¤\idx*{mode!vertical}¤ + \parindent=0pt% empêche l'indentation¤\idx*\parindent¤ + \tt% passer en fonte à chasse fixe¤\idx*\tt¤ + \setbox0 = \hbox{M}% la boite 0 contient un caractère¤\idx*\setbox\idx*\hbox¤ + \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en fonte \tt¤\idx*\the\idx*\wd¤ + \edef\text@width{\the\dimexpr#2\relax}% largeur de composition¤\idx*\dimexpr¤ + % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) + \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère¤\idx*\numexpr\idx*\wd¤ + \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax¤\idx*\divide\idx*\wd¤ + % le nombre de caractère par ligne est égal à 1 de plus : + \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% + % calcul de la dimension inter-lettre + \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax + % stocke le texte après avoir enlevé les éventuels espaces extremes : + \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}%¤§*\expsecond§*\removetrailspaces¤ + \unless\ifx\tt@remaintext\empty% si le texte à composer n'est pas vide¤\tidx*{unless}\idx*\empty¤ + \hbox\bgroup% démarrer la boite horizontale contenant la première ligne¤\idx*\hbox\idx*\bgroup¤ + \insert@blankchar\ttindent% insérer une espace d'indentation + \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés + \line@starttrue% il s'agit du début d'une ligne + \expandafter\breaktt@i% aller à la macro récursive + \fi +} + +\def\breaktt@i{% + \print@nchar\maxchar@num% afficher \maxchar@num caractères¤\defline\aaa¤ + \ifx\tt@remaintext\empty% si texte restant est vide¤\idx*\empty¤ + \egroup% fermer la hbox¤\idx*\egroup¤ + \par% aller à la ligne + \endgroup% fermer le groupe ouvert au début + \else + \unless\ifnum\brktt@cnt<\maxchar@num\relax% si la ligne est remplie¤\tidx*{unless}¤ + \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une¤§*\exparg¤ + \fi + \expandafter\breaktt@i% enfin, continuer le processus + \fi +} + +\def\print@nchar#1{% affiche #1 caractères pris dans \tt@remaintext + \for\xxx= 1 to #1 \do 1{%¤§*\for¤ + \ifx\tt@remaintext\empty% si le code restant à composer est vide¤\idx*\empty¤ + \exitfor\xxx%sortir de la boucle prématurément¤§*\exitfor¤ + \else + \@indivifoundfalse% sinon, a priori, les motifs de ligature ne sont pas trouvés + % pour chaque \indivi@tmp dans la liste de ligatures + \expsecond{\doforeach\indivi@tmp\in}{\liglist}%¤§*\expsecond§*\doforeach¤ + {% si le code commence par la ligature courante¤\idx{ligature}¤ + \exptwoargs\ifstartwith\tt@remaintext\indivi@tmp¤§*\exptwoargs§*\ifstartwith¤ + {\let\previous@char\indivi@tmp% prendre le motif pour caractère courant, + \expsecond{\rightofsc\tt@remaintext}{\indivi@tmp}% l'enlever du texte restant¤§*\expsecond¤ + \@indivifoundtrue% marquer qu'un motif a été trouvé + \doforeachexit% et sortir prématurément de la boucle¤§*\doforeachexit¤ + }% + {}% si le code ne commence pas le motif courant -> ne rien faire + }% + \unless\if@indivifound% si aucun motif trouvé,¤\tidx*{unless}¤ + \grab@first\tt@remaintext\previous@char% lire le premier caractère + \fi + \advance\brktt@cnt by 1 % incrémenter le compteur de caractères¤\idx*\advance¤ + \hbox to\ttchar@width{\hss\previous@char\hss}% afficher le caractère lu¤\idx*\hbox\idx*\hss¤ + \line@startfalse% nous ne sommes plus au début d'une ligne + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie + \kern\brktt@interletter% insérer le ressort inter-lettre¤\idx*\kern¤ + \else + \exitfor\xxx% sinon, sortir de la boucle prématurément¤§*\exitfor¤ + \fi + \fi + }% +} +\catcode`\@12 + +\def\liglist{<<,>>}% liste des motifs de ligature¤\idx{ligature}¤ + % ajouter à, é, etc si codage UTF8 + moteur 8 bits +\def\ttindent{3}% valeur de l'indentation' +\vrule\vbox{\breaktt[4pt][.7\hsize]{%¤\idx*\vrule\idx*\vbox\idx*\hsize§*\breaktt¤ +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous des autres d'une ligne à l'autre. Plus aucun +débordement n'a lieu, car une espace correctement calculée est insérée entre +chaque caractère. Les mots, en revanche, sont toujours coupés <<sauvagement>>.% +}% +}\vrule\smallbreak¤\idx*\vrule¤ + +\vrule\vbox{\breaktt[1pt][6cm]{%¤\idx*\vrule\idx*\vbox\idx*\hsize§*\breaktt¤ +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous des autres d'une ligne à l'autre. Plus aucun +débordement n'a lieu, car une espace correctement calculée est insérée entre +chaque caractère. Les mots, en revanche, sont toujours coupés <<sauvagement>>.% +}% +}\vrule¤\idx*\vrule¤| + +\subsection{Aligner et ne pas couper} +Pour éviter que les mots ne soient coupés n'importe comment, la solution radicale consiste à interdire toute coupure. L'inévitable conséquence est que la contrainte \no2 de la page~\pageref{contraintes.tt} (composition justifiée) sera impossible à satisfaire. + +Dans l'algorithme précédent, la macro \verb|\print@nchar| de la ligne \no\aaa{} effectuait la composition aveugle de \verb|\maxchar@num| caractères à chaque ligne. Cette manière de faire est trop grossière et doit être affinée : au fur et à mesure de l'avancement, nous allons calculer la largeur du prochain mot \verb|\next@len| par l'intermédiaire de la macro \verb|\len@tonextcut|. Il faudra ensuite tester si ce mot loge en entier dans ce qu'il reste de la ligne en cours de composition. Dans l'affirmative, nous composerons ce mot avec la macro \verb|\print@nchar| et sinon, il faudra finir la ligne en cours pour en commencer une nouvelle. + +Le raffinement supplémentaire va être de tenir compte des tirets «\verb|-|» éventuellement contenus dans les mots et rendre ces tirets comme possibles caractères de \idx{fin de ligne}. Nous allons donc considérer qu'un mot est ce qui se trouve avant le plus proche des caractères «\verb*| |» ou «\verb|-|». Il faudra tenir compte du fait que le tiret \emph{ne disparait pas} en \idx{fin de ligne} contrairement à l'espace. Pour ce faire, une macro \verb|\extra@char| contenant 0 ou 1 nous permettra de savoir par la suite si, en plus du mot courant, il faut loger un caractère de plus (le tiret) sur la ligne ou pas. + +Nous allons baptiser §\breakttA cette variante qui compose du texte sans couper les mots. Les macros \verb|\insert@blankchar|, \verb|\restart@hbox| et \verb|\print@nchar| de la précédente variante §\breaktt seront réutilisées telles quelles. Elles ne sont donc pas réécrites dans le code ci-dessous. La macro \verb|\breakttA@i| ne fait plus appel à une boucle §\for, mais contient des appels récursifs jusqu'à ce que \verb|\tt@remaintext| ne contienne plus de texte à composer. + +\showcode|\catcode`\@11 +\newcount\brktt@cnt¤\idx*\newcount¤ +\newdimen\brktt@interletter¤\idx*\newdimen¤ +\newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché¤\idx*\newif¤ +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré¤\idx*\newif¤ + +\newmacro\breakttA[0.3em][\hsize]1{%¤§*\breakttA§*\newmacro¤ +% arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte +% #3 = texte à espacer + \begingroup% ouvrir un groupe et le fermer à la toute fin + \par% commencer un nouveau paragraphe -> passage en mode vertical¤\idx*{mode!vertical}¤ + \parindent=0pt% empêche l'indentation¤\idx*\parindent¤ + \tt% passer en fonte à chasse fixe¤\idx*\tt¤ + \setbox0 = \hbox{M}% la boite 0 contient un caractère¤\idx*\setbox\idx*\hbox¤ + \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en fonte \tt¤\idx*\the\idx*\wd¤ + \edef\text@width{\the\dimexpr#2\relax}% largeur de composition¤\idx*\dimexpr¤ + % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) + \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère¤\idx*\numexpr¤ + \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax¤\idx*\divide\idx*\wd¤ + % le nombre de caractères par ligne est égal à 1 de plus : + \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% + % calcul de la dimension inter-lettre + \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax¤\idx*\dimexpr¤ + % stocke le texte après avoir enlevé les éventuels espaces extremes : + \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}%¤§*\expsecond§*\removetrailspaces¤ + \unless\ifx\tt@remaintext\empty% si le texte à composer n'est pas vide¤\tidx*{unless}\idx*\empty¤ + \hbox\bgroup% démarrer la boite horizontale contenant la première ligne¤\idx*\hbox\idx*\bgroup¤ + \insert@blankchar\ttindent% insérer une espace d'indentation + \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés + \line@starttrue% il s'agit du début d'une ligne + \expandafter\breakttA@i% aller à la macro récursive + \fi +} + +\def\breakttA@i{% + \edef\remaining@chars{% calculer le nombre de caractères restant à placer sur la ligne + \numexpr\maxchar@num-\brktt@cnt\relax}%¤\idx*\numexpr¤ + \len@tonextword% \next@len contient le nombre de caractères du prochain mot + % si le mot + l'eventuel "-" qui le suit ne peut pas loger sur la ligne en cours + \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax¤\defline\aaa¤ + \ifline@start% et si c'est le début d'une ligne + % avertir l'utilisateur + \message{Largeur de composition trop faible pour + \unexpanded\expandafter{\next@word}^^J}%¤\idx*\message\idx*\unexpanded\verbidx[ (fin de ligne)]{^^J}¤ + % et composer tout de même "à la sauvage" jusqu'à la fin de la ligne + \exparg\print@nchar{\number\numexpr\maxchar@num-\brktt@cnt}%¤§*\exparg\idx*\numexpr¤ + \else% si la ligne en cours n'est pas au début + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne d'espaces + \fi + \exparg\restart@hbox{\tt@remaintext}% commencer une nouvelle ligne¤§*\exparg¤ + \expandafter\breakttA@i% et poursuivre le processus + % s'il y a assez de place pour accueillir ce qui est avant la prochaine coupure + \else + \print@nchar\next@len% afficher le mot¤\defline\bbb¤ + \ifx\tt@remaintext\empty% si texte restant est vide¤\idx*\empty¤ + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \egroup% fermer la hbox en cours¤\idx*\egroup¤ + \par% aller à la ligne + \endgroup% fermer le groupe ouvert au début. Fin du processus + \else% si le texte restant n'est pas vide + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas remplie¤\defline\ccc¤ + \print@nchar{1}% afficher le caractère qui suit le mot (" " ou "-")¤\defline\ddd¤ + \else% si la ligne est remplie + \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une¤§*\exparg¤ + \fi + \expandafter\expandafter\expandafter\breakttA@i% enfin, continuer le processus + \fi + \fi +} + +\def\leftofsc#1#2{% dans la macro #1, garde ce qui est à gauche de #2 + \def\leftofsc@i##1#2##2\@nil{\def#1{##1}}% + \expandafter\leftofsc@i#1#2\@nil +} + +\def\len@tonextword{% stocke dans \next@len le nombre de caractères avant + % le prochain point de coupure dans \tt@remaintext + \let\next@word\tt@remaintext% copie \tt@remaintext dans la macro temporaire \next@word + \leftofsc\next@word{ }% ne prend que ce qui est avant le prochain espace¤§*\leftofsc¤ + \exparg\ifin\next@word{-}% si le mot contient un tiret¤§*\exparg§*\ifin¤ + {\leftofsc\next@word{-}% prendre ce qui est à gauche de ce tiret¤§*\leftofsc¤ + \def\extra@char{1}% il y a un caractère de plus à loger après le mot + }% sinon, le caractère après le mot est un espace + {\def\extra@char{0}% qu'il ne faut pas compter + }% + \setbox0=\hbox{\next@word}% enfermer le mot dans une boite¤\idx*\setbox\idx*\hbox¤ + % et en calculer le nombre de caractères = dim(boite)/dim(caractère) + \edef\next@len{\number\numexpr\dimexpr\wd0 \relax/\dimexpr\ttchar@width\relax\relax}%¤\idx*\wd¤ +} +\catcode`\@12 + +\def\liglist{<<,>>}% liste des motifs de ligature (mettre à, é, etc si codage UTF8)¤\idx{ligature}¤ +\def\ttindent{3} + +\vrule\vbox{\breakttA[3pt][8cm]{%¤§*\breakttA¤ +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<~sauvagement~>>. +}}\vrule\medbreak¤\idx*\medbreak\idx*\vrule¤ + +\leavevmode\vrule\vbox{\breakttA[1pt][5cm]{%¤\idx*\leavevmode§*\breakttA¤ +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<~sauvagement~>>. +}}\vrule\qquad\def\ttindent{0}% +\vrule\vbox{\breakttA[0.6pt][2cm]{mot-composé mot-clé passe-droit au-dessus +là-bas remonte-pente vingt-huit Notre-Dame-de-Lourdes Mont-Blanc +Saint-Jean-de-Luz}}\vrule¤§*\breakttA\idx*\vrule¤| + +\subsection{Aligner et couper} +Entrons maintenant dans la phase la plus difficile\ldots{} Nous allons essayer de composer du texte en fonte à chasse fixe tout en alignant les caractères les uns au-dessous des autres et en permettant que les coupures de mots se fassent. + +Voici dans les grandes lignes la démarche que nous allons suivre lorsque le prochain mot ne loge pas sur l'espace restant de la ligne en cours : + +\begin{enumerate} + \item pour le prochain mot du texte à composer (un « mot » étant ce qui se trouve avant le plus proche espace ou tiret), nous allons trouver toutes les coupures possibles; + \item nous allons mesurer les longueurs des syllabes ainsi obtenues et cumuler leurs longueurs de façon à construire la liste des nombres croissants exprimant les longueurs (en caractères) jusqu'aux coupures possibles du mot; + \item enfin, nous parcourons cette liste et effectuerons, si c'est possible, la coupure la plus longue possible tout en ne dépassant pas le nombre de caractères qu'il reste à composer sur la ligne en cours. Ceci fait, il nous restera à enfermer la ligne en cours dans une \idx\hbox et en commencer une nouvelle. +\end{enumerate} + +Comme plusieurs difficultés se dressent devant nous, nous allons procéder pas à pas. + +\subsubsection{Provoquer toutes les coupures}\label{provoquer.toutes.les.coupures} +Tout d'abord, comment forcer \TeX{} à effectuer toutes les coupures possibles pour un texte donné ? Pour l'obliger à procéder de la sorte, nous allons composer ce texte dans une boite verticale de largeur \emph{nulle}. Bien sûr, des débordements auront lieu, mais nous neutraliserons leur signalement en réglant \idx\hfuzz à \idx\maxdimen. + +Avant de commencer à composer du texte en largeur nulle, certains réglages de composition doivent être faits. Intéressons-nous à la façon dont \TeX{} fabrique un paragraphe\idx*{paragraphe!composition} et comment nous pouvons l'influencer. Il faut savoir que \emph{plusieurs} passes peuvent être faites par \TeX{} pour composer un paragraphe\footnote{Pour en savoir plus, lire le \TeX book pages~112--113.}. La première est tentée si l'entier \idx\pretolerance est positif et a lieu sans faire de coupures de mots. Ceci fait, si aucune ligne du paragraphe obtenu n'a de médiocrité\footnote{Lire le \TeX book page 144 notamment pour comprendre comment est calculée la médiocrité d'une ligne.} supérieure à l'entier \idx\pretolerance, le paragraphe ainsi obtenu sera retenu. Dans le cas contraire, une seconde passe, où les coupures de mots sont permises, est effectuée lors de laquelle, si c'est possible, \TeX{} s'efforce de fabriquer des lignes de telle sorte qu'aucune n'ait de médiocrité supérieure à l'entier \idx\tolerance. + +Comme nous cherchons à provoquer les coupures de mots, la première passe ne nous intéresse pas et nous règlerons donc \idx\pretolerance à $-1$. Par ailleurs, nous fixerons \idx\tolerance à $10000$ de telle sorte que toutes les lignes construites à la deuxième passe soient acceptables\footnote{Toute valeur supérieure à $10000$ est comprise comme étant égale à $10000$.} aux yeux de \TeX. + +Ensuite, il nous faut modifier localement les réglages de composition en vue du but que nous nous sommes fixé. Comme la boite verticale de longueur nulle que nous obtiendrons a vocation à être coupée en lignes avec \idx\vsplit, nous devons nous assurer qu'aucune \idx{pénalité} additionnelle entre les lignes n'interdit cette coupure. Voici donc les réglages à effectuer : + +\begin{itemize} + \item l'entier \idx\hyphenpenalty, qui caractérise la \idx{pénalité} d'une coupure de mots sera pris égal à $-10000$ et donc les coupures, extrêmement favorisées par cette pénalité négative, seront toujours effectuées. Par ailleurs, \idx\exhyphenpenalty est la pénalité d'une coupure sur un caractère explicite (le tiret). Notre algorithme ne donnera jamais à composer un mot contenant un tiret puisqu'un «mot» s'étend jusqu'au prochain espace ou tiret. Par conséquent, \idx\exhyphenpenalty, rendu égal à 0, ne jouera aucun rôle. + \item l'entier \idx\clubpenalty est ajouté à la pénalité de coupure de ligne pour la première ligne du paragraphe. Nous prendrons $0$ comme valeur de cet entier. L'entier \idx\widowpenalty agit de même pour la dernière ligne : il sera réglé à~0; + \item l'entier \idx\interlinepenalty, ajouté à la \idx{pénalité} d'une coupure de ligne, sera pris égal à $0$; + \item les entiers \idx\lefthyphenmin et \idx\righthyphenmin, qui caractérisent la longueur minimale du fragment laissé au début ou à la fin d'un mot coupé, seront réglés à leurs valeurs par défaut, soit \number\lefthyphenmin{} et \number\righthyphenmin{}; + \item nous réinitialiserons \idx\rightskip et \idx\leftskip à \verb|0pt| au cas où ces dimensions ne seraient pas nulles au moment de la composition. Le ressort \idx\parfillskip sera réinitialisé à «\verb|0pt plus 1fil|» qui est sa valeur par défaut. Nous viderons également \idx\everypar qui contient la liste de tokens exécutée au début de chaque paragraphe; + \item l'entier \idx\hyphenchar sera sauvegardé puis pris égal à \verb|`\-| et enfin restauré en fin de composition; + \item l'indentation sera désactivée avec \idx\noindent tout en provoquant le passage en mode horizontal\idx*{mode!horizontal}; + \item un ressort de longueur nulle sera inséré avant le texte à composer, car une coupure ne peut intervenir sur un mot si ce mot commence le paragraphe. Ce ressort tiendra lieu de remplissage factice et autorisera la coupure du premier mot; + \item la largeur de composition \idx\hsize sera rendue égale à \verb|0pt|. +\end{itemize} + +Forts de tous ces réglages, nous allons bâtir la macro §\zerocompose : + +\centrecode-\zerocompose[<code>]{<registre de boite>}{<texte>}- + +\noindent où \verb|<code>| est un code optionnel qui sera exécuté avant que la composition ne commence, \verb|<registre de boite>| est le numéro du registre de la boite verticale \idx\vbox recevant le résultat de la composition du \verb|<texte>|. + +\showcode/\newmacro\zerocompose[]2{%¤§*\zerocompose§*\newmacro¤ +% #1=code à exécuter avant la composition +% #2=registre de boite recevant le résultat +% #3= texte à composer en largeur 0pt + \setbox#2=\vbox{%¤\idx*\setbox\idx*\vbox¤ + #1% code a exécuter (changement de fonte par exemple) + \hfuzz=\maxdimen% annule les avertissements pour débordement horizontaux¤\idx*\hfuzz\idx*\maxdimen\idx*{message d'avertissement}¤ + \hbadness=10000 % annule les avertissements pour mauvaise boite horizontale¤\idx*\hbadness\idx*{message d'avertissement}¤ + \pretolerance=-1 % désactive la première passe (celle sans coupures)¤\idx*\pretolerance¤ + \tolerance=10000 % passe avec coupures acceptée¤\idx*\tolerance¤ + \hyphenpenalty=-10000 % favorise fortement les copures de mots¤\idx*\hyphenpenalty¤ + \lefthyphenmin=2 \righthyphenmin=3 % longueur mini des fragments de début et fin¤\idx*\lefthyphenmin\idx*\righthyphenmin¤ + \clubpenalty=0 % pas de pénalité supplémentaire après la première ligne¤\idx*\clubpenalty\idx{pénalité}¤ + \interlinepenalty=0 % pas de pénalité inter-ligne¤\idx*\interlinepenalty\idx{pénalité}¤ + \widowpenalty=0 % pas de pénalité supplémentaire avant la dernière ligne¤\idx*\widowpenalty\idx{pénalité}¤ + \exhyphenpenalty=0 % ne pas pénaliser une coupure explicite¤\idx*\exhyphenpenalty¤ + \leftskip=0pt \rightskip=0pt % désactive les éventuels ressorts latéraux¤\idx*\leftskip\idx*\rightskip¤ + \everypar={}% désactive l'éventuel \everypar¤\idx*\everypar¤ + \parfillskip=0pt plus1fil % règle le \parfillskip par défaut¤\idx*\parfillskip¤ + \hsize=0pt % largeur de composition = 0pt¤\idx*\hsize¤ + \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% + \hyphenchar\font=`\- % impose "-" comme caractère de coupure + \noindent % pas d'indentation + passage en mode horizontal¤\idx*\noindent\idx*{mode!horizontal}¤ + \hskip0pt \relax% premier noeud horizontal pour permettre la coupure de la suite¤\idx*\hskip¤ + #3\par% compose #3 + \restorehyphenchar% restaure le caractère de coupure + }% +} +\zerocompose{0}{Programmation}%¤\idx*\hyphenchar\idx*\font¤ +\leavevmode\box0 ¤\idx*\leavevmode\idx*\box¤ +\hskip 5cm ¤\idx*\hskip¤ +\zerocompose{0}{Composer en largeur nulle}%¤§*\zerocompose\idx*\hyphenchar\idx*\font¤ +\box0 ¤\idx*\box¤/ + +Malgré les apparences, les boites \no0 affichées dans l'exemple ci-dessus ont bel et bien une largeur \emph{nulle}. C'est \verb|\hskip 5cm| qui évite qu'elles ne se chevauchent. + +Nous allons appliquer cette méthode à des mots seuls et utiliser \idx\vsplit pour vider peu à peu (par le haut) la boite verticale résultante afin d'obtenir à chaque itération une boite horizontale contenant une ligne de la boite initiale. Le problème est que la boite récupérée sera de largeur nulle puisque la \idx\vbox initiale avait cette largeur. Dès lors, il sera impossible de la mesurer pour trouver le nombre de caractères qui la composent ! + +Nous allons donc faire appel à une primitive, non encore vue jusqu'alors qui va nous permettre de redonner à la boite contenant la première ligne sa largeur naturelle. + +\subsubsection{La primitive \texttt{\char`\\lastbox}}\idx*[|(]\lastbox + +\begin{regle} +La primitive \verb|\lastbox| a un comportement très particulier : + +\begin{itemize} + \item non seulement elle contient la dernière boite composée dans la liste en cours, mais utiliser \verb|\lastbox| \emph{retire} cette boite de la liste dans laquelle elle était placée; + \item si le dernier élément n'est pas une boite, \verb|\lastbox| est vide; + \item si l'on souhaite accéder à la dernière boite composée, \verb|\lastbox| s'utilise seule. Il est incorrect de la faire précéder de \idx\box. +\end{itemize} + +L'opération \verb|\lastbox| n'est pas permise dans le mode mathématique\idx*{mode!mathématique} ni dans le mode vertical\idx*{mode!vertical} \emph{principal} : elle ne peut donc pas servir à retirer une boite de la page courante en cours de composition. En revanche, elle peut très bien être utilisée dans le mode vertical interne\idx*{mode!vertical interne}. + +La boite \verb|\lastbox| hérite de la nature --~horizontale ou verticale~-- de la dernière boite composée. + +Si un registre de boite est vidé par \verb|\lastbox|, le registre ne devient pas vide, mais contient une boite vide (le test \tidx{ifvoid} est donc inadapté pour tester si un registre est vidé par \verb|\lastbox|). +\end{regle} + +La façon habituelle de se servir de \verb|\lastbox| est de capturer la dernière boite pour l'assigner à un registre de boite normal que l'utilisateur peut ensuite manier à sa guise. On rencontre donc la syntaxe + +\centrecode-\setbox<registre de boite>=\lastbox- + +Par exemple, à l'intérieur d'un groupe et avec l'aide de la boite brouillon \no0, \verb|\lastbox| permet d'effacer la dernière boite composée : + +\centrecode-{\setbox0 =\lastbox}- + +On peut également s'en servir pour capturer la dernière ligne d'un paragraphe composé dans une boite verticale (dans ce cas, \verb|\lastbox| sera une boite horizontale, celle qui contient la dernière ligne) : + +\showcode/\def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. } +\setbox0=\vtop{% définit la boite 0¤\idx*\setbox\idx*\vtop¤ + \hsize=8cm ¤\idx*\hsize¤ + \dummytext\dummytext +} + +a) Boite pleine : \copy0 ¤\idx*\copy¤ + +b) \setbox0=\vtop{%¤\idx*\setbox\idx*\vtop¤ + \unvbox0 % boite précédemment composée¤\idx*\unvbox¤ + \global\setbox1=\lastbox% et capture la dernière ligne dans la boite 1¤\idx*\global\idx*\setbox¤ + }% + Dernière ligne = |\hbox{\unhbox1 }|\par% redonne à box1 sa largeur naturelle¤\idx*\unhbox¤ + Boite restante = \box0 / + +Cet exemple illustre comment \verb|\lastbox| permet de « couper » une boite verticale par le bas. La similarité devient évidente entre \verb|\lastbox| qui coupe par le bas et \idx\vsplit qui coupe par le haut. En répétant la man\oe uvre plusieurs fois, il deviendrait possible de vider peu à peu une boite verticale en enlevant à chaque itération la ligne du bas : + +\showcode/\def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. } +\setbox0=\vtop{% définit la boite 0¤\idx*\setbox\idx*\vtop¤ + \hsize=8cm ¤\idx*\hsize¤ + \dummytext\dummytext +} + +\setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox}¤\idx*\setbox\idx*\vtop\idx*\global\idx*\unvbox¤ +\setbox1=\hbox{\unhbox1 }¤\idx*\unhbox¤ +1) |\box1|¤\idx*\box¤ + +\setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} +\setbox1=\hbox{\unhbox1 }¤\idx*\unhbox¤ +2) |\box1| + +\setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox}¤\idx*\setbox\idx*\vtop\idx*\global\idx*\unvbox¤ +\setbox1=\hbox{\unhbox1 }¤\idx*\unhbox¤ +3) |\box1|¤\idx*\box¤/ + +Pourquoi \verb|\lastbox| échoue à capturer la dernière ligne dans les deux derniers cas ? C'est que \verb|\lastbox| est plus contraignante que \idx\vsplit et la réponse demande de connaitre un peu la façon dont est construit un paragraphe\idx*{paragraphe!composition} par \TeX{} : le ressort d'interligne, qui est inséré entre chaque boite horizontale contenant une ligne, est assorti d'une \idx{pénalité} qui le précède. + +Pour s'en persuader, il est possible de faire remonter des entrailles de \TeX{} vers le \idx[!log]{fichier} \verb|log| les composants élémentaires des boites qu'il construit. Pour ce faire, plusieurs réglages sont à faire : + +\begin{itemize} + \item pour que les informations ne soient écrites que dans le \idx[!log]{fichier} \verb|log| (et non pas également sur le terminal), il faut que la primitive \idx\tracingonline soit strictement positive; + \item l'entier \idx\showboxdepth spécifie le nombre de niveaux d'imbrication de boites qui doivent être montrés; + \item l'entier \idx\showboxbreadth spécifie le nombre d'éléments montrés dans chaque niveau; + \item la primitive \idx\showbox\label{showbox}, suivie d'un registre de boite donne l'ordre à \TeX{} d'examiner la boite contenue dans le registre et de délivrer les informations selon les réglages spécifiés. +\end{itemize} + +\errcode|\setbox0=\vbox{\hsize=2.5cm Programmer en TeX est facile}%¤\idx*\setbox\idx*\vbox\idx*\hsize¤ +\showboxbreadth=5 \showboxdepth=3 \tracingonline=1 ¤\idx*\showboxbreadth\idx*\showboxdepth\idx*\tracingonline¤ +\showbox0 ¤\idx*\showbox¤|{> \string\box0=\par +\string\vbox(14.77199+0.092)x71.13188\par +.\string\hbox(5.27199+1.888)x71.13188, glue set - 1.0\par +..\string\hbox(0.0+0.0)x0.0\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 P\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 r\par +..\string\kern-0.06401\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 o\par +..etc.\par +.\string\penalty 300\par +.\string\glue(\string\baselineskip) 2.08801\par +.\string\hbox(5.52399+0.092)x71.13188, glue set 53.71591fil\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 f\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 a\par +..\string\discretionary\par +...\string\T1/LinuxLibertineT-TLF/m/n/8 -\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 c\par +..\string\T1/LinuxLibertineT-TLF/m/n/8 i\par +..etc.} + +On constate que la \idx{pénalité} (ici de 300) est bien insérée \emph{avant} le ressort d'interligne (\idx\baselineskip). Notons par ailleurs que les «\verb|.\hbox|» représentent les boites horizontales contenant les lignes (le nombre de «\verb|.|» indique le niveau d'imbrication des boites, étant entendu qu'une absence de point indique la boite mère). Il faut également remarquer, juste après le «\verb|r|», que «\verb|\kern-0.06401|» règle le \idx{crénage} (kerning) entre les caractères «r» et «o» dans la fonte « LinuxLibertine-TLF » et montre donc que pour des raisons esthétiques, ces caractères sont légèrement rapprochés. +\grandsaut + +Pour en revenir à \verb|\lastbox|, lorsqu'elle a capturé la dernière ligne, elle laisse en bas de la boite restante une pénalité et un ressort vertical. Par conséquent, la \verb|\lastbox| suivante, ne pouvant capturer une boite, sera vide. Pour que la méthode fonctionne, il faut supprimer \emph{dans cet ordre} le ressort avec \idx\unskip puis la \idx{pénalité} avec \idx\unpenalty avant d'extraire la prochaine \verb|\lastbox|. Construire une macro \verb|\reversebox| qui vide une boite verticale par le bas devient une simple routine, à condition de ne pas effectuer le test \tidx{ifvoid}, inadapté à un registre vidé par \verb|\lastbox|, mais les tests §\ifzerodimbox ou §\ifvoidorempty que nous avons programmés (voir page~\pageref{tester.boite.vide} et suivantes) : + +\showcode/\newbox\tempbox¤\idx*\newbox¤ +\def\reversebox#1{% affiche le contenu de la boite verticale #1 + \setbox#1=\vbox{% + \unvbox#1% composer la boite #1¤\idx*\unvbox¤ + \unskip\unpenalty% retirer ce qui n'est pas capturable par \lastbox¤\idx*\unskip\idx*\unpenalty¤ + \global\setbox0=\lastbox% la boite 1 devient la dernière ligne¤\idx*\global\idx*\setbox¤ + }% + |\hbox{\unhbox0 }|\par% afficher la boite 1 dans sa largeur d'origine¤\idx*\hbox\idx*\unhbox¤ + \ifvoidorempty{#1}% si le registre #1 contient une boite vide¤§*\ifvoidorempty¤ + \relax% ne rien faire + {\reversebox{#1}}% sinon, rencommencer +} +\def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler + la page de façon artificielle. } +\setbox\tempbox=\vbox{% définit la boite 0¤\idx*\setbox\idx*\vbox¤ + \hsize=8cm \dummytext\dummytext¤\idx*\hsize¤ +} + +\reversebox\tempbox/ + +Mettons tout ceci en application. La macro §\zerocompose va nous permettre, conjointement avec \verb|\lastbox| et \idx\vsplit, de récupérer une boite horizontale de largeur naturelle contenant la première ligne. + +\showcode/% compose "Programmation" en largeur 0 dans la boite 0 : +\zerocompose{0}{Programmation}%¤§*\zerocompose\idx*\font¤ +Boite initiale : \copy0 \par% compose la boite résultante¤\idx*\copy¤ +\vfuzz=\maxdimen% annule les avertissements pour débordements¤\idx*\vfuzz\idx*\maxdimen\idx*{message d'avertissement}¤ +\splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip¤ +\setbox1 =\vsplit0 to 0pt % coupe la boite 0 à 0pt¤\idx*\setbox\idx*\vsplit¤ +{% dans un groupe (où la boite 0 sert de brouillon) + \setbox0 =\vbox{% affecter à la boite brouillon¤\idx*\setbox\idx*\vbox¤ + \unvbox1 % la boite 1 composée dans sa largeur naturelle¤\idx*\unvbox¤ + \unskip\unpenalty% annule ce qui n'est pas une boite¤\idx*\unskip\idx*\unpenalty¤ + \global\setbox2 =\lastbox% affecte globalement la dernière (et unique) ligne¤\idx*\global¤ + % à la boite 1 + }% +}% ferme le groupe +\setbox2=\hbox{\unhbox2}% rend à la boite 2 sa largeur naturelle¤\idx*\hbox\idx*\unhbox¤ +Première ligne = "\box2 "% compose la boite 2¤\idx*\box¤/\idx*[|)]\lastbox + +\subsubsection{Mesurer toutes les coupures} +Le but est de construire une macro contenant les longueurs (en caractères) avant les possibles coupures du mot, en tenant compte du caractère de coupure. Par exemple, le mot « programmation » aura 3 coupures possibles auxquelles correspondent les longueurs suivantes : + +\begin{centrage} + \small + \begin{tabular}{rc}\hline + Caractères avant la coupure & Longueur\\\hline + \verb|pro-|&4\\ + \verb|program-|&8\\ + \verb|programma-|&10\\\hline + \end{tabular} +\end{centrage} + +Nous allons donc construire une macro §*\hyphlengths + +\centrecode-\hyphlengths{<mot>}{\<macro>}- + +\noindent qui assigne à la \verb|\<macro>| la liste de toutes les longueurs avant les coupures possibles. Par exemple, si l'on écrit «\verb|\hyphlengths{programmation}\foo|», la macro \verb|\foo| a comme texte de remplacement «\verb|4,8,10|». + +La méthode pour y parvenir fera intervenir + +\centrecode-\vsplit<boite> to 0pt- + +\noindent pour couper la boite résultante de la macro §\zerocompose et en obtenir la première ligne dans une boite verticale. Ensuite, en composant cette boite dans une boite verticale, en capturant la dernière boite horizontale composée avec \verb|\lastbox| puis en appliquant \idx\unhbox, cette ligne se retrouvera dans une boite horizontale ayant sa largeur naturelle. Dès lors qu'on peut la mesurer, il sera facile de trouver le nombre de caractères sur chaque ligne (4, 5 et 3 pour «\verb|pro-|», «\verb|gram-|» et «\verb|ma-|») : la simple division de la largeur de la boite par la largeur d'un caractère donne le quotient cherché. Il faudra lui soustraire 1 pour écarter le caractère de coupure. En cumulant ces nombres et en ajoutant 1 à chaque cumul pour prendre en compte le caractère de coupure, on arrive bien à la liste «4,8,10». + +\showcode|\catcode`@11 +\def\hyphlengths#1#2{%#2 = macro contenant les longueurs de coupures du mot #1¤§*\hyphlengths¤ + \begingroup + \zerocompose¤§*\zerocompose¤ + [\tt% passer en fonte à chasse fixe¤\idx*\tt¤ + \setbox\z@\hbox{M}\xdef\ttwidth{\the\wd\z@}% mesurer la largeur des caractères¤\idx*\setbox\idx*\hbox\idx*\the\idx*\wd\idx*\z@\idx*\xdef¤ + ]\z@{#1}% compose en 0pt dans la boite 0¤\idx*\z@¤ + \let#2 = \empty% initialise la macro #2¤\idx*\empty¤ + \def\cumul@length{0}% le cumul des longueurs initialisé à 0 + \vfuzz=\maxdimen% annule les avertissements pour débordement¤\idx*\vfuzz\idx*\maxdimen\idx*{message d'avertissement}¤ + \splittopskip=\z@ % ne rajouter aucun espace au sommet de la boite restante¤\idx*\splittopskip\idx*\z@¤ + \loop¤\idx*\loop¤ + \setbox1=\vsplit\z@ to \z@% couper la boite à 0pt de hauteur¤\idx*\vsplit¤ + {\setbox\z@=\vbox{\unvbox1 \unskip\unpenalty\global\setbox1=\lastbox}}%¤\idx*\vbox\idx*\unvbox\idx*\unskip\idx*\unpenalty\idx*\lastbox\idx*\global¤ + \setbox1=\hbox{\unhbox1 }%¤\idx*\unhbox¤ + \unless\ifvoid\z@% si la boite 0 n'est pas encore vide¤\tidx*{unless}\tidx*{ifvoid}\idx*\z@¤ + \edef\cumul@length{% mettre à jour \cumul@length + \number\numexpr + \cumul@length + +% ajouter le quotient "largeur syllabe/largeur d'1 caractère"¤\idx*\wd¤ + \wd1/\dimexpr\ttwidth\relax + -1% et soustraire 1 (le caractère "-") + \relax + }% + % ajouter à #2 la virgule et le cumul actuel +1 + \edef#2{% définir la macro #2 : + #2% reprendre le contenu de #2 + \ifx#2\empty\else,\fi% ajouter "," si #2 non vide + \number\numexpr\cumul@length+1\relax% et le cumul + }% + \repeat% et recommencer¤\idx*\repeat¤ + \expsecond{% avant de fermer le groupe¤§*\expsecond¤ + \endgroup + \def#2}{#2}% définit #2 hors du groupe +} +\catcode`\@12 +a) \hyphlengths{programmation}\foo liste = "\foo"\par +b) \hyphlengths{typographie}\foo liste ="\foo"\par +c) \hyphlengths{j'entendrai}\foo liste ="\foo"\par +d) \hyphlengths{vite}\foo liste ="\foo"¤§*\hyphlengths¤| + +\subsubsection{Une solution} +Il est temps de mettre en \oe uvre toute cette théorie. Curieusement, l'algorithme vu précédemment avec §\breakttA n'a pas à subir beaucoup de changements pour autoriser les coupures de mots. + +Tout ce qui est nouveau va être contenu dans la macro récursive \verb|\breakttA@i|. En premier lieu, après avoir calculé la longueur du prochain mot (qui s'étend jusqu'à un espace ou un tiret), nous devons tester si ce mot (ainsi que l'éventuel tiret qui le suit) peut loger sur l'espace restant de la ligne en cours. Ce n'est qu'en cas de réponse négative que nous devons entreprendre la coupure du mot. À l'aide de §\hyphlengths et en parcourant la liste de longueurs cumulées qu'elle génère, nous pouvons trouver combien mesure la plus longue coupure possible de ce mot compatible avec la place restante sur la ligne. La macro \verb|\nexthyph@len| sera cette longueur. À l'issue de ce processus, si cet entier est nul, soit aucune coupure n'était nécessaire, soit aucune coupure n'a été trouvée. Dans le cas où il n'est pas nul, il sera copié dans \verb|\next@len|. + +Si une coupure doit être faite, le test suivant, qui s'assure que \verb|\next@len| est inférieur au nombre de caractères restant à placer, est toujours vrai. Nous devons alors afficher toutes les lettres de la syllabe retenue (c'est-à-dire \verb|\next@len-1| caractères), puis afficher un tiret, mission qu'exécutera la macro \verb|\print@hyphchar|. Il reste ensuite à remplir la ligne avec des caractères blancs, fermer et ouvrir une nouvelle boite et appeler la macro \verb|\breakttA@i|. + +Seules les macros \verb|\breakttA@i| et \verb|\print@hyphchar| ont été écrite dans cet exemple puisque toutes les autres macros déjà vues sont strictement identiques. + +\showcode|\catcode`\@11 +\newcount\brktt@cnt¤\idx*\newcount¤ +\newdimen\brktt@interletter¤\idx*\newdimen¤ +\newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché¤\idx*\newif¤ +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré¤\idx*\newif¤ + +\let\breakttB=\breakttA¤§*\breakttB¤ + +\def\breakttA@i{% + \edef\remaining@chars{% calculer le nombre de caractères restant à placer sur la ligne + \numexpr\maxchar@num-\brktt@cnt\relax}%¤\idx*\numexpr¤ + \len@tonextword% calculer dans \next@len le nombre de caractères avant le prochain mot + \def\next@hyphlen{0}% initialiser la longueur de coupure possible du mot¤\defline\aaa¤ + % si le mot + l'eventuel "-" qui le suit ne rentre pas sur ce qui reste de la ligne + \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax + \hyphlengths\next@word\list@hyphlengths% bâtir la liste des longueurs de coupures¤§*\hyphlengths¤ + \unless\ifx\list@hyphlengths\empty% si une coupure est possible¤\tidx*{unless}¤ + \expsecond{\doforeach\xx\in}{\list@hyphlengths}% les examiner toutes¤§*\expsecond§*\doforeach¤ + {% si la coupure examinée peut loger sur la ligne + \unless\ifnum\xx>\remaining@chars\relax% + \let\next@hyphlen\xx% la stocker + \fi% pour que \next@hyphlen soit maximal + }% + \fi + \fi + \ifnum\next@hyphlen>0 % si une coupure est nécessaire et a été trouvée + \let\next@len\next@hyphlen% mettre à jour la longueur du mot à placer + \fi¤\defline\bbb¤ + % si le mot + l'eventuel "-" qui le suit ne peut pas loger sur la ligne en cours + \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax + \ifline@start% si c'est le début d'une ligne + % avertir l'utilisateur + \message{Largeur de composition trop faible pour + \unexpanded\expandafter{\next@word}^^J}%¤\idx*\message\verbidx[ (fin de ligne)]{^^J}\idx*\unexpanded¤ + % et composer tout de même jusqu'à la fin de la ligne + \exparg\print@nchar{\number\numexpr\maxchar@num-\brktt@cnt}%¤§*\exparg¤ + \else% sinon + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \fi + \exparg\restart@hbox{\tt@remaintext}% ré-ouvrir une boite¤§*\exparg¤ + \expandafter\breakttA@i% et poursuivre le processus + % s'il y a la place pour accueillir ce qui est avant la prochaine coupure + \else + \ifnum\next@hyphlen>0 % si une coupure de mot doit être faite¤\defline\ccc¤ + \exparg\print@nchar{\number\numexpr\next@len-1}% afficher les lettres de la syllabe¤§*\exparg¤ + \print@hyphchar% et le caractère de coupure + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \exparg\restart@hbox{\tt@remaintext}% ré-ouvrir une boite¤§*\exparg¤ + \expandafter\expandafter\expandafter\breakttA@i% et continuer le processus + \else% si pas de coupure dans le mot¤\defline\ddd¤ + \print@nchar\next@len% afficher le mot + \ifx\tt@remaintext\tt@emptytext% si texte restant est vide + \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne + \egroup% fermer la hbox + \par% aller à la ligne + \endgroup% fermer le groupe ouvert au début. Fin du processus + \else% si le texte restant n'est pas vide + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas remplie + \print@nchar{1}% afficher le caractère qui suit le mot + \else% si la ligne est remplie + \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une¤§*\exparg¤ + \fi + \expandafter\expandafter\expandafter\breakttA@i% enfin, continuer le processus + \fi + \fi + \fi +} + +\def\print@hyphchar{% + \advance\brktt@cnt by 1 % augmenter le compteur de caractères¤\idx*\advance¤ + \hbox to\ttchar@width{\hss-\hss}% afficher "-"¤\idx*\hbox\idx*\hss¤ + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie + \kern\brktt@interletter\relax% insérer le ressort inter-lettre¤\idx*\kern¤ + \fi +} + +\catcode`\@12 + +\def\liglist{<<,>>}% liste des motifs de ligature (mettre à, é, etc si codage UTF8)¤\idx{ligature}¤ +\def\ttindent{3} + +\vrule\vbox{\breakttB[3pt][8cm]{%¤\idx*\vrule\idx*\vbox§*\breakttB¤ +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<sauvagement>>. +}% +}\vrule¤\idx*\vrule¤ + +\vrule\vbox{\breakttB[1pt][5cm]{%¤§*\breakttB¤ +Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche +et droite du texte ont été tracées, on constate que les caractères sont +exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement +n'a lieu, car une espace correctement calculée est insérée entre chaque +caractère. Dorénavant, les mots ne sont plus coupés <<sauvagement>>. +}% +}\vrule¤\idx*\vrule¤| + +La macro \verb|\breakttA@i| est la même que vue précédemment sauf que les lignes \nos\aaa--\bbb{} et \ccc--\ddd{} sont ajoutées pour permettre les coupures de mots. + +Concluons ce difficile défi en remarquant qu'en nous aidant de celui de \TeX{}, nous avons \emph{programmé} un algorithme de coupures, mais il n'a pas toutes les fonctionnalités de l'original. En effet, les coupures se feront \emph{toujours} lorsqu'elles sont possibles. Il n'y a par exemple aucun moyen de décourager des coupures, que ce soit sur des lignes adjacentes comme l'augmentation de \idx\doublehyphendemerits le permet ni sur l'avant-dernière ligne d'un paragraphe avec \idx\finalhyphendemerits. Nous avons cependant gagné sur un point : les coupures se feront sur des mots comprenant un trait d'union, aussi bien sur ce trait que sur le mot qui le précède ou le suit, alors que \TeX{} n'autorise une coupure que sur le trait d'union.\idx*[|)]{composition en fonte à chasse fixe} + +\section{Afficher des algorithmes} +Bien que la programmation qui entre en jeu ici est \emph{linéaire} (c'est-à-dire sans boucle), il est intéressant de décortiquer la méthode à déployer pour afficher des algorithmes sous forme de \idx{pseudocode} comme cela est parfois fait dans ce livre (voir par exemple ceux des pages \pageref{algo1}, \pageref{algo2} ou \pageref{algo3}). + +\subsubsection{La syntaxe et les fonctionnalités} +Commençons par la syntaxe finale. La macro §\algorithm admettra un argument optionnel qui est la liste des tokens dont le catcode sera rendu égal à 12 pendant le pseudocode. Elle admettra aussi un argument obligatoire qui est le titre de l'algorithme (éventuellement vide). Le \idx{pseudocode} proprement dit est contenu entre deux tokens identiques, à la mode de §\litterate : + +\centrecode-\algorithm[<liste de tokens>]{<titre>}<délimiteur><texte><délimiteur>- + +Pour que la saisie soit la plus « naturelle » possible, voici les principales fonctionnalités à mettre en \oe uvre: +\begin{enumerate} + \item chaque caractère de tabulation \verb|^^I|\verbidx[ (tabulation)]{^^I} sera traduit par une espace horizontale. La valeur de cette espace est contenue dans une macro \verb|\algoindent|; + \item les espaces perdent leur comportement naturel et plusieurs espaces consécutifs seront affichés; + \item les retours à la ligne \verbidx[ (retour charriot)]{^^M} perdent également leur comportement naturel et sont reprogrammés pour provoquer un retour à la ligne; + \item le caractère \cidx\~ reste actif, mais est reprogrammé pour que le \verb|<texte>| se trouvant entre deux \cidx\~ soit affiché en caractères gras; + \item les caractères «\verb|:=|», utilisés pour une assignation, sont traduits à l'affichage par «$\leftarrow$», provoqué par la macro de plain-\TeX \idx\leftarrow; + \item le caractère «\cidx[ (mise en indice)]\_» garde son comportement normal en mode mathématique\idx*{mode!mathématique} mais est affiché tel quel sinon; + \item le caractère «\cidx\%» met en italique et en gris tout ce qui reste sur la ligne courante; + \item les lignes sont numérotées dans la marge de gauche, mais il est possible de désactiver cette fonctionnalité. +\end{enumerate} +\grandsaut + +Voici ce que nous devons pouvoir écrire (où «$\longrightarrow$» représente le caractère de tabulation): + +\begingroup +\def\showcodeactivechars{\defactive\^^I{\begingroup\color{gray}${}\longrightarrow{}$\endgroup}} +\showcode/Voici un algorithme élémentaire : +\algorithm[\#]{Algorithme {\bf MinMax}}| +macro ~Min_Max~ (#1) % afficher la valeur max et min d'une liste de valeurs #1 + $V_{max}:=-\infty$ % $-\infty$ ou la plus petite valeur possible + $V_{min}:=+\infty$ % $+\infty$ ou la plus grande valeur possible + ~Pour~ chaque $x$ dans (#1) + ~Si~ $x>V_{max}$ + ~alors~ $V_{max}:=x$ + ~FinSi~ + ~Si~ $x<V_{min}$ + ~alors~ $V_{min}:=x$ + ~FinSi~ + ~FinPour~ + Afficher "$V_{min}$ et $V_{max}$" +~FinMin_Max~| +Suite du texte.../ +\endgroup +\grandsaut + +Le problème étant posé, seuls les points \nos1 et 8 vont faire intervenir des méthodes nouvelles. + +\subsubsection{Rendre actif le caractère de tabulation} +Pour que le caractère de tabulation ait le comportement souhaité, la première idée serait de le rendre actif et de lui faire insérer horizontalement l'espace dont la mesure est contenue dans \verb|\alogoindent| : + +\begingroup +\def\showcodeactivechars{\defactive\^^I{\begingroup\color{gray}${}\longrightarrow{}$\endgroup}} +\showcode/\def\algoindent{1cm}% +\begingroup +\parindent=0pt % pas d'indentation¤\idx*\parindent¤ +\defactive\^^M{\leavevmode\par}% rend le retour charriot actif¤§*\defactive\verbidx*[ (retour charriot)]{^^M}¤ +\defactive\^^I{\hskip\algoindent\relax}% tabulation active¤§*\defactive\verbidx[ (tabulation)]{^^I}\idx*\hskip¤ +Pas d'indentation ici\ldots + Voici une première ligne + une seconde plus indentée + retour à l'indentation normale + \for\xx=1to10\do{un long texte } + pour terminer, la dernière ligne +\endgroup/ +\endgroup + +Le comportement est celui souhaité sauf lorsque le texte est trop long pour loger sur la ligne entière, comme celui écrit par la boucle §\for. Pour garder une cohérence dans l'alignement vertical, il faut que l'indentation soit rémanente. La méthode la plus immédiate est d'incrémenter le ressort \idx\leftskip de la quantité \verb|\algoindent| à chaque tabulation rencontrée et remettre ce ressort à \verb|0pt| à chaque nouvelle ligne. Dans tout l'algorithme, nous assignerons un ressort infini à \idx\rightskip. + +\begingroup +\def\showcodeactivechars{\defactive\^^I{\begingroup\color{gray}${}\longrightarrow{}$\endgroup}} +\showcode/\def\algoindent{1cm}% +\begingroup +\leftskip=0pt \rightskip=0pt plus1fil \relax % initialise les ressorts latéraux¤\idx*\leftskip\idx*\rightskip¤ +\parindent=0pt % pas d'indentation¤\idx*\parindent¤ +\defactive\^^M{\leavevmode\par\leftskip=0pt }% rend le retour charriot actif¤§*\defactive\verbidx*[ (retour charriot)]{^^M}¤ +\defactive\^^I{\advance\leftskip by \algoindent\relax}% tabulation active¤§*\defactive\verbidx[ (tabulation)]{^^I}\idx*\hskip\idx*\advance\idx*\leftskip¤ +Pas d'indentation ici\ldots + Voici une première ligne + une seconde plus indentée + retour à l'indentation normale + \for\xx=1to10\do{un long texte } + pour terminer, la dernière ligne +\endgroup/ +\endgroup + +\subsubsection{Numérotation des lignes} +Passons maintenant au point \no8 qui consiste à numéroter chaque ligne de l'algorithme. Une « ligne » de l'algorithme est ce qui se trouve entre deux frappes de la touche entrée ce qui revient à considérer ici qu'une ligne est donc un paragraphe pour \TeX. La primitive \idx\everypar, qui se comporte comme un registre de tokens, permet de définir un ensemble de tokens qui seront exécutés (en mode horizontal) lorsqu'un paragraphe débute : + +\centrecode-\everypar= {<texte>}- + +Nous allons mettre cette primitive à contribution pour numéroter chaque paragraphe. Pour que le numéro soit dans la marge de gauche, nous allons le placer dans une boite en débordement à gauche de type \idx\llap et laisser une espace de \verb|4pt| entre ce numéro et le début de la ligne. Afin que les numéros soient correctement placés, nous devons tenir compte du ressort \idx\leftskip. Il faut donc laisser une espace de \verb|4pt|${}+{}$\idx\leftskip entre le numéro et le début de la ligne. + +\begingroup +\def\showcodeactivechars{\defactive\^^I{\begingroup\color{gray}${}\longrightarrow{}$\endgroup}} +\showcode/¤\string\newcount\string\algocnt¤ % compteur de lignes pour l'algorithme¤\idx*\newcount¤ +\def\algoindent{1cm}% +\begingroup +\algocnt=1 % initialise le compteur à 1 +\leftskip=0pt \rightskip=0pt plus1fil\relax% initialise les ressorts à 0pt¤\idx*\leftskip\idx*\rightskip¤ +\parindent=0pt % pas d'indentation¤\idx*\parindent¤ +\everypar={\llap{$\scriptstyle\number\algocnt$\kern\dimexpr4pt+\leftskip\relax}}%¤\idx*\llap\idx*\scriptstyle\idx*\kern\idx*\dimexpr\idx*\leftskip\idx*\everypar¤ + +\defactive\^^M{\leavevmode\par\advance\algocnt by1 \leftskip=0pt }% retour charriot actif¤§*\defactive\verbidx*[ (retour charriot)]{^^M}\idx*\leavevmode\idx*\leftskip\idx*\advance¤ +\defactive\^^I{\advance\leftskip by \algoindent\relax}% tabulation active¤§*\defactive\verbidx[ (tabulation)]{^^I}\idx*\leftskip\idx*\advance¤ +Pas d'indentation ici\ldots + Voici une première ligne + une seconde plus indentée + retour à l'indentation normale + \for\xx=1to10\do{un long texte } + pour terminer, la dernière ligne +\endgroup/ +\endgroup + +\subsubsection{Ignorer les indésirables en début d'algorithme} +Il nous reste une astuce à prévoir pour que la macro §\algorithm soit la plus agréable à utiliser. Il s'agit de ce qui se trouve au début de l'algorithme, juste après le \verb|<token>| qui délimite le texte de l'algorithme. Pour que les choses soient plus claires, supposons que ce \verb|<token>| est «\verb-|-». Il faudrait que l'affichage soit le même que l'on écrive + +\begin{enumerate} + \item \verb-\algorithm{<titre>}|début de l'algorithme...|- + \item \verb-\algorithm{<titre>}|-\par +\verb-début de l'algorithme...|-§*\algorithm +\end{enumerate} + +Il faut donc que l'on supprime tous les retours charriots (actifs) et tous les espaces (actifs) qui pourraient se trouver au tout début du corps de l'algorithme. Cela suppose une macro non linéaire \verb|\sanitizealgo|, la seule de cette section. En voici l'algorithme : + +\begingroup +\numalgofalse +\def\algoleftskip{.1\hsize} +\def\algorightskip{.1\hsize} +\algorithm[]{Manger tous les \texttt{\string^\string^M} et \texttt\textvisiblespace}/ +macro ~lire_prochain_token~ + lire le token suivant $x$ à l'aide de \idx\futurelet + aller à ~tester_prochain_token~ +~fin~\medskip +macro ~tester_prochain_token~ + ~si~ ($x={}$\verb|espace| actif) ~ou~ ($x={}$\verbidx*[ (retour charriot)]{^^M}\verb|^^M| actif) + manger $x$ + aller à ~lire_prochain_token~ + ~finsi~ +~fin~/ +\endgroup + +Nous appellerons cette macro \emph{en dernier} puis, une fois son travail effectué, nous laisserons \TeX{} composer le texte constituant l'algorithme. On ne pourra reprendre la main que lorsqu'il exécutera le \verb|<délimiteur>| final rendu actif qui fermera le groupe ouvert tout au début, tracera la ligne horizontale de fin d'algorithme et sautera une petite espace verticale à l'aide de \idx\smallbreak. + +\subsubsection{Commentaires grisés avec le package \texttt{color}} +Enfin, pour que les commentaires soient en italique et en gris, nous allons rendre le caractère «\cidx\%» actif de façon à ce qu'il sélectionne la \idx{fonte} italique (avec \idx\it) et colore le texte à venir en gris (avec \verb|\color{gray}|). Cette dernière action suppose que la couleur «\verb|gray|» a été définie et que le \idx[!color]{package} «\verb|color|» a préalablement été chargé moyennant : + +\begin{itemize} + \item \idx\usepackage\verb|{color}| pour le \idx[!\LaTeX]{format} \LaTeX{}; + \item \idx\input\verb| color| pour le \idx[!plain-\TeX]{format} plain-\TeX{}; + \item \idx\setupcolor\verb|[state=start]| pour le \idx[!Con\TeX t]{format} Con\TeX t. +\end{itemize} + +Pour que la typographie normale (caractères non italiques et couleur noire) soit à nouveau en vigueur à la ligne suivante, nous chargerons le caractère actif \verbidx*[ (retour charriot)]{^^M}\verb|^^M| d'effectuer les actions «\idx\rm» pour un retour en \idx{fonte} droite et «\verb|\color{black}|» pour restaurer la couleur noire. + +\subsubsection{Le code complet} +Voici le code complet commenté qui reprend des méthodes déjà vues plusieurs fois (programmation de caractères actifs notamment) : + +\begingroup +\defactive\W{\begingroup\color{gray}${}\longrightarrow{}$\endgroup} +\showcode[\defactive\W{\advance\leftskip\algoindent\relax}]/\newif\ifnumalgo¤\idx*\newif¤ +\newcount\algocnt¤\idx*\newcount¤ +\numalgotrue + +\def\algoindent{2em} +\def\algorule{.4pt}% épaisseur des réglures de début et de fin + +\def\algohrulefill{% remplit avec une ligne horizontale + \leavevmode¤\idx*\leavevmode¤ + \leaders\hrule height\algorule\hfill¤\idx*\leaders\idx*\hrule\idx*\hfill¤ + \kern0pt % rajoute un noeud au cas où la commande est en fin de ligne¤\idx*\kern¤ +} + +\newmacro\algorithm[\\,\#,\{,\}]2{%¤§*\algorithm§*\newmacro¤ + \medbreak¤\idx*\medbreak¤ + \begingroup% ouvre un groupe (sera fermé à la fin de l'algorithme)¤\defline\aaa¤ + \algocnt=1 % initialise le compteur de lignes + \leftskip=0pt \rightskip=0pt plus1fil\relax% initialise les ressorts latéraux¤\idx*\leftskip\idx*\rightskip¤ + \parindent=0pt % pas d'indentation¤\idx*\parindent¤ + %%%%%%%%%%%% affichage du titre %%%%%%% + \algohrulefill% remplir avec une ligne horizontale + \ifempty{#2}% si #2 est vide¤§*\ifempty¤ + {}% ne rien insérer + {% sinon + \lower.5ex\hbox{ #2 }% insérer le titre abaissé de 0.5ex¤\idx*\lower\idx*\hbox¤ + \algohrulefill% insérer une ligne horizontale + }% + \par% aller à la ligne + \nointerlineskip% ne pas insérer le ressort d'interligne¤\idx*\nointerlineskip¤ + \kern7pt % et sauter 7pt verticalement¤\idx*\kern¤ + \nobreak% empêcher une coupure de page¤\idx*\nobreak¤ + %%%%%%%%%%%%%% fin du titre %%%%%%%%%% + % + %%%%%%%%%%%%%% rend les caractères actifs %%%%%%%%%%%%%% + \def~##1~{\begingroup\bf##1\endgroup}%¤\idx*\bf¤ + \defactive:{% rend ":" actif¤§*\defactive¤ + \futurelet\nxttok\algoassign% \nxttok = token suivant¤\idx*\futurelet¤ + }% + \def\algoassign{% suite du code de ":" + \ifx=\nxttok% si ":" est suivi de "=" + \ifmmode% si mode math¤\tidx*{ifmmode}\idx*{mode!mathématique}¤ + \leftarrow% afficher "\leftarrow"¤\idx*\leftarrow¤ + \else% si mode texte + ${}\leftarrow{}$% passer en mode math pour "\leftarrow"¤\idx*{mode!mathématique}¤ + \fi + \expandafter\gobone% manger le signe "=" + \else% si ":" n'est pas suivi de "="' + \string:% afficher ":"¤\idx*\string¤ + \fi + }% + \ifnumalgo% si la numérotation est demandée, + \everypar={% définir le \everypar + \llap{$\scriptstyle\number\algocnt$\kern\dimexpr4pt+\leftskip\relax}% + }%¤\idx*\llap\idx*\scriptstyle\idx*\kern\idx*\dimexpr\idx*\leftskip\idx*\everypar¤ + \fi + \doforeach\currentchar\in{#1}{\catcode\expandafter`\currentchar=12 }%¤§*\doforeach¤ + \def\algocr{% définit ce que fait ^^M + \color{black}% repasse en couleur noire¤\idx*\color¤ + \rm% fonte droite¤\idx*\rm¤ + \leavevmode\par% termine le paragraphe + \advance\algocnt by1 % incrémente le compteur de lignes¤\idx*\advance¤ + \leftskip=0pt % initialise le ressort gauche¤\idx*\leftskip¤ + } + \letactive\^^M=\algocr%¤§*\letactive\verbidx*[ (retour charriot)]{^^M}¤ + \defactive\^^I{\advance\leftskip by \algoindent\relax}%¤§*\defactive\verbidx[ (tabulation)]{^^I}\idx*\advance\idx*\leftskip¤ + \defactive\ {\hskip1.25\fontdimen2\font\relax}% espace = 25% de + que + % la largeur naturelle¤\idx*\hskip\idx*\fontdimen\idx*\font¤ + \defactive\%{\it\color{gray}\char`\%}% passe en italique et gris puis affiche "%"¤\idx*\it\idx*\char\idx*\color\cidx*\%¤ + \defactive_{\ifmmode_\else\string_\fi}%¤\tidx*{ifmmode}\idx*\string¤ + \defactive#3{% rend le token #3 actif (il sera rencontré à la fin de l'algorithme)¤§*\defactive¤ + \everypar{}% neutralise le \everypar¤\idx*\everypar¤ + \par% aller à la ligne + \nointerlineskip% ne pas insérer le ressort d'interligne¤\idx*\nointerlineskip¤ + \kern7pt % et sauter 7pt verticalement¤\idx*\kern¤ + \nobreak% empêcher une coupure de page¤\idx*\nobreak¤ + \algohrulefill% tracer la ligne de fin + \endgroup% ferme le groupe ouvert au début de l'algorithme¤\defline\bbb¤ + \smallbreak% saute un petit espace vertical¤\idx*\smallbreak¤ + }% + %%%%%%%%%%%%%%%%% fin des caractères actifs %%%%%%%%%%%%%%%% + \sanitizealgo% va manger les espaces et les ^^M au début de l'algo +} + +\def\sanitizealgo{\futurelet\nxttok\checkfirsttok}% récupère le prochain token¤\idx*\futurelet¤ + +\def\checkfirsttok{% teste le prochaun token + \def\nextaction{% a priori, on considère que la suite est " " ou "^^M" donc + \afterassignment\sanitizealgo% aller à \sanitizealgo¤\idx*\afterassignment¤ + \let\nexttok= % après avoir mangé ce "^^M" ou cet espace + }% + \unless\ifx\nxttok\algocr% si le prochain token n'est pas un ^^M¤\tidx*{unless}¤ + \unless\ifx\space\nxttok% et si le prochain token n'est pas un espace¤\idx*\space¤ + \let\nextaction\relax% ne rien faire ensuite + \fi + \fi + \nextaction% faire l'action décidée ci-dessus +} + +Voici un algorithme élémentaire +\algorithm[\#]{Algorithme {\bf MinMax}}| +macro ~Min_Max~ (#1) % afficher la valeur max et min d'une liste de valeurs #1 +W$V_{max}:=-\infty$ % $-\infty$ ou la plus petite valeur possible +W$V_{min}:=+\infty$ % $+\infty$ ou la plus grande valeur possible +W~Pour~ chaque $x$ dans (#1) +WW~Si~ $x>V_{max}$ +WWW~alors~ $V_{max}:=x$ +WW~FinSi~ +WW~Si~ $x<V_{min}$ +WWW~alors~ $V_{min}:=x$ +WW~FinSi~ +W~FinPour~ +WAfficher "$V_{min}$ et $V_{max}$" +~FinMin_Max~| +Suite du texte.../% TODO : dans le fichier des codes, remplacer "W" par "TAB" +\endgroup + +L'algorithme obtenu peut franchir des sauts de page et donc s'étendre sur plusieurs pages. Ce comportement peut être facilement interdit en englobant la totalité de l'algorithme dans une boite verticale de type \idx\vbox ou \idx\vtop. On peut par exemple remplacer le \idx\begingroup de la ligne \no\aaa{} par \idx\vbox\idx\bgroup qui marquera le début de la boite et en profiter, si on le souhaite pour ajuster la largeur \idx\hsize. Il faudra également remplacer le \idx\endgroup de la ligne \no\bbb{} par \idx\egroup qui fermera la boite. + +\chapter*{Conclusion} +\addcontentsline{toc}{part}{Conclusion} + +Nous voilà, cher lecteur, arrivés à la fin de notre périple. Nous en savons beaucoup plus qu'au début, mais il reste tant à découvrir, tant à expérimenter\ldots{} Le maitre mot est d'essayer de coder par soi même; c'est un poncif tant c'est répété, mais c'est en programmant qu'on devient programmeur. Pas en lisant un livre sur le sujet. + +Si les notions abordées dans ce livre ont balayé le plus large champ possible pour permettre un début d'autonomie, elles ne sont pas exhaustives. Par exemple, nous n'avons jamais parlé de la routine de sortie qui occupe à elle seule un chapitre entier (le \no23) du \TeX book. De la même façon, pas un mot n'a été dit sur les insertions, mécanisme souple et puissant utilisé dans les notes de bas de page ou les notes marginales. Nous avons à peine effleuré les alignements et les tableaux de \TeX{} alors que ce sujet est d'une grande richesse. Il y reste donc des pans entiers à découvrir\ldots +\grandsaut + +Bonne chance donc et avant tout, cher lecteur, prenez du plaisir avec \TeX{} ! +%| | +%| Fin partie 5 | +%|____________________________________________________________________________| + +% ____________________________________________________________________________ +%| | +%| Annexes | +%| | +\immediate\closeout\reglewrite +\part{Annexes} +\chapter{Débogage}\idx*[|(]{débogage} +Le débogage est un art difficile qui consiste à trouver pour quelle(s) raison(s) un programme ne fonctionne pas comme on s'y attend. Certes, la relecture du code accompagnée de la simulation mentale de son fonctionnement permet parfois de trouver où se trouve l'erreur. Bien souvent hélas, cet exercice est insuffisant pour débusquer l'endroit où se cache le «\emph{bug}» . Il faut alors s'en remettre à des instructions de débogage. + +\section{Délivrer les informations stockées} +Nous allons d'abord examiner les instructions qui font remonter des entrailles de \TeX{} ce qui est contenu dans les différentes structures de données permettant de stocker de l'information (primitives, macros ou registres). Nous avons déjà vu que \idx\meaning\verb|<token>| se développait en tokens de catcode 12\idx*{catcode!12 (autre)} qui, selon ce qu'est le \verb|<token>| : + +\begin{itemize} + \item est le nom de la primitive si ce \verb|<token>| est une primitive ; + \item est le \idx{texte de paramètre} et le \idx{texte de remplacement} dans le cas d'une macro; + \item est le \verb|<token>| lui-même accompagné de sa nature si c'est un caractère. +\end{itemize} + +\showcode/a) \meaning a\par +b) \meaning {\par +c) \meaning _\par +d) \meaning\def\par % une primitive +e) \meaning\baselineskip\par% une primitive¤\idx*\baselineskip¤ +f) \long\def\foo#1#2.#3{#1 puis #2 et #3 !}\meaning\foo¤\idx*\long¤/ + +La primitive \idx\show a un comportement identique sauf que ces informations sont écrites dans le \idx[!log]{fichier} \verb|log| et sur le terminal. Cela présente l'avantage de ne pas surcharger l'affichage comme peut le faire \idx\meaning, à condition d'aller lire le \idx[!log]{fichier} \verb|log| après la compilation. + +Pour connaitre l'argument d'une macro, \idx\show manque de souplesse puisqu'il faut définir une macro auxiliaire dont le texte de remplacement est cet argument et enfin, donner cette macro à \idx\show : + +\errcode/\def\foo#1#2{% + \def\argA{#1}\show\argA% débogage¤\idx*\show¤ + Bonjour #1 et #2} +\foo{{\bf X}}{Y}¤\idx*\bf¤/{\ttfamily> \string\argA=macro:\par +->\string{\string\bf\space X\string}.} + +Le moteur \eTeX{}\idx*{moteur!etex} fournit la primitive\idx*\showtokens + +\centrecode-\showtokens{<ensemble de tokens>}- + +\noindent et écrit sur le terminal et dans le \idx[!log]{fichier} \verb|log| cet \verb|<ensemble de tokens>|. De plus, cette primitive devant être suivie d'une accolade ouvrante, elle obéit à la règle que partagent les primitives possédant cette propriété : elle développe tout jusqu'à trouver cette accolade ouvrante. + +\errcode/\def\foo#1#2{% + \showtokens{#1}% débogage¤\idx*\showtokens¤ + Bonjour #1 et #2} +\foo{{\bf X}}{Y}/{\ttfamily>\string{\string\bf\space X\string}.} +\grandsaut + +La primitive \idx\show reste pourtant inopérante pour afficher quel est le contenu d'un registre, qu'il soit primitive (comme \idx\baselineskip) ou défini par l'utilisateur (comme un registre d'entier par exemple). + +\errcode/\newtoks\footoks +\footoks={foo bar} +\show\footoks¤\idx*\show¤/{\ttfamily> \string\footoks=\string\toks62.} + +Pour extraire les valeurs contenues dans des registres de dimension, de ressort, d'entier ou de tokens, la primitive \idx\showthe doit être utilisée : + +\errcode/\showthe\baselineskip% registre-primitive¤\idx*\showthe\idx*\baselineskip¤ +\newtoks\footoks \footoks={foo bar} \showthe\footoks¤\idx*\newtoks¤ +\newcount\foocount \foocount=987 \showthe\foocount¤\idx*\newcount¤ +\newdimen\foodimen \foodimen=3.14pt \showthe\foodimen¤\idx*\newdimen¤ +\newskip\fooskip \fooskip=10 pt plus 1pt minus 1fil \showthe\fooskip¤\idx*\newskip¤ +\newbox\foobox \foobox\hbox{foo} \showthe\foobox¤\idx*\newbox\idx*\showthe¤/{> 12.0pt.\par +> foo bar.\par +> 987.\par +> 3.14pt.\par +> 10.0pt plus 1.0pt minus 1.0fil.\par +> 101.} + +Le dernier registre, celui de boite, échappe à \idx\showthe. Cela est dû au fait que \idx\newbox définit \verb|\foobox| à l'aide de \idx\chardef puisqu'il n'existe pas de primitive \verb|\boxdef|. Nous avons vu que pour demander à \TeX{} de donner la composition d'une boite, il faut passer par la primitive \verb|\showbox| (voir page~\pageref{showbox}). +\grandsaut + +Correctement placer les instructions de débogage telles que \idx\show, \idx\showthe et \idx\showtokens afin de se donner le maximum de chances de trouver le dysfonctionnement n'est pas trivial et demande souvent plusieurs essais-erreurs. Bien souvent, l'enquête consiste à suivre l'évolution du \idx{texte de remplacement} d'une macro (avec \idx\show{}), comprendre comment se transmet un argument entre plusieurs macros (avec \idx\showtokens{}) ou «\emph{tracer}» la valeur d'un registre (avec \idx\showthe{}). + +\section{Délivrer des informations sur l'exécution} +\TeX{} fournit également des primitives qui permettent de contrôler quelles informations seront affichées dans le \idx[!log]{fichier} \verb|log| lors de l'exécution du code. Une fois la compilation faite, l'examen de ces informations retrace, selon la précision demandée, ce que \TeX{} a fait. + +La primitive \idx\tracingmacros se comporte comme un registre d'entier. Lorsqu'elle reçoit un entier strictement positif, \TeX{} donnera dans le \idx[!log]{fichier} \verb|log| des informations sur chaque macro développée : son nom, son texte de paramètre, son texte de remplacement et si elle admet des arguments, la valeur des arguments lus au moment du développement. + +\errcode-\def\foo#1,#2\endfoo{Bonjour #1 et #2} +\tracingmacros=1 +\foo moi,toi\endfoo +\tracingmacros=0-{\ttfamily\string\foo\space\#1,\#2\string\endfoo{} ->Bonjour \#1 et \#2\par +\#1<-moi\par +\#2<-toi} + +Une autre aide au diagnostic est fournie par la primitive \idx\tracingcommands, qui est également de type entier. En assignant un nombre strictement positif à cette primitive, \TeX{} écrira dans le \idx[!log]{fichier} \verb|log| toutes les commandes qu'il exécute. Si l'entier vaut 2 ou plus, les tests et leurs issues seront également montrés. + +\errcode-\tracingcommands=2 +Foo \hbox{et \ifnum2>1 bar\else toi\fi}\par +Suite +\tracingcommands=0-{\ttfamily\{vertical mode: the letter F\}\par +\{horizontal mode: the letter F\}\par +\{blank space \}\par +\{\string\hbox\}\par +\{restricted horizontal mode: the letter e\}\par +\{blank space \}\par +\{\string\ifnum\}\par +\{true\}\par +\{the letter b\}\par +\{\string\else\}\par +\{end-group character \}\}\par +\{horizontal mode: \string\par\}\par +\{vertical mode: the letter S\}\par +\{horizontal mode: the letter S\}\par +\{blank space \}\par +\{\string\tracingcommands\}\par} + +Les espaces parasites générés par des fins de ligne non commentées peuvent facilement être débusqués en cherchant «\texttt{blank space}» dans le \idx[!log]{fichier} \verb|log|. +\grandsaut + +Pour déboguer des programmes, la macro \idx\tracingrestores peut également s'avérer utile. Elle est également de type entier et indique dans le \idx[!log]{fichier} \verb|log| ce que \TeX{} garde en mémoire lorsque des assignations sont locales. Ces instructions placées dans la \emph{\idx{pile de sauvegarde}} sont dépilées à la sortie du groupe pour restaurer la variable à son état antérieur. Si une assignation est globale, rien n'est placé dans la pile et la dernière valeur globale assignée sera celle en vigueur à la sortie du groupe + +Prenons un exemple inspiré de celui du \TeX book. Afin d'éviter de définir un compteur, utilisons celui qui porte le \no255 et définissons la macro \verb|\i| qui incrémente ce compteur. + +\errcode/\def\i{\advance\count255 1 } +\tracingrestores=1 +\count255=2 {\i\i\i}¤\defline\aaa¤ +\tracingrestores=0/{\ttfamily\{restoring \string\count255=2\}} + +L'instruction «\verb|{restoring \count255=2}|» reflète ce qui exécuté à la fin du groupe. L'instruction «\verb|\count255=2|» a été placée sur la \idx{pile de sauvegarde} lorsque le premier \verb|\i| a été exécuté. + +Lorsqu'une assignation est globale, la dernière valeur globale assignée devient celle qui sera en vigueur à la sortie du groupe. Dès lors, les précédentes instructions de sauvegarde placées sur la \idx{pile de sauvegarde} deviennent inutiles, mais elles y restent et seront ignorées à la sortie du groupe. Mettons ceci en évidence avec la macro \verb|\gi| qui incrémente globalement le compteur \no255 : + +\errcode/\def\i{\advance\count255 1 } \def\gi{\global\i}¤\idx*\global¤ +\tracingrestores=1 +\count255=2 {\i\i\gi\i\gi\i} +\tracingrestores=0/{\{restoring \string\count255=7\}\par +\{retaining \string\count255=7\}\par +\{retaining \string\count255=7\}} + +Les empilements sur la \idx{pile de sauvegarde} ont lieu : + +\begin{enumerate} + \item lorsque le premier \verb|\i| est exécuté; + \item au troisième \verb|\i|, qui modifie localement une valeur rendue globale par le \verb|\gi| précédent; + \item au dernier \verb|\i| qui à nouveau, modifie localement une valeur globale. +\end{enumerate} + +Au dépilement qui se fait dans l'\emph{ordre inverse} de l'empilement, seule la dernière valeur de sauvegarde 7 est prise en compte. Les deux derniers ordres de sauvegarde sont ignorés et la valeur 7 est retenue. Cela se traduit dans le \idx[!log]{fichier} \verb|log| par lignes contenant le mot «\texttt{retaining}» et non pas «\texttt{restoring}». + +Il faut donc être conscient qu'effectuer localement des assignations locales \emph{et} globales peut charger la \idx{pile de sauvegarde} inutilement. Dans les faits, une instruction de sauvegarde est empilée chaque fois qu'une assignation locale suit une assignation globale. La \idx{pile de sauvegarde} n'est évidemment pas infinie et un dépassement de sa capacité se traduit par une erreur à la compilation de type «\texttt{save size capacity exceeded}.»\idx*[|)]{débogage} + +\chapter{Programmer l'addition décimale}\label{decadd}§*\decadd[|(]% +Comme nous l'avons vu et expliqué à la page~\pageref{arrondis.sur.dimensions}, l'addition sur les dimensions pose des problèmes d'arrondis conduisant à des résultats faux. Ces erreurs, insignifiantes pour une dimension, sont inacceptables lorsque ces nombres sont considérés comme entités mathématiques. Il est donc utile de programmer une macro purement développable §\decadd\verb|{<décimal 1>}{<décimal 2>}| qui ne présente pas les défauts de l'addition sur les dimensions. + +Ayant à notre disposition l'addition des entiers relatifs, programmer une simple addition de deux nombres décimaux relatifs semble trivial. Pourtant\ldots{} + +\section{Procédure mathématique} +Appelons $a$ et $b$ les deux nombres à additionner. Si les deux nombres sont entiers, le problème est résolu : il suffit de les additionner avec \idx\numexpr qui ne procède à aucun arrondi. + +Si l'un d'entre eux est entier, il faut d'abord le transformer en nombre décimal en ajoutant « \verb|.0| ». Ceci fait, les deux nombres $a$ et $b$ sont de la forme «$x.y$» où $x$ est un entier relatif et $y$ un entier naturel. Ensuite, nous allons transmettre le signe de $x$ à $y$ de telle sorte que l'entier représentant la partie entière et celui représentant la partie décimale aient le même signe. Par exemple, si $a=-3.14$, alors $x_a=-3$ et $y_a=-14$. + +L'addition se fait donc entre deux nombres de la forme $x_a.y_a$ et $x_b.y_b$ où $x_a$, $x_b$, $y_a$ et $y_b$ sont des entiers \emph{relatifs}. Nous allons transformer les parties décimales pour qu'elles aient le même nombre de chiffres en rajoutant à l'une d'entre-elles, si c'est nécessaire, des 0 inutiles à sa droite. Une fois ceci effectué, on définit $10^n$ comme la puissance de 10 immédiatement supérieure à $|y_a|$ ou $|y_b|$ que l'on nomme «seuil de retenue». + +La méthode consiste ensuite à ajouter indépendamment les parties entières et les parties décimales. Pour que les choses soient plus claires, les parties entières et décimales sont séparées par une ligne verticale : + +\begin{centrage}\small + \begin{tabular}{rr|l} + &$x_a$&$y_a$\\ + + &$x_b$&$y_b$\\\hline + &$x$ &$y$ + \end{tabular} +\end{centrage} + +On examine ensuite les nombres relatifs $x$ et $y$ obtenus. Si l'on note $\sigma_x$ le signe de $x$ (ou celui de $y$ si $x$ est nul) et $\sigma_y$ celui de $y$, alors, le cas est favorable se produit lorsque $|y|<10^n$ (pas de retenue) et $\sigma_x=\sigma_y$. Le résultat est alors le nombre décimal + +\[ +\begin{cases} +x.|y|&\text{ si } x\neq0\\ +\sigma_y0.|y|&\text{ si } x=0 +\end{cases} +\] + +Deux cas défavorables peuvent se présenter : + +\begin{enumerate} + \item $x$ et $y$ sont de signes contraires. Ces additions en sont des illustrations : + \begin{centrage}\small + \begin{tabular}{rr|r} + &$7$&$20$\\ + + &$-4$&$-45$\\\hline + &$3$ &$-25$ + \end{tabular}\hskip0.1\linewidth + \begin{tabular}{rr|r} + &$2$&$70$\\ + + &$-4$&$-15$\\\hline + &$-2$ &$55$ + \end{tabular} + \end{centrage} + + Dans ce cas, afin que $x$ et $y$ satisfassent les conditions du cas favorable, les modifications suivantes doivent y être apportées : + + \[\begin{cases} + x\longleftarrow x-\left(\sigma_x1\right)\\ + y\longleftarrow y-\left(\sigma_y10^n\right) + \end{cases}\] + + Les additions précédentes deviennent : + \begin{centrage}\small + \begin{tabular}{rr|r} + &$7$&$20$\\ + + &$-4$&$-45$\\\hline + &$3$ &$-25$\\ + &$3-(1)$&$-25-(-100)$\\ + = &$2$&$75$ + \end{tabular}\hskip0.1\linewidth + \begin{tabular}{rr|r} + &$2$&$70$\\ + + &$-4$&$-15$\\\hline + &$-2$ &$55$\\ + &$-2-(-1)$&$55-(100)$\\ + = &$-1$&$-45$ + \end{tabular} + \end{centrage} + + \item $x$ et $y$ sont de même signe \emph{et} $|y|\geqslant10^n$, comme dans ces additions : + \begin{centrage}\small + \begin{tabular}{rr|r} + &$7$&$20$\\ + + &$4$&$95$\\\hline + &$11$ &$115$ + \end{tabular}\hskip0.1\linewidth + \begin{tabular}{rr|r} + &$-2$&$-70$\\ + + &$-4$&$-65$\\\hline + &$-6$ &$-135$ + \end{tabular} + \end{centrage} + Pour que $x$ et $y$ vérifient les conditions du cas favorable, leurs valeurs doivent être modifiées selon la règle suivante : + + \[\begin{cases} + x\longleftarrow x+\left(\sigma_x1\right)\\ + y\longleftarrow y-\left(\sigma_y10^n\right) + \end{cases}\] + + Les deux additions précédentes deviennent alors : + \begin{centrage}\small + \begin{tabular}{rr|r} + &$7$&$20$\\ + + &$4$&$95$\\\hline + &$11$ &$115$\\ + &$11+(1)$&$115-(100)$\\ + = &$12$&$15$ + \end{tabular}\hskip0.1\linewidth + \begin{tabular}{rr|r} + &$-2$&$-70$\\ + + &$-4$&$-65$\\\hline + &$-6$ &$-135$\\ + &$-6+(-1)$&$-135-(-100)$\\ + = &$-7$&$-35$\\ + \end{tabular} + \end{centrage} +\end{enumerate} + +\section{Mise en \oe uvre} +\subsection{Rendre les nombres décimaux} +Dans un premier temps, la macro §\decadd doit s'assurer que les parties décimales sont bien présentes et le cas échéant, ajouter «\verb|.0|» comme partie décimale. Une fois ceci fait, elle passera la main à une macro auxiliaire \verb|\decadd@i|, à arguments délimités. La macro §\ifnodecpart, utilisée par §\formatnum, sera chargée de tester si le point est présent ou pas. + +\showcode/\catcode`@11 +\def\ifnodecpart#1{\ifnodecpart@i#1.\@nil}¤§*\ifnodecpart¤ +\def\ifnodecpart@i#1.#2\@nil{\ifempty{#2}}¤§*\ifempty¤ +\def\decadd#1#2{% #1 et #2=nombre à additionner¤§*\decadd¤ + \ifnodecpart{#1}% si #1 est un entier¤§*\ifnodecpart¤ + {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr¤§*\ifnodecpart¤ + {\number\numexpr#1+#2\relax}%¤\idx*\numexpr¤ + {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 + } + {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2¤§*\ifnodecpart¤ + {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 + {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes + }% +} +\def\decadd@i#1.#2\@nil#3.#4\@nil{% affiche les parties entières et décimales reçues + $x_a=#1\quad y_a=#2\qquad x_b=#3\quad y_b=#4$ +} +\catcode`@12 +a) \decadd{5}{9.4}\par +b) \decadd{-3.198}{-6.02}\par +c) \decadd{0.123}{123}¤§*\decadd¤/% +\def\olddecadd#1#2{% #1 et #2=nombre à additionner + \ifnodecpart{#1}% si #1 est un entier¤§*\ifnodecpart¤ + {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr¤§*\ifnodecpart¤ + {\number\numexpr#1+#2\relax}% + {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 + } + {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2¤§*\ifnodecpart¤ + {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 + {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes + }% +} + +\subsection{Rendre les parties décimales de même longueur} +Venons-en maintenant à la partie la plus complexe. Nous allons programmer la §\addzeros qui va se charger d'ajouter des 0 inutiles à la fin d'un des deux nombres \verb|#2| ou \verb|#4| reçus par \verb|\decadd@i| pour que ces parties décimales aient le même nombre de chiffres. Afin de rester purement développable, cette macro va utiliser le système des « arguments réservoirs ». Son texte de paramètre sera + +\centrecode-#1#2/#3.#4#5/#6.#7/- + +\noindent où : + +\begin{itemize} + \item \verb|#1| est le premier chiffre de $y_a$ et \verb|#2| les chiffres restants; + \item \verb|#3| est le réservoir contenant les chiffres de $y_a$ déjà examinés; + \item \verb|#4|, \verb|#5| et \verb|#6| jouent le même rôle pour $y_b$ que \verb|#1|, \verb|#2| et \verb|#3| pour $y_a$; + \item \verb|#7| est un réservoir recevant le seuil de retenue : il contient 1 au début et un «\verb|0|» est ajouté à chaque itération. +\end{itemize} + +L'idée directrice est que le point d'arrêt se produit lorsque les deux arguments délimités \verb|#2| et \verb|#5| sont vides. Dans le cas contraire, à chaque itération, \verb|#1| passe en fin de \verb|#3|, \verb|#4| passe en fin de \verb|#6| et un 0 est ajouté à \verb|#7|. Si un des arguments \verb|#2| ou \verb|#5| devient vide, il faut alimenter l'argument non délimité \verb|#1| ou \verb|#4| qui le précède avec 0. + +Pour se représenter la situation, imaginons que les nombres $y_a=457$ et $y_b=689714$ soient traités par §\addzeros et que l'appel suivant ait été fait : + +\centrecode-\addzeros457/.689714/.1-§*\addzeros + +\noindent Le chiffre 4 et le chiffre 6 vont être transférés dans les réservoirs et un 0 sera ajouté après le 1. L'appel récursif après la première itération sera donc + +\centrecode-\addzeros57/4.89714/6.10- + +\noindent Puis le processus identique sera effectué et l'appel suivant sera + +\centrecode-\addzeros7/45.9714/68.100-§*\addzeros + +Comme il ne reste plus aucun chiffre après le 7 (ce qui signifie que l'argument délimité \verb|#2| est vide), le 7 sera transféré dans le réservoir, mais un 0 sera mis à sa place pour les appels suivants. Ces appels seront donc: + +\begin{centrage}\small + \verb|\addzeros0/457.714/689.1000|\par§*\addzeros + \verb|\addzeros0/4570.14/6897.10000|\par + \verb|\addzeros0/45700.4/68971.100000|§*\addzeros +\end{centrage} + +Enfin, comme il ne reste plus de chiffre ni après $y_a$ ni après $y_b$, le \idx{point d'arrêt} est atteint : \verb|#2| et \verb|#5| sont vides. La macro transfère une dernière fois un 0 en fin de réservoir \verb|#3|, transfère aussi le 4 en fin de réservoir \verb|#5| et ajoute un dernier 0 au dernier argument pour mettre dans le flux de lecture de \TeX{} les trois arguments suivants : + +\centrecode-{457000}{689714}{1000000}- + + Pour comprendre le cheminement des arguments, la macro affiche ici ce qu'elle fait : + +\showcode|\def\addzeros#1#2/#3.#4#5/#6.#7/{%¤§*\addzeros¤ + Arguments reçus : #1#2/#3.#4#5/#6.#7/\par% afficher ce que la macro reçoit + \ifempty{#2}% si #1 est le dernier chiffre de y¤§*\ifempty¤ + {\ifempty{#5}% et si #4 est le dernier chiffre de y2¤§*\ifempty¤ + {Résultat final : \detokenize{{#3#1}{#6#4}{#70}}}% afficher le résultat final + {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 + } + {\ifempty{#5}% si #4 est le dernier chiffre de y2¤§*\ifempty¤ + {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 + {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides + }% +} +\addzeros457/.689714/.1/¤§*\addzeros¤| + +\subsection{Additionner séparément} +Intéressons-nous tout d'abord à la macro qui va donner le signe du nombre décimal. Si $x$ est sa partie entière, la macro §\sgn, vue précédemment ne va pas convenir. Voici quel était son code : + +\centrecode|\def\sgn#1{\ifnum#1<0 -\fi}|§*\sgn + +Par exemple, si le décimal à examiner vaut $-0.123$, alors, la partie entière $x=-0$ et \verb|\sgn{-0}| ne revoit pas un signe négatif. Pour se prémunir de cette erreur, la macro §\true@sgn que nous allons employer renverra un signe «\verb|-|» lorsque son argument commence par «\verb|-|». Pour ce faire, elle se contente de tester le signe de son argument une fois lui avoir ajouté le chiffre 1 en dernière position : + +\centrecode|\def\true@sgn#1{\ifnum#11<\z@-\fi}|\idx*\z@§*\true@sgn + +Il faut rester conscient qu'ajouter le chiffre 1 au nombre \verb|#1| et évaluer le tout avec \verb|\ifnum| n'est pas anodin. Le résultat évalué par \verb|\ifnum|, qui est $\verb|#1|\times10\pm1$, doit être dans l'intervalle $I=[-2^{31}+1\;\string;\;2^{31}-1]$. Par conséquent, l'intervalle dans lequel doit se trouver \verb|#1| est plus étroit que $I$. Même si cet écueil n'est pas vraiment gênant, on aurait pu s'en prémunir en testant si \verb|#1| commence par «\verb|-|». + +La macro \verb|\decadd@i| va utiliser le fait que \idx\romannumeral\verb|-`\@|, placé avant la macro §\addzeros, lance le développement maximal pour obtenir les trois arguments finaux. Elle appellera donc une nouvelle macro \verb|\decadd@ii|, admettant ces trois arguments auxquels nous rajouterons, en 4\ieme{} et 5\ieme{} arguments, les parties entières. La macro \verb|\decadd@ii|, qui reçoit ces 5 arguments, se contentera d'additionner les parties décimales entre elles (après avoir leur avoir ajouté le signe des parties entières) ainsi que les parties entières entre elles et transmettra le tout à la macro suivante \verb|\decadd@iii| : + +\showcode|\catcode`\@11 +\def\addzeros#1#2/#3.#4#5/#6.#7/{%¤§*\addzeros¤ + \ifempty{#2}% si #1 est le dernier chiffre de y1¤§*\ifempty¤ + {\ifempty{#5}% et si #4 est le dernier chiffre de y2¤§*\ifempty¤ + {{#3#1}{#6#4}{#70}}% redonner les 3 arguments + {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 + } + {\ifempty{#5}% si #4 est le dernier chiffre de y2¤§*\ifempty¤ + {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 + {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides + }% +} +\def\decadd#1#2{% #1 et #2=nombre à additionner¤§*\decadd¤ + \ifnodecpart{#1}¤§*\ifnodecpart¤ + {\ifnodecpart{#2}{\number\numexpr#1+#2\relax}{\decadd@i#1.0\@nil#2\@nil}} + {\ifnodecpart{#2}{\decadd@i#1\@nil#2.0\@nil}{\decadd@i#1\@nil#2\@nil}}%¤§*\ifnodecpart¤ +} +\def\decadd@i#1.#2\@nil#3.#4\@nil{% + \expandafter\decadd@ii + \romannumeral-`\0\addzeros#2/.#4/.1/% se développe en 3 arguments¤\idx*\romannumeral§*\addzeros¤ + {#1} + {#3}% +} +\def\decadd@ii#1#2#3#4#5{% +% #1 et #2=parties décimales (mêmes longueurs); +% #3=seuil de retenue; #4 et #5=parties entières + \exptwoargs{\decadd@iii{#3}}% envoyer le seuil de retenue tel quel¤§*\exptwoargs¤ + % sommer les parties décimales signées + {\number\numexpr\true@sgn{#4}#1+\true@sgn{#5}#2\relax}%¤\defline\aaa§*\true@sgn¤ + % et les parties entières + {\number\numexpr#4+#5\relax}% +} +\def\decadd@iii#1#2#3{% + seuil de retenue = #1 \qquad nombre = "#3"."#2"% +} +\catcode`\@12 +a) \decadd{6.7}{3.498}\par +b) \decadd{1.67}{-4.9}\par +c) \decadd{3.95}{2.0005}\par +d) \decadd{1.007}{2.008}\par +e) \decadd{-7.123}{3.523}¤§*\decadd¤| + +\subsection{Correctement gérer les 0} +Les deux derniers cas montrent que les 0 au début de la partie décimale sont supprimés par le \idx\number de la ligne \no\aaa{} tandis que les 0 inutiles de la fin sont conservés. C'est exactement le comportement inverse de celui qui est souhaité ! Afin de rétablir une partie décimale ayant des 0 là où il faut, nous devons écrire une macro + +\centrecode-\format@dec{<partie décimale>}{<seuil de retenue>}- + +\noindent qui est purement développable et qui supprime les 0 de droite tout en ajoutant les 0 de gauche. +\grandsaut + +Si par exemple, on obtient une partie décimale égale à 710 avec un seuil de retenue de 100000, la partie décimale avait 5 chiffres, c'est-à-dire 00710 qui se doit être transformée en 0071. Pour parvenir à ses fins, la macro \verb|\format@dec| va d'abord ajouter le seuil de retenue à la valeur absolue de la partie décimale : + +\[710+100000=100710\] + +\noindent Elle va ensuite inverser le résultat avec §\reverse + +\[017001\] + +\noindent Puis soumettre le nombre obtenu à \idx\number ce qui aura pour effet de supprimer les 0 de début (qui étaient ceux de fin) + +\[17001\] + +\noindent Ensuite, il faut à nouveau inverser le résultat pour rétablir l'ordre de chiffres + +\[10071\] + +\noindent Enfin, il reste à manger le "1" en première position avec \verb|\gobone| pour obtenir $0071$. L'ordre des opérations est critique : + +\showcode/\catcode`\@11 +\def\format@dec#1#2{% #1=partie décimale #2=seuil de retenue + \expandafter\gobone% le \gobone agira en dernier, + \romannumeral-`\0% mais avant, tout développer¤\idx*\romannumeral¤ + \expandafter\reverse\expandafter% et retarder le \reverse de fin¤§*\reverse¤ + % pour développer son argument qui + {\number% développe tout et évalue avec \number + \expandafter\reverse\expandafter% l'inversion de¤§*\reverse¤ + {\number\numexpr\abs{#1}+#2\relax}% |#1|+#2¤§*\abs¤ + }% +} +a) \format@dec{710}{100000}\par +b) \format@dec{6}{100}\par +c) \format@dec{-12300}{1000000} +\catcode`\@12/ + +\subsection{Calculer le résultat} +Reprenons notre chemin dans le texte de remplacement de \verb|\decadd@iii|. Elle doit effectuer les éventuelles modifications sur les parties entières et décimales selon que $x$ et $y$ sont de signes contraires et sinon, effectuer d'autres modifications si $|y|\geqslant10^n$. Ces tests ne présentent pas de difficulté et seront faits avec \tidx{ifnum}. À la toute fin, la macro \verb|\decadd@iv| reçoit 3 arguments de \verb|\decadd@iii|, la partie entière, la partie décimale et le seuil de retenue. Cette macro se chargera d'afficher \verb|#1|, et si \verb|#2| est différent de 0, le point décimal ainsi que la partie décimale formatée avec \verb|\format@dec|. Pour que le 2-développement de §\decadd donne le résultat, le développement maximal sera engagé via \idx\romannumeral début de la macro. Comme rien n'est dirigé vers l'affichage jusqu'à \verb|\decadd@iv|, ce développement sera en vigueur à l'entrée de cette macro. Il faut ensuite le propager pour qu'il atteigne \verb|\format@dec| avant que la partie entière ne soit affichée. + +Voici le code complet : + +\showcode|\catcode`@11 +\def\true@sgn#1{\ifnum#11<\z@-\fi}¤\idx*\z@§*\true@sgn¤ +\def\decadd#1#2{% #1 et #2=nombre à additionner¤§*\decadd¤ + \romannumeral-`\.% tout développer jusqu'à l'affichage du nombre (\decadd@iv) + \ifnodecpart{#1}% si #1 est un entier¤§\ifnodecpart¤ + {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr¤§*\ifnodecpart¤ + {\numexpr#1+#2\relax}% + {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 + } + {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2¤§*\ifnodecpart¤ + {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 + {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes + }% +} +\def\decadd@i#1.#2\@nil#3.#4\@nil{% + \expandafter\decadd@ii + \romannumeral-`\0\addzeros#2/.#4/.10/% se développe en 3 arguments¤\idx*\romannumeral§*\addzeros¤ + {#1} + {#3}% +} +\def\decadd@ii#1#2#3#4#5{% +% #1 et #2=parties décimales (mêmes longueurs) +% #3=seuil de retenue; #4 et #5=parties entières + \exptwoargs{\decadd@iii{#3}}% envoyer le seuil de retenue tel quel¤§*\exptwoargs¤ + % sommer les parties décimales signées + {\number\numexpr\true@sgn{#4}#1+\true@sgn{#5}#2\relax}%¤\defline\aaa§*\true@sgn¤ + % et les parties entières + {\number\numexpr#4+#5\relax}% +} +\def\decadd@iii#1#2#3{% #1=seuil de retenue #2=partie décimale #3= partie entière + \ifnum\true@sgn{#2}\true@sgn{\ifnum#3=\z@#2\else#3\fi}1=-1 % si les signes sont + % différents¤\idx*\z@§*\true@sgn¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\exptwoargs\decadd@iv% transmettre les arguments modifiés :¤§*\exptwoargs¤ + {\number\numexpr#3-\true@sgn{#3}1}% #3:=#3-sgn(#3)1¤\idx*\numexpr¤ + {\number\numexpr#2-\true@sgn{#2}#1}% #2:=#2-sgn(#2)10^n¤§*\true@sgn¤ + {#1}% + }% si les signes sont égaux + {\ifnum\abs{#2}<#1 % et si abs(y)<10^n¤§*\abs¤ + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\decadd@iv{#3}{#2}{#1}% tranmettre les arguments tels quels + } + {\exptwoargs\decadd@iv% sinon¤§*\exptwoargs¤ + {\number\numexpr#3+\true@sgn{#3}1}% #3:=#3+sgn(#3)1 + {\number\numexpr#2-\true@sgn{#2}#1}% #2:=#2-sgn(#2)10^n + {#1}% + }% + }% +} +\def\decadd@iv#1#2#3{% affiche le décimal "#1.#2" + % le développement maximal initié par le \romannumeral de \decadd est actif + \ifnum#1=\z@\ifnum#2<\z@% si #1=0 et #2<0¤\idx*\z@¤ + \expandafter\expandafter\expandafter% transmettre le développement à \number + -% puis afficher le signe "-" + \fi\fi + \number#1% affiche #1 qui est la somme des parties entières + % poursuivre le développement initié par \number + \unless\ifnum#2=\z@% si la partie décimale est différente de 0¤\tidx*{unless}¤ + \antefi% se débarrasser de \fi¤§*\antefi¤ + \expandafter.% afficher le "." décimal après avoir + \romannumeral-`\0\format@dec{#2}{#3}% correctement géré les 0 de #2¤\idx*\romannumeral¤ + \fi +} +\def\addzeros#1#2/#3.#4#5/#6.#7/{%¤§*\addzeros¤ + \ifempty{#2}% si #1 est le dernier chiffre de y1¤§*\ifempty¤ + {\ifempty{#5}% et si #4 est le dernier chiffre de y2 + {{#3#1}{#6#4}{#7}}% redonner les 3 arguments + {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0¤§*\addzeros¤ + } + {\ifempty{#5}% si #4 est le dernier chiffre de y2¤§*\ifempty¤ + {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 + {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides¤§*\addzeros¤ + }% +} +\def\format@dec#1#2{% #1=partie décimale #2=seuil de retenue + \expandafter\gobone% le \gobone agira en dernier, + \romannumeral-`\0% mais avant, tout développer¤\idx*\romannumeral¤ + \expandafter\reverse\expandafter% et retarder le \reverse de fin¤§*\reverse¤ + % pour développer son argument qui + {\number% développe tout et évalue avec \number + \expandafter\reverse\expandafter% l'inversion de¤§*\reverse¤ + {\number\numexpr\abs{#1}+#2\relax}% abs(#1)+#2¤§*\abs\idx*\z@¤ + }% +} +\catcode`@12 +a) $-3.78+1.6987=\decadd{-3.78}{1.6987}$\par +b) $3.56-3.06=\decadd{3.56}{-3.06}$\par +c) $4.125+13.49=\decadd{4.125}{13.49}$\par +d) $-0.99+1.005=\decadd{-0.99}{1.005}$\par +e) $16.6-19.879=\decadd{16.6}{-19.879}$\par +f) $5.789-0.698=\decadd{5.789}{-0.698}$\par +g) $0.123-0.123=\decadd{0.123}{-0.123}$\par +h) $3.14-16.4912=\decadd{3.14}{-16.4912}$\par +i) $0.1-0.98=\decadd{0.1}{-0.98}$\par +j) $2.43+7.57=\decadd{2.43}{7.57}$\par +h) \edef\foo{\decadd{1.23}{9.78}}\meaning\foo\par +j) \detokenize\expandafter\expandafter\expandafter{\decadd{3.14}{-8.544}}¤§*\decadd¤|§*\decadd[|)]% + +\chapter{Primitives spécifiques à un moteur} +Les nouvelles primitives introduites par \eTeX{} font désormais partie du vocabulaire reconnaissable par tous les moteurs (pdf\TeX, \XeTeX{} et lua\TeX). + +Cette compatibilité est malheureusement cassée par des primitives \emph{spécifiques} aux moteurs, c'est-à-dire prises en charge par lui seul. On franchit là une ligne de démarcation importante pour la compatibilité puisqu'utiliser ces primitives rend obligatoire la compilation avec un moteur spécifique. Beaucoup de ces primitives spécifiques sont certes \emph{très} pratiques, mais il est utile de se demander si les fonctionnalités additionnelles qu'elles apportent valent une rupture de compatibilité. +\grandsaut + +Il n'est évidemment pas question de décrire ici toutes les primitives spécifiques à tous les moteurs. Cela prendrait beaucoup de place (pdf\TeX{} en a plus de 130 !) pour un intérêt limité, car la plupart sont correspondent à un besoin très précis et sont d'une utilisation assez pointue. Les documentations des moteurs en dressent la liste exhaustive, en précisent la syntaxe et décrivent les fonctionnalités offertes. Nous nous contenterons de présenter deux primitives de pdf\TeX{}, moteur utilisé pour compiler le code source de ce livre. + +Le lecteur pourra, s'il le souhaite, se plonger dans la documentation de son moteur préféré, explorer les primitives qu'il offre et pourquoi pas, les utiliser pour ses besoins propres. Il faut cependant garder à l'esprit que ces primitives imposant un moteur sont déconseillées si le code source a vocation à être diffusé et compilé par d'autres personnes non avisées de la rupture de compatibilité. + +\section{Comparer deux textes avec \texttt{\char`\\ pdfstrcmp}}\idx*[|(]{comparer des textes} +La primitive purement développable \idx*\pdfstrcmp + +\centrecode-\pdfstrcmp{<texte 1>}{<texte 2>}- + +\noindent permet de comparer les deux textes. Avant qu'elle n'entre en jeu, cette primitive développe au maximum ses deux arguments et les détokenize. \emph{Elle ne tient donc pas compte des catcodes} contrairement au test \verb|\ifx| : + +\centrecode-\def\foo{<texte 1>} \def\bar{<texte 2>} +\ifx\foo\bar<code vrai>\else<code faux>\fi- + +La primitive \idx\pdfstrcmp se développe en «\verb|0|» si tes textes sont égaux, en «\verb|-1|» si le \verb|<texte 1>| vient avant le \verb|<texte 2>| et en «\verb|1|» s'il vient après. Le classement des textes se fait en fonction des codes ASCII des caractères, pris successivement de gauche à droite jusqu'à ce qu'une différence apparaisse. + +\showcode/a) \pdfstrcmp{foo}{bar}\qquad¤\idx*\pdfstrcmp¤ +b) \pdfstrcmp{bar}{foo}\qquad +c) \def\foo{ABC}\pdfstrcmp{\foo}{ABC}\qquad +d) \edef\foo{\string_}\pdfstrcmp{1_2}{1\foo2}\qquad +e) \pdfstrcmp{\string\relax}{\relax}¤\idx*\string\idx*\pdfstrcmp¤/ + +La dernière comparaison est fausse, car «\idx\detokenize\verb|{\relax}|» se développe en \verb*|\relax | alors que \idx\string n'introduit pas d'espace après les séquences de contrôle. Les textes «\verb*|\relax |» et «\verb|\relax|» ne sont pas égaux. + +Cette primitive dispose d'un équivalent pour le moteur \XeTeX{}\idx*{moteur!xetex}, mais elle ne porte pas le même nom : \verb|\strcmp|.\idx*[|)]{comparer des textes} + +\section{Mesurer le temps}\idx*[|(]{chronométrer le temps d'exécution} +Voici un deuxième exemple de primitive spécifique à pdf\TeX{}, \idx\pdfelapsedtime qui permet de mesurer le temps de compilation. Cette primitive est de type entier et contient le nombre de secondes \emph{d'échelle} écoulées depuis le début de la compilation : 1 \idx{seconde d'échelle} vaut $\frac1{65536}$ de seconde tout comme un \idx{point d'échelle} vaut $\frac1{65536}$ de point. Convertir les secondes d'échelle en secondes revient à convertir les sp\idx*{sp (unité)} en pt\idx*{pt (unité)}, ce que permet très facilement la macro §\convertunit. + +\showcode/\edef\tempcompil{\number\pdfelapsedtime}%¤\idx*\pdfelapsedtime¤ +Depuis le début de la compilation, il s'est écoulé \tempcompil{} secondes d'échelle,¤\idx*{seconde d'échelle}¤ +soit \convertunit{\tempcompil sp}{pt} secondes.¤§*\convertunit¤/ + +\Qu elle que soit la durée de compilation, l'entier \idx\pdfelapsedtime ne peut excéder $2^{31}-1$ qui sera sa limite supérieure et qui représente \numprint{32768} secondes. + +La primitive \idx\pdfresettimer, qui remet à 0 le compteur interne de secondes d'échelle\idx*{seconde d'échelle}, ouvre des perspectives intéressantes. Couplée à \idx\pdfelapsedtime, elle permet de mesurer avec précision la durée qui s'écoule entre deux moments arbitraires de la compilation. Nous allons ici mettre à profit cette possibilité pour mesurer la vitesse relative entre la boucle \idx\loop...\idx\repeat (celle de \TeX{} puis celle de \LaTeX) et la boucle §\for, dont la programmation a été abordée à partir de la page \no\pageref{for}. Chaque boucle sera parcourue 100000 fois, mais, afin de mesurer la vitesse de la boucle elle-même, aucun code ne sera exécuté à chaque itération : + +\showcode/%%%%%%%%%% definition de \loop...\repeat comme plain-TeX %%%%%%%%%% +\def\loop#1\repeat{\def\body{#1}\iterate}¤\idx*\loop\idx*\repeat¤ +\def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next}¤\idx*\iterate¤ +\let\repeat=\fi¤\idx*\repeat¤ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\newcount\testcnt¤\idx*\newcount¤ +\pdfresettimer% remet le compteur à 0¤\idx*\pdfresettimer¤ + \testcnt=0 + \loop % Test no 1¤\idx*\loop¤ + \ifnum\testcnt<100000 \advance\testcnt 1 ¤\idx*\advance¤ + \repeat¤\idx*\repeat¤ +Temps 1 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \TeX)\par¤§*\convertunit\idx*\pdfelapsedtime¤ +%%%%%%%%%%%% definition de \loop...\repeat comme LaTeX %%%%%%%%%%%% +\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}% + \iterate \let\iterate\relax}¤\idx*\iterate¤ +\let\repeat\fi +\pdfresettimer% remet le compteur à 0¤\idx*\pdfresettimer¤ + \testcnt=0 + \loop % Test no 2¤\idx*\loop¤ + \ifnum\testcnt<100000 + \advance\testcnt 1 ¤\idx*\advance¤ + \repeat¤\idx*\repeat¤ +Temps 2 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \LaTeX)\par¤§*\convertunit\idx*\pdfelapsedtime¤ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\pdfresettimer% remet le compteur à 0 + \for\ii=1 to 100000\do{}% Test no 3¤§*\for¤ +Temps 3 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle for)¤§*\convertunit\idx*\pdfelapsedtime¤/ + +Notre boucle §\for semble donc environ 3 fois plus lente que la boucle \idx\loop{}\linebreak[2]\verb|...|{}\linebreak[2]\idx\repeat de \LaTeX{} qui est la plus rapide. Il faut dire «\emph{semble}», car l'incrémentation ne se fait pas sur le même type de donnée. La boucle §\for incrémente le \emph{texte de remplacement} d'une macro tandis que dans le code ci-dessus, un \emph{compteur} est incrémenté dans la boucle \idx\loop...\idx\repeat. Pour être strictement équitable et pour être sûr de comparer les codes des deux boucles, la boucle \idx\loop...\idx\repeat doit imiter la boucle §\for et incrémenter le texte de remplacement d'une macro : + +\showcode/%%%%%%%%%%%% definition de \loop...\repeat comme LaTeX %%%%%%%%%%%% +\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}%¤\idx*\loop¤ + \iterate \let\iterate\relax}¤\idx*\iterate¤ +\let\repeat\fi¤\idx*\repeat¤ +\pdfresettimer% remet le compteur à 0¤\idx*\pdfresettimer¤ + \def\ii{0}% + \loop % Test no 1¤\idx*\loop¤ + \ifnum\ii<100000 + \edef\ii{\number\numexpr\ii+1\relax}% + \repeat¤\idx*\repeat¤ +Temps 1 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \LaTeX)\par¤§*\convertunit\idx*\pdfelapsedtime¤ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\pdfresettimer% remet le compteur à 0¤\idx*\pdfresettimer¤ + \for\ii=1 to 100000\do{}% Test no 2¤§*\for¤ +Temps 2 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle for)¤§*\convertunit\idx*\pdfelapsedtime¤/ + +La différence de vitesse est maintenant plus ténue, de l'ordre de 30\%, ce qui semble une contrepartie acceptable en regard des fonctionnalités d'imbrication qu'offre §\for par rapport à \idx\loop...\idx\repeat. On en tire par ailleurs l'enseignement qu'incrémenter un compteur est \emph{plus rapide} que de recourir à \idx\numexpr et \idx\edef pour incrémenter un entier contenu dans le texte de remplacement d'une macro. En voici la démonstration : + +\showcode/\newcount\testcnt¤\idx*\newcount¤ +\pdfresettimer¤\idx*\pdfresettimer¤ +\testcnt=0 +\for\ii=1 to 100000\do{\advance\testcnt1 }¤§*\for¤ +Temps 1 : \convertunit{\pdfelapsedtime sp}{pt} s (incrémentation compteur)¤§*\convertunit\idx*\pdfelapsedtime¤ + +\pdfresettimer¤\idx*\pdfresettimer¤ +\def\foo{0}% +\for\ii=1 to 100000\do{\edef\foo{\number\numexpr\foo+1\relax}}%¤§*\for¤ +Temps 2 : \convertunit{\pdfelapsedtime sp}{pt} s (incrémentation du texte de remplacement)¤§*\convertunit\idx*\pdfelapsedtime¤/ + +On peut ainsi comparer les vitesses entre les variantes d'un algorithme afin de trouver celle qui optimise la vitesse d'exécution.\idx*[|)]{chronométrer le temps d'exécution} + +\chapter{Recueil des règles}\label{recapregle} +Les règles se trouvant dans les cadres grisés ont été assez nombreuses (il y en a exactement \number\csname cnt@regle\endcsname). Les voici rassemblées ici. +\makeatletter +\renewcommand\tidx@i[2][]{% #2 est le nom de la primitive SANS le caractère d'échappement + \print@idx{\texttt{\char92 #2}}% +} +\makeatother +\input regles.txt +%| | +%| Fin annexes | +%|____________________________________________________________________________| +\chapter*{Bibliographie} +\addcontentsline{toc}{part}{Bibliographie} + +\begin{enumerate}[leftmargin=*,itemsep=5pt,topsep=10pt] + \item \textsc{D.E. Knuth}. Le \TeX book, Vuibert, 2003.\par + {\footnotesize\verb|http://www.vuibert.fr/ouvrage-9782711748198-le-texbook.html|} + \item \textsc{D.E. Knuth}. \TeX{} The Program.\par + {\footnotesize\verb|http://tug.org/texlive/devsrc/Build/source/texk/web2c/tex.web|} + \item \textsc{V. Eijkhout}. \TeX{} by Topic, Addison-Wesley, 1992.\par + {\footnotesize\verb|http://www.eijkhout.net/texbytopic/texbytopic.html|} + \item \textsc{P.W. Abrahams \& K. Hargreaves \& K. Berry}. \TeX{} pour l'impatient.\par + {\footnotesize\verb|http://ctan.mirrorcatalogs.com/info/impatient/fr/fbook.pdf|} + \item \textsc{J. Braams \& D. Carlisle \& A. Jeffrey \& L. Lamport \&\endgraf F.Mittelbach \& C. Rowley \& R. Sch\"opf}. The \LaTeXe{} sources\par + {\footnotesize\verb|http://www.tug.org/texlive/Contents/live/texmf-dist/doc/latex/base/source2e.pdf|} + \item \textsc{E. Gregorio}. Appunti di programmazione in \LaTeX{} e \TeX.\par + {\footnotesize\verb|http://profs.scienze.univr.it/~gregorio/introtex.pdf|} +\end{enumerate} + +% ____________________________________________________________________________ +%| | +%| Index | +%| | +\cidx*[|voiraussi{mode mathématique}]\$% +\cidx*[|voiraussi{\texttt{\char92 halign}}]\&% +\idx*[|voiraussi{\texttt{\char92 chardef}}]\& +\cidx*[|voiraussi{\texttt{\char92 catcode} $\rightsquigarrow$ 13}]\\ +\idx*[|voir{\texttt{\char`\^\char`\^M}}]{retour charriot}% +\idx*[|voiraussi{\texttt{\char92 loop}}]\body +\idx*[|voiraussi{\texttt{\char92 catcode} $\rightsquigarrow$ 13}]{caractère actif}% +\idx*[|voiraussi{\texttt{\char92 hyphenchar}}]{caractère de coupure}% +\idx*[|voiraussi{\texttt{\char92 catcode} $\rightsquigarrow$ 0, «\texttt{\char92}»}]{caractère d'échappement}% +\idx*[|voiraussi{\texttt{\char92 endlinechar}, \texttt{\char92 newlinechar}}]{caractère de fin de ligne}% +\idx*[|voir{\texttt{\char92 catcode}}]{code de catégorie} +\idx*[|voiraussi{\texttt{\char92 uccode}, \texttt{\char92 uppercase}}]{code majuscule} +\idx*[|voiraussi{\texttt{\char92 lccode}, \texttt{\char92 lowercase}}]{code minuscule} +\idx*[|voiraussi{\texttt{\char92 edef}}]{code purement développable} +\idx*[|voiraussi{\texttt{\char92 /}}]{correction d'italique} +\idx*[|voiraussi{\texttt{\char92 vsplit}}]{couper une boite verticale} +\idx*[|voiraussi{\texttt{\char92 llap}, \texttt{\char92 rlap}, \texttt{\char92 hfuzz},\texttt{\char92 vfuzz}}]{débordement de boite} +\idx*[|voir{\texttt{\char92 moveleft}, \texttt{\char92 moveright}, \texttt{\char92 lower}, \texttt{\char92 raise}}]{déplacement de boite} +\idx*[|voiraussi{\texttt{\char92 edef}, \texttt{\char92 csname}, \texttt{\char92 romannumeral}, \texttt{\char92 write}, nombre $\rightsquigarrow$ définition}]{développement maximal} +\idx*[|voiraussi{\texttt{\char92 fontdimen}}]{dimensions de fonte} +\idx*[|voiraussi{\texttt{\char92 long}, \texttt{\char92 par}}]\endgraf +\idx*[|voir{compteur, nombre}]{entier} +\idx*[|voiraussi{\texttt{\char`\^\char`\^J}}]{fin de ligne}% +\idx*[|voir{macro fille}]{imbrication de macros} +\idx*[|voiraussi{\texttt{\char92 write}, \texttt{\char92 openout}}]{\immediate}% +\idx*[|voir{\texttt{\char92 loop}}]{\iterate}% +\idx*[|voir{\texttt{\char92 loop}, \texttt{\char92 for}$^\star$, \texttt{\char92 doforeach}$^\star$}]{boucle} +\idx*[|voir{«\texttt{\char`@}»}]{macro privée} +\idx*[|voiraussi{boite $\rightsquigarrow$ géométrie}]{point de référence} +\idx*[|voiraussi{sp (unité)}]{point d'échelle} +\index{registre!entier|voir{compteur}} +\idx*[|voir{\texttt{\char92 meaning}, \texttt{\char92 show}}]{signification d'un token} +\idx*[|voir{\texttt{\char`\^\char`\^J}}]{tabulation}% +\idx*[|voiraussi{\texttt{\char92 relax} $\rightsquigarrow$ spécial}]{test incomplet}% +\idx*[|voiraussi{\texttt{\char92 litterate}$^\star$}]{verbatim}% +\idx*[|voiraussi{\texttt{\char92 let}}]{caractère implicite}% +\cidx*[ (notation hexadécimale)|voiraussi{nombre $\rightsquigarrow$ définition}]\" +\cidx*[ (notation octale)|voiraussi{nombre $\rightsquigarrow$ définition}]\' +\makeatletter +\idx*[|voiraussi{\texttt{\char92 input}}]\@@input +\makeatother +\cidx*[ (notation octale)|voiraussi{nombre $\rightsquigarrow$ définition}]\` +\idx*[!implicite|voiraussi{\texttt{\char92 bgroup}, \texttt{\char92 egroup}}]{accolade} +\idx*[|voiraussi{\texttt{\char92 hbadness}, \texttt{\char92 vbadness}}]{boite insuffisamment remplie} + + +\indexprologue{L'index ci-dessous liste les commandes et les notions abordées dans le livre. Les commandes, qu'elles soient primitives ou macros, sont classées alphabétiquement sans tenir compte du caractère d'échappement «\texttt{\char`\\}». Les commandes précédées d'une étoile sont des primitives. + +Certaines séquences de contrôle ont été très fréquemment employées dans les codes exposés dans ce livre. On trouve par exemple \texttt{\char`\\def}, \texttt{\char`\\expandafter}, \texttt{\char`\\catcode}, etc. Afin de ne pas inutilement surcharger l'index, seuls les numéros de pages des premières apparitions ont été retenus. Ils sont suivis par « \ldots » qui symbolise les nombreux autres numéros de page qui ont été omis. + +Lorsqu'un renvoi « \textit{voir} » à une macro définie dans ce livre figure dans cet index, cette macro est suivie de «$^\star$». L'index des macros définies dans ce livre se trouve à la page~\pageref{index.macros}.\par +} +\def\indexspace{\vskip 18pt plus 7pt minus 4pt \relax} +\small +\printindex + +\normalsize +\indexprologue{\label{index.macros}L'index ci-dessous liste les macros définies dans ce livre. Toutes n'y figurent pas. À de rares exceptions près, seules les macros \emph{publiques}, c'est-à-dire sans «\verb|@|», y figurent.\par +} +\small +\printindex[macros]% + +\normalsize +%| | +%| Fin index | +%|____________________________________________________________________________| +\immediate\closeout\codewrite +\cleardoublepage\pagestyle{empty}\null\newpage +\null\vfill +\begin{centrage} + \footnotesize + Christian \textsc{Tellechea} + + --- + + Dépôt légal: septembre 2014 +\end{centrage}% +\null +\vskip1cm\null +\end{document}
\ No newline at end of file diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/mandelbrot.pdf b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/mandelbrot.pdf Binary files differnew file mode 100644 index 00000000000..0a6e16eca2f --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/mandelbrot.pdf diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/mandelbrot.tex b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/mandelbrot.tex new file mode 100644 index 00000000000..ec7b663eefc --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/mandelbrot.tex @@ -0,0 +1,43 @@ +\documentclass[crop=true,border=5pt]{standalone} +\usepackage[T1]{fontenc} +\usepackage{libertine} +\usepackage[libertine]{newtxmath} +\input macroperso.tex +\begin{document} +\def\mandeltest#1#2{% + \def\zx{0}\def\zy{0}% + \def\zxx{0}\def\zyy{0}% + \def\mandelresult{1}% + \for\ii=1to\maxiter\do1{% + \advance\count255 by1 + \edef\zy{\dimtodec\dimexpr\decmul{\decmul2\zx}\zy pt+#2pt\relax}% + \edef\zx{\dimtodec\dimexpr\zxx pt-\zyy pt+#1pt\relax}% + \edef\zxx{\decmul\zx\zx}% + \edef\zyy{\decmul\zy\zy}% + \ifdim\dimexpr\zxx pt+\zyy pt\relax>4pt + \def\mandelresult{0}% + \exitfor\ii + \fi + }% +} +\def\mandel#1#2{% + \graphzone{-2}[1]{1}[2]{-1}[1]{1}[2]{% + \def\maxiter{#2}% + \edef\plotstuff{\the\dimexpr\xunit/#1\relax}% + \edef\plotstuff{\vrule height\plotstuff width\plotstuff}% + \edef\increment{\decdiv{1}{#1}}% + \count255=0 + \FOR\xxx = -2 to 1 \do \increment{% + \FOR\yyy = 0 to 1 \do \increment{% + \mandeltest\xxx\yyy + \ifnum\mandelresult=1 + \plot(\xxx,\yyy)\plot(\xxx,-\yyy)% + \fi + }% + }% + \edef\plotstuff{$\scriptstyle\number\count255 $}% + \plot(-1.99,0.92)% + }% +} +\xunit=3cm \yunit=3cm \mandel{400}{500} +\end{document} diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0001.pdf b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0001.pdf Binary files differnew file mode 100644 index 00000000000..0463c26de30 --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0001.pdf diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0002.pdf b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0002.pdf Binary files differnew file mode 100644 index 00000000000..504474aaace --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0002.pdf diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0003.pdf b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0003.pdf Binary files differnew file mode 100644 index 00000000000..07f61cbde57 --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/pg0003.pdf diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/progtexmacro.tex b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/progtexmacro.tex new file mode 100644 index 00000000000..e6547c0c2da --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/progtexmacro.tex @@ -0,0 +1,2734 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% Macros définies dans le livre %% +%% « Apprendre à programmer en TeX » %% +%% et nécessaires à sa compilation %% +%% %% +%% Encodage ISO 8859-1 %% +%% _____ %% +%% %% +%% © 2014 Christian Tellechea %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %% +%% Ce fichier contient celles des macros définies dans le livre qui sont %% +%% nécessaires à sa compilation. %% +%% %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Les codes et les macros donnés dans ce fichier sont diffusés sous licence +% libre « Creative Commons "BY-SA" ». +% +% https://creativecommons.org/licenses/by-sa/4.0/ +% https://creativecommons.org/licenses/by-sa/4.0/legalcode +% +% Vous êtes autorisé à +% +% * partager : copier, distribuer et communiquer le matériel par tous moyens +% et sous tous formats ; +% * adapter : remixer, transformer et créer à partir du matériel pour toute +% utilisation, y compris commerciale +% +% selon les conditions suivantes +% +% * paternité : vous devez citer le nom de l'auteur de façon appropriée, +% fournir un lien vers la licence, et indiquer si des modifications ont +% été apportées. Vous pouvez le faire de manière raisonnable, mais pas +% d'une façon qui suggère le donneur de licence vous approuve ou approuve +% l'utilisation que vous en faites ; +% * partage dans les mêmes conditions : dans le cas où vous effectuez un +% remix, que vous transformez, ou créez à partir du matériel composant +% l'Oeuvre originale, vous devez diffuser l'Oeuvre modifiée dans les mêmes +% conditions, c'est-à-dire avec la même licence avec laquelle l'Oeuvre +% originale a été diffusée. +% +% +% Les commentaires dont sont assorties les macros dans le livre ne sont pas +% sous licence libre et ne sont pas reproduits dans ce fichier. + +\edef\restoreatcoatcode{\catcode`\noexpand\@=\the\catcode`\@} +\catcode`\@11 +{\def\:{\let\sptoken= }\expandafter}\: % + +\long\def\gobone#1{} + +\long\def\identity#1{#1} + +\long\def\firstoftwo#1#2{#1} + +\long\def\secondoftwo#1#2{#2} + +\def\gobtwo#1#2{}% + +\def\defactive#1{% + \catcode`#1=13 + \begingroup + \lccode`~=`#1 + \lowercase{\endgroup\def~}} + +\def\letactive#1{% + \catcode`#1=13 + \begingroup + \lccode`~=`#1 + \lowercase{\endgroup\let~}} + +\def\litterate#1{% #1=lit le token frontière choisi + \begingroup% ouvre un groupe pour y faire les modifications + \def\do##1{\catcode`##1=12 }% + \dospecials% rend inoffensifs tous les tokens spéciaux + \defactive\^^M{\leavevmode\par}% + \defactive\ {\space}% + \defactive<{\string<{}}\defactive>{\string>{}}% + \defactive-{\string-{}}\defactive`{\string`{}}% + \defactive,{\string,{}}\defactive'{\string'{}}% + \defactive#1{\endgroup}% #1 sera un \endgroup + \tt% passe en police à chasse fixe +} + +\long\def\firstto@nil#1#2\@nil{#1} + +\long\def\remainto@nil#1#2\@nil{#2} + +\def\rightofsc#1#2{% + \exparg\ifin{#1}{#2}% + {% si #1 contient le #2 + \def\right@of##1#2##2\@nil{\def#1{##2}}% définit la macro auxiliaire + \expandafter\right@of#1\@nil% appelle la macro auxiliaire + }% + {\let#1=\empty}% sinon, ne rien faire +} + +\def\defname#1{\expandafter\def\csname#1\endcsname} + +\def\letname#1{\expandafter\let\csname#1\endcsname} + +\long\def\addtomacro#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}} + +\long\def\eaddtomacro#1#2{\expandafter\addtomacro\expandafter#1\expandafter{#2}} + +\long\def\addtotoks#1#2{#1\expandafter{\the#1#2}} + +\long\def\eaddtotoks#1#2{\expandafter\addtotoks\expandafter#1\expandafter{#2}} + +\long\def\swaparg#1#2{#2{#1}} + +\long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} +\let\exparg\expsecond + +\def\exptwoargs#1#2{\expsecond{\expsecond{#1}{#2}}} + +\def\sgn#1{\ifnum#1<0 -\fi} + +\def\abs#1{\sgn{#1}#1 } + +\def\truncdiv#1#2{% + \exptwoargs\truncdiv@i{\number\numexpr#1\relax}{\number\numexpr#2\relax}% +} +\def\truncdiv@i#1#2{% + \numexpr(2*\sgn{#1}#1-\sgn{#2}#2+1)/(\sgn{#1}2*#2)\relax +} + +\def\ifinside#1[#2,#3]{% + \ifnum\sgn{\dimexpr#1pt-#2pt\relax}\sgn{\dimexpr#1pt-#3pt}1=1 + \expandafter\secondoftwo + \else + \expandafter\firstoftwo + \fi +} + +\def\absval#1{\expandafter\absval@i\expandafter{\number\numexpr#1\relax}} +\def\absval@i#1{\number\ifnum#1<\z@-\fi#1 } + +\edef\saved@catcode{\number\catcode`\:}% sauvegarde du catcode de ":" +\catcode`\:12 % ":" est désormais un caractère non actif normal +\def\hdif{% + \begingroup% ouvrir un groupe semi-simple + \catcode`\:12 % modifier le catcode de ":" + \hdif@i% aller lire les deux arguments +} + +\def\hdif@i#1#2{% lit les 2 arguments + \hdif@ii#1-#2\@nil% et les transmet à la macro à argument délimités +} + +\def\hdif@ii#1:#2:#3-#4:#5:#6\@nil{% + %%%%%% calcul des nombres à afficher %%%%%% + \edef\nb@hrs{\number\numexpr#1-#4\relax}% différence des heures + \edef\nb@min{\number\numexpr#2-#5\relax}% différence des minutes + \edef\nb@sec{\number\numexpr#3-#6\relax}% différence des secondes + \ifnum\nb@sec<\z@ % si la différence des secondes est <0 + \edef\nb@sec{\number\numexpr\nb@sec+60\relax}% ajouter 60 sec + \edef\nb@min{\number\numexpr\nb@min-1\relax}% enlever 1 aux minutes + \fi + \ifnum\nb@min<\z@ % si les minutes sont <0 + \edef\nb@min{\number\numexpr\nb@min+60\relax}% ajouter 60 min + \edef\nb@hrs{\number\numexpr\nb@hrs-1\relax}% enlever 1 aux heures + \fi + %%%%%% affichage du résultat %%%%%% + \let\inter@space\empty% pas d'espace avant un nombre pour l'instant + \ifnum\nb@hrs>\z@ % si les heures sont >0 + \nb@hrs h% afficher les heures et "h" + \let\inter@space\space% espace pour plus tard + \fi + \ifnum\nb@min>\z@ % si les minutes sont >0 + \inter@space% afficher une espace éventuelle + \nb@min min% afficher les minutes et "min" + \let\inter@space\space + \fi + \ifnum\nb@sec>\z@ % si les secondes sont >0 + \inter@space% afficher une espace éventuelle + \nb@sec s% afficher les secondes et "s" + \fi + \endgroup% fermer le groupe ouvert au début +} +\catcode`\:=\saved@catcode\relax% restaure le code de catégorie de ":" + +\long\def\afterfi#1#2\fi{#2\fi#1} + +\long\def\antefi#1\fi{\fi#1} + +\def\for#1=#2to#3\do#4#{% + \edef\temp@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel + \ifnum\temp@increment=\z@% s'il est nul, + \edef\temp@increment{% le redéfinir à -1 (si #3<#2) et 1 sinon + \ifnum\numexpr#3-#2\relax<\z@ -1\else 1\fi + }% \temp@increment vaut donc 1 ou -1 + \fi + \ifnum\numexpr\temp@increment*(#3-#2)\relax<\z@ + \expandafter\gobone% si argument optionnel incompatible, manger le {<code>} + \else + \edef#1{\number\numexpr#2}% initialise la variable + \edef\macro@args{% définit et développe les arguments de la macro + %#1=nom de la macro à définir : + \expandafter\noexpand\csname for@ii@\string#1\endcsname + \ifnum\temp@increment<\z@ <\else >\fi% #2=signe de comparaison + {\temp@increment}% #3=incrément + \noexpand#1% #4=variable + {\number\numexpr#3\relax}% #5=entier max + }% + \antefi + \expandafter\for@i\macro@args + \fi +} + +% #1=nom de la macro récursive de type "\for@ii@<variable>" +% #2=signe de comparaison % #3=incrément +% #4=variable % #5=entier max % #6=code à exécuter +\long\def\for@i#1#2#3#4#5#6{% + \def#1{% dfinit la sous macro récursive + \unless\ifnum#4#2#5\relax% tant que la variable n'a pas dépassé l'entier max + \afterfi{% rendre la récursivité terminale + #6% exécute le code + \edef#4{\number\numexpr#4+#3\relax}% incrémente la variable #1 + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive +} + +\def\exitfor#1{% #1=variable correspondant à la boucle de laquelle on veut sortir + \expandafter\let\csname for@ii@\string#1\endcsname\empty +} + +\def\quark{\quark} + +\long\def\ifempty#1{% + \expandafter\ifx\expandafter\relax\detokenize{#1}\relax + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi} + +\def\reverse#1{% + \ifempty{#1} + {}% s'il n'y a rien à inverse, ne rien faire + {\reverse@i#1\@nil\@nil}% initialisation des réservoirs et appeler \reverse@i +} +\def\reverse@i#1#2\@nil#3\@nil{% #1 est le 1er caractère du réservoir A + \ifempty{#2}% si ce qui reste après ce 1er caractère est vide + {#1#3}% renvoyer #1#3 qui est le résultat final + {\reverse@i#2\@nil#1#3\@nil}% sinon, recommencer en déplaçant #1 + % en 1re position du réservoir B +} +\long\def\ifin#1#2{% + \long\def\ifin@i##1#2##2\@nil{% définit la macro auxiliaire + \ifempty{##2}% si ce qu'il y a derrière le motif est vide + \secondoftwo% aller à "faux" + \firstoftwo% sinon à "vrai" + }% + \ifin@i#1#2\@nil% appel de la macro auxiliaire +} + +\def\ifstart#1#2{% + \def\if@start##1#2##2\@nil{\ifempty{##1}}% + \ifempty{#2}\firstoftwo{\if@start#1\relax#2\@nil}} + +\def\substin#1#2#3{% + \def\substin@i##1{% + \ifempty{##1}% si le code est vide + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#2}% sinon, si le code contient motif1 + {\substin@ii##1\@nil}% appeler la macro à argument délimités + {##1}% sinon, afficher le code + }% + }% + \def\substin@ii##1#2##2\@nil{% + ##1#3% afficher ##1 et #3 (qui est <motif2>) + \substin@i{##2}% et recommencer avec ce qui reste + }% + \substin@i{#1}% +} + +\def\substtocs#1#2#3#4{% + \def\substtocs@i##1{% + \ifempty{##1}% si le code est vide + \relax% ne rien faire -> fin du processus + {\ifin{##1}{#3}% sinon, si le code contient motif1 + {\substtocs@ii##1\@nil}% appeler la macro à argument délimités + {\addtomacro#1{##1}}% sinon, ajouter le code + }% + }% + \def\substtocs@ii##1#3##2\@nil{% + \addtomacro#1{##1#4}% ajouter ##1#4 + \substtocs@i{##2}% et recommencer avec ce qui reste + }% + \let#1=\empty% initialise la macro à vide + \substtocs@i{#2}% +} + +\long\def\cnttimestocs#1#2#3{% #3=macro recevant le résultat + \long\def\cnttimestocs@i##1\@nil##2#2##3\@nil{% + % ##1=nb d'occurrences rencontrées jusqu'alors + % ##2 et ##3=ce qui est avant/après le <motif> + \ifin{##3}{#2}% si le <motif> est dans ce qui reste + {\expandafter\cnttimestocs@i% appeler la macro récursive + \number\numexpr##1+1\relax\@nil% avec une occurrence de plus + ##3\@nil% et ce qui reste + }% + {\edef#3{\number\numexpr##1+1\relax}}% sinon, stocker 1 occurrence de plus dans #3 + }% + \ifin{#1}{#2}% si le <motif> est dans le <code> + {\cnttimestocs@i 0\@nil#1\@nil}% appeler la macro récursive avec 0 occurrence + {\def#3{0}}% sinon, mettre 0 dans #3 +} + +\def\ifcs#1{% + \begingroup + \escapechar=`\@ % prend "@" comme caractère d'échappement + \if% les premiers caractères de + \expandafter\firstto@nil\string#1a\@nil% "#1a" + \expandafter\firstto@nil\string\relax\@nil% et "\relax" sont-ils égaux ? + \endgroup\expandafter\firstoftwo% si oui, fermer le groupe et renvoyer "vrai" + \else% sinon, fermer le groupe et renvoyer "faux" + \endgroup\expandafter\secondoftwo + \fi +} + +\def\endif{\endif}\def\elseif{\elseif} +\def\idto@endif#1\endif{#1} +\def\firstto@endif#1#2\endif{#1} +\def\ifxcase#1#2{% #1=<token> #2=prochain argument + \ifx\elseif#2% si #2 est \elseif + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + \idto@endif% aller à \idto@endif, sinon : + {\ifx\endif#2% si #2 est \endif + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% ne rien faire, sinon : + {\ifx#1#2% s'il y a égalité + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + \firstto@endif% aller à \firstto@endif + {\ifxcase@i{#1}}% sinon aller à \ifxcase@i + }% + }% +} +\def\ifxcase@i#1#2{% #1=<nombre> #2=<code> + \ifxcase{#1}% manger le <code> et aller à \ifxcase en transmettant #1 +} + +\newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication +\def\end@foreach{\end@foreach} +\long\def\save@macro#1#2{\expandafter\let\csname saved@var@\number\cnt@nest#1\endcsname#2} +\long\def\save@macro@i#1/#2{\save@macro{a}#1\save@macro{b}#2} +\long\def\restore@macro#1#2{% + \expandafter\let\expandafter#2\csname saved@var@\number\cnt@nest#1\endcsname +} +\long\def\restore@macro@i#1/#2{\restore@macro{a}#1\restore@macro{b}#2} +\def\defseplist#1{% + \long\def\doforeach##1\in##2##3{% + \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication + \ifin{##1}{/}% si ##1 contient "/" + {\save@macro@i##1% sauvegarde les macros + \doforeach@ii% appeler la macro récursive après avoir + {##3}%\csname loop@code@\number\cnt@nest\endcsname% formé la macro contenant le code + ##1% mettre les variables ##1 en 2e position + ##2#1\end@foreach/\end@foreach#1% puis la liste ##2 suivie de ",\end@foreach," + \restore@macro@i##1% une fois sorti de toutes les boucles, restaurer les macros + }% si ##1 ne contient pas "/" + {\save@macro{}##1% sauvegarde la macro + \doforeach@i% appeler la macro récursive + {##3}% mettre en premier le <code> + ##1% puis la variable ##1 en 2e position + ##2#1\end@foreach#1% enfin la liste ##2 suivie de ",\end@foreach," + \restore@macro{}##1% une fois sorti de toutes les boucles, restaurer la macro + }% + \global\advance\cnt@nest-1 % décrémente le compteur d'imbrications + }% + % ##1 = code à exécuter, ##2= variable, ##3=valeur courante + \long\def\doforeach@i##1##2##3#1{% + \def##2{##3}% fait l'assignation à la variable + \global\let\allow@recurse\identity% permet l'appel récursif plus bas + \ifx\end@foreach##2% si la fin est atteinte + \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: + {##1% le code puis + \allow@recurse{\doforeach@i{##1}##2}% recommencer + }% + }% + % ##1 = code à exécuter, ##2/##3= variables, ##4/##5=valeurs courantes + \long\def\doforeach@ii##1##2/##3##4/##5#1{% + \def##2{##4}\def##3{##5}% fait l'assignation des deux variables + \global\let\allow@recurse\identity% permet l'appel récursif plus bas + \ifx\end@foreach##2% si la fin est atteinte + \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: + {##1% le code puis + \allow@recurse{\doforeach@ii{##1}##2/##3}% recommencer + }% + }% + % macro qui, si elle remplace \allow@recurse, annule l'appel récursif + \long\def\forbid@recurse##1\end@foreach#1{}% tout manger jusqu'à "\end@foreach," +} +% macro pour annuler le prochain appel récursif +\def\doforeachexit{\global\let\allow@recurse\forbid@recurse} +\defseplist{,} + +\begingroup% ouvrir un groupe + \edef\temp{\endgroup\def\noexpand\removept##1.##2\string p\string t}% +\temp{#1\ifnum#2>0 .#2\fi}% et le fermer avant de définir \removept + +\def\dimtodec{\expandafter\removept\the} + +\def\FOR#1=#2to#3\do#4#{% + \ifempty{#4} + {\let\FOR@increment\z@} + {\edef\FOR@increment{\the\dimexpr#4pt\relax}}% lit et normalise l'argument optionnel + \ifdim\FOR@increment=\z@% s'il est nul, + \edef\FOR@increment{% le redéfinir à -1pt (si #3<#2) et 1pt sinon + \ifdim\dimexpr#3pt-#2pt\relax<\z@ -1\else 1\fi pt + }% \FOR@increment vaut donc 1 ou -1 + \fi + \ifdim\dimtodec\dimexpr#3pt-#2pt\relax\dimexpr\FOR@increment\relax<\z@ + \expandafter\gobone% si argument optionnel incompatible, manger le {<code>} + \else + \edef#1{\dimtodec\dimexpr#2pt\relax}% initialise la \<macro> + \edef\macro@args{% définit et développe les arguments à passer à \FOR@i + %#1=nom de la macro récursive : + \expandafter\noexpand\csname FOR@ii@\string#1\endcsname + \ifdim\FOR@increment<\z@ <\else >\fi% #2=signe de comparaison + {\FOR@increment}% #3=incrément + \noexpand#1% #4=\<macro> + {\the\dimexpr#3pt\relax}% #5=dimension n2 + }% + \antefi% externalise la ligne ci-dessous de la portée du test + \expandafter\FOR@i\macro@args% appelle \FOR@i avec les arguments définis ci-dessus + \fi +} + +% #1=nom de la macro récursive de type "\FOR@ii@\<macro>" +% #2=signe de comparaison % #3=incrément +% #4=\<macro> % #5=dimension n2 % #6=<code> à exécuter +\long\def\FOR@i#1#2#3#4#5#6{% + \def#1{% dfinit la sous macro récursive + \unless\ifdim#4pt#2#5\relax% tant que la \<macro> variable n'a pas dépassé n2 + \afterfi{% rendre la récursivité terminale + #6% exécute le code + \edef#4{\dimtodec\dimexpr#4pt+#3\relax}% incrémente la \<macro> + #1% recommence + }% + \fi + }% + #1% appelle la sous-macro récursive +}% +\def\exitFOR#1{% #1=\<macro> correspondant à la boucle de laquelle on veut sortir + \defname{FOR@ii@\string#1}{}% +} + +\def\ifexitFOR#1{% envoie vrai si on est prématurément sorti de la boucle de \<macro> #1 + % si la macro récursive est \empty + \expandafter\ifx\csname FOR@ii@\string#1\endcsname\empty + \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai" + \else + \expandafter\secondoftwo% sinon, renvoyer "faux" + \fi +} + +\def\decmul#1#2{\dimtodec\dimexpr#1\dimexpr#2pt\relax\relax} + +\def\decdiv#1#2{% divise le décimal #1 par le décimal #2 + \dimtodec\dimexpr 1pt*\dimexpr#1pt\relax/\dimexpr#2pt\relax\relax +} + +\def\convertunit#1#2{% + \dimtodec + \dimexpr + \numexpr + \dimexpr #1 \relax * 65536 / \dimexpr 1#2 \relax + \relax + sp + \relax +} + +\def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1 + +\def\cbox#1{\setbox0\vbox{#1}\lower\dimexpr(\ht0-\dp0)/2\relax\box0 } + +\def\clap#1{\hbox to\z@{\hss#1\hss}} + +\def\ifzerodimbox#1{% #1=registre de boite +% revoie vrai si le registre est vide ou contient une boite vide + \csname% former le nom "\firstoftwo" ou "secondoftwo" + \ifvoid#1first%% si le registre est vide "first" + \else% sinon + \ifnum\wd#1=\z@% si la largeur + \ifnum\ht#1=\z@% la hauteur + \ifnum\dp#1=\z@ first% et la profondeur=0pt, "first" + \else second% dans les autres cas "second" + \fi + \else second% + \fi + \else second% + \fi + \fi + oftwo% compléter avec "oftwo" + \endcsname +} + +\def\ifvoidorempty#1{% teste si le registre de boite #1 est vide ou contient une boite vide + \ifvoid#1\relax + \expandafter\firstoftwo + \else + \begingroup% dans un groupe + \setbox0=% affecter à la boite 0 + \ifhbox#1\hbox\bgroup\unhcopy% un boite horizontale + \else \vbox\bgroup\unvcopy% ou verticale + \fi% dans laquelle on compose + #1\relax% composer #1 en dimensions naturelles + \expandafter\egroup% sauter la fin de la boite + \expandafter% et le \endgroup + \endgroup + \ifnum\lastnodetype<0 % pour tester si le dernier noeud est vide + \expandafter\expandafter\expandafter\firstoftwo + \else + \expandafter\expandafter\expandafter\secondoftwo + \fi + \fi +} + +\def\showdim#1{% + \vrule width0.2pt height 1ex depth 0pt + \vrule width#1 height0.4pt depth 0pt + \vrule width0.2pt height 1ex depth 0pt \relax} + + +\newdimen\frboxrule \frboxrule0.4pt +\newdimen\frboxsep \frboxsep5pt +\def\FRbox#1{% ne pas changer le mode H ou V en cours + \hbox{% mettre à la suite horizontalement + \vrule width\frboxrule% 1) réglure gauche + \vbox{% 2) un empilement vertical comprenant + \hrule height\frboxrule% a) réglure supérieure + \kern\frboxsep% b) espace verticale haute + \hbox{% c) contenu + espaces en mode H + \kern\frboxsep#1\kern\frboxsep + }% + \kern\frboxsep% d) espace verticale basse + \hrule height\frboxrule% e)réglure inférieure + }% + \vrule width\frboxrule% 3) réglure droite + }% +} +\def\frbox#1{% ne pas changer le mode H ou V en cours + \hbox{% enferme dans une \hbox + \vrule width\frboxrule% réglure gauche + \vtop{% + \vbox{% 1er élément de la \vtop + \hrule height\frboxrule% réglure supérieure + \kern\frboxsep% espace haut + \hbox{% + \kern\frboxsep% espace gauche + #1% contenu + \kern\frboxsep% espace droite + }% + }% puis autres éléments de la \vtop, sous la ligne de base + \kern\frboxsep% espace bas + \hrule height\frboxrule% réglure inférieure + }% + \vrule width\frboxrule% réglure droite + }% +} +\def\souligne#1{% + \setbox0=\hbox{#1}% stocke le contenu dans le registre no 0 + \setbox0=\hbox{% puis, dans une \hbox, construit une réglure + \vrule width\wd0 % de la longueur du contenu + depth\dimexpr\dp0 + 1.4pt\relax % dp = profondeur texte + 1.4pt + height\dimexpr-\dp0 - 1pt\relax % ht = -profondeux texte - 1pt + }% + \wd0=0pt \dp0=0pt \ht0=0pt % annule toutes les dimensions + \leavevmode \box0 % affiche la réglure + #1% puis le contenu +} + +\def\Souligne#1{% + \setbox0=\hbox{#1}% + \setbox0=\hbox{\vrule width\wd0 depth1.4pt height-1pt }% + \wd0=0pt \dp0=0pt \ht0=0pt + \leavevmode \box0 #1% +} + +\newdimen\stackwd \stackwd=3em +\catcode`\@11 +\def\stackbox#1{% + \par% termine le paragraphe en cours + \begingroup% dans un groupe semi-simple + \parindent=0pt% pas d'indentation + \parskip=0pt% annuler le \parskip + \setbox0\hbox{Àgjp}% boite pour le strut + \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut + \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i + \unkern% annule la dernière compensation verticale + \par + \endgroup +} +\def\stackbox@i#1\\{% #1=ligne courante + \def\temp@{#1}% stocke la ligne courante + \unless\ifx\quark\temp@% si ce n'est pas la fin + \hfill % ressort infini de centrage (passe en mode horizontal) + \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... + {\frbox{% ...encadrer + \hbox to\stackwd{% une \hbox de largeur \stackwd contenant + \hss% 1) ressort de centrage + \stack@strut% 2) strut de dimension verticale + \current@item%3) l'élement courant + \hss}% 4)ressort de centrage + }% fin de la \fbox + \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales + }% fin de \doforeach + \unkern% annuler la dernière compensation horizontale + \hfill% ressort infini de centrage + \null% fait prendre en compte le dernier ressort + \par% termine le paragraphe + \nobreak% interdit une coupure de page + \nointerlineskip% sinon, ne pas ajouter le ressort interligne + \kern-\frboxrule% superposer les réglures horizontales + \expandafter\stackbox@i% et recommencer + \fi +} + +\def\vlap#1{\vbox to\z@{\vss#1\vss}} +\newdimen\xunit \newdimen\yunit +\newdimen\mainrule \newdimen\subrule +\xunit=1cm \yunit=1cm \mainrule=0.8pt \subrule=0.2pt +\def\grid#1#2#3#4{% #1=nb xunit #2=nb xsubunit #3=nb yunit #4=nb ysubunit + \vbox{% + \offinterlineskip + \edef\total@wd{\the\dimexpr\xunit*#1\relax}% + \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% + \for\iii=1to#3\do + {\ifnum#4>1 + \vbox to\z@{% + \for\jjj=2to#4\do + {\kern\sub@unit + \vlap{\hrule width\total@wd height\subrule}% + }% + \vss + }% + \fi + \vlap{\hrule width\total@wd height\mainrule}% + \kern\yunit + }% + \vlap{\hrule width\total@wd height\mainrule}% + \vbox to\z@{% + \vss + \hbox to\total@wd{% + \edef\total@ht{\the\dimexpr\yunit*#3\relax}% + \edef\sub@unit{\the\dimexpr\xunit/#2\relax}% + \for\iii=1to#1\do + {\ifnum#2>1 + \rlap{% + \for\jjj=2to#2\do + {\kern\sub@unit + \clap{\vrule width\subrule height\total@ht}% + }% + }% + \fi + \clap{\vrule width\dimexpr\mainrule height\total@ht}% + \kern\xunit + }% + \clap{\vrule width\mainrule height\total@ht}% + }% + }% + }% +} + +\def\iffileexists#1#2{% #1=canal de lecture #2=nom du fichier + \openin#1=#2 % + \ifeof#1% le fichier n'existe pas + \closein#1% + \expandafter\secondoftwo% renvoyer faux + \else + \closein#1 + \expandafter\firstoftwo% sinon renvoyer vrai + \fi +} + +\def\xread{% doit être suivie de "<nombre> to \<macro>" + \edef\restoreendlinechar{\endlinechar=\the\endlinechar}% + \endlinechar=-1 % neutralise \endlinechar + \afterassignment\restoreendlinechar% après l'assignation restaurer \endlinechar + \read% attend <nombre> to \<macro> +} + +\def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier + \begingroup + \tt% sélectionner la fonte à chasse fixe + \openin#1=#2\relax + \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et + Le fichier n'existe pas% afficher le message + \else% le fichier existe + \def\do##1{\catcode`##1=12 }% + \dospecials% neutraliser tous les tokens spéciaux + \obeyspaces% rendre l'espace actif + \def\magicpar{\let\magicpar=\par}% + \loop + \xread#1 to \currline% lire une ligne + \unless\ifeof#1% si la fin du fichier n'est pas atteinte + \leavevmode\magicpar% former le paragraphe (sauf à la 1er itération) + \currline% afficher la ligne + \repeat% recommencer + \fi + \closein#1\relax% + \endgroup +} + +\def\exactwrite#1#2{% #1=numéro de canal #2=caractère délimiteur + \begingroup + \def\canal@write{#1}% sauvegarde le numéro de canal + \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12 + \expandafter\exactwrite@i\expandafter{\string#2}% rend #2 de catcode 12 +} + +\def\exactwrite@i#1{% #1=caractère frontière (de catcode 12) + % la macro \exactwrite@ii lit tout jusqu'au caractère #2 et passe + % ce qu'elle a lu à la macro \exactwrite@iii en mettant un \@nil à la fin + \def\exactwrite@ii##1#1{\exactwrite@iii##1\@nil}% + \exactwrite@ii% va lire tout ce qui se trouve jusqu'au prochain #2 +} + +{\catcode`\^^M 12 \gdef\EOL@char{^^M}}% définit le caractère <EOL> de catcode 12 + +\def\exactwrite@iii#1\@nil{% #1 = contenu à écrire + \expsecond{\ifin{#1}}\EOL@char% si #1 contient "retour chariot" + {\write@line#1\@nil}% écrire la première ligne de #1 + {\immediate\write\canal@write{#1}% sinon : dernière ligne atteinte, l'écrire + \endgroup% puis sortir du groupe + }% +} + +% les \expandafter provoquent le 1-développement de \EOL@char : +\expandafter\def\expandafter\write@line\expandafter#\expandafter1\EOL@char#2\@nil{% + \immediate\write\canal@write{#1}% écrit la ligne (ce qui se trouve avant ^^M) + \exactwrite@iii#2\@nil% continue avec ce qui se trouve après ^^M +} + +\def\quark@list{\quark@list}% quark de fin de liste + +\def\finditemtocs#1#2#3{% #1 = \<macro> #2=position #3=macro à définir + \def\finditemtocs@iii##1##2\quark@list,{% renvoyer #1 et manger le reste de la liste + \expandafter\def\expandafter#3\expandafter{\gobone##1}% + }% + \let#3=\empty% + \exparg\finditemtocs@i{#1}{#2}% 1-développe la \<macro> +} +\def\finditemtocs@i#1#2{% #1 = liste #2=position cherchée + \ifnum#2>\z@% ne faire quelque chose que si la position est >0 + \antefi\finditemtocs@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive + \fi +} +\def\finditemtocs@ii#1#2#3,{% +% #1=position courante #2=position cherché #3=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone#3% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si fin de liste ne rien faire. Sinon, si position bonne + {\ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\finditemtocs@iii{#3}% renvoyer #3 et manger les éléments restants + }% si la position n'est pas la bonne, recommencer en incrémentant #1 + {\exparg\finditemtocs@ii{\number\numexpr#1+1}{#2}\relax% + }% + }% +} + +\def\insitem#1#2#3{% #1 = macro #2=position cherchée #3=élément à insérer + \let\item@list=\empty + \ifnum#2<1 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + % si position < 1 + {\addtomacro\item@list{#3,}% ajouter l'élement à insérer en premier + \eaddtomacro\item@list#1% puis la liste entière + } + % si la position > 1 + {% définir la macro récursive + \def\insitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone##2% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\addtomacro\item@list{#3}}% si fin de liste, ajouter l'élément en dernier + {% sinon, si la position cherchée est atteinte + \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\addtomacro\item@list{#3,}% ajouter l'élement + \add@remainlist##2,% et ##2 (en supprimant le \relax) et le reste + }% si la position n'est pas atteinte + {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément + \exparg\insitem@i{\number\numexpr##1+1}\relax% et recommencer + }% + }% + }% + % appel de la macro récursive + \expandafter\insitem@i\expandafter1\expandafter\relax#1,\quark@list,% + }% + \let#1=\item@list% rendre #1 égal au résultat +} + +\def\add@remainlist#1,\quark@list,{% + \eaddtomacro\item@list{\gobone#1}% ajouter #1 ainsi que les autres +} +\def\delitem#1#2{% #1 = macro #2=position cherchée + \ifnum#2>0 % ne faire quelque chose que si la position est >0 + \def\delitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant + \expandafter\ifx\expandafter\quark@list\gobone##2% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {}% si fin de liste, ne rien faire + {% sinon, si la position cherchée est atteinte + \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\add@reaminingitems% et ##2 (en supprimant le \relax) et le reste + }% si la position n'est pas la bonne + {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément + \exparg\delitem@i{\number\numexpr##1+1}\relax% et recommencer + }% + }% + }% + \let\item@list=\empty% initialiser la macro tempporaire + % appel de la macro récursive + \expandafter\delitem@i\expandafter1\expandafter\relax#1,\quark@list,% + \expandafter\remove@lastcomma\item@list\@nil + \let#1=\item@list% rendre #1 égal au résultat + \fi +} + +\def\add@reaminingitems#1\quark@list,{% + \eaddtomacro\item@list{#1}% ajouter tout jusqu'au quark +} +\def\remove@lastcomma#1,\@nil{\def\item@list{#1}} + +\def\runlist#1\with#2{% #1=liste #2=macro + \def\runlist@i##1,##2\@nil{% + \addtomacro\collect@run{#2{##1}}% ajouter "\<macro>{<élément>}"" + \ifempty{##2}% plus d'élément ? + \relax% ne rien faire (donc terminer le processus) + {\runlist@i##2\@nil}% sinon, recommencer avec les éléments restants + }% + \begingroup% fait la collecte dans un groupe + \let\collect@run=\empty% initialiser la macro + \ifcs{#1}% si #1 est une séquence de contrôle + {\expandafter\runlist@i#1,\@nil%% aller à \runlist@i + }% sinon, former la sc + {\expandafter\expandafter\expandafter\runlist@i\csname#1\endcsname,\@nil + }% + \expandafter\endgroup% ferme le groupe et détruit \collect@run + \collect@run% après l'avoir développé ! +} + +\def\ifspacefirst#1{% + \expandafter\ifspacefirst@i\detokenize{#1W} \@nil% "W" se prémunit d'un argument vide +} +\def\ifspacefirst@i#1 #2\@nil{\ifempty{#1}} + +\expandafter\def\expandafter\gobspace\space{} + +\def\removefirstspaces{% + \romannumeral% lance le développement maximal + \removefirstspaces@i% et passe la main à la macro récursive +} + +\def\removefirstspaces@i#1{% + \ifspacefirst{#1}% si #1 commence par un espace + {\expandafter\removefirstspaces@i\expandafter% recommencer + {\gobspace#1}% après avoir supprimé cet espace + } + {\z@#1}% sinon, renvoyer l'argument où \z@ stoppe l'action de \romannumeral +} + +\edef\catcodezero@saved{\number\catcode0 }% stocke le catcode de ^^00 +\catcode0=12 % le modifie à 12 +\def\removelastspaces#1{% + \romannumeral% lance le développement maximal + \removelastspaces@i#1^^00 ^^00\@nil% et passe la main à \removelastspaces@i +} +\def\removelastspaces@i#1 ^^00{% + \removelastspaces@ii#1^^00% +} +\def\removelastspaces@ii#1^^00#2\@nil{% + \ifspacefirst{#2}% si le reliquat commence par un espace + {\removelastspaces@i#1^^00 ^^00\@nil}% recommencer sans passer par \removelastspaces + {\z@#1}% sinon "\z@" stoppe l'action de \romannumeral +} +\catcode0=\catcodezero@saved\relax% restaure le catcode de ^^00 + +\def\removetrailspaces#1{% + \romannumeral% lance le développement maximal + \expandafter\expandafter\expandafter% le pont d'\expandafter + \removelastspaces + \expandafter\expandafter\expandafter% fait agir \removefirstspaces en premier + {% + \expandafter\expandafter\expandafter + \z@% stoppe le développement initié par \romannumeral + \removefirstspaces{#1}% + }% +} + +\newcount\test@cnt +\def\ifinteger#1{% + \ifstart{#1}{-}% si "-" est au début de #1 + {\expandafter\ifinteger\expandafter{\gobone#1}% l'enlever et recommencer + } + {\ifempty{#1}% sinon, si #1 est vide, lire l'argument <faux> + \secondoftwo% lire l'argument <faux> + {\afterassignment\after@number% sinon, après l'assignation, aller à \after@number + \test@cnt=0#1\relax% faire l'assignation (\relax termine le nombre et n'est pas mangé) + }% + }% +} +\def\after@number#1\relax{% #1 est ce qui reste après l'assignation + \ifempty{#1}% teste si #1 est vide et lit l'argument <vrai> ou <faux> qui suit +} + +\newif\iftestspace \testspacefalse + +\def\deftok#1#2{\let#1= #2\empty} + +\def\ifnexttok#1#2#3{% #1=token #2=code vrai #3=code faux + \let\test@tok= #1% stocke le token à tester + \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter + \iftestspace \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@ii}% + \else \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@iii}% + \fi% après avoir défini la macro récursive selon le booléen + \ifnexttok@i% l'exécuter +} + +\def\ifnexttok@ii{% macro qui teste aussi les espaces + \ifx\nxttok\test@tok \expandafter\true@code% exécuter le code vrai + \else \expandafter\false@code% sinon code faux + \fi +} + +\def\ifnexttok@iii{% + \ifx\nxttok\sptoken% si le prochain token est un espace + \def\donext{% + \afterassignment\ifnexttok@i% aller "sentir" le token d'après + \let\nxttok= }% après avoir absorbé l'espace + \else \let\donext\ifnexttok@ii% sinon, faire le test + \fi + \donext% faire l'action décidée ci-dessus +} + +\def\ifstarred#1{\ifnexttok*{\firstoftwo{#1}}} + +\def\parsestop{\parsestop}% définit le quark se trouvant en fin de code +\newtoks\code@toks% alloue le registre contenant le code lu +\def\parseadd#1{\code@toks\expandafter{\the\code@toks#1}} +\newif\ifparse@group +\def\parse{% + \code@toks{}% initialise le collecteur de tokens + \ifstarred + {\parse@grouptrue + \ifnexttok{ }% si un espace suit l'étoile + {\afterassignment\parse@i \let\nxttok= }% le manger et aller à \parse@i + {\parse@i}% sinon, aller à \parse@i + } + {\parse@groupfalse + \parse@i + }% +} +\def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token et va à \parse@ii +\def\parse@ii{% + \ifx\nxttok\parsestop% si la fin va être atteinte, aller à \parsestop@i + \let\next@action\parsestop@i + \else + \ifx\nxttok\@sptoken% si un espace va être lu, aller à \read@space + \let\next@action\read@space + \else + \ifx\nxttok\bgroup% si une accolade ouvrante va être lue + \let\next@action\read@bracearg% aller à \read@bracearg + \else + \let\next@action\testtoken% dans les autres cas, aller à \testtoken + \fi + \fi + \fi + \next@action% faire l'action décidée ci-dessus +} +\def\parsestop@i{% la fin est atteinte + \expandafter\the\expandafter\code@toks% afficher le registre de tokens + \gobone% après avoir mangé \parsestop +} +\expandafter\def\expandafter\read@space\space{% \read@space mange un espace dans le code + \testtoken{ }% et va à \testtoken +} +\def\read@bracearg{% + \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i +} +\def\read@bracearg@i#1\parsestop{% l'argument tout jusqu'à \parsestop + \expsecond\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{" + {\expandafter\read@bracearg@ii\gobone#1\parsestop}% lire l'argument entre accolades + {\expandafter\testtoken\gobone#1\parsestop}% sinon, tester le token +} +\def\read@bracearg@ii#1{% l'argument entre accolades est lu + \ifparse@group\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\begingroup% ouvre un groupe pour parser l'intérieur de l'accolade + \def\parsestop@i{% redéfinir localement \parsestop@i pour + \expandafter% retarde \endgroup + \endgroup% pour ajouter le contenu local de \code@toks entre accolades + \expandafter% au contenu de ce même registre avant le groupe + \parseadd + \expandafter + {\expandafter{\the\code@toks}}% + \expandafter\parse@i% puis va lire le token suivant + \gobone% après avoir mangé le \parsestop + }% + \parse*#1\parsestop% <- le \parsestop@i fermera le groupe semi simple + } + {\parseadd{{#1}}% macro non étoilée, on ajoute #1 tel quel entre accolades + \parse@i% puis va lire le token suivant + }% +} +\def\testtoken#1{% macro qui teste le token + \parseadd{#1}% ici, ne rien faire à part ajouter le token + \parse@i% aller lire le token suivant +} +\def\ifbracefirst#1{% teste si #1 commence par un token de catcode 1 + \ifspacefirst{#1}% si #1 commence par un espace + {\secondoftwo}% renvoyer faux + {\ifnum + \catcode + \expandafter\expandafter\expandafter + `% + \expandafter + \firstto@nil\detokenize{#1W}\@nil=1 + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi + }% +} + +\def\grab@first#1#2{% + \ifx#1\empty\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {\let#2\empty% si #1 est vide, ne rien faire et assigner <vide> à #2 + }% si #1 n'est pas vide + {\def\arg@b{#2}% stocke la macro #2 dans \arg@b + \exparg\ifbracefirst#1% si le 1er token de #1 est "{" + {\expandafter\grab@arg#1\@nil#1% aller lire l'argument avec \grab@arg + } + {% sinon, développer #1 avant de le regarder avec \futurelet : + \expandafter\futurelet\expandafter\nxttok\expandafter\test@nxttok#1\@nil#1% + % puis aller à \test@nxttok + }% + }% +} +\def\test@nxttok{% si le premier token de l'arg #1 de \grab@first est + \ifx\nxttok\sptoken% un espace + \expandafter\grab@spc% aller le lire avec \grab@spc + \else + \expandafter\grab@tok% sinon, lire le token avec \grab@tok + \fi +} +\def\grab@arg#1{% assigne l'argument de \grab@first (mis entre accolades) + \expandafter\def\arg@b{{#1}}% à #2 + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +\expandafter\def\expandafter\grab@spc\space{% + \expandafter\def\arg@b{ }% assigne un espace à #2 de \grab@first + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +\def\grab@tok#1{%% assigne le premier token de l'arg #1 de \grab@first + \expandafter\def\arg@b{#1}% à la macro #2 de \grab@first + \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first +} +% assigne tout ce qui reste à lire (moins le "\relax") à la macro +% #1 de \grab@first +\def\assign@tonil#1\@nil#2{\expsecond{\def#2}{\gobone#1}} + +\def\ifstartwith#1#2{% + \def\startwith@code{#1}\def\startwith@pattern{#2}% + \ifempty{#2} + \firstoftwo% renvoyer vrai si #2 est vide + {\ifempty{#1} + \secondoftwo% renvoyer faux si #1 est vide et pas #2 + \ifstartwith@i% dans les autres cas, aller à \ifstartwith@i + }% +} + +\def\ifstartwith@i{% + \grab@first\startwith@code\first@code% extrait le premier "argument" de <code> + \grab@first\startwith@pattern\first@pattern% et celui de <motif> + \ifx\first@code\first@pattern% s'il sont égaux + \expandafter\ifempty\expandafter{\startwith@pattern} + {\expandafter\firstoftwo}% et que <motif> ne contient plus rien => vrai + {\expandafter\ifempty\expandafter{\startwith@code} + {\expandafter\secondoftwo}% si <code> ne contient plus rien => faux + {\expandafter\ifstartwith@i}% sinon poursuivre les tests + }% + \else% s'ils ne sont pas égaux + \expandafter\secondoftwo% renvoyer faux + \fi +} + +\newif\ifin@group +\def\ifcontain{% + \ifstarred + {\in@groupfalse\ifcontain@i\ifcontain@star}% + {\ifcontain@i\ifcontain@nostar}% +} + +\def\ifcontain@i#1#2#3{% #1 = macro à appeler selon étoile ou pas. #2 = code. #3 = motif + \def\main@arg{#2}\def\pattern@arg{#3}% + \ifempty{#3}% + \firstoftwo + {\ifempty{#2} + \secondoftwo + #1% aller à \ifcontain@star ou \ifcontain@nostar + }% +} + +\def\ifcontain@nostar{% + \exptwoargs\ifstartwith\main@arg\pattern@arg + \firstoftwo% si motif est au début de code => vrai + {\expandafter\ifempty\expandafter{\main@arg} + \secondoftwo% sinon, si code est vide => faux + {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code + \ifcontain@nostar% et recommencer + }% + }% +} + +\def\ifcontain@star{% + \expandafter\ifbracefirst\expandafter{\main@arg}% si code commence par "{" + {\grab@first\main@arg\aux@arg% enlever {<sous code>} de main@arg + \begingroup% ouvrir un groupe + \in@grouptrue% mettre le booléen à vrai + \expandafter\assign@main@arg\aux@arg\@nil% assigner "sous code" à \main@arg + \ifcontain@star% et recommencer avec ce nouveau \main@arg + }% si code ne commence pas par "{" + {\exptwoargs\ifstartwith\main@arg\pattern@arg% si motif est au début de code + \return@true% renvoyer vrai + {\expandafter\ifempty\expandafter{\main@arg}% si code est vide + {\ifin@group% et que l'on est dans un groupe + \endgroup \expandafter\ifcontain@star% en sortir et recommencer + \else% si on n'est pas dans un groupe, le code a été parcouru + \expandafter\secondoftwo% sans trouver <motif> => renvoyer faux + \fi + }% si code n'est pas vide + {\grab@first\main@arg\arg@code% manger le 1er "argument" de code + \ifcontain@star% et recommencer + }% + }% + }% +} + +\def\return@true{% + \ifin@group% tant qu'on est dans un groupe + \endgroup \expandafter\return@true% en sortir et recommencer + \else + \expandafter\firstoftwo% sinon, renvoyer vrai + \fi +} + +\def\forcemath#1{% compose #1 en mode math, quel que soit le mode en cours + \ifmmode\expandafter\firstoftwo\else\expandafter\secondoftwo\fi + {#1}{$#1$}% +} + +\newcount\macro@cnt% numéro à mettre dans le nom des sous macros +\newcount\arg@cnt% compte le nombre d'arguments +\newtoks\param@text% texte de paramètre des macros sous forme #x et/ou [#x] +\newtoks\arg@text% arguments sous forme {#x} et/ou [#x] + +\def\newmacro#1{% + % stocke le nom de la macro et éventuellement "@[<chiffre romain>]" + \def\macro@name##1{\expandafter\gobone\string#1\ifnum##1>0 @[\romannumeral##1]\fi}% + \macro@cnt=0 \arg@cnt=0 % initialise les compteurs + \param@text{}\arg@text{}% vide les registres de texte de paramètre et d'argument + \newmacro@i% va voir le prochain token +} + +\def\newmacro@i{\futurelet\nxttok\newmacro@ii}% met le prochain token dans \nxttok... +% ...puis va à la macro : +\def\newmacro@ii{% + \ifx\nxttok[% si le prochain token est un crochet + \let\donext\newmacro@optarg% aller à \newmacro@optarg + \else + \ifx\nxttok\bgroup% si c'est un accolade ouvrante + % le texte de paramètre est fini et il faut définir la macro + \def\donext{% + \expandafter\def + \csname\macro@name\macro@cnt\expandafter\endcsname + \the\param@text}% <- code juste après non encore lu + \else% sinon, c'est donc un chiffre + \let\donext\newmacro@arg% aller à \newmacro@arg + \fi + \fi + \donext% faire l'action décidée ci-dessus +} + +\def\newmacro@optarg[#1]{% lit la valeur par défaut de l'argument optionnel + % définit la macro \<nom>@<nbre> qui lit tous les arguments (optionnels ou pas) jusqu'alors définis + % à l'aide de \param@text. Puis, elle testera si le prochain token est un crochet + \expandafter\edef\csname\macro@name\macro@cnt\expandafter\endcsname\the\param@text{% + \noexpand\ifnexttok[% + % si oui : la macro \<nom>@<nbr+1> le lira + {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname + \the\arg@text}% + % si non : transmettre à \<nom>@<nbr+1> l'argument optionnel par défaut lu + {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname + \the\arg@text[\unexpanded{#1}]}% + }% + \advance\arg@cnt 1 % incrémenter le numéro d'argument + % pour ajouter "[#<x>]" à \param@text et à \arg@text + \eaddtotoks\param@text{\expandafter[\expandafter##\number\arg@cnt]}% + \eaddtotoks\arg@text{\expandafter[\expandafter##\number\arg@cnt]}% + \advance\macro@cnt 1 % incrémenter le numéro de nom de macro + \newmacro@i% va voir le token suivant +} + +\def\newmacro@arg#1{% #1=nb arguments obligatoires à ajouter + % boucle qui ajoute "#<x>#<x+1>etc" dans \param@text + % et "{#<x>}{#<x+1>}etc" dans \arg@text + \ifnum#1>0 % tant qu'on n'a pas ajouté le nombre de #x nécessaire + \advance\arg@cnt 1 % incrémenter le numéro d'argument + % pour ajouter #x à \param@text et {#x} à \arg@text + \eaddtotoks\param@text{\expandafter##\number\arg@cnt}% + \eaddtotoks\arg@text{\expandafter{\expandafter##\number\arg@cnt}}% + \expandafter\newmacro@arg\expandafter{\number\numexpr#1-1\expandafter}% reboucler + \else% après avoir rajouté e qu'il fallait à \param@text et à \arg@text + \expandafter\newmacro@i% lire le token suivant + \fi +} + +% encadrement avec les réglures où l'on veut +\newmacro\framebox[ULRD]1{% #1 = ULRD (Up, Down, Right, Left) +% ne pas changer le mode H ou V en cours + \hbox{% enferme dans une \hbox + \uppercase{\ifin{#1}L}{\vrule width\frboxrule}{}% réglure gauche + \vtop{% + \vbox{% 1er élément de la \vtop + \uppercase{\ifin{#1}U}{% si la réglure sup doit être tracée + \hrule height\frboxrule% réglure supérieure + \kern\frboxsep% espace haut + } + {}% + \hbox{% + \uppercase{\ifin{#1}L}{\kern\frboxsep}{}% espace gauche + #2% contenu + \uppercase{\ifin{#1}R}{\kern\frboxsep}{}% espace droite + }% + }% puis autres éléments de la \vtop, sous la ligne de base + \uppercase{\ifin{#1}D}{% + \kern\frboxsep% espace bas + \hrule height\frboxrule% réglure inférieure + }% + {}% + }% + \uppercase{\ifin{#1}R}{\vrule width\frboxrule}{}% réglure droite + }% +} + +\def\retokenize#1{% + \immediate\openout\wtest=retokenize.tex % ouvre le fichier + \immediate\write\wtest{\unexpanded{#1}}% y écrit l'argument + \immediate\closeout\wtest% ferme le fichier + \input retokenize.tex % lit le fichier selon les catcodes en vigueur + \unskip% mange l'espace précédemment ajouté qui provient de la fin du fichier +} + +\newtoks\alter@toks% collecteur de tokens + +\def\alter#1#2{% #1= délimiteur #2 = macro altérée + \let\alter@macro#2% sauvegarde la macro + \edef\alter@restorecatcode{% restaurera le catcode de #1 + \catcode`\noexpand#1=\the\catcode`#1 }% + \edef\alter@tmp{\let\noexpand\alter@markertoks= \string#1}% + \alter@tmp% et sauvegarder le délimiteur après avoir mis son catcode à 12 + \edef\alter@tmp{\def\noexpand\alter@readlitterate@i\string#1####1\string#1}% + % développe les \string#1 pour que les arguments délimités aient + % des délimiteurs de catcode 12 + \alter@tmp{% <- comme si on écrivait "\def\alter@readlitterate@i#1##1#1" + \endgroup% après avoir lu ##1 (tokens rendus inoffensifs), fermer le groupe + \addtotoks\alter@toks{{\tt##1}}% ajouter ces tokens + \alter@i% et aller lire le prochain token + }% + \alter@toks{}% initialise le collecteur de tokens + \afterassignment\alter@i% aller lire le premier token après avoir + \let\alter@tmptok= % mangé l'accolade ouvrante de l'argument qui suit +} + +\def\alter@i{% lit le prochain token et va à \alter@ii + \futurelet\alter@nxttok\alter@ii} + +\def\alter@ii{% + % teste le token qui doit être lu (car \futurelet ne l'a pas mangé) + \ifx\alter@nxttok\egroup% si la fin va être atteinte + \let\alter@next\alter@stop% aller à \alterstop@i + \else + \ifx\alter@nxttok\@sptoken% si un espace va être lu + \let\alter@next\alter@readspc% aller à \alter@readspc + \else + \ifx\alter@nxttok\bgroup% si une accolade ouvrante av être lue + \let\alter@next\alter@readarg% aller à \alter@readarg + \else + \ifx\alter@nxttok\alter@markertoks% si le délimiteur va être lu + \let\alter@next\alter@readlitterate% aller à \alter@readlitterate + \else + \let\alter@next\alter@readtok% dans les autres cas, aller à \alter@readtok + \fi + \fi + \fi + \fi + \alter@next% faire l'action décidée ci-dessus +} + +\def\alter@readlitterate{% + % le prochain token est le délimiteur + \begingroup% ouvrir un groupe + \for\alter@tmp=0to255\do{\catcode\alter@tmp=12 }% + % mettre tous les catcodes à 12 + \defactive{ }{\ }% sauf l'espace + \doforeach\alter@tmp\in{<,>,-,`,{,},'}% pour chaque motif de ligature + {\unless\if\alter@tmp\alter@markertoks% s'il est différent du délimiteur + % le définir pour éviter la ligature + \expandafter\alter@defligchar\alter@tmp + \fi + }% + \alter@readlitterate@i% puis aller à \alter@readlitterate@i... + % ...qui a été définie dans \alter +} + +\def\alter@defligchar#1{% définit le caractère pour ne pas provoquer de ligature + \defactive#1{\string#1{}}% +} + +\expandafter\def\expandafter\alter@readspc\space{% \alter@readspc mange un espace dans le code + \addtotoks\alter@toks{ }% ajoute l'espace + \alter@i% puis lire le token suivant +} + +\def\alter@readarg{% + % le token qui suit est "{" + \begingroup% ouvrir un groupe + \def\alterstop@ii{% et modifier localement la macro appelée à la toute fin, + % après que l'accolade fermante ait été mangée (par \alterstop@i) + \expandafter\endgroup% retarder la fermeture de groupe ouvert ci-dessus + \expandafter\addtotoks\expandafter\alter@toks\expandafter + {\expandafter{\the\alter@toks}}% + % pour ajouter hors du groupe ce qui a été collecté à l'intérieur, + % le tout mis entre accolades + \alter@i% puis, lire le token suivant + }% + \alter@toks{}% au début du groupe, initialiser le collecteur + \afterassignment\alter@i% aller lire le prochain token après + \let\alter@tmptok= % avoir mangé l'accolade ouvrante +} + +\def\alter@readtok#1{% + % le prochain token n'est pas un token demandant une action spéciale + \addtotoks\alter@toks{#1}% l'ajouter au collecteur + \alter@i% et aller lire le token suivant +} + +\def\alter@stop{% + % le token à lire est "}" + \afterassignment\alterstop@ii% aller à \alterstop@ii après + \let\alter@tmptok= % avoir mangé l'accolade fermante +} + +\def\alterstop@ii{% + % donner à la macro #2 de \alter tout ce qui a été récolté + \expandafter\alter@macro\expandafter{\the\alter@toks}% + \alter@restorecatcode% puis restaure le catcode du délimiteur +} + +\newdimen\pixelsize +\newdimen\pixelsep +\pixelsize=.9pt \pixelsep=0pt +\def\pixel{\vrule height\pixelsize width\pixelsize} +\def\blankpixel{\vrule height\pixelsize width0pt \vrule height0pt width\pixelsize} +\def\vblankpixel{\vrule height\pixelsize width0pt} +\def\gap{\kern\pixelsep} + +\newskip\letter@skip + +\begingroup% dans ce groupe : +\expandafter\gdef\csname impact@" "\endcsname{\hskip4\pixelsize plus0.5\pixelsize minus0.5\pixelsize\relax}% +\catcode`\^^M=13\relax% le retour à la ligne est actif +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\catcode`\ =12\relax% l'espace devient un "caractère autre" +\global\deftok\otherspc{ }% définit un espace de catcode 12 +\gdef\impact@alphabet{ +a/ +*** + * + *** +* * +****, +a`/ +* + * + +*** + * + *** +* * +****, +b/ +* +* +*** +* * +* * +* * +***, +c/ + *** +* +* +* + ***, +d/ + * + * + *** +* * +* * +* * + ***, +e/ + ** +* * +*** +* + ***, +e'/ + * + * + + ** +* * +*** +* + ***, +e^/ + * + * * + + ** +* * +*** +* + ***, +e`/ + * + * + + ** +* * +*** +* + ***, +f/ + ** +* +*** +* +* +* +*, +g/ + *** +* * +* * +* * + ***_ + * + **, +h/ +* +* +* +*** +* * +* * +* * +* *, +i/ +* + +* +* +* +* +*, +j/ + * + + * + * + * + * + *_ + * +**, +k/ +* +* +* * +* * +** +* * +* *, +l/ +* +* +* +* +* +* + *, +m/ +** * +* * * +* * * +* * * +* * *, +n/ +*** +* * +* * +* * +* *, +o/ + ** +* * +* * +* * + **, +o^/ + * + * * + + ** +* * +* * +* * + **, +p/ +*** +* * +* * +* * +***_ +* +*, +q/ + *** +* * +* * +* * + ***_ + * + *, +r/ +* ** +** +* +* +*, +s/ + *** +* + ** + * +***, +t/ +* +* +** +* +* +* + **, +u/ +* * +* * +* * +* * + ***, +u`/ + * + * + +* * +* * +* * +* * + ***, +v/ +* * +* * +* * + * * + *, +w/ +* * +* * +* * * +* * * + * *, +x/ +* * + * * + * + * * +* *, +y/ +* * +* * + * * + ** + *_ + * + **, +z/ +**** + * + * +* +****, +TEX/ +***** * * + * * * + * * * + * ***** * + * * * * + * * * * + * *** * *_ + * + * + *****, +A/ + *** +* * +* * +* * +***** +* * +* *, +B/ +*** +* * +* * +**** +* * +* * +****, +C/ + **** +* +* +* +* +* + ****, +D/ +**** +* * +* * +* * +* * +* * +****, +E/ +***** +* +* +*** +* +* +*****, +F/ +***** +* +* +*** +* +* +*, +G/ + *** +* * +* +* ** +* * +* * + ***, +H/ +* * +* * +* * +***** +* * +* * +* *, +I/ +* +* +* +* +* +* +*, +J/ + * + * + * + * + * +* * + **, +K/ +* * +* * +* * +** +* * +* * +* *, +L/ +* +* +* +* +* +* +****, +M/ +* * +** ** +* * * +* * * +* * +* * +* *, +N/ +* * +** * +** * +* * * +* ** +* ** +* *, +O/ + *** +* * +* * +* * +* * +* * + ***, +P/ +**** +* * +* * +**** +* +* +*, +Q/ + *** +* * +* * +* * +* * * +* ** + ***, +R/ +**** +* * +* * +**** +* * +* * +* *, +S/ + *** +* * + * + * + * +* * + ***, +T/ +***** + * + * + * + * + * + *, +U/ +* * +* * +* * +* * +* * +* * + ***, +V/ +* * +* * +* * +* * +* * + * * + *, +W/ +* * +* * +* * +* * +* * * +** ** +* *, +X/ +* * +* * + * * + * + * * +* * +* *, +Y/ +* * +* * + * * + * + * + * + *, +Z/ +***** + * + * + * + * +* +*****, +?/ + *** +* * + * + * + * + + *, +!/ +* +* +* +* +* + +*, +'/ + * +* + + + +, +./ +*, +{,}/ + *_ +*, +1/ + * + ** +* * + * + * + * + *, +2/ + *** +* * + * + * + * + * +*****, +3/ + *** +* * + * + ** + * +* * + ***, +4/ + * + ** + * * +* * +***** + * + *, +5/ +***** +* +**** + * + * + * +****, +6/ + *** +* +* +**** +* * +* * + ***, +7/ +***** + * + * + * + * + * + *, +8/ + *** +* * +* * + *** +* * +* * + ***, +9/ + *** +* * +* * + **** + * + * + ***, +0/ + *** +* * +* * +* * * +* * +* * + ***, +@/ + ** + * * +* ** +* * * +* * + * * + ***, +error/ +* * * + * * +* * * + * * +* * * + * * +* * *}% +\endgroup% + +\def\makecar#1#2{% #1=nom recevant le code final #2=macro contenant le dessin + \let\pixabove\empty \let\pixbelow\empty \let\pix@line\empty% initialise à vide + \exparg\ifin{#2}_% si le code contient _ + {\expandafter\makecar@iii#2\@nil}% aller à \makecar@iii + {\exparg\makecar@i{#2}}% sinon, à \makecar@i + \edef#1{% définit la macro #1 comme + \vtop{% une \vtop contenant : + \unexpanded{\offinterlineskip\lineskip\pixelsep}% réglage d'espace inter ligne + \vbox{\unexpanded\expandafter{\pixabove}}% \vbox des pixels au-dessus + % de la ligne de base + \unless\ifx\pixbelow\empty% s'il y a des pixels au-dessous de la baseline + \unexpanded\expandafter{\pixbelow}% les ajouter dans la \vtop + \fi + }% + }% +} + +\def\makecar@i#1{% #1 = dessin de la lettre avec les caractères "," "*" et " " + \doforeach\current@line\in{#1}% pour chaque ligne dans #1 : + {\ifx\empty\current@line% si la ligne est vide + \addtomacro\pixabove{\hbox{\vblankpixel}}% ajouter une fausse ligne + \else% sinon + \let\pix@line\empty% initialiser le code de la ligne à vide + \expandafter\makecar@ii\current@line\quark% et la construire + \fi + }% +} + +\def\makecar@ii#1{% #1=caractère de dessin de la ligne en cours + \ifxcase#1% si le caractère est + * {\addtomacro\pix@line\pixel}% + \otherspc{\addtomacro\pix@line\blankpixel}% + \endif + \ifx#1\quark% si la fin est atteinte + \addtomacro\pix@line\unkern% annuler le dernier espace interpixel + \eaddtomacro\pixabove{% et encapsuler \pix@line dans une \hbox + \expandafter\hbox\expandafter{\pix@line}}% + \else% si la fin n'est pas atteinte, ajouter l'espace interpixel + \addtomacro\pix@line\gap% + \expandafter\makecar@ii% recommencer avec le caractère suivant + \fi +} + +\def\makecar@iii#1_,#2\@nil{% + \makecar@i{#2}% construit la partie au-dessous de la baseline + \let\pixbelow\pixabove% et affecte le code à \pixbelow + \let\pixabove\empty \let\pix@line\empty% ré-initialise + \makecar@i{#1}% construit la partie au-dessus de la baseline +} + +% On parcourt le texte de remplacement de \impact@alphabet +\edef\saved@crcatcode{\catcode13=\the\catcode13\relax}% +\catcode`\^^M=13\relax% le retour à la ligne est actif +\edef^^M{\string,}% et se développe en une virgule (de catcode 12) +\expsecond{\doforeach\letter@name/\letter@code\in}\impact@alphabet% + {\edef\letter@name{\letter@name}% développe la lettre (^^M devient ",") + \edef\letter@code{\letter@code}% développe le code (^^M devient ",") + \exparg\ifstart\letter@name,% si la lettre commence par "," + {\edef\letter@name{\expandafter\gobone\letter@name}}% la retirer + {}% + \exparg\ifstart\letter@code,% si le code commence par "," + {\edef\letter@code{\expandafter\gobone\letter@code}}% la retirer + {}% + \expandafter\makecar\csname impact@"\letter@name"\endcsname\letter@code% +}% +\saved@crcatcode% redonne le catcode de ^^M +\def\end@process{\end@process}% + +\def\impactend{\endgroup} + +\newmacro\impact[0.2ex][0pt]{% + \begingroup + \pixelsize#1 \pixelsep#2 + \letter@skip=#1 plus.1pt minus.1pt + \catcode`\!12 \catcode`\?12 + \impact@i +} + +\def\impact@i{\futurelet\nxtletter\impact@ii} + +\def\impact@ii{% + \ifx\nxtletter\impactend + \let\next\unskip + \else + \leavevmode + \ifx\nxtletter\sptoken + \let\next\impact@spc + \else + \let\next\impact@arg + \fi + \fi + \next +} + +\expandafter\def\expandafter\impact@spc\space{% + \csname impact@" "\endcsname + \impact@i +} + +\def\impact@arg#1{% + \ifcsname impact@"#1"\endcsname + \csname impact@"#1"\endcsname + \else + \csname impact@"error"\endcsname + \fi + \hskip\letter@skip + \impact@i +} + +\newdimen\maingraddim \maingraddim=4pt +\newdimen\maingradwd \maingradwd=0.5pt +\newdimen\subgraddim \subgraddim=2.5pt +\newdimen\subgradwd \subgradwd=0.2pt +\newdimen\axiswd \axiswd=0.5pt +\newdimen\plotincrement \plotincrement=0.1pt +\newdimen\tmpdim + +\def\maingradx#1{% + \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous + \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure +} + +\def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }} + +% #1= dim entre 2 grad principales #2=nb départ #3=incrément #4=nb arrivée #4=nb intervalles secondaires +\newmacro\xaxis[1cm]1[1]1[4]{% + \hbox{% tout mettre dans une \hbox + \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions + \for\xx=1 to #5-1 \do{% insérer #5-1 fois + \kern\dimsubgrad% une espace secondaire + \subgradx% une graduation secondaire + }% + }% + \rlap{% en débordement à droite : + \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale + \maingradx{\xx}% imprimer l'abscisse + \ifdim\xx pt<#4pt % et en débordement à droite, + \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière + \fi + \kern#1\relax% se déplacer vers la droite + }% + }% + \vrule% tracer l'axe des abscisses + height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 + width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax + depth 0pt\relax % et de profondeur nulle + }% +} + +\def\maingrady#1{% affiche... + \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée... + \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure +} + +% affiche une subdiv +\def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }} +% #1= dim entre 2 grad principales #2=abscisse départ #3=incrément +% #4=abscisse arrivée #5=nb intervalles secondaires +\newmacro\yaxis[1cm]1[1]1[4]{% + \vbox{% + \offinterlineskip% désactiver le ressort interligne + \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions + \for\xx=1 to #5-1 \do{% insérer #5-1 fois + \kern\dimsubgrad% une espace secondaire + \subgrady% une graduation secondaire + }% + }% + \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions + \vbox to 0pt{% en débordement vers le bas + \FOR\xx = #4to#2\do-#3{% + \maingrady{\xx}% imprimer l'abscisse + \ifdim\xx pt>#2pt % et en débordement à droite, + \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière + \fi + \kern#1\relax% se déplacer vers la droite + }% + \vss% assure le débordement vers le bas + }% + \clap{\vrule% tracer l'axe des abscisses + width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 + height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax + depth 0pt\relax % profondeur nulle + } + }% +} + +\newdimen\xunit \xunit=1cm +\newdimen\yunit \yunit=1cm +\newmacro\graphzone1[1]1[4]1[1]1[4]{% + \quitvmode + \begingroup + \def\graphxmin{#1}\def\graphxmax{#3}% + \def\graphymin{#5}\def\graphymax{#7}% + \def\xincrement{#2}\def\yincrement{#6}% + \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% + \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% + \edef\graphboxht{\the\ht0 }% + \edef\graphboxwd{\the\wd1 }% + \rlap{\box1 \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }}% + \rlap{\box0 }% + \raise\graphboxht\rlap{\kern-.5\axiswd\vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}% + \begingroup + \catcode`\^^M=9\relax + \graphzone@i +} + +\def\graphzone@i#1{% + \endgroup + \xunit=\decdiv1\xincrement\xunit + \yunit=\decdiv1\yincrement\yunit + \setbox0\hbox{#1}% + \wd0=\dimexpr\graphboxwd+\axiswd\relax + \ht0=\dimexpr\graphboxht+\axiswd\relax + \box0 + \endgroup + \ignorespaces +} + +\def\plot(#1,#2){% + \edef\x@plot{#1}\edef\y@plot{#2}% + \ifinside\x@plot[\graphxmin,\graphxmax]% + {\ifinside\y@plot[\graphymin,\graphymax]% + {\putat{\dimexpr\x@plot\xunit-\graphxmin\xunit\relax}{\dimexpr\y@plot\yunit-\graphymin\yunit\relax}\plotstuff}% + \relax + } + \relax +} + +\def\showyaxis{% + \ifinside0[\graphymin,\graphymax]% + {\putat\z@{-\graphymin\yunit}{\vrule width\graphboxwd height\axiswd}}% + \relax +} + +\def\showxaxis{% + \ifinside0[\graphxmin,\graphxmax]% + {\putat{-\graphxmin\xunit}\z@{\clap{\vrule width\axiswd height\graphboxht}}}% + \relax +} + +\def\showaxis{\showxaxis\showyaxis} + +\def\putat#1#2#3{% + \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}% +} + +\newmacro\cross[2pt][0.2pt]{% + \quitvmode + \vlap{% + \clap{% + \vrule height#2 depth0pt width#1 + \vrule height#1 depth#1 width#2 + \vrule height#2 depth0pt width#1 + }% + }% +} + +\protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres + +\def\formatdecpart#1{% #1=série de chiffres + \ifempty{#1}% si la partie décimale est vide + {}% ne rien afficher + {{,}\formatdecpart@i1.#1..}% sinon, afficher la virgule et mettre en forme +} + +% #1=compteur de caractères #2= chiffre à déplacer +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatdecpart@i#1.#2#3.#4.{% #1=compteur de caractères #2= chiffres traité #3= chiffres traités + \ifempty{#3}% si #2 est le dernier chiffre à traiter + {#4#2}% le mettre en dernière position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo + \else\expandafter\secondoftwo + \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatdecpart@i\number\numexpr#1+1.#3.#4#2.}% + }% +} + +\def\formatintpart#1{% #1=série de chiffres + \expandafter\formatintpart@i\expandafter1\expandafter.% + \romannumeral\reverse{#1\z@}..% appelle la macro récursive +} + +% #1=compteur de caractères #2= chiffre à déplacer +% #3= chiffres restants #4 = chiffres déjà traités +\def\formatintpart@i#1.#2#3.#4.{% #1=compteur de caractères #2= chiffres traité #3= chiffres traités + \ifempty{#3}% si #2 est le dernier chiffre à traiter + {#2#4}% le mettre en première position et tout afficher, sinon + {\ifnum#1=3 \expandafter\firstoftwo + \else\expandafter\secondoftwo + \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et + {\formatintpart@i 1.#3.\numsep#2#4.}% mettre \numsep#2 en premier puis recommencer + % sinon, mettre #2 en dernière position et recommencer + % tout en incrémentant #1 de 1 + {\expandafter\formatintpart@i\number\numexpr#1+1.#3.#2#4.}% + }% +} + +\def\removefirstzeros#1{% + \removefirstzeros@i#1\removefirstzeros@i +} +\def\removefirstzeros@i#1{% + \ifx\removefirstzeros@i#1% \removefirstzeros@i est lu donc tout le nombre a été parcouru + \expandafter0% laisser un zéro + \else + \ifx0#1% si le chiffre lu est un 0 + \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer + \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i + \expandafter\expandafter\expandafter\removefirstzeros@ii\expandafter\expandafter\expandafter#1% + \fi + \fi +} +\def\removefirstzeros@ii#1\removefirstzeros@i{#1} + +\def\ifnodecpart#1{\ifnodecpart@i#1.\@nil} +\def\ifnodecpart@i#1.#2\@nil{\ifempty{#2}} + +\newbox\remainbox% boite contenant le texte total +\newbox\currentline% boite contenant le ligne en cours +\newcount\linecnt% compteur pour numéroter les lignes + +\newmacro\leftline[0pt]{% définit ce qui se trouve à gauche de chaque ligne + \def\wd@left{#1}% + \def\stuff@left +} + +\newmacro\rightline[0pt]{% définit ce qui se trouve à droite de chaque ligne + \def\wd@right{#1}% + \def\stuff@right +} + +\let\formatline=\identity + +\leftline[15pt]{$\scriptscriptstyle\number\linecnt$\kern5pt }% numérotation à gauche +\rightline{}% rien à droite + +\def\numlines{% + \par\smallskip + \begingroup% dans un groupe semi simple + \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante + \savingvdiscards=1 % autorise la sauvagarde des éléments supprimés + \linecnt=0 % initialiser le compteur de lignes + \setbox\remainbox=\vbox\bgroup% compose la boite... + \advance\hsize by% diminuer la \hsize + -\dimexpr\wd@left+\wd@right\relax% de la largeur des contenus +} + +\def\endnumlines{% + \egroup + \offinterlineskip + \split@line +} + +\def\split@line{% + \ifvoid\remainbox% si la boite est vide + \par% fin du processus + \endgroup% fermer le groupe ouvert au début + \else% sinon + \advance\linecnt 1 % incrémente le compteur de lignes + \edef\htbefore{\the\vdim\remainbox}% + \edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% sauvegarde le \vfuzz + \vfuzz=\maxdimen% annule les avertissements pour débordement + \setbox\currentline=\vsplit\remainbox to 0pt % couper la boite à 0pt de hauteur + \restorevfuzz + \setbox\currentline=\vbox{\unvbox\currentline}% redonner à la boite sa hauteur + \let\savedsplitdiscards\splitdiscards + \edef\intersplitspace{% calcul de l'espace vertical perdu à la coupure + \the\dimexpr\htbefore-(\vdim\remainbox+\vdim\currentline)\relax + }% + \hbox{% en mode vertical et dans une hbox, afficher : + \hbox to\wd@left{\hss\stuff@left}% 1) ce qui est à gauche + \formatline{\box\currentline}% 2) la ligne courante + \hbox to\wd@right{\stuff@right\hss}% 3) ce qui est à droite + }% + \splitdiscards% affiche ce qui a été ignoré à la coupure + \expandafter\split@line% recommencer + \fi +} + +\newskip\interletterskip +\newskip\interwordskip +\catcode`\@11 +\newtoks\spacetxt@toks% le registre qui contient le texte final + +\def\spacetxt{% + \let\spacetxt@endprocess\spacetxt@endnormal + % définit la macro appelée en fin de processus -> à priori : fin normale + \ifstarred% si la macro est étoilée + {\let\spacetxt@recurse\spacetxt@star% définir la macro récursive + \spacetxt@i% et aller à \spacetxt@i + }% sinon + {\let\spacetxt@recurse\spacetxt@nostar% définir la macro récursive + \spacetxt@i% et aller à \spacetxt@i + }% +} + +\newmacro\spacetxt@i[0.3em][3\interletterskip]1{% +% arg optionnel #1 et #2 = ressorts inter-lettre et inter--mot +% #3 = texte à espacer + \interletterskip=#1\relax + \interwordskip=#2\relax + \def\spacetxt@code{#3}% met le texte à espacer dans \spacetxt@code + \spacetxt@toks{}% initialiser le registre contenant le texte final + \spacetxt@recurse% aller à la macro récursive précédemment définie +} + +\newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré + +\def\spacetxt@nostar{% + \expandafter\ifempty\expandafter{\spacetxt@code}% si texte restant est vide + \spacetxt@endprocess% aller à la fin du processus + {\@indivifoundfalse% sinon, à priori, les motifs non réguliers ne sont pas trouvés + \expandafter\doforeach\expandafter\indivi@tmp\expandafter\in\expandafter{\indivilist} + % pour chaque \indivi@tmp dans \indivilist + {\exptwoargs\ifstartwith\spacetxt@code\indivi@tmp + % si le code commence par le motif courant + {\eaddtotoks\spacetxt@toks{\indivi@tmp\hskip\interletterskip}% + % l'ajouter dans le registre ainsi que l'espace inter-lettre + \expandafter\rightofsc\expandafter\spacetxt@code\expandafter{\indivi@tmp}% + % et enlever le motif du texte restant à lire + \@indivifoundtrue + % marquer qu'un motif a été trouvé + \doforeachexit + % et sortir prématurément de la boucle + }% + \relax + % si le code ne commence pas le motif courant -> ne rien faire + }% + \if@indivifound + \else% si aucun motif n'a été trouvé + \grab@first\spacetxt@code\spacetxt@temp + % retirer le 1er caractère du texte +% \ifcat\noexpand\spacetxt@temp\sptoken + \ifx\spacetxt@temp\space + % si le 1er caractère est un espace + \addtotoks\spacetxt@toks{\hskip\interwordskip}% + % ajouter l'espace inter-mot au registre de token + \else + % si le 1er caractère n'est pas un espace + \eaddtotoks\spacetxt@toks{\spacetxt@temp\hskip\interletterskip}% + % ajouter ce caractère et l'espace inter-lettre au registre de token + \fi + \fi + \spacetxt@recurse% enfin, continuer le processus + }% +} + +\def\spacetxt@star{% + \expandafter\ifempty\expandafter{\spacetxt@code}% si texte restant est vide + \spacetxt@endprocess% aller à la fin du processus + {\expandafter\ifbracefirst\expandafter{\spacetxt@code}% + % sinon, si texte commence par "{" + {\grab@first\spacetxt@code\spacetxt@temp + % mettre {<argument} dans \spacetxt@temp + \begingroup% ouvrir un groupe + \expandafter\def\expandafter\spacetxt@code\spacetxt@temp + % mettre le contenu de l'argument dans \spacetxt@code + \let\spacetxt@endprocess\spacetxt@endingroup% changer le processus de fin + \spacetxt@toks{}% initialiser + \spacetxt@recurse% exécuter le processus avec ce nouveau texte + }% si le code ne commence pas par "{", aller à \spacetxt@nostar mais comme + \spacetxt@nostar% \spacetxt@recurse vaut \spacetxt@star, n'y faire qu'1 boucle + }% +} + +\def\spacetxt@endnormal{% fin de processus normal + \the\spacetxt@toks% afficher le registre à token + \unskip% et supprimer le dernier ressort +} + +\def\spacetxt@endingroup{% fin du processus dans un groupe : + \expandafter\endgroup\expandafter% avant de fermer le groupe + \addtotoks\expandafter\spacetxt@toks\expandafter% ajouter au registre hors du groupe + {\expandafter{\the\spacetxt@toks}}% ce qui est collecté localement mis entre {} + \spacetxt@recurse% puis aller à la macro récursive +} + +\def\indivilist{<<,>>}% liste des motifs spéciaux + +\def\insert@blankchar{% + \ifstarred\insert@blankchar@ii\insert@blankchar@i +} + +\def\insert@blankchar@i#1{% insère une espace de largeur #1 caractères complets + \ifnum\numexpr#1\relax>0 + \kern\numexpr#1\relax\dimexpr\ttchar@width+\brktt@interletter\relax + \fi +} + +\def\insert@blankchar@ii#1{% insère #1-1 caractères complets + 1 largeur de caractère + \ifnum\numexpr#1\relax>0 + \insert@blankchar@i{#1-1}\kern\ttchar@width + \fi +} + +\def\restart@hbox#1{% + \egroup + \hbox\bgroup + \expsecond{\def\tt@remaintext} + {\romannumeral\removefirstspaces@i{#1}}% initialiser le code à composer + \let\previous@char\space% initialise le caractère précédent + \line@starttrue% aucun caractère n'a encore été imprimé + \brktt@cnt=0\relax% remettre le compteur à 0 +} + +\def\print@nchar#1{% affiche #1 caractères pris dans \tt@remaintext + \for\xxx= 1 to #1 \do 1{% + \ifx\tt@remaintext\empty% si le code restant à composer est vide + \exitfor\xxx%sortir de la boucle prématurément + \else + \@indivifoundfalse% sinon, à priori, les motifs de ligature ne sont pas trouvés + % pour chaque \indivi@tmp dans la liste de ligatures + \expsecond{\doforeach\indivi@tmp\in}{\liglist}% + {% si le code commence par la \idx{ligature} courante + \exptwoargs\ifstartwith\tt@remaintext\indivi@tmp% + {\let\previous@char\indivi@tmp% prendre le motif pour caractère courant + \expsecond{\rightofsc\tt@remaintext}{\indivi@tmp}% et l'enlever du texte restant à lire% + \@indivifoundtrue% marquer qu'un motif a été trouvé + \doforeachexit% et sortir prématurément de la boucle + }% + {}% si le code ne commence pas le motif courant -> ne rien faire + }% + \unless\if@indivifound% si aucun motif trouvé, + \grab@first\tt@remaintext\previous@char% lire le premier caractère + \fi + \advance\brktt@cnt by 1 % incrémenter le compteur de caractères + \hbox to\ttchar@width{\hss\previous@char\hss}% afficher le caractère lu + \line@startfalse% nous ne sommes plus au début d'une ligne + \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie + \kern\brktt@interletter% insérer le ressort inter-lettre + \else + \exitfor\xxx% sinon, sortir de la boucle prématurément + \fi + \fi + }% +} + +\newmacro\breakttA[0.3em][\hsize]1{% +% arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte +% #3 = texte à espacer + \begingroup% ouvrir un groupe et le fermer à la toute fin + \par% commencer un nouveau paragraphe -> passage en mode vertical + \parindent=0pt% empêche l'indentation + \tt% passer en police à chasse fixe + \setbox0 = \hbox{M}% la boite 0 contient un caractère + \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en police \tt + \edef\text@width{\the\dimexpr#2\relax}% largeur de composition + % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) + \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère + \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax + % le nombre de caractères par ligne est égal à 1 de plus : + \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% + % calcul de la dimension inter-lettre + \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax + \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}% met le texte à espacer dans \tt@remaintext + \addtomacro\tt@remaintext{ \relax}% ajouter " \relax" à la fin : le code finit donc par " \relax" + \def\tt@emptytext{ \relax}% sera le code lorsque tout est composé + \unless\ifx\tt@remaintext\tt@emptytext% si le texte à composer n'est pas vide + \hbox\bgroup% démarrer la boite horizontale contenant la première ligne + \insert@blankchar\ttindent% insérer une espace d'indentation de longueur (l+Delta')*\ttindent + \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés + \line@starttrue% il s'agit du début d'une ligne + \expandafter\breakttA@i% aller à la macro récursive + \fi +} + +\def\leftofsc#1#2{% dans la sc #1, garde ce qui est à gauche de #2 + \def\leftofsc@i##1#2##2\@nil{\def#1{##1}}% + \expandafter\leftofsc@i#1\@nil +} + +\def\len@tonextword{% stocke dans \next@len le nombre de caractères avant + % le prochain point de coupure dans \tt@remaintext + \let\next@word\tt@remaintext% copie \tt@remaintext dans la macro temporaire \next@word + \leftofsc\next@word{ }% ne prend que ce qui est avant le prochain espace + \exparg\ifin{\next@word}{-}% si le mot contient un tiret + {\leftofsc\next@word{-}% prendre ce qui est à gauche de ce tiret + \def\extra@char{1}% il y a un caractère de plus à loger après le mot + } + {% sinon, le caractère après le mot est un espace + \def\extra@char{0}% qu'il ne faut pas compter + }% + \setbox0=\hbox{\next@word}% enfermer le mot dans une boite + % et en calculer le nombre de caractères + \edef\next@len{\number\numexpr\dimexpr\wd0 \relax/\dimexpr\ttchar@width\relax\relax}% +} + +\newmacro\zerocompose[]2{% +% #1=code à exécuter avant la composition +% #2=registre de boite recevant le résultat +% #3= texte à composer en largeur 0pt + \setbox#2=\vbox{% + #1% code a exécuter (changement de fonte par exemple) + \hfuzz=\maxdimen% annule les avertissements pour débordement horizontaux + \hbadness=10000 % annule les avertissements pour mauvaise boite horizontale + \pretolerance=-1 % désactive la première passe (celle sans coupures) + \tolerance=10000 % passe avec coupures acceptée + \hyphenpenalty=-10000 % favorise fortement les copures de mots + \lefthyphenmin=2 \righthyphenmin=3 % longueur mini des fragments de début et fin + \clubpenalty=0 % pas de \idx{pénalité} supplémentaire après la première ligne + \interlinepenalty=0 % pas de \idx{pénalité} inter-ligne + \widowpenalty=0 % pas de \idx{pénalité} supplémentaire avant la dernière ligne + \exhyphenpenalty=0 % ne pas pénaliser une coupure explicite + \leftskip=0pt \rightskip=0pt % désactive les éventuels ressorts latéraux + \everypar={}% désactive l'éventuel \everypar + \parfillskip=0pt plus1fil % règle le \parfillskip par défaut + \hsize=0pt % largeur de composition = 0pt + \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% + \hyphenchar\font=`\- % impose "-" comme caractère de coupure + \noindent % pas d'indentation + passage en mode horizontal + \hskip0pt \relax% premier noeud horizontal pour permettre la coupure de la suite + #3\par% compose #3 + \restorehyphenchar% restaure le caractère de coupure + }% +} + +\def\hyphlengths#1#2{%#2 = macro contenant les longueurs de coupures du mot #1 + \begingroup + \zerocompose + [\tt% passer en police à chasse fixe + \setbox\z@\hbox{M}\xdef\ttwidth{\the\wd\z@}% mesurer la largeur des caractères + \hyphenchar\font=`\- % choisit "-" comme caractère de coupure + ]\z@{#1}% compose en 0pt dans la boite 0 + \let#2 = \empty% initialise la macro #2 + \def\cumul@length{0}% + \vfuzz=\maxdimen% annule les avertissements pour débordement + \splittopskip=\z@ % ne rajouter aucun espace au sommet de la boite restante + \loop + \setbox1=\vsplit\z@ to \z@% couper la boite à 0pt de hauteur + {\setbox\z@=\vbox{\unvbox1 \unskip\unpenalty\global\setbox1=\lastbox}}% + \setbox1=\hbox{\unhbox1 }% + \unless\ifvoid\z@% si la boite 0 n'est pas encore vide + \edef\cumul@length{% mettre à jour \cumul@length + \number\numexpr + \cumul@length + +% ajouter le quotient "largeur syllabe/largeur d'1 caractère" + \wd1/\dimexpr\ttwidth\relax + -1% et soustraire 1 (le caractère "-") + \relax + }% + % ajouter à #2 la virgule et le cumul actuel +1 + \edef#2{% définir la macro #2 : + #2% reprendre le contenu de #2 + \ifx#2\empty\else,\fi% ajouter "," si #2 non vide + \number\numexpr\cumul@length+1\relax% et le cumul + }% + \repeat% et recommencer + \expsecond{% avant de fermer le groupe + \endgroup + \def#2}{#2}% définit #2 hors du groupe +} + +\newif\ifnumalgo +\newcount\algocnt +\numalgotrue + +\def\algoindent{2em} +\def\algorule{.4pt}% épaisseur des réglures de début et de fin + +\def\algohrulefill{% remplit avec une ligne horizontale + \leavevmode + \leaders\hrule height\algorule\hfill + \kern0pt % rajoute un noeud au cas où la commande est en fin de ligne +} + +\def\algoleftskip{10pt} +\def\algorightskip{10pt} + +\newmacro\algorithm[\\,\#,\{,\}]2{% + \medbreak + \begingroup% ouvre un groupe (sera fermé à la fin de l'algorithme) + \footnotesize + \algocnt=1 % initialise le compteur de lignes + \leftskip=\algoleftskip \rightskip=\algorightskip% initialise les ressorts latéraux + \parindent=0pt % pas d'indentation + %%%%%%%%%%%% affichage du titre %%%%%%% + \algohrulefill% remplir avec une ligne horizontale + \ifempty{#2}% si #2 est vide + {}% ne rien insérer + {% sinon + \lower.5ex\hbox{ #2 }% insérer le titre abaissé de 0.5ex + \algohrulefill% insérer une ligne horizontale + }% + \par% aller à la ligne + \nointerlineskip% ne pas insérer le ressort interligne + \kern7pt % et sauter 7pt verticalement + \nobreak% empêcher une coupure de page + %%%%%%%%%%%%%% fin du titre %%%%%%%%%% + % + %%%%%%%%%%%%%% rend les caractères actifs %%%%%%%%%%%%%% + \def~##1~{\begingroup\bf##1\endgroup}% + \defactive:{% rend ":" actif + \futurelet\nxttok\algoassign% \nxttok = token suivant + }% + \def\algoassign{% suite du code de ":" + \ifx=\nxttok% si ":" est suivi de "=" + \ifmmode% si mode math + \leftarrow% afficher "\leftarrow" + \else% si mode texte + ${}\leftarrow{}$% passer en mode math pour "\leftarrow" + \fi + \expandafter\gobone% manger le signe "=" + \else% si ":" n'est pas suivi de "="' + \string:% afficher ":" + \fi + }% + \expandafter\def\expandafter\oldeverypar\expandafter% sauvegarder... + {\the\everypar}% ... l'action effectuée à chaque paragraphe + \ifnumalgo% si la numérotation est demandée, + \everypar\expandafter{% à chaque paragraphe + \the\everypar% reprendre le contenu précédent + \llap{% et à droite du début de la ligne, + $\scriptstyle\number\algocnt$% afficher le numéro + \kern\dimexpr4pt+\leftskip-\algoleftskip\relax% en tenant compte de l'indentation + }% + }% + \fi + \def\algopar{% actions effectués par ^^M + \advance\algocnt by 1 % incrémente le compteur de lignes + \color{black}% repasse en couleur noire + \rm% fonte droite + \par% termine le paragraphe + \leftskip=\algoleftskip % initialise le ressort gauche + }% + \doforeach\currentchar\in{#1}{\expandafter\catcode\expandafter`\currentchar=12 }% + \defactive\^^I{\advance\leftskip by \algoindent\relax}% + \defactive\ {\hskip1.25\fontdimen2\font\relax}% espace = 25% de + que la largeur naturelle + \letactive\^^M\algopar + \defactive\%{\it\color{gray}\char`\%}% passe en italique et gris puis affiche "%" + \defactive_{\ifmmode_\else\string_\fi}% + \defactive#3{% rend le token #3 actif (il sera rencontré à la fin de l'algorithme) + \everypar\expandafter{\oldeverypar}% restaurer l'ancien \everypar + \par% aller à la ligne + \nointerlineskip% ne pas insérer le ressort interligne + \kern7pt % et sauter 7pt verticalement + \nobreak% empêcher une coupure de page + \algohrulefill% tracer la ligne de fin + \smallbreak% saute un petit espace vertical + \endgroup% ferme le groupe ouvert au début de l'algorithme + }% + %%%%%%%%%%%%%%%%% fin des caractères actifs %%%%%%%%%%%%%%%% + % + \sanitizealgo% va manger les espaces et les ^^M au début de l'algo +} + +\def\sanitizealgo{\futurelet\nxttok\checkfirsttok}% récupère le prochain token + +\def\checkfirsttok{% teste le prochaun token + \def\nextaction{% à priori, on considère que la suite est " " ou "^^M" donc + \afterassignment\sanitizealgo% aller à \sanitizealgo + \let\nexttok= % après avoir mangé ce "^^M" ou cet espace + }% + \unless\ifx\nxttok\algopar% si le prochain token n'est pas un ^^M + \unless\ifx\space\nxttok% et si le prochain token n'est pas un espace + \let\nextaction\relax% ne rien faire ensuite + \fi + \fi + \nextaction% faire l'action décidée ci-dessus +} + +\def\true@sgn#1{\ifnum#11<\z@-\fi} + +\restoreatcoatcode\relax +\endinput
\ No newline at end of file diff --git a/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/vignette.png b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/vignette.png Binary files differnew file mode 100644 index 00000000000..8cb018a4d6d --- /dev/null +++ b/Master/texmf-dist/doc/plain/apprendre-a-programmer-en-tex/source/vignette.png diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index 2989439a0f9..11945481f7c 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -41,7 +41,9 @@ my @TLP_working = qw( animate anonchap anonymouspro answers antiqua antomega antt anufinalexam anyfontsize anysize aobs-tikz aomart apa apa6 apa6e apacite apalike2 apalike-german apnum - appendix appendixnumberbeamer apprends-latex apptools apxproof + appendix appendixnumberbeamer + apprendre-a-programmer-en-tex apprends-latex + apptools apxproof arabi arabi-add arabluatex arabtex arabxetex aramaic-serto arara archaeologie archaic arcs arev arimo armtex around-the-bend arphic arphic-ttf arrayjobx arraysort arsclassica diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds index 94269404ce4..137250f657f 100755 --- a/Master/tlpkg/libexec/ctan2tds +++ b/Master/tlpkg/libexec/ctan2tds @@ -2056,6 +2056,7 @@ $standardtex 'allrunes', 'fonts', 'amstex', 'amstex', 'antomega', 'omega', + 'apprendre-a-programmer-en-tex', 'plain', 'arabxetex', 'xelatex', 'archaic', 'fonts', 'arev', 'fonts', diff --git a/Master/tlpkg/tlpsrc/apprendre-a-programmer-en-tex.tlpsrc b/Master/tlpkg/tlpsrc/apprendre-a-programmer-en-tex.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Master/tlpkg/tlpsrc/apprendre-a-programmer-en-tex.tlpsrc diff --git a/Master/tlpkg/tlpsrc/collection-langfrench.tlpsrc b/Master/tlpkg/tlpsrc/collection-langfrench.tlpsrc index 6324622a043..fb307193d18 100644 --- a/Master/tlpkg/tlpsrc/collection-langfrench.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-langfrench.tlpsrc @@ -5,6 +5,7 @@ longdesc Support for French and Basque. depend collection-basic # depend aeguill +depend apprendre-a-programmer-en-tex depend apprends-latex depend babel-basque depend babel-french |