解决了
感谢@PhelypeOleinik 提供的解决方案!也感谢所有迅速提供帮助的人。对于以后看到这篇文章的人来说,这个错误实际上与 ConTeXt 无关,因此即使您使用 LaTeX 或纯 TeX,下面的解释也会很有用。
原始帖子
我正在 ConTeXt 中创建一个模块,该模块主要通过命令访问\setup<name>[]
(对于名为 的某个通用结构<name>
)。除了这个命令和其他一些供最终用户使用的命令之外,我还为所有内部命令添加了命名空间前缀,但由于我希望此命令如何运行,我选择不使用 ConTeXt 的内置\installcommandhandler
。
尽管如此,除了一个非常恼人的与命名空间相关的错误,我几乎完成了整个事情,而我还没有能够解决它。有一次,我的一个命令调用了 和 ,由于命名空间前缀,\processcommacommand[<list>]<command>
两者<list>
都是<command>
样式\csname
,并且两者都需要用\expandafter
链进行扩展。我已经隔离了有问题的部分并更改了下面的命令名称:
\unprotect
\installnamespace{nms}
%\def\????nms{@@@@nms} % For testing only
% A simpler namespace creator could just run e.g. \def\????nms{@@@@nms}, but since in
% practice the actual namespace installer won't determine the prefix string until runtime,
% I have to define everything as generally as possible below, in terms of
% \csname\????nms<other>\endcsname.
% ... other module code ...
% If #1 consists of <arg1A>,<arg1B>,... separated by commas, and #2 consists of
% <arg2Akey>=<arg2Aval>, <arg2Bkey>=<arg2Bval>,... separated by commas, the
% \<namespace>_processor command below attempts to create a series of commands like so:
% % \def\<namespace>@<tag>@<arg1A>@<arg2Akey>{arg2Aval}
% % \def\<namespace>@<tag>@<arg1B>@<arg2bkey>{arg2Bval}
% % etc.
% ...for some externally defined string \tag.
\expandafter\unexpanded\expandafter\def\csname\????nms _processor\endcsname[#1][#2]{%
\expandafter\def\csname\????nms _params\endcsname##1{%
\doifnotempty{##1}{\getparameters[\????nms @\tag @##1@][#2]}
}
\expandafter\def\csname\????nms _csvs\endcsname{#1}
\expandafter\expandafter\expandafter\processcommacommand\expandafter\expandafter
\expandafter[\expandafter\csname\????nms _csvs\endcsname\expandafter]%
\csname\????nms _params\endcsname
% The above should simplify to
% % \processcommacommand[\<namespace>_csvs]\<namespace>_params,
% ...but in practice it throws a "Missing \endcsname inserted" error.
}
% ... other module code ...
\protect
\endinput
但是,如果我将顶部的命名空间设置为由 定义,\def\????nms{@@@@nms}
并将有问题的行(以 3 个\expandafter
调用开头)替换为对命名空间前缀的直接引用(例如\processcommacommand[\@@@@nms_csvs]\@@@@nms_params
),则整个模块都可以正常工作,这让我感到困惑,因为理论上这应该没有什么不同。我使用\expandafter
不正确吗?我尝试重写该特定行几次,结果完全相同,所以我无法说出我在那里犯了什么错误(如果有的话)。
答案1
正如我在评论中所说,我是 ConTeXt 的 Jon Snow,所以这个答案是关于链条的\expandafter
。
您使用的第一个\expandafter
s 是正确的。最后一个是问题:
\expandafter\expandafter\expandafter\processcommacommand\expandafter\expandafter
\expandafter[\expandafter\csname\????nms _csvs\endcsname\expandafter]
\csname\????nms _params\endcsname
让我们来看看。第一个链扩展了突出显示的标记:
因此第一个链仅扩展\????nms
并停止,所以我们剩下:
\expandafter\processcommacommand\expandafter
[\csname<\????nms>_csvs\endcsname\expandafter]
\csname\????nms _params\endcsname
其中<\????nms>
表示 的一次展开。为了举例,\????nms
我们假设\????nms
展开为。NMS
然后,第二条链:
扩展\csname NMS_csvs\endcsname
并停止,现在我们剩下:
\processcommacommand
[\NMS_csvs\expandafter]
\csname\????nms _params\endcsname
接下来执行的是\processcommacommand
一些奇怪的参数。这不是我们想要的。
正确的\expandafter
链条应该是:
\expandafter\processcommacommand\expandafter
[\csname\????nms _csvs\expandafter\endcsname\expandafter]%
\csname\????nms _params\endcsname
但为什么?
首先,你不需要扩展 中的元素\csname...\endcsname
。\csname
原语将执行所有元素的完全扩展,直到下一个\endcsname
,因此:
\def\my{MY}
\def\cmd{COMMAND}
\expandafter\def\csname\my\cmd\endcsname{Hello}
\MYCOMMAND
仅与一个 一起工作\expandafter
。
因此,在您的代码中,您只需扩展两者\csname
,它们就会\????nms
在需要时扩展。
第二点(也是第一点的结果)是,您可以\expandafter
从 内部“继续”一个链\csname
(它执行完全扩展,还记得吗?)。因此\csname\expandafter\endcsname\hello
将\hello
在 之前扩展\csname
。
现在,写下来。我们从以下开始:
\processcommacommand
[\csname\????nms _csvs\endcsname]%
\csname\????nms _params\endcsname
首先,我们展开第一个\csname
。只\expandafter
需要两个:
\expandafter\processcommacommand\expandafter
[\csname\????nms _csvs\endcsname]%
\csname\????nms _params\endcsname
第一个会跳过\processcommacommand
,第二个也会跳过[
。现在我们处于 内部的完整扩展上下文中\csname
。我们可以利用这一点来扩展下一个,方法是在 之前\csname
插入一个并从这里继续链条:\expandafter
\endcsname
\expandafter\processcommacommand\expandafter
[\csname\????nms _csvs\expandafter\endcsname\expandafter]%
\csname\????nms _params\endcsname
第一个\expandafter
将跳过\endcsname
,下一个将跳过]
。