定义 \keys_filter:nnnN 而不修改核心函数

定义 \keys_filter:nnnN 而不修改核心函数

我想定义一个函数\keys_filter:nnnN,它可以做与设置完全相同的事情,\keys_set_filter:nnnN但没有设置。我意识到设置是由核心函数实现的\__keys_set_elt_aux:,如果我在本地禁用它,一切都会正常工作。(LaTeX3 警察正在追捕我。我知道。)但是,我修改了核心,这是不被接受的。有没有更干净的方法来实现这一点?

下面我将展示一个示例,说明如何使用此功能分离经过筛选属于不同密钥组的密钥。

\documentclass{article}

\usepackage{expl3}[2014/11/25]
\usepackage{xparse}

\ExplSyntaxOn

\cs_new:Nn \my_keys_filter:nnnN {
    \group_begin:
        % Disable the L3 core fucntion responsible for setting the key
        \cs_set_eq:NN \__keys_set_elt_aux: \prg_do_nothing:
        % Use \keys_set_filter
        \keys_set_filter:nnnN { #1 } { #2 } { #3 } \l_my_temp_tl
        % Globalize the token list
        \tl_gset_eq:NN \g_my_temp_tl \l_my_temp_tl
    \group_end:
    % Assign the token list to the output
    \tl_set_eq:NN #4 \g_my_temp_tl
}
\cs_generate_variant:Nn \my_keys_filter:nnnN { nnV }

\keys_define:nn { my } {

    % group A key
    one .code:n = { (ONE:#1) },
    one .groups:n = { grpA },

    % group A+B key
    two .code:n = { (TWO:#1) },
    two .groups:n = { grpAB },

    % group A+B+C key
    three .code:n = { (THREE:#1) },
    three .groups:n = { grpABC },

}

\cs_generate_variant:Nn \msg_error:nnn { nnV }

% Just messages
\msg_new:nnn { my } { keys-only-A } { The~key~'#1'~can~appear~only~in~\token_to_str:N \takesABC . }
\msg_new:nnn { my } { keys-only-AB } { The~key~'#1'~can't~appear~in~\token_to_str:N \takesC . }

% If a key falls in my-only-A(B) key module, we call the error function
\keys_define:nn { my-only-A } { unknown .code:n = { \msg_error:nnV { my } { keys-only-A } \l_keys_key_tl } }
\keys_define:nn { my-only-AB } { unknown .code:n = { \msg_error:nnV { my } { keys-only-AB } \l_keys_key_tl } }

% Takes anything
\DeclareDocumentCommand \takesABC { O{} } {
    \keys_set:nn { my } { #1 }
    ...
}

% Doesn't take A keys
\DeclareDocumentCommand \takesBC { O{} } {
    \keys_set_filter:nnnN { my } { grpA } { #1 } \l_my_filtered_keys_tl
    \keys_set:nV { my-only-A } \l_my_filtered_keys_tl
    ...
}

% Takes only C keys
\DeclareDocumentCommand \takesC { O{} } {
    \keys_set_filter:nnnN { my } { grpA, grpAB } { #1 } \l_my_filtered_keys_tl
    \my_keys_filter:nnVN { my } { grpA } \l_my_filtered_keys_tl \l_my_filtered_B_keys_tl
    \my_keys_filter:nnVN { my } { grpAB } \l_my_filtered_keys_tl \l_my_filtered_C_keys_tl
    \keys_set:nV { my-only-A } \l_my_filtered_B_keys_tl
    \keys_set:nV { my-only-AB } \l_my_filtered_C_keys_tl
    ...
}

\ExplSyntaxOff

\begin{document}

\takesABC[one = 11, two = 22, three = 33]

\takesBC[two = 222, three = 333]

\takesC[three = 3333]

These generate errors:

\takesBC[one = 1, two = 2, three = 3]

\takesC[one = I, two = II, three = III]

\end{document}

我实际上想做什么?我有一个类的 KV 接口,并且有 3 个地方可以设置键:

\documentclass[one=1]{myclass}
\mysetup{ two=2 }
\myprocessthings
\mysetup{ three=3 }
\begin{document}
...
\end{document}

并且有些键在某些“后续上下文”中没有用,例如,当调用类时,字体大小是固定的,而 babel 是在 中调用的\myprocessthings,因此使用的语言在那里是固定的。我可能会犯一个一般性错误Key '#1' used later than where it is allowed,但我想更准确地表达我的意思,为此,我需要区分这些键。(很抱歉,这个的 MWE 会更长,因为它需要一个类文件,所以我决定让它更简单,可能太多了。)

答案1

密钥过滤适用于相当特殊的情况,即多个密钥有意义但需要额外的处理或排序。特别是,它并不是处理密钥的正确方法,因为从某个固定点开始,它们不再有意义。例如,许多软件包都有只影响整个文档的前言选项:这些密钥不需要过滤,它们需要禁用!例如,可以使用钩子来\AtBeginDocument处理

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\keys_define:nn { mymodule }
  {
    key-one .tl_set:N = \l_my_a_tl ,
    key-two .tl_set:N = \l_my_b_tl
  }
\AtBeginDocument
  {
    \keys_define:nn { mymodule }
      {
        key-one .code:n = \msg_error:nnn
          { mymodule } { key-preamble-only } { key-one }
      }
  }
\msg_new:nnn { mymodule } { key-preamble-only }
  { Key~'#1'~only~valid~in~the~preamble! }
\cs_new_eq:NN \SetKeys \keys_set:nn
\ExplSyntaxOff
\SetKeys{mymodule}{key-one = value-a, key-two = value-b}
\begin{document}
\SetKeys{mymodule}{key-one = value-c, key-two = value-d}
\end{document}

如果有两组键在两个文档命令中具有并行用法,并且在第三个文档命令中具有通用用法,则可以使用键分组

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\keys_define:nn { mymodule }
  {
    key-one .tl_set:N = \l_my_a_tl ,
    key-one .groups:n = a,
    key-two .tl_set:N = \l_my_b_tl,
    key-two .groups:n = b
  }
\cs_new_eq:NN \SetKeys      \keys_set:nn
\cs_new_eq:NN \SetGroupKeys \keys_set_groups:nnn
\ExplSyntaxOff
\begin{document}
\SetGroupKeys{mymodule}{a}{key-one = value-a, key-two = value-b}
\SetGroupKeys{mymodule}{b}{key-one = value-c, key-two = value-d}
\SetKeys{mymodule}{key-one = value-e, key-two = value-f}
\end{document}

或者使用两个子模块和多遍方法,例如

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\keys_define:nn { mymodule }
  {
    a / key-one .tl_set:N = \l_my_a_tl ,
    b / key-two .tl_set:N = \l_my_b_tl
  }
\cs_new_eq:NN \SetKeys      \keys_set:nn
\cs_new_eq:NN \SetKnownKeys \keys_set_known:nn
\ExplSyntaxOff
\begin{document}
\SetKnownKeys{mymodule / a}{key-one = value-a, key-two = value-b}
\SetKnownKeys{mymodule / b}{key-one = value-c, key-two = value-d}
\end{document}

相关内容