Commit | Line | Data |
---|---|---|
86f6a31e | 1 | % \begin{meta-comment} <general public licence> |
2 | %% | |
3 | %% doafter package -- insert a token really after a group | |
8bc5bdd2 MW |
4 | %% Copyright (c) 1996 Peter Schmitt |
5 | %% Copyright (c) 1996, 2002, 2003 Mark Wooding | |
86f6a31e | 6 | %<*package> |
7 | %% | |
3d509049 | 8 | %% This file is part of the `mdwtools' LaTeX package collection. |
86f6a31e | 9 | %% |
3d509049 MW |
10 | %% `mdwtools' is free software: you can redistribute it and/or modify it |
11 | %% under the terms of the GNU General Public License as published by the | |
12 | %% Free Software Foundation; either version 2 of the License, or (at your | |
13 | %% option) any later version. | |
14 | %% | |
15 | %% `mdwtools' is distributed in the hope that it will be useful, but | |
16 | %% WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | %% General Public License for more details. | |
86f6a31e | 19 | %% |
20 | %% You should have received a copy of the GNU General Public License | |
3d509049 MW |
21 | %% along with `mdwtools'. If not, write to the Free Software Foundation, |
22 | %% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
86f6a31e | 23 | %</package> |
24 | %% | |
25 | % \end{meta-comment} | |
26 | % | |
27 | % \begin{meta-comment} <Package preamble> | |
28 | %<+latex2e>\NeedsTeXFormat{LaTeX2e} | |
29 | %<+latex2e>\ProvidesPackage{doafter} | |
af8af7eb | 30 | %<+latex2e> [2020/09/06 1.14.0 Aftergroup hacking] |
86f6a31e | 31 | % \end{meta-comment} |
32 | % | |
33 | % \CheckSum{259} | |
34 | %\iffalse | |
35 | %<*package> | |
36 | %\fi | |
37 | %% \CharacterTable | |
38 | %% {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 | |
39 | %% 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 | |
40 | %% Digits \0\1\2\3\4\5\6\7\8\9 | |
41 | %% Exclamation \! Double quote \" Hash (number) \# | |
42 | %% Dollar \$ Percent \% Ampersand \& | |
43 | %% Acute accent \' Left paren \( Right paren \) | |
44 | %% Asterisk \* Plus \+ Comma \, | |
45 | %% Minus \- Point \. Solidus \/ | |
46 | %% Colon \: Semicolon \; Less than \< | |
47 | %% Equals \= Greater than \> Question mark \? | |
48 | %% Commercial at \@ Left bracket \[ Backslash \\ | |
49 | %% Right bracket \] Circumflex \^ Underscore \_ | |
50 | %% Grave accent \` Left brace \{ Vertical bar \| | |
51 | %% Right brace \} Tilde \~} | |
52 | %% | |
53 | %\iffalse | |
54 | %</package> | |
55 | %\fi | |
56 | % | |
57 | % \begin{meta-comment} <driver> | |
58 | % | |
59 | %<*driver> | |
60 | \input{mdwtools} | |
61 | \describespackage{doafter} | |
62 | \author{Peter Schmitt\thanks{% | |
63 | Peter came up with the basic implementation after I posed the problem | |
64 | in the \texttt{comp.text.tex} newsgroup. I fixed some really piddly little | |
65 | things, to improve it a bit, wrote the documentation, and turned the code | |
66 | into a nice \package{doc}ced package. Then Peter gave me an updated | |
67 | version, and I upgraded this from memory. Then he gave me some more tweaks | |
68 | which I haven't incorporated.} | |
69 | \and Mark Wooding} | |
70 | \def\author#1{} | |
71 | \mdwdoc | |
72 | %</driver> | |
73 | % | |
74 | % \end{meta-comment} | |
75 | % | |
76 | % \section{Description} | |
77 | % | |
78 | % \subsection{What it's all about} | |
79 | % | |
80 | % \DescribeMacro{\doafter} | |
81 | % It's common for the \TeX\ primitive |\aftergroup| to be used to `tidy up' | |
82 | % after a group. For example, \LaTeX's colour handling uses this to insert | |
83 | % appropriate |\special|s when the scope of a colour change ends. This | |
84 | % causes several problems, though; for example, extra grouping must be added | |
85 | % within boxes to ensure that the |\special|s don't `leak' out of their box | |
86 | % and appear in odd places in the document. \LaTeX\ usually solves this | |
87 | % problem by reading the box contents as an argument, although this isn't | |
88 | % particularly desirable. The |\doafter| macro provided here will solve the | |
89 | % problem in a different way, by allowing a macro to regain control after | |
90 | % all the |\aftergroup| things have been processed. | |
91 | % | |
92 | % The macro works like this: | |
93 | % \begin{grammar} | |
94 | % <doafter-cmd> ::= \[[ | |
95 | % "\\doafter" <token> <group> | |
96 | % \]] | |
97 | % \end{grammar} | |
98 | % The \<token> can be any token you like, except an explicit braces, since | |
99 | % it's read as an undelimited macro argument. The \<group> is a normal | |
100 | % \TeX\ group, surrounded by either implicit or explicit braces, or by | |
101 | % |\begingroup| and |\endgroup| tokens. Once the final closing token of the | |
102 | % \<group> is read, and any tokens saved up by |\aftergroup| have been | |
103 | % processed, the \<token> is inserted and processed. Under normal | |
104 | % circumstances, this will be a macro. | |
105 | % | |
106 | % There are some subtle problems with the current implementation, which you | |
107 | % may need to be aware of: | |
108 | % | |
109 | % \begin{itemize} | |
110 | % | |
111 | % \item Since we're inserting things after all the |\aftergroup| tokens, | |
112 | % those tokens might read something they're not expecting if they try | |
113 | % to look ahead at the text after the group (e.g., with |\futurelet|). | |
114 | % This is obviously totally unavoidable. | |
115 | % | |
116 | % \item Implicit braces (like |\bgroup| and |\egroup|) inserted using | |
117 | % |\aftergroup| may be turned into \emph{explicit} $|{|_1$ and $|}|_2$ | |
118 | % characters within a |\doafter| group. This can cause probems under | |
119 | % very specialised circumstances. The names |\bgroup| and |\egroup| | |
120 | % are treated specially, and they will work normally (remaining as | |
121 | % implicit braces). This should minimise problems caused by this | |
122 | % slight difference. (This only applies to the last |\aftergroup| | |
123 | % token in a group.) | |
124 | % | |
125 | % \item To handle the |\aftergroup| tokens properly, |\doafter| has to insert | |
126 | % some |\aftergroup| tokens of its own. It will then process the | |
127 | % other tokens some more, and set them up to be read again. This does | |
128 | % mean that after the group ends, some assignments and other `stomach | |
129 | % operations' will be performed, which may cause problems in | |
130 | % alignments and similar places. | |
131 | % | |
132 | % \end{itemize} | |
133 | % | |
134 | % | |
135 | % \subsection{Package options} | |
136 | % | |
137 | % There are a fair few \textsf{docstrip} options provided by this packge: | |
138 | % | |
139 | % \begin{description} | |
140 | % \item [driver] extracts the documentation driver. This isn't usually | |
141 | % necessary. | |
142 | % \item [package] extracts the code as a standalone package, formatted for | |
143 | % either \LaTeXe\ or Plain~\TeX. | |
144 | % \item [latex2e] inserts extra identification code for a \LaTeXe\ package. | |
145 | % \item [plain] inserts some extra code for a Plain \TeX\ package. | |
146 | % \item [macro] just extracts the raw code, for inclusion in another package. | |
147 | % \item [test] extracts some code for testing the current implementation. | |
148 | % \end{description} | |
149 | % | |
150 | % | |
151 | % \implementation | |
152 | % | |
153 | % \section{Implementation} | |
154 | % | |
155 | % \subsection{The main macro} | |
156 | % | |
157 | % We start outputting code here. If this is a Plain~\TeX\ package, we must | |
158 | % make \lit{@} into a letter. | |
159 | % | |
160 | % \begin{macrocode} | |
161 | %<*macro|package> | |
162 | %<+plain>\catcode`\@=11 | |
163 | % \end{macrocode} | |
164 | % | |
165 | % \begin{macro}{\doafter} | |
166 | % | |
167 | % The idea is to say \syntax{"\\doafter" <token> <group>} and expect the | |
168 | % \synt{token} to be processed after the group has finished its stuff, | |
169 | % even if it contains |\aftergroup| things. My eternal gratitude goes to | |
170 | % Peter Schmitt, who came up with most of the solution implemented here; | |
171 | % I've just tidied up some very minor niggles and things later. | |
172 | % | |
173 | % Let's start with some preamble. I'll save the (hopefully) primitive | |
174 | % |\aftergroup| in a different token. | |
175 | % | |
176 | % \begin{macrocode} | |
177 | \let\@@aftergroup\aftergroup | |
178 | % \end{macrocode} | |
179 | % | |
180 | % Now to define the `user' interface. It takes a normal undelimited | |
181 | % argument, although this must be a single token; otherwise eveything will | |
182 | % go wrong. It assumes that the token following is some kind of group | |
183 | % opening thing (an explicit or implicit character with catcode~1, or | |
184 | % a |\begingroup| token). To make this work, I'll save the token, | |
185 | % together with an |\@@aftergroup| (to save an |\expandafter| later) in | |
186 | % a temporary macro which no-one will mind me using, and then look ahead at | |
187 | % the beginning-group token. | |
188 | % | |
189 | % \begin{macrocode} | |
190 | \def\doafter#1{% | |
191 | \def\@tempa{\@@aftergroup#1}% | |
192 | \afterassignment\doafter@i\let\@let@token% | |
193 | } | |
194 | % \end{macrocode} | |
195 | % | |
196 | % I now have the token in |\@let@token|, so I'll put that in. I'll then | |
197 | % make |\aftergroup| do my thing rather than the normal thing, and queue | |
198 | % the tokens |\@prepare@after| and the |\doafter| argument for later use. | |
199 | % | |
200 | % \begin{macrocode} | |
201 | \def\doafter@i{% | |
202 | \@let@token% | |
203 | \let\aftergroup\@my@aftergroup% | |
204 | \@@aftergroup\@prepare@after\@tempa% | |
205 | } | |
206 | % \end{macrocode} | |
207 | % | |
208 | % \end{macro} | |
209 | % | |
210 | % \begin{macro}{\@my@aftergroup} | |
211 | % | |
212 | % Now the cleverness begins. We keep two macros (Peter's original used | |
213 | % count registers) which keep counts of the numbers of |\aftergroup|s, | |
214 | % both locally and globally. Let's call the local counter~$n$ and the | |
215 | % global one $N$. Every time we get a call to our |\aftergroup| hack, | |
216 | % we set~$n := n+1$ and~$N := n$, and leave the token given to us for later | |
217 | % processing. When we actually process an |\aftergroup| token properly, | |
218 | % set~$N := N-1$ to indicate that it's been handled; when they're all done, | |
219 | % we'll have $N=n$, which is exactly what we'd have if there weren't any | |
220 | % to begin with. | |
221 | % | |
222 | % \begin{macrocode} | |
223 | \def\ag@cnt@local{0 } | |
224 | \let\ag@cnt@global\ag@cnt@local | |
225 | % \end{macrocode} | |
226 | % | |
227 | % Now we come to the definition of my version of |\aftergroup|. I'll just | |
228 | % add the token |\@after@token| before every |\aftergroup| token I find. | |
229 | % This means there's two calls to |\aftergroup| for every one the user makes, | |
230 | % but these things aren't all that common, so it's OK really. I'll also | |
231 | % bump the local counter, and synchronise them. | |
232 | % | |
233 | % \begin{macrocode} | |
234 | \def\@my@aftergroup{% | |
235 | \begingroup% | |
236 | \count@\ag@cnt@local% | |
237 | \advance\count@\@ne% | |
238 | \xdef\ag@cnt@global{\the\count@\space}% | |
239 | \endgroup% | |
240 | \let\ag@cnt@local\ag@cnt@global% | |
241 | \@@aftergroup\@after@token\@@aftergroup% | |
242 | } | |
243 | % \end{macrocode} | |
244 | % | |
245 | % \end{macro} | |
246 | % | |
247 | % Now what does |\@after@token| we inserted above actually do? Well, this | |
248 | % is more exciting. There are actually two different variants of the | |
249 | % macro, which are used at different times. | |
250 | % | |
251 | % \begin{macro}{\@after@token} | |
252 | % | |
253 | % The default |\@after@token| starts a group, which will `catch' | |
254 | % |\aftergroup| tokens which I throw at it. I put the two counters into | |
255 | % some scratch count registers. (There's a slight problem here: Plain \TeX\ | |
256 | % only gives us one. For the sake of evilness I'll use |\clubpenalty| as the | |
257 | % other one. Eeeek.) I then redefine |\@after@token| to the second | |
258 | % variant, and execute it. The |\@start@after@group| macro starts the | |
259 | % group, because this code is shared with |\@prepare@after| below. | |
260 | % | |
261 | % \begin{macrocode} | |
262 | \def\@after@token{% | |
263 | \@start@after@group% | |
264 | \@after@token% | |
265 | } | |
266 | \def\@start@after@group{% | |
267 | \begingroup% | |
268 | \count@\ag@cnt@global% | |
269 | \clubpenalty\ag@cnt@local% | |
270 | \let\@after@token\@after@token@i% | |
271 | } | |
272 | % \end{macrocode} | |
273 | % | |
274 | % \end{macro} | |
275 | % | |
276 | % \begin{macro}{\@after@token@i} | |
277 | % | |
278 | % I have $|\count@| = N$ and $|\@tempcnta| = n$. I'll decrement~$N$, | |
279 | % and if I have $N = n$, I know that this is the last token to do, so I | |
280 | % must insert an |\@after@all| after the token. This will close the group, | |
281 | % and maybe insert the original |\doafter| token if appropriate. | |
282 | % | |
283 | % \begin{macrocode} | |
284 | \def\@after@token@i{% | |
285 | \advance\count@\m@ne% | |
286 | \ifnum\count@=\clubpenalty% | |
287 | \global\let\ag@cnt@global\ag@cnt@local% | |
288 | \expandafter\@after@aftertoken\expandafter\@after@all% | |
289 | \else% | |
290 | \expandafter\@@aftergroup% | |
291 | \fi% | |
292 | } | |
293 | % \end{macrocode} | |
294 | % | |
295 | % Finally, establish a default meaning for |\@after@all|. | |
296 | % | |
297 | % \begin{macrocode} | |
298 | \let\@after@all\endgroup | |
299 | % \end{macrocode} | |
300 | % | |
301 | % \end{macro} | |
302 | % | |
303 | % \begin{macro}{\@prepare@after} | |
304 | % | |
305 | % If this group is handled by |\doafter|, then the first |\aftergroup| token | |
306 | % isn't |\@after@token|; it's |\@prepare@after|. | |
307 | % | |
308 | % There are some extra cases to deal with: | |
309 | % \begin{itemize} | |
310 | % \item If $N=n$ then there were no |\aftergroup| tokens, so we have an easy | |
311 | % job. I'll just let the token do its stuff directly. | |
312 | % \item Otherwise, $N>n$, and there are |\aftergroup| tokens. I'll open | |
313 | % the group, and let |\@after@token| do all the handling. | |
314 | % \end{itemize} | |
315 | % | |
316 | % \begin{macrocode} | |
317 | \def\@prepare@after{% | |
318 | \ifx\ag@cnt@local\ag@cnt@global\else% | |
319 | \expandafter\@prepare@after@i% | |
320 | \fi% | |
321 | } | |
322 | \def\@prepare@after@i#1{% | |
323 | \@start@after@group% | |
324 | \def\@after@all{\@@aftergroup#1\endgroup}% | |
325 | } | |
326 | % \end{macrocode} | |
327 | % | |
328 | % \end{macro} | |
329 | % | |
330 | % \begin{macro}{\@after@aftertoken} | |
331 | % | |
332 | % This is where all the difficulty lies. The next token in the stream is | |
333 | % an |\aftergroup| one, which could be more or less anything. We have an | |
334 | % argument, which is some code to do \emph{after} the token has been | |
335 | % |\aftergroup|ed. | |
336 | % | |
337 | % If the token is anything other than a brace (i.e., an explicit character | |
338 | % of category~1 or~2) then I have no problem; I can scoop up the token with | |
339 | % an undelimited macro argument. But the only way I can decide if this token | |
340 | % is a brace (nondestructively) is with |\futurelet|, which makes the token | |
341 | % implicit, so I can't decide whether it's really dangerous. | |
342 | % | |
343 | % There is a possible way of doing this\footnote{Due to Peter Schmitt, | |
344 | % again.} which relates to nobbling the offending token with |\string| and | |
345 | % sifting through the results. The problem here involves scooping up all the | |
346 | % tokens of a |\string|ed control sequence, which may turn out to be | |
347 | % `|\csname\endcsname|' or something equally horrid. | |
348 | % | |
349 | % The solution I've used is much simpler: I'll change |\bgroup| and |\egroup| | |
350 | % to stop them from being implicit braces before comparing. | |
351 | % | |
352 | % \begin{macrocode} | |
353 | \def\@after@aftertoken#1{% | |
354 | \let\bgroup\relax\let\egroup\relax% | |
355 | \toks@{#1}% | |
356 | \futurelet\@let@token\@after@aftertoken@i% | |
357 | } | |
358 | \def\@after@aftertoken@i{% | |
359 | \ifcat\noexpand\@let@token{% | |
360 | \@@aftergroup{% | |
361 | \else\ifcat\noexpand\@let@token}% | |
362 | \@@aftergroup}% | |
363 | \else% | |
364 | \def\@tempa##1{\@@aftergroup##1\the\toks@}% | |
365 | \expandafter\expandafter\expandafter\@tempa% | |
366 | \fi\fi% | |
367 | } | |
368 | % \end{macrocode} | |
369 | % | |
370 | % \end{macro} | |
371 | % | |
372 | % | |
373 | % Phew! | |
374 | % | |
375 | % \begin{macrocode} | |
376 | %<+plain>\catcode`\@=12 | |
377 | %</macro|package> | |
378 | % \end{macrocode} | |
379 | % | |
380 | % \subsection{Test code} | |
381 | % | |
382 | % The following code gives |\doafter| a bit of a testing. It's based on | |
383 | % the test suite I gave to comp.text.tex, although it's been improved a | |
384 | % little since then. | |
385 | % | |
386 | % The first thing to do is define a control sequence with an \lit{@} sign | |
387 | % in its name, so we can test catcode changes. This also hides an | |
388 | % |\aftergroup| within a macro, making life more difficult for prospective | |
389 | % implementations. | |
390 | % | |
391 | % \begin{macrocode} | |
392 | %<*test> | |
393 | \catcode`\@=11 | |
394 | \def\at@name{\aftergroup\saynine} | |
395 | \def\saynine{\say{ix}} | |
396 | \catcode`\@=12 | |
397 | % \end{macrocode} | |
398 | % | |
399 | % Now define a command to write a string to the terminal. The name will | |
400 | % probably be familiar to REXX hackers. | |
401 | % | |
402 | % \begin{macrocode} | |
403 | \def\say{\immediate\write16} | |
404 | % \end{macrocode} | |
405 | % | |
406 | % Test one: This is really easy; it just tests that the thing works at all. | |
407 | % If your implementation fails this, it's time for a major rethink. | |
408 | % | |
409 | % \begin{macrocode} | |
410 | \say{Test one... (1--2)} | |
411 | \def\saytwo{\say{ii}} | |
412 | \doafter\saytwo{\say{i}} | |
413 | % \end{macrocode} | |
414 | % | |
415 | % Test two: Does |\aftergroup| work? | |
416 | % | |
417 | % \begin{macrocode} | |
418 | \say{Test two... (1--4)} | |
419 | \def\saythree{\say{iii}} | |
420 | \def\sayfour{\say{iv}} | |
421 | \doafter\sayfour{\say{i}\aftergroup\saythree\say{ii}} | |
422 | % \end{macrocode} | |
423 | % | |
424 | % Test three: Test braces and |\iffalse| working as they should. Several | |
425 | % proposed solutions based on |\write|ing the group to a file get upset by | |
426 | % this test, although I forgot to include it in the torture test. It also | |
427 | % tests whether literal braces can be |\aftergroup|ed properly. (Added a new | |
428 | % test here, making sure that |\bgroup| is left as an implicit token.) | |
429 | % | |
430 | % \begin{macrocode} | |
431 | \say{Test three... (1--4, `\string\bgroup', 5)} | |
432 | \def\sayfive{\say{v}} | |
433 | \doafter\sayfive{% | |
434 | \say{i}% | |
435 | \aftergroup\say% | |
436 | \aftergroup{% | |
437 | \aftergroup\romannumeral\aftergroup3% | |
438 | \aftergroup}% | |
439 | \iffalse}\fi% | |
440 | \aftergroup\def% | |
441 | \aftergroup\sayfouretc% | |
442 | \aftergroup{% | |
443 | \aftergroup\say% | |
444 | \aftergroup{% | |
445 | \aftergroup i% | |
446 | \aftergroup v% | |
447 | \aftergroup}% | |
448 | \aftergroup\say% | |
449 | \aftergroup{% | |
450 | \aftergroup\string% | |
451 | \aftergroup\bgroup% | |
452 | \aftergroup}% | |
453 | \aftergroup}% | |
454 | \aftergroup\sayfouretc% | |
455 | \say{ii}% | |
456 | } | |
457 | % \end{macrocode} | |
458 | % | |
459 | % Test four: Make sure the implementation isn't leaking things. This just | |
460 | % makes sure that |\aftergroup| is its normal reasonable self. | |
461 | % | |
462 | % \begin{macrocode} | |
463 | \say{Test four... (1--3)} | |
464 | {\say{i}\aftergroup\saythree\say{ii}} | |
465 | % \end{macrocode} | |
466 | % | |
467 | % Test five: Nesting, aftergroup, catcodes, grouping. This is the `torture' | |
468 | % test I gave to comp.text.tex, slightly corrected (oops) and amended. It | |
469 | % ensures that nested groups and |\doafter|s work properly (the latter is | |
470 | % actually more likely than might be imagined). | |
471 | % | |
472 | % \begin{macrocode} | |
473 | \say{Test five... (1--14)} | |
474 | \def\sayten{\say{x}} | |
475 | \def\saythirteen{\say{xiii}} | |
476 | \def\sayfourteen{\say{xiv}} | |
477 | \doafter\sayfourteen\begingroup% | |
478 | \say{i}% | |
479 | {\say{ii}\aftergroup\sayfour\say{iii}}% | |
480 | \def\saynum{\say{viii}}% | |
481 | \doafter\sayten{% | |
482 | \say{v}% | |
483 | \def\saynum{\say{vii}}% | |
484 | \catcode`\@=11% | |
485 | \aftergroup\saynum% | |
486 | \say{vi}% | |
487 | \at@name% | |
488 | \saynum% | |
489 | }% | |
490 | \say{xi}% | |
491 | \aftergroup\saythirteen% | |
492 | \say{xii}% | |
493 | \endgroup | |
494 | \end | |
495 | %</test> | |
496 | % \end{macrocode} | |
497 | % | |
498 | % That's it. All present and correct. | |
499 | % | |
500 | % \Finale | |
501 | % | |
502 | \endinput |