Управление сообщениями об ошибках, создаваемыми командами, определенными с помощью \NewDocumentCommand

Управление сообщениями об ошибках, создаваемыми командами, определенными с помощью \NewDocumentCommand

Команда \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
}

Связанный контент