• Non ci sono risultati.

Trattamento di liste di token in ε-TEX

Nel documento Appunti di programmazione in L (pagine 141-146)

Si è detto più volte che quando un token carattere è entrato nel meccanismo di lettura di TEX l’attribuzione del codice di categoria è immutabile. Ciò non è più del tutto vero conε-TEX che ha introdotto la primitiva\scantokens. Con

\scantokens{〈lista di token 〉}

la lista viene reimmessa nel meccanismo di lettura, come se fosse stata in un file esterno, con i codici di categoria validi al momento della nuova lettura. Perciò, per esempio,

\begingroup\catcode‘\!=\active

\def\x{\def!{Ciao}}\expandafter\endgroup\x \def\abc{\catcode‘\!=\active\scantokens{!}} Il seguente è un saluto: \abc.

produce

Il seguente è un saluto: Ciao .

cosa che senza\scantokensnon sarebbe stata possibile senza scrivere qualcosa su un file esterno. C’è qualcosa che non va, perché fra ‘Ciao’ e il punto c’è uno spazio. In queste definizioni è bene terminare la lista di token dentro a\scantokenscon \noexpandoppure\empty: lo verifichiamo provando.

Il seguente è un saluto: Ciao.

Il motivo è che alla fine di un file (come quello virtuale che viene prodotto da \scantokens) c’è sempre un carattere invisibile; lo scopo di\noexpandè di ren-dere questo carattere uguale a\relax. Un altro modo di risolvere il problema è di porre

\everyeof{\noexpand}

in quanto\everyeofè un registro di tipo\toksil cui contenuto viene espanso a ogni chiusura di\scantokens.

Va da sé che eventuali comandi interni con@devono essere ‘riprotetti’, cioè occorre dare\makeatletterse si deve interpretare con\scantokensuna lista in cui ne compaia uno. Per esempio

\makeatletter \def\abc@abc{xyz} \def\pippo{\abc@abc} \makeatother

\scantokens\expandafter{\pippo}

darebbe l’errore che\abcnon è definito. Anche\scantokens, come altre primitive, espande ciò che segue fino a che trova la graffa, esplicita, che delimita il testo da rileggere e quindi\expandaftercausa l’espansione di\pippoe ora il guaio accade: \abc@abcviene letto quando@ha categoria 12.

La primitiva\detokenizepuò essere d’aiuto in questo contesto, con certe limi-tazioni. Il risultato di

\detokenize{\abc xyz} è la lista di token

\12 a12 b12 c1210 x12 y12 z12

dove tutti i caratteri hanno categoria 12, e solo gli spazi hanno categoria 10. Se però ripassiamo questa lista con\scantokens, i caratteri riceveranno la loro categoria corretta secondo le impostazioni attuali.

Possiamo risolvere con\scantokensanche il problema di passare come argo-mento un comando nel quale si debbano eseguire cambi di codice di categoria, purché non si tocchi il codice delle graffe: ridefiniremo\greystringin modo molto più semplice.

\def\greystring#1{% \underline{%

