![使用 expl3 解析不可扩展命令(如 \small)](https://linux22.com/image/459900/%E4%BD%BF%E7%94%A8%20expl3%20%E8%A7%A3%E6%9E%90%E4%B8%8D%E5%8F%AF%E6%89%A9%E5%B1%95%E5%91%BD%E4%BB%A4%EF%BC%88%E5%A6%82%20%5Csmall%EF%BC%89.png)
使用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
\protected
expl3
\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}