帮助 xpatch-ing 来自 xkeyval:\setkeys 的命令

帮助 xpatch-ing 来自 xkeyval:\setkeys 的命令

我正在尝试修补\setkeysxkeyval以便它包含一个键掩码,在\setkeys调用时完全忽略。请注意,\setkeys在各种宏中调用,并且有很多宏,因此修改调用站点是不优雅的,甚至不可行。我的目标是以优雅和自动的方式动态隐藏和显示某些类型理论表中的某些字段。

经过几天的辛苦和调试,我决定在这里发布我的问题,看看是否能引起人们的兴趣。

最小示例:

\documentclass{article}
\usepackage{xkeyval, xpatch, xstring, luacode, xargs}

\makeatletter
\define@cmdkey[prim]{frame}{D}{(#1)}

% Our global mask
\def\bonak@mask{}

% First we define \appendmask
\long\def\bonak@appendmask[#1]{
  \XKV@for@eo\XKV@fams\XKV@tfam{
    \def\bonak@hdr{\XKV@prefix\XKV@tfam @}
    \XKV@for@o{#1}\XKV@tempa{
      \XKV@addtolist@x\bonak@mask{\bonak@hdr\XKV@tempa}
      }
  }
}
\def\appendmask{\XKV@testopta{\XKV@testoptc\bonak@appendmask}}

% Now we patch \XKV@setkeys
\xpretocmd{\XKV@setkeys}{
  \XKV@for@o\XKV@fams\XKV@tfam{
    \def\bonak@hdr{\XKV@prefix\XKV@tfam @}
    \XKV@for@o{\bonak@mask}\XKV@tempa{
      \IfBeginWith{\XKV@tempa}{\bonak@hdr}{
        \def\XKV@tempb{\StrBehind{\XKV@tempa}{\bonak@hdr}}
        \XKV@delete#2{\XKV@tempb}\XKV@getkeyname}{}
    }
  }
}{}{}
\makeatother

% Some lua magic to join a variable number of arguments with commas
% Comma-separeated optional arguments
\begin{luacode*}
  function remove_empty(tbl)
    for i, v in ipairs(tbl) do
        if v == "" then
          table.remove(tbl, i)
          return remove_empty(tbl)
        end
    end
    return tbl
  end
  function join_with_comma(prefix, tbl)
    str = table.concat(remove_empty(tbl), ",")
    if str == "" then
      return str
    end
    return prefix .. str
  end
\end{luacode*}

\newcommandx{\joinargs}[6][2=,3=,4=,5=,6=]{\directlua{tex.sprint(join_with_comma("#1", {"\luaescapestring{#2}";"\luaescapestring{#3}";"\luaescapestring{#4}";"\luaescapestring{#5}";"\luaescapestring{#6}"}))}}

\newcommandx*{\prim}[6][2=,3=,4=,5=,6=]{
  \ensuremath{\mathsf{#1}_{#2}^{
    \joinargs{}[#3][#4][#5]}}
  \setkeys*[prim]{#1}{#6}
}
\newcommandx{\framep}[5][1=,2=,3=,4=,5=]{\prim{frame}[][#2][#3][#4][#5]}
\appendmask[prim]{frame}[D]

\begin{document}
\framep[m][n][p][][D=D]
\end{document}

我得到:

[4] (./tab-frames.tex) (./tab-faces.tex
! Missing control sequence inserted.
<inserted text> 
\inaccessible 
l.19 \end{eqntable}

这根本没用,但这就是 LaTeX 中的错误报告。我知道问题与我的使用有关\XKV@delete,扰乱它周围的扩展会产生不同的错误。我经历xkeyval代码一遍又一遍,希望能有所启发,但可惜的是:我决定休息一段时间。我很确定它正在发生\@nil\XKV@g@tkeynames因为定义\@nil会使编译错误消失,但当然,代码不会做任何事情。

我的 LaTeX 扩展功能不太好,而且我思考我的问题主要是缺少一些\protect\expandafter。我还希望有一个资源,比如一个完整的手册,详细描述 TeX 扩展。

谢谢。

答案1

我很高兴地报告我已经完成了上述任务,并将其完善为xkeymask.sty,如下所示。

\RequirePackage{kvoptions, xkeyval}
\ProvidesPackage{xkeymask}[2022/06/01 An extension of xkeyval with a mask]

% Option processing
\newif\ifxkeymask@prefix
\DeclareStringOption{prefix}
\AddToKeyvalOption*{prefix}{\xkeymask@prefixtrue}
\ProcessKeyvalOptions*

\ifxkeymask@prefix
  % We patch xkeyval's \setkeys to use a mask to ignore keys
  \makeatletter
  % Our global mask
  \def\XKM@mask{}

  % First we define \appendmask
  \long\def\XKM@appendmask[#1]{
    \XKV@for@o\XKV@fams\XKV@tfam{
      \xdef\XKM@hdr{\XKV@prefix\XKV@tfam @}
      \XKV@for@o{#1}\XKV@tempa{
        % Remove spaces around key name
        \expandafter\KV@@sp@def\expandafter\XKV@tempa\expandafter{\XKV@tempa}
        \XKV@addtolist@x\XKM@mask{\XKM@hdr\XKV@tempa}
      }
    }
  }
  \def\appendmask{\XKV@testopta{\XKV@testoptc\XKM@appendmask}}

  % A quick way to clear the mask
  \def\clearmask{\global\let\XKM@mask\@empty}

  % Now we override the definition of \XKV@setkeys
  \long\def\XKM@setkeys[#1]#2{
    \XKV@checksanitizea{#2}\XKV@resb
    \let\XKV@resa\@empty
    \XKV@for@o\XKV@fams\XKV@tfam{
      \XKV@for@o\XKV@resb\XKV@tempb{
        \expandafter\XKV@g@tkeyname\XKV@tempb=\@nil\XKV@tempc
        % Remove spaces around key name
        \expandafter\KV@@sp@def\expandafter\XKV@tempc\expandafter{\XKV@tempc}
        \xdef\XKV@tempd{\XKV@prefix\XKV@tfam @\XKV@tempc}
        % If \XKV@tempd is in \XKM@mask,
        % remove the corresponding kv from \XKV@resb
        \@expandtwoargs\in@\XKV@tempd\XKM@mask
        \ifin@\else\XKV@addtolist@o\XKV@resa\XKV@tempb\fi
      }
    }
    \ifnum\XKV@depth=\z@\let\XKV@rm\@empty\fi
    \expandafter\XKV@s@tkeys\expandafter{\XKV@resa}{#1}
    \let\CurrentOption\@empty
  }

  % A dispatcher to \XKM@setkeys or \XKV@setkeys
  \long\def\XKM@setkeys@dispatch{
    \xdef\XKV@tempa{\xkeymask@prefix @}
    \ifx\XKV@prefix\XKV@tempa
      \expandafter\XKM@setkeys
    \else
      \expandafter\XKV@setkeys
    \fi
  }

  % Finally, override \setkeys itself to call the dispatcher
  \def\setkeys{\XKV@testopta{\XKV@testoptc\XKM@setkeys@dispatch}}
  \makeatother
\fi

用法:在代码按预期工作后,将xkeyval的导入替换为。一切都应按预期工作。现在,有两个简单的命令:和,可在主体内使用。xkeyval\usepackage[prefix=<yourprefix>]{xkeymask}\appendmask\clearmask

我给出了一个实际使用片段:

\input{tab-frames.tex}

% For the last two tables
\appendmask[bonak]{frame}[D]
\appendmask[bonak]{layer}[D]
\appendmask[bonak]{filler}[D, E]
\appendmask[bonak]{restrframe}[D]
\appendmask[bonak]{restrlayer}[D, d]
\appendmask[bonak]{restrfiller}[D, d]

\input{tab-faces.tex}

% For the last table,
% (1) make the {d} arguments of restr implicit
% (2) make the {D, E} arguments of coh implicit
\appendmask[bonak]{cohframe}[D]
\appendmask[bonak]{cohlayer}[D]
\appendmask[bonak]{cohfiller}[D, E]

\input{tab-coh.tex}
\clearmask

相关内容