
Я пытаюсь создать команду, которая идет в \everymath
формате определенных указанных символов (автоматически вставляет v
и \mathbf
т. д.). Эту часть я уже понял, но что вызывает у меня проблемы, так это сама обработка. Похоже, она разделяет команды в математическом режиме, отображая их неправильно или просто полностью ломая.
Это минимальная версия, которая просто проходит и ничего не меняет (теоретически):
\ExplSyntaxOn
\NewDocumentCommand{\mformat}{+m}{%
\exp_after:wN #1 \mode_if_math:TF { \mformat } { }
}
\ExplSyntaxOff
Используется так:
\everymath{\mformat}
Вот несколько примеров того, как это происходит:
\(\vec{v}\)
\(\mathbf{v}\) % Just gives "Missing } inserted." error
Есть какие нибудь идеи как это починить?
Редактировать: Я заметил что-то новое; если я заключаю проблемные команды в скобки (например mathbf
, \({\mathbf{v}}\)
работает отлично), они внезапно работают идеально. Понятия не имею, почему так происходит.
Я отметил ответ egreg как решение, поскольку он ответил на вопрос, который я задал, но для потомков вот решение вопроса, который я должен был задать (на основе комментариев 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
Который используется как
\mformat{\mathbb[R]\mathbf[vw]}
решение1
Это очень похоже на проблему XY. Вы говорите
и ничего не меняет (теоретически)
Извините, это неправда. Первый уровень расширения
\mode_if_math:TF { \mformat } { }
является
\if_mode_math: \__prg_TF_true:w \fi: \use_ii:nn {\mformat}{}
Если у вас есть, $\vec{v}$
вы получаете
\vec\if_mode_math: \__prg_TF_true:w \fi: \use_ii:nn {\mformat}{}
и поэтому аргументом \vec
является \if_mode_math:
. Пример:
\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 file mform.aux.
> \if_mode_math: .
\vec #1->\showtokens {#1}
l.13 $\vec
Было бы лучше с\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}
где консоль печатает
> v.
\vec #1->\showtokens {#1}
Но это тоже не очень хорошо: с чем-то вроде
\mathrm{x}
результат — длинная череда ошибок. И даже если вы исправите их, сделав длинный список случаев, что-то вроде
\mathrm{abc}
превратился бы в
\exp_last_unbraced:Nf abc { \mode_if_math:TF { \mformat } { } }
что определенно не то, что вы хотели бы увидеть.
решение2
Шрифт по умолчанию для каждого символа в математическом режиме уже явно объявлен, поэтому вам просто нужно скорректировать объявления для v. Я использую здесь полужирный римский шрифт (соответствующий \mathbf
), хотя ваше «ожидаемое» изображение отображало курсив.
\documentclass{article}
\DeclareSymbolFont{boldrm} {OT1}{cmr}{bx}{n}
\DeclareSymbolFontAlphabet{\mathbf} {boldrm}
\DeclareMathSymbol{v}{\mathalpha}{boldrm}{`v}
\begin{document}
$\vec{v} + \vec{w}$
\end{document}