\mbox{\@sanitize\color[gray]{.5}% \scantokens{#1}\unskip}}}

Trattiamo qui in modo diverso il problema dello spazio finale dovuto a\scantokens: non possiamo inserire un comando nell’argomento di questo, visto che\@sanitize cambia il codice di categoria della barra rovescia. Ma lo spazio può essere cancellato tramite\unskipe questo token è già assorbito da TEX al momento della definizione. Vediamo all’opera la nuova definizione anche come argomento di un comando:

\greystring{a_b#c}→a_b##c

\fbox{?\greystring{a_b#c}?}→ ?a_b##c?

Rimangono limitazioni: alcuni comandi come\emphe, in generale tutti quelli di cambiamento di caratteri, usano internamente\edefe questo fa sì che l’argomento sia letto troppo presto, in questo caso TEX protesterebbe per#; l’altra limitazione è che non possiamo usare%nell’argomento di\greystring, per evidenti motivi.

*Vogliamo maneggiare stringhe di caratteri, dando un formato speciale ad alcuni di essi, per esempio stampando in nero i primi tre. Ci serve allora un comando per suddividere queste stringhe; siccome prevediamo che queste stringhe contengano caratteri anche a 8 bit, useremo \detokenizeper neutralizzarli e renderli di categoria 12 (sappiamo cheinputencli definisce come attivi) e poi useremo\scantokensper ripristinarli come attivi.

\def\@split#1{\begingroup\catcode‘\ =12 \count@=#1\relax\@spl@t} \def\@spl@t#1{% \gdef\split@start{}% \xdef\split@end{\detokenize\expandafter{#1}\noexpand\empty}% \loop\ifnum\count@>0\relax \xdef\split@start{\split@start\expandafter\@car\split@end\@nil}% \xdef\split@end{\expandafter\@cdr\split@end\@nil}% \ifx\split@end\empty \count@=0\relax \GenericError{\space\space\space\@spaces\@spaces\@spaces}% {Splitting error}%

{You probably went beyond the string length.}% {The number you gave is too large for the

requested operation}% \else

\advance\count@\m@ne \fi

\repeat\endgroup}

Definiamo\@splitcon un argomento, che è il numero di caratteri rispetto al quale vogliamo spezzare la stringa. Impostiamo il contatore temporaneo\count@al valore dato come argo-mento e chiamiamo la macro\@spl@tche farà il resto del lavoro che comincia svuotando globalmente il testo di sostituzione di\split@start; poi si definisce il testo di sostituzione di\split@endcome l’argomento che è apparentemente il secondo argomento di\@splite che potrebbe anche contenere una macro che si espande a una stringa di caratteri. Usiamo il trucco di farne passare l’espansione attraverso\detokenizeche renderà tutti i caratteri di categoria 12, visto che vogliamo usare anche lettere accentate che corrispondono, come sappiamo, a caratteri attivi. Alla fine di tutto mettiamo, non espansa,\empty.

A questo punto parte un ciclo che, con\@care\@cdraggiunge via via un carattere a \split@starttogliendolo da\split@end; a ogni passo si decrementa\count@, ottenendo alla fine i caratteri nella prima e n − i caratteri nella seconda, dove i è l’argomento dato a \@splite n è la lunghezza della stringa. Se il processo trova\emptyin\split@end, significa che siamo andati oltre il limite massimo. Per finire si chiude il gruppo.

Perciò, dopo aver eseguito\@split{4}{abcxyz}, in\split@startsi troveràabcxe in \split@endsi troveràyz. È facile, a questo punto, definire un comando\esaltaprimeche

prende come argomento opzionale un numero i e come argomento obbligatorio una stringa: i primi i caratteri della stringa saranno stampati in nero.

\newcommand{\esaltaprime}[2][0]{% \@split{#1}{#2}%

\textbf{\scantokens\expandafter{\split@start\empty}}% \scantokens\expandafter{\split@end\empty}}

Facendo passare attraverso\scantokensl’espansione di\split@starte\split@end, il codice di categoria dei caratteri attivati dainputencritornerà 13.

Se vogliamo eliminare o estrarre i primi i caratteri, basta che scriviamo \newcommand{\estraiprime}[2][0]{% \@split{#1}{#2}% \scantokens\expandafter{\split@start\empty}} \newcommand{\togliprime}[2][0]{% \@split{#1}{#2}% \scantokens\expandafter{\split@end\empty}}

Ora però vorremmo mettere in risalto o eliminare gli ultimi i caratteri. Un modo può essere di contare il numero di caratteri e definire macro simili alle precedenti per trattare questo caso. Ma è più semplice rovesciare la stringa, spezzarla al punto giusto e rovesciare le due stringhe ottenute stampandole nell’ordine corretto.

\def\@reverse#1{% \edef\@temp{\detokenize\expandafter{#1}}% \def\@esrever{}% \loop\unless\ifx\@temp\empty \edef\@esrever{\expandafter\@car\@temp\@nil\@esrever}% \edef\@temp{\expandafter\@cdr\@temp\@nil}% \repeat}

La macro\@reversemette in\@templa stringa, in cui tutti i caratteri sono stati resi di ca-tegoria 12, esattamente come prima. Ora inizializziamo\@esrevercome vuota e facciamo partire un ciclo di tipo ‘while’: continuiamo finché\@tempnon sarà vuota. Il primo passo è di ridefinire (con\edef)\@esrever: prima viene espansa\@tempdi cui viene preso il primo carattere e poi viene espansa\@esrever. Perciò al primo passo in\@esreverviene posto il primo carattere della stringa; al secondo passo il secondo carattere andrà a sinistra del primo, e così via. Il resto della stringa va di nuovo in\@tempe si prosegue fino a esaurimento dei caratteri. Alla fine del ciclo in\@esreverci sarà la stringa originale ma alla rovescia.

Ora è facile isolare gli ultimi i caratteri: rovesciamo la stringa, isoliamo i primi e rovesciamo di nuovo. \newcommand{\esaltaultime}[2][0]{% \@reverse{#2}\@split{#1}{\@esrever}% \@reverse{\split@end}% \scantokens\expandafter{\@esrever\empty}% \@reverse{\split@start}% \textbf{\scantokens\expandafter{\@esrever\empty}}} \newcommand{\estraiultime}[2][0]{% \@reverse{#2}\@split{#1}{\@esrever}% \@reverse{\split@start}% \scantokens\expandafter{\@esrever\empty}} \newcommand{\togliultime}[2][0]{% \@reverse{#2}\@split{#1}{\@esrever}% \@reverse{\split@end}% \scantokens\expandafter{\@esrever\empty}}

\newcommand{\inverti}[1]{% \@reverse{#1}\scantokens\expandafter{\@esrever\empty}} \newcommand\esaltacarattere[2][1]{% \count@=#1\relax \advance\count@-1\relax \@split{\the\count@}{#2}% \split@start\esaltaprime[1]{\split@end}} \newcommand\estraicarattere[2][1]{% \count@=#1\relax \advance\count@-1\relax \@split{\the\count@}{#2}% \estraiprime[1]{\split@end}}

La macro\invertiusa\@reversee fa passare, in modo analogo a prima, il contenuto di \@esreverattraverso\scantokens. Per mettere in risalto l’i -esimo carattere spezziamo la stringa prima dell’i -esimo e mettiamo in risalto il primo carattere della seconda parte. Con \estraicarattereotteniamo l’i -esimo carattere della stringa.

Qui occorre\emptyinvece di\noexpand. Il motivo è piuttosto riposto.

Gli spazi non possono essere trattati con queste macro, se occorresse inserirli nelle stringhe si può usare~.

Per evitare problemi durante l’esecuzione di\edef, ma non solo, è stata intro-dotta la primitiva\unexpandedche prende come argomento, tra graffe esplicite, una lista di token; l’espansione del tutto è la lista di token. Il modo classico di procedere doveva essere del tipo

\toks@={xyz}

\edef\abc{... \the\toks@ ...}

Con la nuova primitiva si può semplicemente dire \edef\abc{... \unexpanded{xyz} ...}

dove, ovviamente,xyzindica una qualsiasi lista di token. Vale quindi la stessa regola di\therispetto ai registri\toks.

Un’altra utile primitiva è\protectedche può essere usata come prefisso a\def, \edef,\gdefe\xdef. Le macro definite con questo prefisso diventano robuste, cioè non vengono espanse durante il processo di scrittura sui file esterni né nel testo di sostituzione di una\edefo\xdef. Un comando può essere dichiarato robusto in modo molto più semplice:

\protected\def\pippo#1{...}

ma non esiste un’interfaccia simile a\newcommand.

*Il pacchettoetoolboxcaricaetexe mette a disposizione parecchi comandi per usare le estensioni in modo simile a quello tradizionale di LATEX; per esempio\newrobustcmdha una sintassi identica a\newcommandcon l’unica differenza che il comando è definito con il prefisso \protected.

Ci sono anche estensioni riguardanti la composizione del testo da destra a si-nistra e la diagnosi di problemi, oltre a qualche cosa più esoterica. Va certamente menzionata\middlein costruzioni come

\left\{... \;\middle|\; ...\right\}

così che il delimitatore ‘mediano’ possa essere automaticamente ingrandito. Si noti però che non viene trattato come un simbolo di relazione, quindi le spaziature vanno date esplicitamente.

Nel documento Appunti di programmazione in L (pagine 141-146)