核心问题
如何处理由两个终止符之一分隔的文本?例如,如果我想处理到下一个的文本\A
,我可以写
\def\CmdA#1\A{...}
但假设\A
可能不存在,在这种情况下\B
应该终止处理。有没有办法写一个命令
\def\CmdAB#1[\A|\B]{...} % Nonsense syntax
使得
\CmdAB\first\A\second\B
设置#1
为\first
并留\second\B
在输入流中,并且
\CmdAB\only\B
设置#1
为\only
,并且在输入流中不留任何内容? \A
应该优先于\B
,这样
\CmdAB\other\B\order\A\after\B
设置#1
为\other\B\order
并留\after\B
在输入流中。
此外,由于我的目标是预处理文本然后使用它,我需要知道选择了哪一个\A
或。\B
我尝试过的方法
我曾想过一次扫描一个标记,将扫描到的标记保存在标记寄存器中以供以后处理,并在看到任一结束标记时停止扫描,但我无法让它工作。我编写了一些更简单的代码,用于\let
从输入中挑选字符,检查单个终止符,并在此过程中将其添加到标记寄存器中,但这不起作用,因为\let
控制序列不会扩展。代码如下:
\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
}
这看起来不错,似乎在运行它时可以正常工作,但是从输出结果来看,它并不完全正常工作:
*\Scan 1 2 3 \QScan
*\showthe\ScanToks
> \Scanned \Scanned \Scanned .
一旦你思考了这个问题(并且读了正确的部分TeXbook和正确的 TeX.SE 问题),这让人感到沮丧;如果我们有\let\a=1
和\let\b=\a
,那么我们怎么可能知道\a
应该“扩展”成1
但\b
应该“扩展”成\a
?但不幸的是,我看不出有办法编写一个\def
只从输入流中吸收一个标记的。如果\def
取任意平衡的文本,我可能可以处理它,但相反\def
需要一个支撑组,否则将事物解析为参数规范的一部分。
另外,此\Scan
命令不处理空格,但相比之下,这只是小事一桩。
动机
我正在尝试编写一个处理align
-like 环境第一行的命令。此命令需要:
- 吸收代币直到第一个
\\
。 - 处理这些标记(即,计算“&”符号以确定列数)。
- 将这些标记放回输入流中,前面是计算的信息(即列数)。
但是,\\
如果环境只有一行长,分隔符可能不存在;在这种情况下,我需要处理整个环境,直到完成\end{EnvName}
(其余部分都相同)。但在环境开始时,我不知道这两种情况中的哪一种,所以我想向前扫描到下一个\\
或者(\end{EnvName}
从而引发这个问题)。
答案1
这是一种可能的方法。
它的工作原理是首先读取整个环境主体,然后提取第一行。
调用之后\getfirstline
,整个环境的内容在 中可用\BODY
,第一行的内容在 中\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}