Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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:

  1. Absorb tokens up until the first \\.
  2. Process those tokens (that is, count ampersands to determine the number of columns).
  3. 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).

share|improve this question
    
I would first read the entire content of the environment (for instance with the environ package) and then check whether that content contains \\ . –  Stephan Lehmke Dec 15 '12 at 5:20
    
@StephanLehmke: I had thought about that, but had hoped to avoid it, since it's just sort of ugly (and probably inefficient, especially for large environments)—I don't need all that information :-) But it's true, that would work. –  Antal S-Z Dec 15 '12 at 5:27
    
As long as you don't expect your environment to contain huge amounts of material spanning several pages (like longtable) I wouldn't worry about that. For instance, the amsmath 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. –  Stephan Lehmke Dec 15 '12 at 5:33
2  
If you are indeed using amsmath then you can also hook into the measuring step and extract the first line without additional overhead. –  Stephan Lehmke Dec 15 '12 at 6:01
1  
@StephanLehmke: A very good idea! For anybody who's wondering, the input-gathering logic happens somewhere in \collect@body/\collect@@body; for the environments that call into \start@align (e.g., align and alignat and their starred variants), this body is passed to \measure@, which you can hack into. Note that aligned and alignedat (at least) don't pre-gather their input (as far as I can tell). –  Antal S-Z Dec 16 '12 at 0:48
show 1 more comment

1 Answer 1

up vote 8 down vote accepted

Here is one possible approach.

It works by first reading the entire environment body and then extracting the first line.

After the \getfirstline call, the content of the whole environment is available in \BODY and the content of the first line in \firstline.

\documentclass{article}
\usepackage{environ}

\NewEnviron{foo}
{%
  \expandafter\getfirstline\BODY\\\@nil
  \show\firstline
}

\def\getfirstline#1\\#2\@nil
{%
  \def\firstline{#1}%
}

\begin{document}

\begin{foo}
  first line
\end{foo}

\begin{foo}
  first line\\
  second line
\end{foo}

\end{document}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.