Os comandos quebram ao processar token por token

Os comandos quebram ao processar token por token

Estou tentando fazer um comando que formata \everymathdeterminados caracteres especificados (coloca automaticamente v, \mathbfetc.). Essa parte eu já descobri, mas o que está me dando problemas é o processamento em si. Parece estar separando comandos no modo matemático, exibindo incorretamente ou quebrando completamente.

Esta é uma versão mínima que apenas percorre e não muda nada (em teoria):

\ExplSyntaxOn
\NewDocumentCommand{\mformat}{+m}{%
  \exp_after:wN #1 \mode_if_math:TF { \mformat } { }
}
\ExplSyntaxOff

É usado assim:

\everymath{\mformat}

Aqui estão alguns exemplos de como ele quebra:

\(\vec{v}\)

Esperado:EsperadoVec

Real:ActualVec(equivalente a \vec{}v)

\(\mathbf{v}\) % Just gives "Missing } inserted." error

Alguma ideia de como consertar isso?

Editar: algo novo que notei; se eu colocar os comandos problemáticos entre colchetes (como with mathbf, \({\mathbf{v}}\)funciona bem), de repente eles funcionam perfeitamente. Não faço ideia por que isso acontece.


Marquei a resposta do egreg como a solução, pois ela respondeu à pergunta que fiz, mas para a posteridade aqui está a solução para a pergunta que eu deveria ter feito (com base nos comentários 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 é usado como

\mformat{\mathbb[R]\mathbf[vw]}

Responder1

Isso se parece muito com um problema XY. Você diz

e não muda nada (em teoria)

Desculpe, isso não é verdade. A expansão de primeiro nível

\mode_if_math:TF { \mformat } { }

é

\if_mode_math: \__prg_TF_true:w \fi: \use_ii:nn {\mformat}{}

Se você tiver $\vec{v}$você obtém

\vec\if_mode_math: \__prg_TF_true:w \fi: \use_ii:nn {\mformat}{}

e então o argumento para \vecé \if_mode_math:. Exemplo:

\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}

No console eu recebo

No file mform.aux.
> \if_mode_math: .
\vec #1->\showtokens {#1}

l.13 $\vec

Seria melhor com\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}

onde o console imprime

> v.
\vec #1->\showtokens {#1}

Mas isso também não é muito bom: com algo como

\mathrm{x}

o resultado é uma longa série de erros. E mesmo que você os conserte fazendo uma longa lista de casos, algo como

\mathrm{abc}

se transformaria em

\exp_last_unbraced:Nf abc { \mode_if_math:TF { \mformat } { } }

o que definitivamente não é o que você deseja ver.

Responder2

A fonte padrão para cada caractere no modo matemático já está explicitamente declarada, então você só precisa ajustar as declarações para v. Eu uso negrito romano aqui (matching \mathbf), embora sua imagem "esperada" estivesse em itálico.

insira a descrição da imagem aqui

\documentclass{article}

\DeclareSymbolFont{boldrm}     {OT1}{cmr}{bx}{n}
\DeclareSymbolFontAlphabet{\mathbf}   {boldrm}
\DeclareMathSymbol{v}{\mathalpha}{boldrm}{`v}

\begin{document}

$\vec{v} + \vec{w}$


\end{document}

informação relacionada