3

I have a basic command I created, aimed to simply bold and move a text to gain time :

\newcommand*\Cote[1]{\footnotesize\emph{\textbf{#1}}}

When I want this result, I still have to use this command \Cote{My text}. I would like to automatize this, because the text I want to replace has always the same structure, which is to know a capital D with numbers next to it : D3 or D89 or D1023.

So I want a new command, which applies only when a number follows my letter D. Like this :

\newcommand*\D[1]{\footnotesize\emph{\textbf{#1}}}

but only when a number follows. Otherwise lots of my words would be replace and it would be a mess.

I tried to use a \ifnum0 command inside \newcommand, but unsuccessfully. Same failure trying to ferret around in other topics, like Conditionally replacing sequences of characters Any ideas ?

I use LuaLatex. A big thanks in advance !

3
  • 1
    So what would the output look like if you have \D{1234} and \D{abcd}? Wouldn't you just use \D with numbers necessarily? Regardless, if you're using LuaLaTeX, you don't even have to use \D{<num>} as you should be able to intercept the input sequence and replace any occurrence of D<num> with D<num> (in bold).
    – Werner
    Commented Jul 28, 2021 at 20:58
  • LaTeX3 allows working with regex. Here it would be an easy way to achieve your goal. Take a look at this answer.
    – projetmbc
    Commented Jul 29, 2021 at 23:30
  • 1
    You wish to have a macro. At the time of expanding/carrying out a macro, everything is about tokens in TeX, not about numbers. What sets of tokens are to follow the explicit character-token D of catcode 11(letter) in cases where the formatting is to be applied to the D of catcode 11 and these following tokens? What about D\seven0 while \def\seven{7} is in effect? What about D\number`\A-- `\A is an alphabetic constant and therefore a valid TeX-<number>-quantity denoting 65(dec)? Commented Jul 30, 2021 at 14:26

3 Answers 3

3

This tokencycle seems to do the job. As you can see, intervening macros and groups in the input stream have no ill effects on the result.

\documentclass{article}
\usepackage{tokcycle,xcolor}
\Characterdirective{\ifx D#1\Cotetest{D}\else\addcytoks{#1}\fi}
\newcommand\Cotetest[1]{\tcpeek\z
  \ifx0\z\Cotetrue\else
  \ifx1\z\Cotetrue\else
  \ifx2\z\Cotetrue\else
  \ifx3\z\Cotetrue\else
  \ifx4\z\Cotetrue\else
  \ifx5\z\Cotetrue\else
  \ifx6\z\Cotetrue\else
  \ifx7\z\Cotetrue\else
  \ifx8\z\Cotetrue\else
  \ifx9\z\Cotetrue\else\addcytoks{#1}\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
\newcommand\Cotetrue{%
  \tcpop\z
  \expandafter\Cote\expandafter{\z}%
  \Cotetest{}}
\def\Cote#1{\addcytoks{\bgroup\footnotesize\emph{\textbf{#1}}\egroup}}
\begin{document}
\tokencyclexpress
D7C2
D93
\textcolor{red}{D123abc}
Dcf
D 
D\today
\endtokencyclexpress
\end{document}

enter image description here

SUPPLEMENT

In response to an OP comment, desiring to retain the "D" in the altered font, in the event of subsequent digits, I might do it this way:

\documentclass{article}
\usepackage{tokcycle,xcolor}
\Characterdirective{\ifx D#1\Cotetest{D}\else\addcytoks{#1}\fi}
\newcommand\Cotetest[1]{\tcpeek\z
  \ifx0\z\Cotetrue{#1}\else
  \ifx1\z\Cotetrue{#1}\else
  \ifx2\z\Cotetrue{#1}\else
  \ifx3\z\Cotetrue{#1}\else
  \ifx4\z\Cotetrue{#1}\else
  \ifx5\z\Cotetrue{#1}\else
  \ifx6\z\Cotetrue{#1}\else
  \ifx7\z\Cotetrue{#1}\else
  \ifx8\z\Cotetrue{#1}\else
  \ifx9\z\Cotetrue{#1}\else\addcytoks{#1}\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
\newcommand\Cotetrue[1]{%
  \tcpop\z
  \expandafter\Cote\expandafter{\expandafter#1\z}%
  \Cotetest{\empty}}
\def\Cote#1{\addcytoks{\bgroup\footnotesize\emph{\textbf{#1}}\egroup}}
\begin{document}
\tokencyclexpress
D7C2
D93
\textcolor{red}{D123abc}
Dcf
D 
D\today
\endtokencyclexpress
\end{document}

enter image description here

5
  • Looks like it answers quite perfectly! Thank you, I wouldn't have achieved this on my own. I'd like the same, with the "D" still displayed (I only want the font settings to be changed). Could it be done with this command? : \def\Cote#1{\addcytoks{\bgroupD\footnotesize\emph{\textbf{#1}}\egroup}}
    – Gaspalet
    Commented Jul 31, 2021 at 11:42
  • 1
    I'm discovering the tokcycle package, it's incredible.
    – Gaspalet
    Commented Jul 31, 2021 at 11:47
  • @Gaspalet \def\Cote#1{\addcytoks{\bgroup D\footnotesize\emph{\textbf{#1}}\egroup}} will insert D in front of each digit of the number. That D will not be formatted like the digits of the number. Commented Jul 31, 2021 at 18:06
  • @Gaspalet Thank you for the compliment. I have added a supplement to my answer, which I think gives you what you desire. Commented Jul 31, 2021 at 18:14
  • @Gaspalet Make sure you check out the "Examples" documentation that shows various usages. ctan.org/pkg/tokcycle Commented Jul 31, 2021 at 18:22
3

Here is a routine

\SurroundDDigits{⟨formatting command which takes one undelimited argument⟩}{⟨tokens⟩}

which in a tail-recursive loop examines ⟨tokens⟩ without expanding them. Tail-recursion/expansion is driven by \romannumeral until all tokens are examined. When all tokens are examined, then a TeX-⟨number⟩-quantity of value 0 trailed by the tokens forming by the result is delivered, yielding that TeX, due to encountering that ⟨number⟩-quantity, stops gathering a ⟨number⟩ for \romannumeral and for the gathered ⟨number⟩-quantity of value 0 delivers no token in return as 0 is not a positive number.

Sequences of pattern
D11 + non-empty sequence of tokens that are elements of {012, 112, 212, 312, 412, 512, 612, 712, 812, 912}
in the result are nested between curly braces ({1 and }2) whereof the left curly brace is preceded by ⟨formatting command which takes one undelimited argument⟩.

As a side-effect in the result

  • any explicit character-token of category code 1(begin-group) is replaced by an explicit { of category-code 1 and
  • any explicit character-token of category code 2(end-group) is replaced by an explicit } of category-code 2.

Due to \romannumeral-expansion the result is delivered by triggering two expansion-steps/after two "hits" with \expandafter.

I did my best to ensure that expansion will not be disturbed within alignments/tables etc, but no warranties.

For the sake of having fun I did not use any ε-TeX- or LuaTeX- or pdfTeX-extensions or the like and I avoided any \if...\else...\fi- and any \csname..\endcsname-expression.
Thus (unbalanced parts of) such expressions occurring within ⟨tokens⟩ does not disturb expansion.

Exclamation-mark as argument-delimiter is used a lot in one place.
This might probably bite you in case the uppercase code or lowercase code of the exclamation-mark is changed and the course of expansion-action is intercepted right at the corresponding stage for applying \uppercase/\lowercase. Due to expansion being driven via \romannumeral until obtaining the result this is extremely unlikely to happen if possible at all.

Usage is at your own risk.

\errorcontextlines=10000
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@removespace, \UD@stopromannumeral, \UD@CheckWhetherNull,
%% \UD@CheckWhetherBrace, \UD@CheckWhetherLeadingExplicitSpace,
%% \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@removespace{\UD@Exchange{ }{\def\UD@removespace}{}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}{}}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \CheckWhetherBrace{<Argument which is to be checked>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked has a leading
%%                     explicit catcode-1-character-token>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked does not have a
%%                     leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does have a
%%                                       leading explicit space-token>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does not have a
%%                                       a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {{AB}}
%%
%% Due to \romannumeral-expansion the result is delivered after two 
%% expansion-steps/after "hitting" \UD@ExtractFirstArg with \expandafter
%% twice.
%%
%% \UD@ExtractFirstArg's argument must not be blank.
%% This case can be cranked out via \UD@CheckWhetherBlank before calling
%% \UD@ExtractFirstArg.
%%
%% Use frozen-\relax as delimiter for speeding things up.
%% I chose frozen-\relax because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%.............................................................................
\@ifdefinable\UD@RemoveTillFrozenrelax{%
  \expandafter\expandafter\expandafter\UD@Exchange
  \expandafter\expandafter\expandafter{%
  \expandafter\expandafter\ifnum0=0\fi}%
  {\long\def\UD@RemoveTillFrozenrelax#1#2}{{#1}}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
  \romannumeral\expandafter
  \UD@PassFirstToSecond\expandafter{\romannumeral
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral#1}%
  }{%
    \UD@stopromannumeral\romannumeral\UD@ExtractFirstArgLoop
  }%
}{%
  \newcommand\UD@ExtractFirstArg[1]%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@stopromannumeral#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%====================================================================
\@ifdefinable\UD@gobbletoexclam{\long\def\UD@gobbletoexclam#1!{}}%
\@ifdefinable\UD@ChooseDDigit{%
  \long\def\UD@ChooseDDigit#1!1!2!3!4!5!6!7!8!9!0!D!#2#3!!!!{#2}%
}%
\newcommand\UD@CollectDDigit[5]{%
  % #1 token to examine
  % #2 pending D
  % #3 digits gathered as probable argument of formatting-command
  % #4 formatting-command, takes 1 undelimited argument
  % #5 tokens forming the result gathered so far
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoexclam#1!}%
  {%
    \UD@ChooseDDigit
    !#1!2!3!4!5!6!7!8!9!0!D!{\UD@firstoftwo}% digit 1
    !1!#1!3!4!5!6!7!8!9!0!D!{\UD@firstoftwo}% digit 2
    !1!2!#1!4!5!6!7!8!9!0!D!{\UD@firstoftwo}% digit 3
    !1!2!3!#1!5!6!7!8!9!0!D!{\UD@firstoftwo}% digit 4
    !1!2!3!4!#1!6!7!8!9!0!D!{\UD@firstoftwo}% digit 5
    !1!2!3!4!5!#1!7!8!9!0!D!{\UD@firstoftwo}% digit 6
    !1!2!3!4!5!6!#1!8!9!0!D!{\UD@firstoftwo}% digit 7
    !1!2!3!4!5!6!7!#1!9!0!D!{\UD@firstoftwo}% digit 8
    !1!2!3!4!5!6!7!8!#1!0!D!{\UD@firstoftwo}% digit 9
    !1!2!3!4!5!6!7!8!9!#1!D!{\UD@firstoftwo}% digit 0
    !1!2!3!4!5!6!7!8!9!0!#1!{\expandafter\UD@firstoftwo\UD@secondoftwo}% character D
    !1!2!3!4!5!6!7!8!9!0!D!{\expandafter\UD@secondoftwo\UD@secondoftwo}!!!!%neither D nor digit
  }{\expandafter\UD@secondoftwo\UD@secondoftwo}%%neither D nor digit
  {% digit
    \UD@CheckWhetherNull{#2}{%
      \UD@stopromannumeral\UD@DDigitReplaceloop{}{}{#4}{#5#1}%
    }{%
      \UD@stopromannumeral\UD@DDigitReplaceloop{#2}{#3#1}{#4}{#5}%
    }%
  }{%
    {% character D
      \UD@CheckWhetherNull{#2}{%
        \UD@PassFirstToSecond{#5}%
      }{%
        \UD@CheckWhetherNull{#3}{%
          \UD@PassFirstToSecond{#5#2}%
        }{%
          \UD@PassFirstToSecond{#5#4{#2#3}}%
        }%
      }%
      {\UD@stopromannumeral\UD@DDigitReplaceloop{#1}{}{#4}}%
    }{% neither D nor digit
      \UD@CheckWhetherNull{#2}{%
        \UD@PassFirstToSecond{#5#1}%
      }{%
        \UD@CheckWhetherNull{#3}{%
          \UD@PassFirstToSecond{#5#2#1}%
        }{%
          \UD@PassFirstToSecond{#5#4{#2#3}#1}%
        }%
      }%
      {\UD@stopromannumeral\UD@DDigitReplaceloop{}{}{#4}}%
    }%
  }%
}%
\newcommand\UD@DDigitReplaceloop[5]{%
  % #1 pending D
  % #2 digits gathered as probable argument of formatting-command
  % #3 formatting-command, takes 1 undelimited argument
  % #4 tokens forming the result gathered so far
  % #5 remaining token list to process
  \UD@CheckWhetherNull{#5}{%
    \UD@CheckWhetherNull{#1}{\UD@stopromannumeral#4#2}{%
      \UD@CheckWhetherNull{#2}{\UD@stopromannumeral#4#1}{\UD@stopromannumeral#4#3{#1#2}}%
    }%
  }{%
    \UD@CheckWhetherBrace{#5}{%
      \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#5}{%
        \expandafter\UD@PassFirstToSecond\expandafter{%
          \romannumeral
          \UD@CheckWhetherNull{#1}{%
            \UD@PassFirstToSecond{\UD@stopromannumeral#4#2}%
          }{%
            \UD@CheckWhetherNull{#2}{%
              \UD@PassFirstToSecond{\UD@stopromannumeral#4#1}%
            }{%
              \UD@PassFirstToSecond{\UD@stopromannumeral#4#3{#1#2}}%
            }%
          }%
          {%
            \expandafter\UD@PassFirstToSecond
            \expandafter{%
              \romannumeral
              \expandafter\expandafter\expandafter\UD@PassFirstToSecond
              \UD@ExtractFirstArg{#5}{\UD@DDigitReplaceloop{}{}{#3}{}}%
            }%
          }%
        }{%
          \UD@DDigitReplaceloop{}{}{#3}%
        }%
      }%
    }{%
      \UD@CheckWhetherLeadingExplicitSpace{#5}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#5}{%
          \UD@CheckWhetherNull{#1}{%
             \UD@PassFirstToSecond{#4 }%
          }{%
            \UD@CheckWhetherNull{#2}{%
              \UD@PassFirstToSecond{#4#1 }%
            }{%
              \UD@PassFirstToSecond{#4#3{#1#2} }%
            }%
          }%
          {\UD@DDigitReplaceloop{}{}{#3}}%
        }%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#5}{%
          \expandafter\UD@firstoftwo\expandafter{%
            \romannumeral
            \expandafter\expandafter\expandafter
            \UD@CollectDDigit\UD@ExtractFirstArg{#5}{#1}{#2}{#3}{#4}%
          }{}%
        }%
      }%
    }%
  }%
}%
\newcommand\SurroundDDigits[2]{%
  \romannumeral\UD@DDigitReplaceloop{}{}{#1}{}{#2}%
}%
\makeatother


\documentclass{article}

\begin{document}

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \SurroundDDigits{\textbf}{%
    Axfd D12FF 23{AxfdD12FF2 3{Axf dD12FF23{}f\relax 
    D114 6ffasdfa}f\relax D1146ff asdfa}f\relax 
    D1146ffa sdfa%
  }%
}%
{\ttfamily \meaning\test}
\end{verbatim}

\noindent yields:
\vskip\partopsep\vskip\topsep

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \SurroundDDigits{\textbf}{%
    Axfd D12FF 23{AxfdD12FF2 3{Axf dD12FF23{}f\relax D114 6ffasdfa}f\relax D1146ff asdfa}f\relax D1146ffa sdfa%
  }%
}%
{\ttfamily \meaning\test}

\vskip\partopsep\vskip\topsep
\noindent\hrule
\vskip\partopsep\vskip\topsep

\begin{verbatim}
\newcommand\MyFontCommand[1]{{\footnotesize\emph{\textbf{#1}}}}%
\SurroundDDigits{\MyFontCommand}{%
  Axfd D12FF 23{AxfdD12FF2 3{Axf dD12FF23{}f\relax 
  D114 6ffasdfa}f\relax D1146ff asdfa}f\relax 
  D1146ffa sdfa%
}%
\end{verbatim}

\noindent yields:
\vskip\partopsep\vskip\topsep

\newcommand\MyFontCommand[1]{{\footnotesize\emph{\textbf{#1}}}}%
\SurroundDDigits{\MyFontCommand}{%
  Axfd D12FF 23{AxfdD12FF2 3{Axf dD12FF23{}f\relax 
  D114 6ffasdfa}f\relax D1146ff asdfa}f\relax 
  D1146ffa sdfa%
}%

\end{document}

enter image description here

0
2

Using Steven B. Segletes' idea of initiating a \Cotetest-\Cotetrue-loop in case of encountering a character D, you can do something like this which does not apply the formatting to each character of D<decimal digits>-sequences but does apply the formatting after gathering the entire sequence:

\documentclass{article}
\usepackage{tokcycle,xcolor}
\Characterdirective{\ifx D#1\Cotetest{D}{0}\else\addcytoks{#1}\fi}
\newcommand\Cotetest[2]{%
  % #1 - character-token D + digits gathered so far for formatting
  % #2 - flag: = 0 -> no digits are gathered yet / 
  %            =/= 0 -> some digits were gathered in previous iterations
  \tcpeek\z
  \ifx0\z\Cotetrue{#1}\else
  \ifx1\z\Cotetrue{#1}\else
  \ifx2\z\Cotetrue{#1}\else
  \ifx3\z\Cotetrue{#1}\else
  \ifx4\z\Cotetrue{#1}\else
  \ifx5\z\Cotetrue{#1}\else
  \ifx6\z\Cotetrue{#1}\else
  \ifx7\z\Cotetrue{#1}\else
  \ifx8\z\Cotetrue{#1}\else
  \ifx9\z\Cotetrue{#1}\else
  \ifnum#2=0 \addcytoks{#1}\else\addcytoks{\bgroup\footnotesize\emph{\textbf{#1}}\egroup}\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
\newcommand\Cotetrue[1]{%
  % #1 character-token D + digits gathered so far for formatting
  \tcpop\z
  \expandafter\expandafter\expandafter\Cotetest
  \expandafter\expandafter\expandafter{\expandafter\exchange\expandafter{\z}{#1}}{1}%
}%
\newcommand\exchange[2]{#2#1}
% Let \one be an implicit character token denoting 1 of catcode 12(other):
\let\one=1
\begin{document}
\tokencyclexpress
D\one7
D7\one1C2
D93
\textcolor{red}{D123abc}
Dcf
D 
D\today
\endtokencyclexpress
\end{document}

enter image description here

A variant which cranks out implicit characters as something that is not considered a digit of a number:

\documentclass{article}
\usepackage{tokcycle,xcolor}
\Characterdirective{\ifx D#1\Cotetest{D}{0}\else\addcytoks{#1}\fi}
\newcommand\Cotetest[2]{%
  % #1 - character-token D + digits gathered so far for formatting
  % #2 - flag: = 0 -> no digits are gathered yet / 
  %            =/= 0 -> some digits were gathered in previous 
  \tcpeek\z
  \ifx0\z\Cotetrue{#1}{#2}{0}\else
  \ifx1\z\Cotetrue{#1}{#2}{1}\else
  \ifx2\z\Cotetrue{#1}{#2}{2}\else
  \ifx3\z\Cotetrue{#1}{#2}{3}\else
  \ifx4\z\Cotetrue{#1}{#2}{4}\else
  \ifx5\z\Cotetrue{#1}{#2}{5}\else
  \ifx6\z\Cotetrue{#1}{#2}{6}\else
  \ifx7\z\Cotetrue{#1}{#2}{7}\else
  \ifx8\z\Cotetrue{#1}{#2}{8}\else
  \ifx9\z\Cotetrue{#1}{#2}{9}\else
  \ifnum#2=0 \addcytoks{#1}\else\addcytoks{\bgroup\footnotesize\emph{\textbf{#1}}\egroup}\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
\newcommand\Cotetrue[3]{%
  % #1 - character-token D + digits gathered so far for formatting
  % #2 - flag: = 0 -> no digits are gathered yet / 
  %            =/= 0 -> some digits were gathered in previous
  % #3 - explicit digit character-token to compare for cranking out
  %      implicit character tokens
  \tcpop\z
  \begingroup
  \def\tempa{#3}%
  \expandafter\endgroup\csname @\ifx\tempa\z first\else second\fi oftwo\endcsname
  {%
    \expandafter\expandafter\expandafter\Cotetest
    \expandafter\expandafter\expandafter{\expandafter\exchange\expandafter{\z}{#1}}{1}%
  }{%
   \ifnum#2=0 
      \addcytoks[2]{\expandafter\exchange\expandafter{\z}{#1}}%
   \else
      \addcytoks[2]{\expandafter\exchange\expandafter{\z}{\bgroup\footnotesize\emph{\textbf{#1}}\egroup}}%
   \fi
  }%
}%
\newcommand\exchange[2]{#2#1}
% Let \one be an implicit character token denoting 1 of catcode 12(other):
\let\one=1
\begin{document}
\tokencyclexpress
D\one7
D7\one1C2
D93
\textcolor{red}{D123abc}
Dcf
D 
D\today
\endtokencyclexpress
\end{document}

enter image description here

4
  • 2
    Note that \expandafter\expandafter\expandafter\addcytoks\expandafter\expandafter\expandafter{\expandafter\exchange\expandafter{\z}{#1}} can be more concisely written as \addcytoks[2]{\expandafter\exchange\expandafter{\z}{#1}}. The optional argument [n] to \addcytoks performs n expansions to the mandatory argument, before placing the result in the \cytoks token list. Commented Jul 31, 2021 at 20:26
  • I would upvote again, if I could Commented Jul 31, 2021 at 20:31
  • 2
    I'm just happy to have people learning and using the tokcycle package. I think it is a versatile tool for building various user applications. Commented Jul 31, 2021 at 20:37
  • A big thanks to both of you ! This tokcycle writing is new to me, so I'm going to take a few days to digest it, be it sounds promising. I am really gratefull.
    – Gaspalet
    Commented Aug 1, 2021 at 9:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .