diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/mdwtools/doafter.dtx |
Initial commit
Diffstat (limited to 'macros/latex/contrib/mdwtools/doafter.dtx')
-rw-r--r-- | macros/latex/contrib/mdwtools/doafter.dtx | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/macros/latex/contrib/mdwtools/doafter.dtx b/macros/latex/contrib/mdwtools/doafter.dtx new file mode 100644 index 0000000000..8642bbba1e --- /dev/null +++ b/macros/latex/contrib/mdwtools/doafter.dtx @@ -0,0 +1,516 @@ +% \begin{meta-comment} +% +% $Id: doafter.dtx,v 1.2 1996/11/19 20:49:08 mdw Exp $ +% +% Insert tokens to be read after a group has been processed +% +% (c) 1996 Peter Schmitt and Mark Wooding +% +%----- Revision history ----------------------------------------------------- +% +% $Log: doafter.dtx,v $ +% Revision 1.2 1996/11/19 20:49:08 mdw +% Entered into RCS +% +% +% \end{meta-comment} +% +% \begin{meta-comment} <general public licence> +%% +%% doafter package -- insert a token really after a group +%% Copyright (c) 1996 Peter Schmitt and Mark Wooding +%<*package> +%% +%% This program is free software; you can redistribute it and/or modify +%% it under the terms of the GNU General Public License as published by +%% the Free Software Foundation; either version 2 of the License, or +%% (at your option) any later version. +%% +%% This program is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with this program; if not, write to the Free Software +%% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +%</package> +%% +% \end{meta-comment} +% +% \begin{meta-comment} <Package preamble> +%<+latex2e>\NeedsTeXFormat{LaTeX2e} +%<+latex2e>\ProvidesPackage{doafter} +%<+latex2e> [1996/05/08 1.2 Aftergroup hacking (PS/MDW)] +% \end{meta-comment} +% +% \CheckSum{259} +%\iffalse +%<*package> +%\fi +%% \CharacterTable +%% {Upper-case \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 +%% Lower-case \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 +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +%% +%\iffalse +%</package> +%\fi +% +% \begin{meta-comment} <driver> +% +%<*driver> +\input{mdwtools} +\describespackage{doafter} +\author{Peter Schmitt\thanks{% + Peter came up with the basic implementation after I posed the problem + in the \texttt{comp.text.tex} newsgroup. I fixed some really piddly little + things, to improve it a bit, wrote the documentation, and turned the code + into a nice \package{doc}ced package. Then Peter gave me an updated + version, and I upgraded this from memory. Then he gave me some more tweaks + which I haven't incorporated.} + \and Mark Wooding} +\def\author#1{} +\mdwdoc +%</driver> +% +% \end{meta-comment} +% +% \section{Description} +% +% \subsection{What it's all about} +% +% \DescribeMacro{\doafter} +% It's common for the \TeX\ primitive |\aftergroup| to be used to `tidy up' +% after a group. For example, \LaTeX's colour handling uses this to insert +% appropriate |\special|s when the scope of a colour change ends. This +% causes several problems, though; for example, extra grouping must be added +% within boxes to ensure that the |\special|s don't `leak' out of their box +% and appear in odd places in the document. \LaTeX\ usually solves this +% problem by reading the box contents as an argument, although this isn't +% particularly desirable. The |\doafter| macro provided here will solve the +% problem in a different way, by allowing a macro to regain control after +% all the |\aftergroup| things have been processed. +% +% The macro works like this: +% \begin{grammar} +% <doafter-cmd> ::= \[[ +% "\\doafter" <token> <group> +% \]] +% \end{grammar} +% The \<token> can be any token you like, except an explicit braces, since +% it's read as an undelimited macro argument. The \<group> is a normal +% \TeX\ group, surrounded by either implicit or explicit braces, or by +% |\begingroup| and |\endgroup| tokens. Once the final closing token of the +% \<group> is read, and any tokens saved up by |\aftergroup| have been +% processed, the \<token> is inserted and processed. Under normal +% circumstances, this will be a macro. +% +% There are some subtle problems with the current implementation, which you +% may need to be aware of: +% +% \begin{itemize} +% +% \item Since we're inserting things after all the |\aftergroup| tokens, +% those tokens might read something they're not expecting if they try +% to look ahead at the text after the group (e.g., with |\futurelet|). +% This is obviously totally unavoidable. +% +% \item Implicit braces (like |\bgroup| and |\egroup|) inserted using +% |\aftergroup| may be turned into \emph{explicit} $|{|_1$ and $|}|_2$ +% characters within a |\doafter| group. This can cause probems under +% very specialised circumstances. The names |\bgroup| and |\egroup| +% are treated specially, and they will work normally (remaining as +% implicit braces). This should minimise problems caused by this +% slight difference. (This only applies to the last |\aftergroup| +% token in a group.) +% +% \item To handle the |\aftergroup| tokens properly, |\doafter| has to insert +% some |\aftergroup| tokens of its own. It will then process the +% other tokens some more, and set them up to be read again. This does +% mean that after the group ends, some assignments and other `stomach +% operations' will be performed, which may cause problems in +% alignments and similar places. +% +% \end{itemize} +% +% +% \subsection{Package options} +% +% There are a fair few \textsf{docstrip} options provided by this packge: +% +% \begin{description} +% \item [driver] extracts the documentation driver. This isn't usually +% necessary. +% \item [package] extracts the code as a standalone package, formatted for +% either \LaTeXe\ or Plain~\TeX. +% \item [latex2e] inserts extra identification code for a \LaTeXe\ package. +% \item [plain] inserts some extra code for a Plain \TeX\ package. +% \item [macro] just extracts the raw code, for inclusion in another package. +% \item [test] extracts some code for testing the current implementation. +% \end{description} +% +% +% \implementation +% +% \section{Implementation} +% +% \subsection{The main macro} +% +% We start outputting code here. If this is a Plain~\TeX\ package, we must +% make \lit{@} into a letter. +% +% \begin{macrocode} +%<*macro|package> +%<+plain>\catcode`\@=11 +% \end{macrocode} +% +% \begin{macro}{\doafter} +% +% The idea is to say \syntax{"\\doafter" <token> <group>} and expect the +% \synt{token} to be processed after the group has finished its stuff, +% even if it contains |\aftergroup| things. My eternal gratitude goes to +% Peter Schmitt, who came up with most of the solution implemented here; +% I've just tidied up some very minor niggles and things later. +% +% Let's start with some preamble. I'll save the (hopefully) primitive +% |\aftergroup| in a different token. +% +% \begin{macrocode} +\let\@@aftergroup\aftergroup +% \end{macrocode} +% +% Now to define the `user' interface. It takes a normal undelimited +% argument, although this must be a single token; otherwise eveything will +% go wrong. It assumes that the token following is some kind of group +% opening thing (an explicit or implicit character with catcode~1, or +% a |\begingroup| token). To make this work, I'll save the token, +% together with an |\@@aftergroup| (to save an |\expandafter| later) in +% a temporary macro which no-one will mind me using, and then look ahead at +% the beginning-group token. +% +% \begin{macrocode} +\def\doafter#1{% + \def\@tempa{\@@aftergroup#1}% + \afterassignment\doafter@i\let\@let@token% +} +% \end{macrocode} +% +% I now have the token in |\@let@token|, so I'll put that in. I'll then +% make |\aftergroup| do my thing rather than the normal thing, and queue +% the tokens |\@prepare@after| and the |\doafter| argument for later use. +% +% \begin{macrocode} +\def\doafter@i{% + \@let@token% + \let\aftergroup\@my@aftergroup% + \@@aftergroup\@prepare@after\@tempa% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\@my@aftergroup} +% +% Now the cleverness begins. We keep two macros (Peter's original used +% count registers) which keep counts of the numbers of |\aftergroup|s, +% both locally and globally. Let's call the local counter~$n$ and the +% global one $N$. Every time we get a call to our |\aftergroup| hack, +% we set~$n := n+1$ and~$N := n$, and leave the token given to us for later +% processing. When we actually process an |\aftergroup| token properly, +% set~$N := N-1$ to indicate that it's been handled; when they're all done, +% we'll have $N=n$, which is exactly what we'd have if there weren't any +% to begin with. +% +% \begin{macrocode} +\def\ag@cnt@local{0 } +\let\ag@cnt@global\ag@cnt@local +% \end{macrocode} +% +% Now we come to the definition of my version of |\aftergroup|. I'll just +% add the token |\@after@token| before every |\aftergroup| token I find. +% This means there's two calls to |\aftergroup| for every one the user makes, +% but these things aren't all that common, so it's OK really. I'll also +% bump the local counter, and synchronise them. +% +% \begin{macrocode} +\def\@my@aftergroup{% + \begingroup% + \count@\ag@cnt@local% + \advance\count@\@ne% + \xdef\ag@cnt@global{\the\count@\space}% + \endgroup% + \let\ag@cnt@local\ag@cnt@global% + \@@aftergroup\@after@token\@@aftergroup% +} +% \end{macrocode} +% +% \end{macro} +% +% Now what does |\@after@token| we inserted above actually do? Well, this +% is more exciting. There are actually two different variants of the +% macro, which are used at different times. +% +% \begin{macro}{\@after@token} +% +% The default |\@after@token| starts a group, which will `catch' +% |\aftergroup| tokens which I throw at it. I put the two counters into +% some scratch count registers. (There's a slight problem here: Plain \TeX\ +% only gives us one. For the sake of evilness I'll use |\clubpenalty| as the +% other one. Eeeek.) I then redefine |\@after@token| to the second +% variant, and execute it. The |\@start@after@group| macro starts the +% group, because this code is shared with |\@prepare@after| below. +% +% \begin{macrocode} +\def\@after@token{% + \@start@after@group% + \@after@token% +} +\def\@start@after@group{% + \begingroup% + \count@\ag@cnt@global% + \clubpenalty\ag@cnt@local% + \let\@after@token\@after@token@i% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\@after@token@i} +% +% I have $|\count@| = N$ and $|\@tempcnta| = n$. I'll decrement~$N$, +% and if I have $N = n$, I know that this is the last token to do, so I +% must insert an |\@after@all| after the token. This will close the group, +% and maybe insert the original |\doafter| token if appropriate. +% +% \begin{macrocode} +\def\@after@token@i{% + \advance\count@\m@ne% + \ifnum\count@=\clubpenalty% + \global\let\ag@cnt@global\ag@cnt@local% + \expandafter\@after@aftertoken\expandafter\@after@all% + \else% + \expandafter\@@aftergroup% + \fi% +} +% \end{macrocode} +% +% Finally, establish a default meaning for |\@after@all|. +% +% \begin{macrocode} +\let\@after@all\endgroup +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\@prepare@after} +% +% If this group is handled by |\doafter|, then the first |\aftergroup| token +% isn't |\@after@token|; it's |\@prepare@after|. +% +% There are some extra cases to deal with: +% \begin{itemize} +% \item If $N=n$ then there were no |\aftergroup| tokens, so we have an easy +% job. I'll just let the token do its stuff directly. +% \item Otherwise, $N>n$, and there are |\aftergroup| tokens. I'll open +% the group, and let |\@after@token| do all the handling. +% \end{itemize} +% +% \begin{macrocode} +\def\@prepare@after{% + \ifx\ag@cnt@local\ag@cnt@global\else% + \expandafter\@prepare@after@i% + \fi% +} +\def\@prepare@after@i#1{% + \@start@after@group% + \def\@after@all{\@@aftergroup#1\endgroup}% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\@after@aftertoken} +% +% This is where all the difficulty lies. The next token in the stream is +% an |\aftergroup| one, which could be more or less anything. We have an +% argument, which is some code to do \emph{after} the token has been +% |\aftergroup|ed. +% +% If the token is anything other than a brace (i.e., an explicit character +% of category~1 or~2) then I have no problem; I can scoop up the token with +% an undelimited macro argument. But the only way I can decide if this token +% is a brace (nondestructively) is with |\futurelet|, which makes the token +% implicit, so I can't decide whether it's really dangerous. +% +% There is a possible way of doing this\footnote{Due to Peter Schmitt, +% again.} which relates to nobbling the offending token with |\string| and +% sifting through the results. The problem here involves scooping up all the +% tokens of a |\string|ed control sequence, which may turn out to be +% `|\csname\endcsname|' or something equally horrid. +% +% The solution I've used is much simpler: I'll change |\bgroup| and |\egroup| +% to stop them from being implicit braces before comparing. +% +% \begin{macrocode} +\def\@after@aftertoken#1{% + \let\bgroup\relax\let\egroup\relax% + \toks@{#1}% + \futurelet\@let@token\@after@aftertoken@i% +} +\def\@after@aftertoken@i{% + \ifcat\noexpand\@let@token{% + \@@aftergroup{% + \else\ifcat\noexpand\@let@token}% + \@@aftergroup}% + \else% + \def\@tempa##1{\@@aftergroup##1\the\toks@}% + \expandafter\expandafter\expandafter\@tempa% + \fi\fi% +} +% \end{macrocode} +% +% \end{macro} +% +% +% Phew! +% +% \begin{macrocode} +%<+plain>\catcode`\@=12 +%</macro|package> +% \end{macrocode} +% +% \subsection{Test code} +% +% The following code gives |\doafter| a bit of a testing. It's based on +% the test suite I gave to comp.text.tex, although it's been improved a +% little since then. +% +% The first thing to do is define a control sequence with an \lit{@} sign +% in its name, so we can test catcode changes. This also hides an +% |\aftergroup| within a macro, making life more difficult for prospective +% implementations. +% +% \begin{macrocode} +%<*test> +\catcode`\@=11 +\def\at@name{\aftergroup\saynine} +\def\saynine{\say{ix}} +\catcode`\@=12 +% \end{macrocode} +% +% Now define a command to write a string to the terminal. The name will +% probably be familiar to REXX hackers. +% +% \begin{macrocode} +\def\say{\immediate\write16} +% \end{macrocode} +% +% Test one: This is really easy; it just tests that the thing works at all. +% If your implementation fails this, it's time for a major rethink. +% +% \begin{macrocode} +\say{Test one... (1--2)} +\def\saytwo{\say{ii}} +\doafter\saytwo{\say{i}} +% \end{macrocode} +% +% Test two: Does |\aftergroup| work? +% +% \begin{macrocode} +\say{Test two... (1--4)} +\def\saythree{\say{iii}} +\def\sayfour{\say{iv}} +\doafter\sayfour{\say{i}\aftergroup\saythree\say{ii}} +% \end{macrocode} +% +% Test three: Test braces and |\iffalse| working as they should. Several +% proposed solutions based on |\write|ing the group to a file get upset by +% this test, although I forgot to include it in the torture test. It also +% tests whether literal braces can be |\aftergroup|ed properly. (Added a new +% test here, making sure that |\bgroup| is left as an implicit token.) +% +% \begin{macrocode} +\say{Test three... (1--4, `\string\bgroup', 5)} +\def\sayfive{\say{v}} +\doafter\sayfive{% + \say{i}% + \aftergroup\say% + \aftergroup{% + \aftergroup\romannumeral\aftergroup3% + \aftergroup}% + \iffalse}\fi% + \aftergroup\def% + \aftergroup\sayfouretc% + \aftergroup{% + \aftergroup\say% + \aftergroup{% + \aftergroup i% + \aftergroup v% + \aftergroup}% + \aftergroup\say% + \aftergroup{% + \aftergroup\string% + \aftergroup\bgroup% + \aftergroup}% + \aftergroup}% + \aftergroup\sayfouretc% + \say{ii}% +} +% \end{macrocode} +% +% Test four: Make sure the implementation isn't leaking things. This just +% makes sure that |\aftergroup| is its normal reasonable self. +% +% \begin{macrocode} +\say{Test four... (1--3)} +{\say{i}\aftergroup\saythree\say{ii}} +% \end{macrocode} +% +% Test five: Nesting, aftergroup, catcodes, grouping. This is the `torture' +% test I gave to comp.text.tex, slightly corrected (oops) and amended. It +% ensures that nested groups and |\doafter|s work properly (the latter is +% actually more likely than might be imagined). +% +% \begin{macrocode} +\say{Test five... (1--14)} +\def\sayten{\say{x}} +\def\saythirteen{\say{xiii}} +\def\sayfourteen{\say{xiv}} +\doafter\sayfourteen\begingroup% + \say{i}% + {\say{ii}\aftergroup\sayfour\say{iii}}% + \def\saynum{\say{viii}}% + \doafter\sayten{% + \say{v}% + \def\saynum{\say{vii}}% + \catcode`\@=11% + \aftergroup\saynum% + \say{vi}% + \at@name% + \saynum% + }% + \say{xi}% + \aftergroup\saythirteen% + \say{xii}% +\endgroup +\end +%</test> +% \end{macrocode} +% +% That's it. All present and correct. +% +% \Finale +% +\endinput |