在编写测试时l3build我在输出使用 的命令的结果时遇到了问题@ifnextchar
。具体来说,\TYPE{\command}
出现了编译错误。我将其简化为以下 MWE:
\documentclass{article}
\begin{document}
\makeatletter
\DeclareRobustCommand\lookahead{\@ifnextchar{z}{hello}{goodbye}}
\immediate\write128{\lookahead z}
\makeatother
\end{document}
在我的计算机上(pdflatex、TeX Live 2020/Debian),上面的代码给出了错误:Argument of \reserved@a has an extra }
。我对此有点不知所措,所以经过一番搜索,我决定最好寻求帮助。你知道为什么会出现这个错误吗?能做些什么来解决它吗?请注意,输出方法不应更改,因为它本质上是l3build。
非常感谢!
答案1
我可以提供一种可扩展的机制\UD@CheckWhetherLeadingTokens
,通过该机制您可以让 LaTeX 通过处理分隔参数的宏来检查宏参数的前导标记是否形成特定的标记集。
\UD@CheckWhetherLeadingTokens
在几个方面不同于\@ifnextchar
:\kernel@ifnextchar
\UD@CheckWhetherLeadingTokens
是可扩展的。\UD@CheckWhetherLeadingTokens
不会“向前看”标记流中的下一个标记。相反,它会“查看”宏参数的第一个标记。
(对于 with \@ifnextchar
/\kernel@ifnextchar
和 with,\UD@CheckWhetherLeadingTokens
您可能需要注意\uppercase
/ \lowercase
/ \MakeUppercase
/\MakeLowercase
等何时发挥作用。)
\documentclass{article}
\makeatletter
%==========[code for checking leading token-sequences in arguments]============
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Exchange two arguments. (From each argument an outermost level of
%% surrounding braces will be removed if present.)
%%-----------------------------------------------------------------------------
\newcommand\UD@Exchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific
%% token-sequence that does not contain explicit character tokens of
%% category code 1 or 2:
%%.............................................................................
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%% {<a <token sequence> without explicit
%% character tokens of category code
%% 1 or 2>}%
%% {a <single non-space token> that does
%% _not_ occur in <token sequence> >}%
%% {<internal token-check-macro>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked> has
%% <token sequence> as leading tokens>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked>
%% does not have <token sequence> as
%% leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[4]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\UD@Exchange{ }\expandafter\@secondoftwo}%
{\expandafter\@secondoftwo\string{\expandafter
\UD@@CheckWhetherLeadingTokens#4#3#1#2}{}}%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\UD@Exchange{\@firstoftwo}}{\UD@Exchange{\@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%% {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[2]{%
\@ifdefinable#1{\long\def#1##1#2{{##1}}}%
}%
%=======[end of code for checking leading token-sequences in arguments]=========
\UD@internaltokencheckdefiner{\zcheck}{z}%
\newcommand\lookahead[1]{%
\UD@CheckWhetherLeadingTokens{#1}{z}{.}{\zcheck}{hello}{goodbye} #1%
}%
\makeatother
\begin{document}
\immediate\write128{ble ble \lookahead{y bla} blu blu}
\immediate\write128{ble ble \lookahead{{z} bla} blu blu}
\immediate\write128{ble ble \lookahead{z bla} blu blu}
\end{document}
通过上面的例子我在终端上得到了这个:
ble ble goodbye y bla blu blu
ble ble goodbye {z} bla blu blu
ble ble hello z bla blu blu
另一种方法可能是在调用另一个宏以实际执行前瞻\lookahead
之前执行括号破解以删除开括号,并执行另一个括号破解以添加开括号并调用参数:\lookaheadb
\@ifnextchar
\@ifnextchar
\immediate\write
\documentclass{article}
\makeatletter
\newcommand\lookahead{%
\expandafter\expandafter\expandafter\lookaheadb\expandafter\@gobble\string
}%
\newcommand\lookaheadb{%
\@ifnextchar{z}%
{\immediate\write128\expandafter\expandafter\expandafter{\expandafter\@gobble\string}hello }%
{\immediate\write128\expandafter\expandafter\expandafter{\expandafter\@gobble\string}goodbye }%
}%
\makeatother
\begin{document}
\lookahead{y bla bla bla}
\lookahead{{z} bla bla bla}
\lookahead{z bla bla bla}
\end{document}
通过上面的例子我在终端上得到了这个:
goodbye y bla bla bla
goodbye {z} bla bla bla
hello z bla bla bla
这种方法\lookahead
不能嵌套在\write
命令里面,而是调用\write
命令。