The core question
How can I process text delimited by one of two terminators? For instance, if I want to process text up to the next \A
, I can just write
\def\CmdA#1\A{...}
But suppose that \A
might not be present, in which case \B
should terminate processing instead. Is there a way to write a command
\def\CmdAB#1[\A|\B]{...} % Nonsense syntax
such that
\CmdAB\first\A\second\B
sets #1
to \first
and leaves \second\B
in the input stream, and
\CmdAB\only\B
sets #1
to \only
and leaves nothing in the input stream? \A
should take precedence over \B
, so that
\CmdAB\other\B\order\A\after\B
sets #1
to \other\B\order
and leaves \after\B
in the input stream.
Furthermore, since my goal is to pre-process the text and then use it, I need to know which of \A
or \B
was selected.
What I've tried
I had the idea to scan tokens one at a time, saving the scanned ones in a token register for later processing, and stop scanning when I saw either end token, but I couldn't get it to work. I wrote some simpler code which uses \let
to pick characters out of the input, checking for a single terminator, and adding to a token register along the way, but this doesn't work because \let
control sequences don't expand. The code is the following:
\newtoks\ScanToks
\newcommand*{\AddTok}[1]{\ScanToks\expandafter{\the\ScanToks#1}}
\newcommand*{\QScan}{\QScan}
\newcommand*{\Scan}{\afterassignment\DoScan\let\Scanned=}
\newcommand*{\DoScan}{%
\ifx\Scanned\QScan\else
\expandafter\AddTok\Scanned
\expandafter\Scan
\fi
}
This looks good, and seems to work when you run it, but it doesn't quite when you look at the output:
*\Scan 1 2 3 \QScan
*\showthe\ScanToks
> \Scanned \Scanned \Scanned .
Once you think about it (and read the right parts of The TeXbook and the right TeX.SE questions), this makes a frustrating sort of sense; if we have \let\a=1
and \let\b=\a
, then how could we possibly we tell that \a
ought to "expand" into 1
but \b
ought to "expand" into \a
? But unfortunately, I don't see a way to write a \def
which only absorbs one token from the input stream. If \def
took arbitrary balanced text, I could probably work with that, but instead \def
requires a braced group, and otherwise parses things as being part of the argument specification.
Also, this \Scan
command isn't processing the spaces, but that's small potatoes in comparison.
The motivation
I'm trying to write a command which processes the first line of an align
-like environment. This command needs to:
- Absorb tokens up until the first
\\
. - Process those tokens (that is, count ampersands to determine the number of columns).
- Place those tokens back in the input stream, preceded by the computed information (that is, the number of columns).
However, the \\
delimiter might not be present if the environment is only one line long; in that case, I'd need to instead process the whole environment, up through \end{EnvName}
(and all the rest is the same). But at the start of an environment, I can't know which of these two situations is the case, and so I want to scan ahead to the next \\
or the \end{EnvName}
(thus motivating the question).
environ
package) and then check whether that content contains\\
.longtable
) I wouldn't worry about that. For instance, theamsmath
environments all pre-read their contents so that the width of the alignment can be measured in a preprocessing step. That's the standard way to do it. All else will lead to a huge amount of work. If you really want to scan tokens, you can study this answer.amsmath
then you can also hook into the measuring step and extract the first line without additional overhead.\collect@body
/\collect@@body
; for the environments that call into\start@align
(e.g.,align
andalignat
and their starred variants), this body is passed to\measure@
, which you can hack into. Note thataligned
andalignedat
(at least) don't pre-gather their input (as far as I can tell).