
Como alguém pode escrever uma meta-macro que adicione uma versão com estrela a um comando?
O uso pretendido seria nos moldes de
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
Responder1
Um método já está disponível com o pacote suffix
de David Kastrup. Escusado será dizer que está cheio de truques inteligentes.
Você pode dizer
\usepackage{suffix}
\newcommand{\foo}[1]{foo is #1}
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}
e pode ser instrutivo ver como o objetivo é alcançado.
Se fizermos isso \show\foo
após a segunda instrução, encontraremos
> \foo=\protected macro:
->\WSF@suffixcheck \foo .
então aprendemos que isso suffix
requer e-TeX (não é um problema hoje em dia) e redefine \foo
para significar \WSF@suffixcheck\foo
. Então adicionamos \makeatletter
e tentamos \show\WSF@suffixcheck
, obtendo
> \WSF@suffixcheck=macro:
#1->\begingroup \def \reserved@a {#1}\futurelet \reserved@b \WSF@suffixcheckii
então o argumento é salvo em \reserved@a
e
\futurelet\reserved@b\WSF@suffixcheckii
É executado. Isso faz com \reserved@b
que seja equivalente ao token a seguir \WSF@suffixcheckii
. Se a chamada for
\foo{foo}
então \reserved@b
será \bgroup
; se a chamada for
\foo*{foo}{bar}
então \reserved@b
será *
. Agora precisamos saber o que \WSF@suffixcheckii
faz:
> \WSF@suffixcheckii=macro:
->\ifcsname \expandafter \SuffixName \reserved@a \reserved@b \endcsname
\expandafter \WSF@suffixcheckiii \else \expandafter \WSF@suffixcheckiv \fi .
OK, vamos ver o que acontece no \foo{foo}
caso: \reserved@a
expande para \foo
, while \reserved@b
is \bgroup
(inexpansível), então o TeX é apresentado pela primeira vez com
\ifcsname\SuffixName\foo\reserved@b\endcsname
e \SuffixName
é definido por
> \SuffixName=\long macro:
#1->WSF:\string #1 \meaning .
então o próximo passo é
\ifcsname WSF:\string\foo \meaning\reserved@b\endcsname
e finalmente conseguimos
\ifcsname WSF:\foo begin-group character {\endcsname
onde todos os caracteres possuem código de categoria 12 (mas os espaços possuem 10). No \foo*{foo}{bar}
caso obteríamos
\ifcsname WSF:\foo the character *\endcsname
O comando \csname WSF:\foo begin-group character {\endcsname
não está definido, então o ramo falso é seguido, ou seja
\expandafter \WSF@suffixcheckiv \fi
que simplesmente deixa
\WSF@suffixcheckiv{foo}
no fluxo de entrada. Agora \show\WSF@suffixcheckiv
dá
> \WSF@suffixcheckiv=macro:
->\expandafter \endgroup \csname \expandafter \NoSuffixName \reserved@a \endcsname .
então o grupo aberto anteriormente está fechado, mas primeiro
\csname \expandafter \NoSuffixName \reserved@a \endcsname
é formado. Lembre-se de que isso \reserved@a
se expande para \foo
, então obtemos
\csname \NoSuffixName \foo \endcsname
e \NoSuffixName
é
> \NoSuffixName=macro:
->WSF:\string .
então finalmente obtemos
\csname WSF:\string\foo\encsname
OK, vamos emitir \expandafter\show\csname WSF:\string\foo\endcsname
:
> \WSF:\foo=\long macro:
#1->foo is #1.
isto é, esta macro complicada é uma cópia do original \foo
.
No caso de \foo*{foo}{bar}
teríamos
\ifcsname WSF:\foo the character *\endcsname
mas neste caso issoédefiniram; de fato
\expandafter\show\csname WSF:\string\foo\space the character *\endcsname
produz
> \WSF:\foo the character *=\long macro:
#1#2->foo is #1, bar is #2.
então esta macro com um nome complicado é o que você definiu como *
-variant.
Quase qualquer token pode ser usado como sufixo com este pacote. Mas a ideia essencial não é diferente daquela que você imaginou; as proteções contra a substituição de possíveis nomes de macro existentes são melhores. O que o pacote faz quando
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}
é processado é
Salve o comando original
\foo
em\csname WSF:\string\foo\endcsname
(se isso já existir devido a um procedimento anterior
\WithSuffix
aplicado a\foo
esta etapa, é claro, omitido)Salve a nova definição em
\csname WSF:\string\foo\space the character *\endcsname
Use a interface abstrata descrita acima para escolher entre diferentes sufixos.
Responder2
Minha própria tentativa de solução está abaixo, com melhorias gentilmente fornecidas por @egreg e @DavidCarlisle.
\documentclass{standalone}
\makeatletter
\newcommand\addstarred[1]{%
\expandafter\let\csname\string#1@nostar\endcsname#1%
\edef#1{\noexpand\@ifstar\expandafter\noexpand\csname\string#1@star\endcsname\expandafter\noexpand\csname\string#1@nostar\endcsname}%
\expandafter\newcommand\csname\string#1@star\endcsname%
}
\makeatother
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
\begin{document}
\foo{red} --- \foo*{red}{green}
\end{document}
Resultado:
Explicação:
- Uma cópia da definição atual do comando
\foo
é armazenada como arquivo\\foo@nostar
. - O comando
\foo
é redefinido para verificar uma estrela e chamar\\foo@star
ou\\foo@nostar
. Isso é feitoedef
para que os nomes dos tokens construídos possam ser expandidos no local e não sempre que o comando for invocado. - Um
\newcommand
for\\foo@star
é iniciado e assumirá o resto da definição como segue\addstarred\foo
.