123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- % \begin{meta-comment}
- %
- % $Id$
- %
- % Insert tokens to be read after a group has been processed
- %
- % (c) 1996 Peter Schmitt and Mark Wooding
- %
- %----- Revision history -----------------------------------------------------
- %
- % $Log$
- % Revision 1.1 1998-09-21 10:19:01 michael
- % Initial implementation
- %
- % 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
|