使用 expl3 解析不可扩展命令(如 \small)

使用 expl3 解析不可扩展命令(如 \small)

使用expl3语法,我希望在我的\coolgloss命令中使用一个或多个格式化命令(例如\small和 )作为​​可选参数\itshape

使用像这样的命令\newcommand{\coolgloss}[3][\small\itshape]{text}{gloss}我没有问题,但我特别想使用expl3以便有一个包含负值的拆分可raise选项\rule

如果我只想small通过传递一个命令名称(例如)\csname...\endcsname,但我希望支持多个命令(例如\small\itshape),并且我希望在参数中包含反斜杠,那么它可以工作。

因此,该命令必须采用拆分可选参数,其第二个子参数(参数#2)专用于格式化:

\coolgloss[5,\small\itshape]{text}{gloss}

但是当我输入它时,该\small命令(不是\itshape那个)会触发TeX capacity exceeded错误。

这肯定是扩展和变量类型的问题,即我应该在某处n用其他类型替换。

平均能量损失

\documentclass{article}

\usepackage{gb4e}

\NewDocumentCommand{\coolglossaux}{mmmm}{%
    \renewcommand{\eachwordtwo}{\rule[-#1pt]{0pt}{0pt}#2}% !! #2 is the one !!
    \getwords(\lineone,\eachwordone)#3 \\%
    \getwords(\linetwo,\eachwordtwo)#4 \\%
    \loop\lastword{\eachwordone}{\lineone}{\wordone}%
    \lastword{\eachwordtwo}{\linetwo}{\wordtwo}%
    \global\setbox\gline=\hbox{\unhbox\gline
        \hskip\glossglue
        \vtop{\box\wordone
            \nointerlineskip
            \box\wordtwo
        }%
    }%
    \testdone
    \ifnotdone
    \repeat
    {\hskip -\glossglue}\unhbox\gline
}

\ExplSyntaxOn

\NewDocumentCommand{\coolgloss}{>{\SplitArgument{1}{,}}omm}
{
    \my_xcoolgloss:nnnn #1 {#2} {#3}
}
\cs_new_protected:Nn \my_xcoolgloss:nnnn
{
    \__my_xcoolgloss:eenn
    { \tl_if_novalue:nTF { #1 } { 10 } { #1 } }
    { \tl_if_novalue:nTF { #2 } {} { #2 } }
    { #3 }
    { #4 }
}

\cs_set_eq:NN \__my_xcoolgloss:nnnn \coolglossaux
\cs_generate_variant:Nn \__my_xcoolgloss:nnnn {ee}

\ExplSyntaxOff

\begin{document}
    
    \coolgloss[5,\itshape]{I like \LaTeX}{I like \LaTeX}
    
    %\coolgloss[5,\small\itshape]{I like \LaTeX}{I like \LaTeX} => TeX capacity exceeded
    
\end{document}

答案1

正如 MaxChernoff 所说,您无法扩展\small。因此,以下使用\exp_not:n两个可选子参数,以便\tl_if_novalue:nTF完全处理测试,但其结果不会进一步扩展。

编辑:我将辅助函数改为通过和\coolglossaux来定义。这可能也不是理想的,但不幸的是,LaTeX2e 没有很好的接口来处理和路由之外的内容。此外,我还修复了参数拆分器中的一个错误,如果根本不使用可选参数的话。\protected\@ifdefinable\protected\def\protectedexpl3\NewDocumentCommand

\documentclass{article}

\usepackage{gb4e}

\makeatletter
% not perfect as well, but LaTeX2e itself has no good interface to `\protected`.
% The `\@ifdefinable` makes this behave like `\newcommand` even though we use
% a primitive `\def` for the definition.
\@ifdefinable{\coolglossaux}
    {%
        \protected\def\coolglossaux#1#2#3#4{%
            \renewcommand{\eachwordtwo}{\rule[-#1pt]{0pt}{0pt}#2}% !! #2 is the one !!
            \getwords(\lineone,\eachwordone)#3 \\%
            \getwords(\linetwo,\eachwordtwo)#4 \\%
            \loop\lastword{\eachwordone}{\lineone}{\wordone}%
            \lastword{\eachwordtwo}{\linetwo}{\wordtwo}%
            \global\setbox\gline=\hbox{\unhbox\gline
                \hskip\glossglue
                \vtop{\box\wordone
                    \nointerlineskip
                    \box\wordtwo
                }%
            }%
            \testdone
            \ifnotdone
            \repeat
            {\hskip -\glossglue}\unhbox\gline
        }
    }
\makeatother

\ExplSyntaxOn

\NewDocumentCommand{\coolgloss}{>{\SplitArgument{1}{,}}omm}
{
    \tl_if_novalue:nTF {#1}
        { \my_xcoolgloss:nnnn {#1} {#1} {#2} {#3} }
        { \my_xcoolgloss:nnnn #1 {#2} {#3} }
}
\cs_new_protected:Nn \my_xcoolgloss:nnnn
{
    \__my_xcoolgloss:eenn
    { \tl_if_novalue:nTF { #1 } { 10 } { \exp_not:n {#1} } }
    { \tl_if_novalue:nTF { #2 } {} { \exp_not:n {#2} } }
    { #3 }
    { #4 }
}

\cs_set_eq:NN \__my_xcoolgloss:nnnn \coolglossaux
\cs_generate_variant:Nn \__my_xcoolgloss:nnnn {ee}

\ExplSyntaxOff

\begin{document}
    
    \coolgloss[5,\itshape]{I like \LaTeX}{I like \LaTeX}
    
    \coolgloss[5,\small\itshape]{I like \LaTeX}{I like \LaTeX} => TeX capacity exceeded
    
\end{document}

答案2

由于提到了 key=value 解决方案,因此必须进行自我宣传:expkv

使用 key=value 解决方案和巧妙的未知键处理的实现(文字数字输入将被假定为space,否则将被假定为style——如果不需要,请删除中的块\makeatletter...\makeatother)使用expkv以下系列:

\documentclass{article}

\usepackage{expkv-unravel}

% has to be loaded before `gb4e` as the latter changes the category code of ^
\usepackage{expkv-cs}

% defining the key=value interface
\NewDocumentCommand\coolgl{O{}mm}
  {\coolglKV{#1}{#2}{#3}}
\ekvcSplitAndForward\coolglKV\coolglossaux
  {%
    space = 10,
    style = \small
  }

% defining a special rule how to handle unknown keys without a value
\makeatletter
\ekvdefunknownNoVal{\string\coolglKV}
  {%
    \coolglKV@ifnumber{#1}% will be true only for literal numerical input
      {\ekvcPass\coolglKV{space}}%
      {\ekvcPass\coolglKV{style}}%
        {#2}%
  }
% defining a smartish parser
\ExplSyntaxOn
\cs_new_eq:NN \coolglKV@ifempty \tl_if_empty:nTF
\ExplSyntaxOff
\protected\long\def\coolglKV@ifnumber#1%
  {%
    \begingroup
      \afterassignment\coolglKV@ifnumber@aux
      \count\z@=\iffalse{\fi0#1}%
  }
\protected\def\coolglKV@ifnumber@aux
  {%
    \endgroup
    \expandafter\coolglKV@ifempty\expandafter{\iffalse}\fi
  }
\makeatother

\protected\def\coolglossaux#1#2#3#4{%
    \renewcommand{\eachwordtwo}{\rule[-#1pt]{0pt}{0pt}#2}% !! #2 is the one !!
    \getwords(\lineone,\eachwordone)#3 \\%
    \getwords(\linetwo,\eachwordtwo)#4 \\%
    \loop\lastword{\eachwordone}{\lineone}{\wordone}%
    \lastword{\eachwordtwo}{\linetwo}{\wordtwo}%
    \global\setbox\gline=\hbox{%
        \unhbox\gline\hskip\glossglue\vtop{%
            \box\wordone\nointerlineskip\box\wordtwo
        }%
    }%
    \testdone
    \ifnotdone
    \repeat
    {\hskip -\glossglue}\unhbox\gline
}

% evil package changing catcodes in the preamble... :P
\usepackage{gb4e}

\begin{document}
\coolgl[style=\itshape\bfseries\footnotesize,space=30]{%
i try something very long in order to get two lines this is so wonderful and now you see the vertical space optional argument is perfectly working}{%
i try something very long in order to get two lines this is so wonderful and now you see the vertical space optional argument is perfectly working}

\coolgl[\itshape\bfseries\footnotesize,60]{%
i try something very long in order to get two lines this is so wonderful and now you see the vertical space optional argument is perfectly working}{%
i try something very long in order to get two lines this is so wonderful and now you see the vertical space optional argument is perfectly working}
\end{document}

在此处输入图片描述

答案3

我认为这对其他人很有用,可以证明只要有key=value系统,就不会出现这样的问题。

请注意,从很多方面来说,它往往是更好的方法。

\documentclass{article}

\usepackage{gb4e}

\protected\def\coolglossaux#1#2#3#4{%
    \renewcommand{\eachwordtwo}{\rule[-#1pt]{0pt}{0pt}#2}% !! #2 is the one !!
    \getwords(\lineone,\eachwordone)#3 \\%
    \getwords(\linetwo,\eachwordtwo)#4 \\%
    \loop\lastword{\eachwordone}{\lineone}{\wordone}%
    \lastword{\eachwordtwo}{\linetwo}{\wordtwo}%
    \global\setbox\gline=\hbox{%
        \unhbox\gline\hskip\glossglue\vtop{%
            \box\wordone\nointerlineskip\box\wordtwo
        }%
    }%
    \testdone
    \ifnotdone
    \repeat
    {\hskip -\glossglue}\unhbox\gline
}

\ExplSyntaxOn

\NewDocumentCommand{\coolgl}{O{}mm}
{
    \group_begin:
    \keys_set:nn { coolgl } { #1 }
    \coolgl:VVnn
    \coolgl_space
    \coolgl_style
    { #2 }
    { #3 }
    \group_end:
}
\keys_define:nn { coolgl }
{
    space.tl_set:N = \coolgl_space,
    style.tl_set:N = \coolgl_style,
    space.initial:n = 10,
    style.initial:n = \small,
}

\cs_set_eq:NN \coolgl:nnnn \coolglossaux

\cs_generate_variant:Nn \coolgl:nnnn { VV }

\ExplSyntaxOff


\begin{document}
        
\coolgl[space=30,style=\itshape\bfseries\footnotesize]{%
i try something very long in order to get two lines this is so wonderful and now you see the vertical space optional argument is perfectly working}{%
i try something very long in order to get two lines this is so wonderful and now you see the vertical space optional argument is perfectly working}
    
\end{document}

在此处输入图片描述

相关内容