这是我正在尝试做的一个最简单的例子,其意图应该是显而易见的(参见评论)。
\documentclass{minimal}
\usepackage{xparse}
\NewDocumentCommand \sayhello { } {
Hello
}
\ExplSyntaxOn
\NewDocumentCommand \foo { m } {
\use:c {
\MakeUppercase{\cs_to_str:N #1}
}
again
}
\ExplSyntaxOff
\foo{\sayhello} % Should construct \SAYHELLO macro.
\begin{document}
\sayhello % Should output "Hello".
\SAYHELLO % Should output "Hello again".
\end{document}
但是,我收到以下错误。
! Missing \endcsname inserted.
<to be read again>
\protect
l.18 \foo{\sayhello}
我想我在上面的代码中缺少一些适当的扩展,但我不确定如何去做。
答案1
您必须实际定义大写变体。当前代码的工作方式是,每当您使用\foo
尚未定义的大写命名变体时,直接尝试调用它。
另外,您不能使用\MakeUppercase
inside of \use:c
(或\csname ...\endcsname
),因为它不可扩展。相反,您可以使用\str_uppercase:n
provided by expl3
。下面使用f
它的 -variant\cs_to_str:N
在发生大写之前先进行扩展。
请注意,这\foo
仅适用于无参数宏。
\documentclass[]{article}
\ExplSyntaxOn
\NewDocumentCommand \foo { m }
{
\exp_args:Nc \NewDocumentCommand { \str_uppercase:f { \cs_to_str:N #1 } } {}
{ #1~ again }
}
\ExplSyntaxOff
\NewDocumentCommand \sayhello {} {Hello}
\foo\sayhello
\begin{document}
\sayhello
\SAYHELLO
\end{document}
答案2
主要问题\MakeUppercase
是
- 触发处理级联,还会产生不可扩展的标记,这些标记不适合作为控制序列标记名称的组成部分。
- 结果以嵌套在花括号组中的形式传递。
\MakeUppercase
定义如下:
\DeclareRobustCommand\MakeUppercase[1]{%
{% -- opening brace of brace-group
\def\i{I}%
\def\j{J}%
\def\reserved@a##1##2{\let##1##2\reserved@a}%
\expandafter\reserved@a\@uclclist\reserved@b{\reserved@b\@gobble}%
\let\UTF@two@octets@noexpand\@empty
\let\UTF@three@octets@noexpand\@empty
\let\UTF@four@octets@noexpand\@empty
\protected@edef\reserved@a{\uppercase{#1}}%
\reserved@a % -- this holds the text wrapped in \uppercase, some symbol-delivering control-sequences replaced by control-sequences that yield the uppercase variant
}% -- closing brace of brace-group
}%
\MakeUppercase
旨在用于排版目的,而不是用于提供/修改控制序列标记的名称。
为了编程目的/为了提供/修改控制序列标记的名称,expl3 带来了\str_uppercase:n
/ \str_uppercase:f
。
不用于日常使用,只是为了展示问题下面的代码提供了\MakeUppercase
不将内容放入花括号组的变体。
在任何情况下,在控制序列名称的上下文中, \str_uppercase:n
/\str_uppercase:f
都是首选:
\MakeUppercase
的大写例程取决于字体编码,并且可能会提供标记,这些标记在排版时会产生所讨论符号的大写变体,但不能用作宏名称的组成部分。
\documentclass{minimal}
\usepackage[T1]{fontenc}
\makeatletter
\DeclareRobustCommand\MyMakeUppercase[1]{%
{%
\def\i{I}%
\def\j{J}%
\def\reserved@a##1##2{\let##1##2\reserved@a}%
\expandafter\reserved@a \@uclclist\reserved@b{\reserved@b\@gobble}%
\let\UTF@two@octets@noexpand\@empty
\let\UTF@three@octets@noexpand\@empty
\let\UTF@four@octets@noexpand\@empty
\protected@edef\reserved@a{\uppercase{#1}}%
% \show\reserved@a
\expandafter}\reserved@a
}%
\makeatother
\usepackage{xparse}
\NewDocumentCommand\sayhello{}{Hello}
\ExplSyntaxOn
\NewDocumentCommand \foo { m } {
\MyMakeUppercase{\exp_not:n{\exp_args:Nc\NewDocumentCommand}{\cs_to_str:N #1}}{}
{#1~again}
}
\ExplSyntaxOff
\foo{\sayhello} % Should construct \SAYHELLO macro.
\begin{document}
\sayhello % Should output "Hello".
\SAYHELLO % Should output "Hello again".
\end{document}