在这个答案egreg 提到包r
中的参数类型xparse
处理嵌套的分隔参数,如这个小例子所示:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\foo{r()}{(#1)}
\begin{document}
\[ \foo(x\cdot\foo(y+\foo(z))\cdot w) \]
\end{document}
不过,软件包文档并未解释如何实现这一点。我尝试查看代码,但它太长了,对于不熟悉 LaTeX3 的人来说几乎无法阅读。
正如上面链接的问题中讨论的那样,TeX 的默认分隔参数不能用于此。我能想到的唯一方法是通过 解析整个以下文本\futurelet
,从而以某种方式确保宏正确扩展,直到找到最终的结束分隔符。有人能解释一下如何xparse
实际实现这一点吗?
答案1
在 LaTeX2e 使用的基本方法中,人们寻找一个可选的[
使用\futurelet
(包含在\@ifnextchar
LaTeX 中),如果找到则使用分隔宏
\def\foo@aux[#1]{% Do stuff
对于 LaTex2e 来说,就是这样,但对于xparse
我们则检查#1
它是否包含[
。如果包含,那么]
我们抓取的就不是与(现已删除的)匹配的正确匹配[
。相反,我们必须继续#1
寻找正确的匹配。例如,使用
\DeclareDocumentCommand\foo{o}{...}
\foo[[AAA]]]
初始抓取将匹配[[AA]
,其中包含一个[
,因此我们必须再找到一个]
。需要注意的一点是
\foo[[a][b]]
需要多次循环才能找到所有内容。
中的代码xparse
使用一些以主命令命名的宏来实现这一点。这样做的目的是,如果缺少]
,则产生的 TeX 错误会将用户指向文档命令,而不是一些奇怪的内部命令。结果是代码中的设置略微复杂一些。(还有一些 Bruno 性能技巧和保留括号的代码。)
对于可扩展抓取,我们不能使用\futurelet
,但对于所有前瞻来说都是如此:相反,我们抓取一个参数并查看它是否是与所需前瞻相匹配的单个标记。一切都必须提前设置(不能动态定义),因此还有更多工作……但基本思想保持不变。