Macro para agregar una versión destacada del comando

Macro para agregar una versión destacada del comando

¿Cómo se puede escribir una metamacro que agregue una versión destacada a un comando?

El uso previsto estaría en la línea de

\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}

Respuesta1

Ya hay un método disponible con el paquete suffixde David Kastrup. No hace falta decir que está lleno de trucos ingeniosos.

Puedes decir

\usepackage{suffix}

\newcommand{\foo}[1]{foo is #1}
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}

y puede resultar instructivo ver cómo se logra el objetivo.

Si lo hacemos \show\foodespués de la segunda instrucción, encontramos

> \foo=\protected macro:
->\WSF@suffixcheck \foo .

entonces aprendemos que suffixrequiere e-TeX (no es un problema hoy en día) y se redefine \foocomo significado \WSF@suffixcheck\foo. Así que sumamos \makeatlettery probamos \show\WSF@suffixcheck, obteniendo

> \WSF@suffixcheck=macro:
#1->\begingroup \def \reserved@a {#1}\futurelet \reserved@b \WSF@suffixcheckii 

entonces el argumento se guarda en \reserved@ay

\futurelet\reserved@b\WSF@suffixcheckii

es ejecutado. Esto hace \reserved@bque sea equivalente al token que sigue \WSF@suffixcheckii. Si la llamada es

\foo{foo}

entonces \reserved@bserá \bgroup; si la llamada es

\foo*{foo}{bar}

entonces \reserved@bserá *. Ahora necesitamos saber qué \WSF@suffixcheckiihace:

> \WSF@suffixcheckii=macro:
->\ifcsname \expandafter \SuffixName \reserved@a \reserved@b \endcsname
  \expandafter \WSF@suffixcheckiii \else \expandafter \WSF@suffixcheckiv \fi .

Bien, veamos qué sucede en el \foo{foo}caso: \reserved@ase expande a \foo, mientras que \reserved@bes \bgroup(no expandible), por lo que TeX se presenta primero con

\ifcsname\SuffixName\foo\reserved@b\endcsname

y \SuffixNameestá definido por

> \SuffixName=\long macro:
#1->WSF:\string #1 \meaning .

entonces el siguiente paso es

\ifcsname WSF:\string\foo \meaning\reserved@b\endcsname

y finalmente conseguimos

\ifcsname WSF:\foo begin-group character {\endcsname

donde todos los caracteres tienen el código de categoría 12 (pero los espacios tienen 10). En el \foo*{foo}{bar}caso de que obtuviéramos

\ifcsname WSF:\foo the character *\endcsname

El comando \csname WSF:\foo begin-group character {\endcsnameno está definido, por lo que se sigue la rama falsa, es decir

\expandafter \WSF@suffixcheckiv \fi

que simplemente deja

\WSF@suffixcheckiv{foo}

en el flujo de entrada. ahora \show\WSF@suffixcheckivda

> \WSF@suffixcheckiv=macro:
->\expandafter \endgroup \csname \expandafter \NoSuffixName \reserved@a \endcsname .

por lo que el grupo previamente abierto se cierra pero primero

\csname \expandafter \NoSuffixName \reserved@a \endcsname

está formado. Recuerde que \reserved@ase expande a \foo, por lo que obtenemos

\csname \NoSuffixName \foo \endcsname

y \NoSuffixNamees

> \NoSuffixName=macro:
->WSF:\string .

entonces finalmente obtenemos

\csname WSF:\string\foo\encsname

Bien, emitamos \expandafter\show\csname WSF:\string\foo\endcsname:

> \WSF:\foo=\long macro:
#1->foo is #1.

es decir, esta complicada macro es una copia del original \foo.

En el caso de \foo*{foo}{bar}tendríamos

\ifcsname WSF:\foo the character *\endcsname

pero en este caso estoesdefinido; en efecto

\expandafter\show\csname WSF:\string\foo\space the character *\endcsname

produce

> \WSF:\foo the character *=\long macro:
#1#2->foo is #1, bar is #2.

entonces esta macro con un nombre complicado es lo que has definido como *-variante.

Casi cualquier token se puede utilizar como sufijo con este paquete. Pero la idea esencial no es diferente de lo que has ideado; las protecciones contra la sobrescritura de posibles nombres de macros existentes son mejores. Qué hace el paquete cuando

\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}

se procesa es

  1. Guarde el comando original \fooen

    \csname WSF:\string\foo\endcsname
    

    (si esto ya existe debido a un precedente \WithSuffixaplicado a \fooeste paso, por supuesto se omite)

  2. Guarde la nueva definición en

    \csname WSF:\string\foo\space the character *\endcsname
    
  3. Utilice la interfaz abstracta descrita anteriormente para elegir entre diferentes sufijos.

Respuesta2

Mi propio intento de solución se encuentra a continuación, con mejoras proporcionadas amablemente por @egreg y @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:

Salida MWE

Explicación:

  • Una copia de la definición actual del comando \foose almacena como \\foo@nostar.
  • El comando \foose redefine para buscar una estrella y llamar a \\foo@staro \\foo@nostar. Esto se hace edefpara que los nombres de los tokens construidos se puedan expandir en el lugar y no cada vez que se invoca el comando.
  • Se inicia un \newcommandfor \\foo@stary tomará el resto de la definición de la siguiente manera \addstarred\foo.

información relacionada