Your request subsumes two tasks:
- A routine for mapping a comma-list to a list of undelimited arguments where a macro-token is prepended for processing the delimited arguments.
- A routine
\enhance{\command}
which defines \commandEnhanced
to apply the mapping-routine and hereby having the token \command
prepended to the list of undelimited arguments which comes into being by the mapping.
Let's look at the first task:
In the example below the routine for mapping a comma-list to a list of undelimited arguments is called \PassCommaListAsUndelimitedListTo
.
\PassCommaListAsUndelimitedListTo{⟨comma list⟩}{⟨code⟩}
turns the ⟨comma list⟩ into a list of undelimited arguments where ⟨code⟩ is prepended.
E.g., \PassCommaListAsUndelimitedListTo{A, B, C}{⟨code⟩}
yields ⟨code⟩{A}{B}{C}
.
The code for \PassCommaListAsUndelimitedListTo
is inspired by the function \clist_map_tokens:nn
from expl3/LaTeX3 release 2021-05-05 or newer.
\clist_map_tokens:nn {⟨comma list⟩} {⟨code⟩}
calls ⟨code⟩{⟨item⟩}
for every ⟨item⟩ stored in the ⟨comma list⟩.
If an ⟨item⟩ of the ⟨comma list⟩ can be considered a set of tokens that is nested into a pair of matching curly braces, that outermost pair of surrounding matching curly braces will be stripped off.
\clist_map_tokens:nn
ignores/discards/does not map empty ⟨item⟩s of the ⟨comma list⟩.
You can use something like \clist_map_tokens:nn
for creating an expandable "mechanism"
\PassCommaListAsUndelimitedListTo{⟨comma list⟩}{⟨code⟩}
which turns a ⟨comma list⟩ into a list of undelimited arguments where ⟨code⟩ is prepended.
"Something like" because the circumstance of \clist_map_tokens:nn
ignoring/discarding/not mapping empty ⟨item⟩s of the ⟨comma list⟩ might be a bit counter-intuitive—e.g., \PassCommaListAsUndelimitedListTo {A, , C}{\macro}
would not yield \macro{A}{}{C}
but would yield \macro{A}{C}
if \clist_map_tokens:nn
was used.
You can can easily derive a function from the definition of \clist_map_tokens:nn
which does not ignore/discard empty ⟨item⟩s of the ⟨comma list⟩ by omitting the check for emptiness/blankness which is in \clist_map_tokens:nn
's definition—if you do that, you need to be picky about the list having a trailing comma denoting an empty item behind that comma:
\ExpöSyntaxOn
\cs_new:Npn \PassCommaListAsUndelimitedList_clist_map_tokens:nn #1#2
{
\__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw {#2}
\prg_do_nothing: #1 , \s__clist_stop \clist_map_break: ,
\prg_break_point:Nn \clist_map_break: { }
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw #1#2 ,
{
\__clist_use_none_delimit_by_s_stop:w #2 \s__clist_stop
\tl_trim_spaces_apply:oN {#2} \use_ii_i:nn
\__clist_map_unbrace:wn , {#1}
\__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw {#1} \prg_do_nothing:
}
\ExplSyntaxOff
The function
\PassCommaListAsUndelimitedList_clist_map_tokens:nn {⟨comma list⟩} {⟨code⟩}
calls ⟨code⟩{⟨item⟩}
for every ⟨item⟩ stored in the ⟨comma list⟩.
Unlike the function \clist_map_tokens:nn
the function \PassCommaListAsUndelimitedList_clist_map_tokens:nn
does not discard/ignore empty comma-list items but maps them to undelimited empty arguments.
Using \PassCommaListAsUndelimitedList_clist_map_tokens:nn
you can define \PassCommaListAsUndelimitedListTo
as follows:
As \PassCommaListAsUndelimitedList_clist_map_tokens:nn
's ⟨code⟩-argument use a macro/function \__PassCommaListAsUndelimitedList_grab_next:nwn
which grabs several arguments:
- the tokens forming the comma-list-item of the current iteration (n-type-argument)
- the tokens forming the next
\PassCommaListAsUndelimitedList_clist_map_tokens:nn
-iteration as a delimited argument (w-type-argument) where the delimiter \__PassCommaListAsUndelimitedList_Reserved:n
is trailed by
- an argument holding the macro-token/code that shall be prepended to the undelimited arguments and the undelimited arguments gathered so far (n-type-argument).
This macro/function reinserts the tokens forming the next \PassCommaListAsUndelimitedList_clist_map_tokens:nn
-iteration, the \__PassCommaListAsUndelimitedList_Reserved:n
-delimiter and the argument holding the macro-token/code that shall be prepended to the undelimited arguments and the undelimited arguments gathered so far with the tokens forming the comma-list-item of the current iteration appended.
This way the combination of \PassCommaListAsUndelimitedList_clist_map_tokens:nn
and \__PassCommaListAsUndelimitedList_grab_next:nwn
iteratively accumulates the comma-list-items within the undelimited argument behind the delimiter \__PassCommaListAsUndelimitedList_Reserved:n
.
When iterating is done, expanding \__PassCommaListAsUndelimitedList_Reserved:n
removes the braces that surround that undelimited argument.
\ExplSyntaxOn
\cs_new:Npn \__PassCommaListAsUndelimitedList_grab_next:nwn #1#2\__PassCommaListAsUndelimitedList_Reserved:n #3 {
% #1 - current comma-list-item
% #2 - tokens forming the next `\clist_map_tokens:nn`-iteration
% #3 - code that shall prepend undelimited arguments and undelimited arguments gathered so far
#2 \__PassCommaListAsUndelimitedList_Reserved:n {#3{#1}}
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_Reserved:n #1 {#1}
\cs_new:Npn \PassCommaListAsUndelimitedListTo #1#2 {
\PassCommaListAsUndelimitedList_clist_map_tokens:nn {#1} {\__PassCommaListAsUndelimitedList_grab_next:nwn}
\__PassCommaListAsUndelimitedList_Reserved:n {#2}
}
\ExplSyntaxOff
With this code you can do, e.g.,
\PassCommaListAsUndelimitedListTo{A, B, C}{\macro}
for obtaining \macro{A}{B}{C}
.
With this code you can do, e.g.,
\PassCommaListAsUndelimitedListTo{A, , C}{\macro}
for obtaining \macro{A}{}{C}
.
With this code you can do, e.g.,
\PassCommaListAsUndelimitedListTo{A, B, }{\macro}
for obtaining \macro{A}{B}{}
.
(You need to be picky about the list having a trailing comma denoting an empty item behind that comma.)
Let's look at the second task:
You can use \cs_to_str:N
for obtaining the name of a control-sequence-token without leading backslash/escape-character. (As a special case with the nameless control-sequence-token, obtainable via \csname\endcsname
or via having at the end of a line of .tex-input while the integer-parameter \endlinechar
has a negative value, you get
csname⟨current escape-char⟩endcsname
.)
Thus you can define \enhance
in terms of \cs_to_str:N
and \PassCommaListAsUndelimitedListTo
:
\ExplSyntaxOn
\cs_new:Npn \enhance #1 {
\cs_new:cpn {\cs_to_str #1 Enhanced} ##1 {
\PassCommaListAsUndelimitedListTo { ##1 }{ #1 }
}
}
\ExplSyntaxOff
Putting the pieces together you get:
\documentclass{article}
% \PassCommaListAsUndelimitedList_clist_map_tokens:nn {⟨comma list⟩} {⟨code⟩}
% Calls ⟨code⟩ {⟨item⟩} for every ⟨item⟩ stored in the ⟨comma list⟩.
% Does - unlike \clist_map_tokens:nn - not discard empty comma-list items.
\ExplSyntaxOn
\cs_new:Npn \__PassCommaListAsUndelimitedList_grab_next:nwn #1#2\__PassCommaListAsUndelimitedList_Reserved:n #3 {
% #1 - current comma-list-item
% #2 - tokens forming the next `\clist_map_tokens:nn`-iteration
% #3 - code that shall prepend undelimited arguments and undelimited arguments gathered so far
#2 \__PassCommaListAsUndelimitedList_Reserved:n {#3{#1}}
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_Reserved:n #1 {#1}
\cs_new:Npn \PassCommaListAsUndelimitedListTo #1#2 {
\PassCommaListAsUndelimitedList_clist_map_tokens:nn {#1} {\__PassCommaListAsUndelimitedList_grab_next:nwn}
\__PassCommaListAsUndelimitedList_Reserved:n {#2}
}
\cs_new:Npn \enhance #1 {
\cs_new:cpn {\cs_to_str:N #1 Enhanced} ##1 {
\PassCommaListAsUndelimitedListTo { ##1 }{ #1 }
}
}
\cs_new:Npn \PassCommaListAsUndelimitedList_clist_map_tokens:nn #1#2
{
\__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw {#2}
\prg_do_nothing: #1 , \s__clist_stop \clist_map_break: ,
\prg_break_point:Nn \clist_map_break: { }
}
\cs_new:Npn \__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw #1#2 ,
{
\__clist_use_none_delimit_by_s_stop:w #2 \s__clist_stop
\tl_trim_spaces_apply:oN {#2} \use_ii_i:nn
\__clist_map_unbrace:wn , {#1}
\__PassCommaListAsUndelimitedList_clist_map_tokens_n:nw {#1} \prg_do_nothing:
}
%\cs_new:Npn \__clist_map_unbrace:wn #1, #2 { #2 {#1} }
\ExplSyntaxOff
\newcommand\macro[3]{%
{\frenchspacing\textnormal{\detokenize{Arg 1: (#1) Arg 2: (#2) Arg 3: (#3)}}}%
}%
\enhance{\macro}
\begin{document}
\noindent \verb|\macroEnhanced{A, B, C}| yields\\
\verb|\PassCommaListAsUndelimitedListTo{A, B, C}{\macro}|, which in turn yields\\
\verb|\macro{A}{B}{C}|.
\smallskip
\noindent Thus the following three commands in the end yield the same:
\smallskip
\noindent \verb|\macroEnhanced{A, B, C}|:\\
\macroEnhanced{A, B, C}
\smallskip
\noindent \verb|\PassCommaListAsUndelimitedListTo {A, B, C}{\macro}|:\\
\PassCommaListAsUndelimitedListTo {A, B, C}{\macro}
\smallskip
\noindent \verb|\macro{A}{B}{C}|:\\
\macro{A}{B}{C}.
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A, , C}| yields\\
\verb|\PassCommaListAsUndelimitedListTo{A, , C}{\macro}|, which in turn yields\\
\verb|\macro{A}{}{C}|.
\smallskip
\noindent Thus the following three commands in the end yield the same:
\smallskip
\noindent \verb|\macroEnhanced{A, , C}|:\\
\macroEnhanced{A, , C}
\smallskip
\noindent \verb|\PassCommaListAsUndelimitedListTo {A, , C}{\macro}|:\\
\PassCommaListAsUndelimitedListTo {A, , C}{\macro}
\smallskip
\noindent \verb|\macro{A}{}{C}|:\\
\macro{A}{}{C}.
\bigskip\hrule\bigskip
\noindent \verb|\macroEnhanced{A, , }| yields\\
\verb|\PassCommaListAsUndelimitedListTo{A, , }{\macro}|, which in turn yields\\
\verb|\macro{A}{}{}|.
\smallskip
\noindent Thus the following three commands in the end yield the same:
\smallskip
\noindent \verb|\macroEnhanced{A, , }|:\\
\macroEnhanced{A, , }
\smallskip
\noindent \verb|\PassCommaListAsUndelimitedListTo {A, , }{\macro}|:\\
\PassCommaListAsUndelimitedListTo {A, , }{\macro}
\smallskip
\noindent \verb|\macro{A}{}{}|:\\
\macro{A}{}{}.
\end{document}

\command
has been called with several arguments, that' s a bad idea.\textbf{Foo}{bar}
is perfectly legitimate TeX syntax.bar
is simply inside a brace group. This plus the fact that in your question, there is a\command
with two different syntaxes...\NewDocumentCommand
with\SplitArgument
? tex.stackexchange.com/a/605337/263192}{
into,
. ... "command" has multiple meanings: control sequence, cs name, definition, syntax, meaning, argument specification, output or result; in sequence or in parallel?; across or within groups?; recursive?; ...