user202729 already provided a piece of code where you can see what a solution could look like.
So let's focus on the question why your code doesn't work:
Some of xparse's argument specifiers indicate that pre-processing takes place with the argument.
Thus you need to distinguish the set of tokens that at the time of grabbing it from the token stream forms the argument from the set of tokens that forms the result of pre-processing the argument.
With an argument that is to be pre-processed right after grabbing it from the token-stream #n
(#n
being either #1
or #2
or #3
or #4
or #5
or #6
or #7
or #8
or #9
) of the corresponding \NewDocumentCommand
-definition's replacement-text does not refer to the set of tokens which forms that argument but does refer to the set of tokens which forms the result of pre-processing that argument.
The manual of the xparse package says about the t
-argument-specifier:
t
An optional ⟨token⟩, which will result in a value \BooleanTrue
if ⟨token⟩ is present and \BooleanFalse
otherwise. Given as t
⟨token⟩.
This means that the token forming a t
-type-argument is pre-processed after grabbing it from the token stream so that the corresponding #n
used in a \NewDocumentCommand
-definition's replacement-text (#1
or #2
or #3
or #4
or #5
or #6
or #7
or #8
or #9
) either is the token \BooleanTrue
or is the token \BooleanFalse
.
Thus t
-type-arguments can only be passed on as arguments to such other macros which "expect" the corresponding arguments to consist either of the token \BooleanTrue
or of the token \BooleanFalse
.
This is what user202729 did.
Your not-working attempt is:
\NewDocumentCommand\dsection{t^ t+ t! m}{%
\section{\decorate \IfBooleanT{#1}{#1}\IfBooleanT{#2}{#2} \IfBooleanT{#3}{#3}{#4}}
%\section{\decorate#1#2#3{#4}} % also does not work
%\section{\decorate \IfBooleanT{#1}{^}\IfBooleanT{#2}{+} \IfBooleanT{#3}{!}{#4}} % causes "missing $ inserted" error
% additional commands not related to \decorate
}
The attempt
\section{\decorate \IfBooleanT{#1}{#1}\IfBooleanT{#2}{#2} \IfBooleanT{#3}{#3}{#4}}
does not work out because neither the token ^
nor the token +
nor the token !
is passed to \decorate
so that optional arguments for \decorate
are not provided and the single token \IfBooleanT
following the token \decorate
is taken for \decorate
's m
-type-argument.
The attempt
\section{\decorate#1#2#3{#4}}
does not work out because instead of tokens ^
, +
, !
a combination of three tokens \BooleanTrue
/\BooleanFalse
coming from pre-processing \dsection
's three optional t
-type-arguments and denoted via #1#2#3
is placed behind the token \decorate
so that with \decorate
TeX does not find tokens ^
, +
, !
which might be taken for optional arguments but does find one of the tokens \BooleanTrue
/\BooleanFalse
which is taken for \decorate
's non-optional m
-type-argument.
The attempt
\section{\decorate \IfBooleanT{#1}{^}\IfBooleanT{#2}{+} \IfBooleanT{#3} {!}{#4}}
does not work out because \decorate
is not trailed by tokens ^
, +
, !
which might be taken for optional arguments but is trailed by the token \IfBooleanT
which is taken for \decorate
's non-optional m
-type-argument.
But this attempt seems very close to what you might need:
Instead of appending the tokens \IfBooleanT{#1}{^}\IfBooleanT{#2}{+}\IfBooleanT{#3}{!}
right behind the token \decorate
the result of fully expanding all these tokens needs to be appended right behind the token \decorate
.
How this can be done using expl3/LaTeX3's e
-expansion is shown at the end of this answer.
The gist of all this is:
With an xparse-argument, which according to the corresponding \NewDocumentCommand
-argument-specifier is pre-processed, #n
(#n
being either #1
or #2
or #3
or #4
or #5
or #6
or #7
or #8
or #9
) within the\NewDocumentCommand
-definition's replacement-text refers to that set of tokens which forms the result of pre-processing that argument.
Information about possible results of pre-processing xparse-arguments can be found in the manual of the package xparse.
If you like it complicated you can use expl3's e
-expansion for creating a sequence of ^
/+
/!
and appending that right behind the token \decorate
:
\documentclass{article}
\usepackage[normalem]{ulem}
\usepackage{xparse}
\newcommand\foo[1]{#1}% foo is abbreviation for "first of one".
\NewDocumentCommand\decorate{t^ t+ t! m}{%
\IfBooleanTF{#1}{\emph}{\foo}%
{%
\IfBooleanTF{#2}{\uline}{\foo}%
{%
\IfBooleanTF{#3}{\sout}{\foo}%
{#4}%
}%
}%
}
\ExplSyntaxOn
\NewDocumentCommand\dsection{t^ t+ t! m}{%
\section{\exp_last_unbraced:Ne \decorate {\IfBooleanT{#1}{^}\IfBooleanT{#2}{+}\IfBooleanT{#3}{!}} {#4}}%
% additional commands not related to \decorate and where #1, #2 and #3 each denote one of the tokens
% \BooleanTrue or \BooleanFalse and #4 denotes the m-type argument.
}
\ExplSyntaxOff
%% With a recent LaTeX release you don't need expl3-syntax but can use \ExpandArgs :
%\newcommand\tot[2]{#1#2}% tot is abbreviation for "two of two".
%\NewDocumentCommand\dsection{t^ t+ t! m}{%
% \section{\ExpandArgs{ne}\tot{\decorate}{\IfBooleanT{#1}{^}\IfBooleanT{#2}{+}\IfBooleanT{#3}{!}}{#4}}%
% % additional commands not related to \decorate and where #1, #2 and #3 each denote one of the tokens
% % \BooleanTrue or \BooleanFalse and #4 denotes the m-type argument.
%}
\begin{document}
\dsection{Section}
\decorate{Text}
\dsection^{Caret section}
\decorate^{Caret text}
\dsection+{Plus section}
\decorate+{plus text}
\dsection!{Bang section}
\decorate!{bang text}
\dsection^+!{Caret Plus Bang section}
\decorate^+!{caret plus bang text}
\end{document}

In case you wonder what \exp_last_unbraced:Ne
is about:
\exp_last_unbraced:Ne
takes two arguments:
With the first argument pre-processing is done according to the rules denoted by the N
-type-argument-specifier.
With the second argument pre-processing is done according to the rules denoted by the e
-type-argument-specifier.
The tokens which form the result of pre-processing the first argument (the N
-type-argument) are inserted into the token-stream, trailed by the tokens which form the result of pre-processing the second argument (the e
-type-argument).
The N
-type-specifier denotes that the argument is to consist of a single token which is to be inserted into the token stream without whatsoever further pre-processing.
The e
-type-specifier denotes that the argument may consist of several tokens and that \expanded{...}
is to be applied to the tokens forming the argument which in turn yields full expansion of the tokens forming the argument.
There are many commands like \exp_args:...
or \exp_last_unbraced:...
with very similar names where only argument-specifiers differ.
With all these commands with arguments (like N
-type) whose pre-processing-result is to consist of a single (control-sequence-)token the single token forming the pre-processing-result is appended to the token-stream without being nested between curly braces {...}
.
With arguments (like e
-type) whose pre-processing-result may consist of several tokens, the tokens forming the pre-processing-result usually are nested between a pair of matching curly braces {...}
at the time of being appended to the token stream.
last_unbraced
denotes that despite the convention of usually wrapping pre-processing-results which may consist of several tokens between a pair of matching curly braces {...}
, the result of pre-processing the last argument, i.e., the resul of pre-processing the e-type-argument, shall be appended to the token-stream without being nested between curly braces.