
Estoy tratando de crear un comando que formatee \everymath
ciertos caracteres específicos (coloca automáticamente v
, \mathbf
etc.). Esa parte ya la he descubierto, pero lo que me está dando problemas es el procesamiento en sí. Parece estar separando comandos dentro del modo matemático, ya sea mostrándose incorrectamente o simplemente rompiéndose por completo.
Esta es una versión mínima que simplemente avanza y no cambia nada (en teoría):
\ExplSyntaxOn
\NewDocumentCommand{\mformat}{+m}{%
\exp_after:wN #1 \mode_if_math:TF { \mformat } { }
}
\ExplSyntaxOff
Se usa así:
\everymath{\mformat}
Aquí hay algunos ejemplos de cómo se rompe:
\(\vec{v}\)
\(\mathbf{v}\) % Just gives "Missing } inserted." error
¿Alguna idea sobre cómo solucionar este problema?
Editar: Algo nuevo que noté; Si rodeo los comandos problemáticos con llaves (como con mathbf
, \({\mathbf{v}}\)
funciona bien), de repente funcionan perfectamente. No tengo idea de por qué sucede esto.
Marqué la respuesta de egreg como la solución, ya que respondió la pregunta que hice, pero para la posteridad aquí está la solución a la pregunta que debería haber hecho (según los comentarios de PhelypeOleinik)
\ExplSyntaxOn
% Used to keep track of already active characters
\tl_new:N \g__mformat_mathactive_tl
% Used to figure out which characters need to be made normal again
\tl_new:N \l__mformat_remove_mathactive_tl
% Used to keep track of added characters from *this* iteration
\tl_new:N \l__mformat_used_tl
% Using https://tex.stackexchange.com/a/611898/261875
% and https://tex.stackexchange.com/a/299805/261875
\NewDocumentCommand{\mformat}{m}{
% By default remove all previous active characters
\tl_set_eq:NN \l__mformat_remove_mathactive_tl \g__mformat_mathactive_tl
\tl_set:Nn \l__mformat_used_tl {}
\tl_if_empty:nTF { #1 } {} {
% Parse the formatting
\cs_set:Npn \__mformat_parse:w ##1[##2]##3\relax {
% Process each character in the set
\tl_map_inline:nn { ##2 } {
\tl_if_in:NnTF \g__mformat_mathactive_tl { ####1 } {
% If this character is already active, keep it active
\tl_remove_once:Nn \l__mformat_remove_mathactive_tl { ####1 }
% Check if the character has been used this iteration
\tl_if_in:NnTF \l__mformat_used_tl {####1} {
% Helper needed to have something expandable once
\cs_set_eq:Nc \__mformat_letter_helper:
{ __mformat_letter_new_####1: }
% Add a formatting option to the letter
\cs_set:cx { __mformat_letter_new_####1: } {
\exp_not:N ##1 { \exp_not:o \__mformat_letter_helper: }
}
} {
% Record that this has been used
\tl_put_right:Nn \l__mformat_used_tl { ####1 }
% Define what the letter will now resolve to
\cs_set:cx { __mformat_letter_new_####1: } {
\exp_not:N ##1 {\mathchar\use:c { __mformat_mathcode_####1: }}
}
}
\char_gset_active_eq:nc { `####1 } { __mformat_letter_new_####1: }
} {
% Record that this is now an active character
\tl_gput_right:Nn \g__mformat_mathactive_tl { ####1 }
% Record that this has been used
\tl_put_right:Nn \l__mformat_used_tl { ####1 }
% Record the normal character so it can be used later
\cs_new:cx { __mformat_mathcode_####1: }
{ \the\mathcode`####1 }
% Define what the letter will now resolve to
\cs_new:cx { __mformat_letter_new_####1: } {
\exp_not:N ##1 {\mathchar\use:c { __mformat_mathcode_####1: }}
}
\char_gset_active_eq:nc { `####1 } { __mformat_letter_new_####1: }
% Set the character to be active in math mode
\char_set_mathcode:nn { `####1 } { "8000 }
}
}
% If there's no more character sets, finish, otherwise recurse
\tl_if_empty:nTF { ##3 } { } { \__mformat_parse:w ##3\relax }
}
% Begin recursive parsing
\__mformat_parse:w #1\relax
}
% \tl_show:N \l__mformat_remove_mathactive_tl
% Remove the active status from the characters that need it
\tl_map_inline:Nn \l__mformat_remove_mathactive_tl {
\tl_gremove_once:Nn \g__mformat_mathactive_tl {##1}
% Reset the math code
\char_set_mathcode:nn { `##1 } { \use:c { __mformat_mathcode_##1: } }
% Deregister functions
\cs_undefine:c { __mformat_letter_new_##1: }
\cs_undefine:c { __mformat_mathcode_##1: }
}
}
\NewDocumentCommand{\std}{m}{ \mathchar\use:c { __mformat_mathcode_#1: } }
\ExplSyntaxOff
que se usa como
\mformat{\mathbb[R]\mathbf[vw]}
Respuesta1
Esto se parece mucho a un problema XY. Tu dices
y no cambia nada (en teoría)
Lo siento, eso no es cierto. La expansión de primer nivel de
\mode_if_math:TF { \mformat } { }
es
\if_mode_math: \__prg_TF_true:w \fi: \use_ii:nn {\mformat}{}
Si tienes $\vec{v}$
obtienes
\vec\if_mode_math: \__prg_TF_true:w \fi: \use_ii:nn {\mformat}{}
y entonces el argumento \vec
es \if_mode_math:
. Ejemplo:
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\mformat}{+m}{%
\exp_after:wN #1 \mode_if_math:TF { \mformat } { }
}
\ExplSyntaxOff
\everymath{\mformat}
\renewcommand{\vec}[1]{\showtokens{#1}}
\begin{document}
$\vec{v}$
\end{document}
En la consola me sale
No file mform.aux.
> \if_mode_math: .
\vec #1->\showtokens {#1}
l.13 $\vec
Sería mejor con\exp_last_unbraced:Nf
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\mformat}{+m}{%
\exp_last_unbraced:Nf #1 { \mode_if_math:TF { \mformat } { } }
}
\ExplSyntaxOff
\everymath{\mformat}
\renewcommand{\vec}[1]{\showtokens{#1}}
\begin{document}
$\vec{v}$
\end{document}
donde imprime la consola
> v.
\vec #1->\showtokens {#1}
Pero eso tampoco es muy bueno: con algo como
\mathrm{x}
el resultado es una larga serie de errores. E incluso si los arregla haciendo una larga lista de casos, algo como
\mathrm{abc}
se convertiría en
\exp_last_unbraced:Nf abc { \mode_if_math:TF { \mformat } { } }
que definitivamente no es lo que quieres ver.
Respuesta2
La fuente predeterminada para cada carácter en el modo matemático ya está declarada explícitamente, por lo que solo necesita ajustar las declaraciones para v. Aquí uso negrita romana (coincidente \mathbf
), aunque su imagen "esperada" estaba en cursiva.
\documentclass{article}
\DeclareSymbolFont{boldrm} {OT1}{cmr}{bx}{n}
\DeclareSymbolFontAlphabet{\mathbf} {boldrm}
\DeclareMathSymbol{v}{\mathalpha}{boldrm}{`v}
\begin{document}
$\vec{v} + \vec{w}$
\end{document}