該\NewDocumentCommand
命令使用了一個巧妙的技巧來產生更好的錯誤訊息 - 如果 的參數\mycommand
混亂,那麼所產生的錯誤將是類似於“文件在掃描使用時結束\__xparse_grab_D:w
”之類的內容,因此每當xparse 想要獲取另一個參數,它定義\mycommand<space>
進行參數抓取,然後使用它來完成工作,這樣如果出現問題,錯誤就會類似於「段落在\mycommand<space>
完成之前結束」。
我想要這樣的命令\NewDocumentCommandAs
需要兩個參數——第一個是要定義的巨集的名稱,第二個是要在錯誤訊息中使用的命令的名稱。
我有兩個用例。一是我有一個套件定義了僅在特定環境中使用的命令。因此,它在套件的私有命名空間中定義了命令,並且\let
是環境啟動時私有版本的命令的公共名稱。顯然,我想將所有錯誤訊息都基於命令的公共名稱,所以我想說類似的話
\NewDocumentCommandAs \__my_pkg_cmd:args \cmd {<args>}{<body>}
第二個用例是,我有一個帶有可選參數的命令,該參數是第一個左括號之前的所有內容,然後是一個由從第一個左括號開始的平衡括號分隔的參數。因為「until」參數類型吸收了它正在掃描的標記,所以我需要重新插入左括號並使用輔助:
\NewDocumentCommand \mycommand { o u( } { \mycommand_aux{#1}{#2}( }
\NewDocumentCommandAs \mycommand_aux \mycommand { m m r() } {
% do stuff here
}
當然,這可以透過製作一個版本的 u(也許是 U?)來重新插入它刪除的令牌來解決,但這可能是一個不尋常的用例。
出於格式化目的,我想我會提交我的兩個實作作為答案。有其他人處理過這個問題嗎?我缺少一些更好的方法嗎?我不應該這樣做並以不同的方式解決這些問題嗎?此功能是否可能添加到 xparse 的未來版本?
答案1
我的第一次嘗試是:
\cs_new_protected:Npn \NewDocumentCommandAs#1#2#3#4{
\group_begin:
% Store the original value of #2
\cs_set_eq:cN { tempsave_ \cs_to_str:N #2 } #2
\cs_set_eq:cc { tempsave_ \cs_to_str:N #2 \c_space_tl code }
{ \cs_to_str:N #2 \c_space_tl code }
% Use \DeclareDocumentCommand with #2 so that error messages work
\DeclareDocumentCommand#2{#3}{\group_end: #4}
% Define #1 to be a wrapper command that sets #2<space>code equal to #1<space>code
\cs_new:Npx #1{
\group_begin:
\exp_not:N \cs_set_eq:NN
\exp_not:c { \cs_to_str:N #2 \c_space_tl code }
\exp_not:c { \cs_to_str:N #1 \c_space_tl code }
\exp_not:c { \cs_to_str:N #1 \c_space_tl inner }
}
% Save the value of #2 set by DeclareDocumentCommand
\cs_gset_eq:cN { \cs_to_str:N #1 \c_space_tl inner } #2
\cs_gset_eq:cc{ \cs_to_str:N #1 \c_space_tl code } { \cs_to_str:N #2 \c_space_tl code }
% Restore the original value of #2
\cs_gset_eq:Nc #2 { tempsave_ \cs_to_str:N #2 }
\cs_gset_eq:cc { \cs_to_str:N #2 \c_space_tl code } { tempsave_ \cs_to_str:N #2 \c_space_tl code }
\group_end:
}
這有點混亂,但工作正常。這樣做的最大好處是,它不依賴\NewDocumentCommand
儲存巨集的內部細節之外的實現#1<space>code
,並且不使用其他輔助命令。一個限制是,如果所有參數都是“m”類型,它將不起作用,但這並不困擾我。此方法也不適用於可擴充巨集。
另一種可能的方法是製作修補版本,\__xparse_declare_cmd_mixed_aux:Nn
該版本會對錯誤命令(儲存在 中)使用不同的名稱\l__xparse_fn_tl
,並暫時安裝定義以便使用\NewDocumentCommand
。這樣做的優點是您可以執行類似的操作\__xparse_declare_cmd_mixed_expandable:Nn
並獲得可擴展版本的工作。即使所有參數都是強制性的,它也可以工作。缺點是它取決於 的具體實現\__xparse_declare_cmd_mixed_aux:Nn
。
% Make two copies of \__xparse_declare_cmd_aux:Nn, one to patch, one as a save
% If \l_..._environment_bool is true, it forces xparse to use argument grabbers even when
% all arguments are mandatory. So we'll set it to be true for \NewDocumentCommandAs
\cs_new_eq:NN \__my_xparse_declare_cmd_aux_as:Nnn \__xparse_declare_cmd_aux:Nnn
\cs_new_eq:NN \__my_xparse_declare_cmd_aux_save:Nnn \__xparse_declare_cmd_aux:Nnn
\patchcmd \__my_xparse_declare_cmd_aux_as:Nnn { \bool_set_false:N \l__xparse_environment_bool } { \bool_set_true:N \l__xparse_environment_bool }{}{\oops}
\cs_new_eq:NN \__my_xparse_declare_cmd_mixed_aux_as:Nn \__xparse_declare_cmd_mixed_aux:Nn
\cs_new_eq:NN \__my_xparse_declare_cmd_mixed_aux_save:Nn \__xparse_declare_cmd_mixed_aux:Nn
% Replace \l__xparse_function_tl with my own token list that I can set independently
\tl_new:N \l__my_xparse_function_as_tl
\patchcmd \__my_xparse_declare_as_cmd_mixed_aux:Nn
{{ \l__xparse_function_tl \c_space_tl }}
{{ \l__my_xparse_function_as_tl \c_space_tl }}{}{\oops}
\cs_new:Npn \NewDocumentCommandAs #1#2#3#4 {
% Patch in my modified version of \__xparse_declare_cmd_mixed_aux
\cs_set_eq:NN \__xparse_declare_cmd_mixed_aux:Nn \__my_xparse_declare_cmd_mixed_aux_as:Nn
\tl_set:Nn \l__my_xparse_function_as_tl { \cs_to_str:N #2 } % Use #2 as the error name
\NewDocumentCommand#1{#3}{#4}
\cs_set_eq:NN \__xparse_declare_cmd_mixed_aux:Nn \__my_xparse_declare_as_cmd_mixed_aux_save:Nn
}