添加 #0 来表示当前扩展的宏的名称有什么缺点?

添加 #0 来表示当前扩展的宏的名称有什么缺点?

假设某些宏触发错误消息。

错误消息还应显示触发错误消息的宏的名称。

可以做这样的事:

\newcommand\MYCOMMAND{%
  % ...
  \GenericError{%
    \space\space\space\@spaces\@spaces\@spaces
  }{%
    A point is reached where carrying out macro
    \string\MYCOMMAND\space triggers an error-message...
  }{%
    See the documentation for more information.%
  }{%
    More explanation.%
  }%
  % ...
}%

问题是:

如果您执行类似 的操作\let\MYSAVEDCOMMAND=\MYCOMMAND,则错误消息\MYSAVEDCOMMAND也将包含短语\MYCOMMAND,尽管此处的短语应该是\MYSAVEDCOMMAND
这是因为该短语在定义文本中是“硬编码”的。

如果您有语法,其中表示应用于当前扩展的宏标记(控制序列标记/活动字符标记)#0的结果,那么您可以不用这样的“硬编码”:\string

\newcommand\MYCOMMAND{%
  % ...
  \GenericError{%
    \space\space\space\@spaces\@spaces\@spaces
  }{%
    A point is reached where carrying out macro
    #0 triggers an error-message...
  }{%
    See the documentation for more information.%
  }{%
    More explanation.%
  }%
  % ...
}%

我的问题是:

实现并将该功能添加到 TeX 程序中会很难吗?

(正如您从编译器生成的错误消息中看到的那样,这些错误消息中包含了处理分隔参数的宏,并因此遇到不平衡的右花括号,TeX 似乎已经跟踪了宏标记的名称:

这个例子

\def\foo#1delimiter{definition text, argument: #1}
\foo bla}delimiter
\bye

提供:

! Argument of \foo has an extra }.

该功能是否存在缺点、缺陷或问题#0

使用当前 TeX 引擎编译编写的代码是否与#0具有此类功能的引擎不兼容?

答案1

实施起来有多难:

当错误

! Argument of \foo has an extra }.

生成后,TeX 还在扫描参数。在这个过程中,调用宏时使用的名称仍然可用。(如果warning_index你想查看源代码,请参阅)读取所有参数后情况会发生变化:然后 TeX 开始新的输入级别并读取宏主体,在此过程中插入参数。这里warning_index不再设置宏名称。

但是,虽然错误消息示例不起作用,但有些事情也起作用了:对于每个输入级别,TeX 都会存储一个name,以防宏扩展,这是 csname。因此名称可用的。

例如在 LuaTeX 中(其他引擎应该类似,但我更喜欢 C 而不是 Pascal),你的 #0 的变体可以通过补丁实现:(此代码#0用宏标记替换,而不是用应用的结果替换\string,因此你必须\string自己添加。这可以结合起来而不需要做更多的工作,但这样更灵活。例如,它允许你使用\csstring

diff --git a/source/texk/web2c/luatexdir/tex/scanning.c b/source/texk/web2c/luatexdir/tex/scanning.c
index 8f7e1d591..5064c5401 100644
--- a/source/texk/web2c/luatexdir/tex/scanning.c
+++ b/source/texk/web2c/luatexdir/tex/scanning.c
@@ -2183,7 +2183,7 @@ halfword scan_toks(boolean macro_def, boolean xpand)
                 else
                     get_token();
                 if (cur_cmd != mac_param_cmd) {
-                    if ((cur_tok <= zero_token) || (cur_tok > t)) {
+                    if ((cur_tok < zero_token) || (cur_tok > t)) {
                         print_err("Illegal parameter number in definition of ");
                         sprint_cs(warning_index);
                         help3(
diff --git a/source/texk/web2c/luatexdir/tex/textoken.c b/source/texk/web2c/luatexdir/tex/textoken.c
index d6c168906..3de298753 100644
--- a/source/texk/web2c/luatexdir/tex/textoken.c
+++ b/source/texk/web2c/luatexdir/tex/textoken.c
@@ -2018,8 +2018,15 @@ static boolean get_next_tokenlist(void)
                 break;
             case out_param_cmd:
                 /*tex Insert macro parameter and |goto restart|. */
-                begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
-                return false;
+                if (cur_chr == 0) {
+                    cur_cs = iname;
+                    cur_cmd = call_cmd;
+                    cur_chr = istart;
+                    return true;
+                } else {
+                    begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
+                    return false;
+                }
                 break;
         }
     }

关于兼容性:此更改仅影响使用 的代码#0。如上代码所示,更改scan_toks,在普通 TeX 中使用#0始终会导致错误Illegal parameter number。因此,它是兼容的,因为每个在普通引擎中不会产生错误消息的 TeX 文档仍将在修改后的引擎中工作,而不会改变行为。

所以我认为最大的问题是它的用途有限:对于错误消息,TeX 已经显示了一些上下文,通常包括宏名称。因此它不会添加很多信息,同时为 TeX 最核心的例程之一()增加了(一点)复杂性get_next_tokenlist,并使 TeX 的宏扩展规则更加复杂。

相关内容