6

I want to write some macro that replaces all digits in its argument by previously defined symbols or macros.

I envision something like

\overrideDigits{1234567890}

which should expand to something like

\one\two\three\four\five\six\seven\eight\nine\zero

to be able to define what the output is by overriding these macros. Or an even more advanced version where

\overrideDigits{1234567890 + \otherMacro}

should result in

    \one\two\three\four\five\six\seven\eight\nine\zero + \otherMacro

I thought the easiest solution (for the simpler version) would be to override the catcodes of the digit in the macro. However, all my tries so far ended in the error message

 Missing control sequence inserted.
<inserted text> 
                \inaccessible
l.59 \overrideDigits{1234567890}

?

I already "improved" from the trivial try to this

\def\overrideDigits#1{%
  \begingroup
  \catcode`0=\active
  \def0{\zero}
  % and so on for the other digits
  \scantokens\expandafter{#1\empty}%
  \endgroup}

which I have adapted from this answer.

  • What am I doing wrong?
  • Is it impossible to \def digits?
  • Is there a better way to achieve this?

2 Answers2

10

Changing category codes is the worst method, in my opinion.

Here's a simple implementation with xparse:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\overrideDigits}{m}
 {
  \tl_map_inline:nn { #1 }
   {
    \nobody_digit:n { ##1 }
   }
 }

\cs_new:Npn \nobody_digit:n #1
 {
  \str_case:nn { #1 }
   {
    {0}{Zero}
    {1}{One}
    {2}{Two}
    {3}{Three}
    {4}{Four}
    {5}{Five}
    {6}{Six}
    {7}{Seven}
    {8}{Eight}
    {9}{Nine}
   }
 }
\ExplSyntaxOff

\begin{document}

\overrideDigits{12345}

\overrideDigits{67890}

\end{document}

The input is mapped character by character and each digit is substituted with whatever you choose (recall that spaces need to be input as ~ in the body of the definition for \nobody_digit:n.

enter image description here

A “classical” implementation:

\documentclass{article}

\makeatletter
\newcommand{\overrideDigits}[1]{%
  \@tfor\next:=#1\do{\csname digit\next subs\endcsname}%
}
\@namedef{digit0subs}{Zero}
\@namedef{digit1subs}{One}
\@namedef{digit2subs}{Two}
\@namedef{digit3subs}{Three}
\@namedef{digit4subs}{Four}
\@namedef{digit5subs}{Five}
\@namedef{digit6subs}{Six}
\@namedef{digit7subs}{Seven}
\@namedef{digit8subs}{Eight}
\@namedef{digit9subs}{Nine}
\makeatother

\begin{document}

\overrideDigits{12345}

\overrideDigits{67890}

\end{document}

Another expl3 implementation with different renderings for the digits. You define named lists and each digit chooses the corresponding item.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\overrideDigits}{O{default}m}
 {
  \tl_map_inline:nn { #2 }
   {
    \clist_item:cn { l_nobody_digits_#1_clist } { ##1 + 1 }
   }
 }
\NewDocumentCommand{\definedigits}{mm}
 {
  \clist_clear_new:c { l_nobody_digits_#1_clist }
  \clist_set:cn { l_nobody_digits_#1_clist } { #2 }
 }
\ExplSyntaxOff

\definedigits{default}{0,1,2,3,4,5,6,7,8,9}

\definedigits{names}{
  Zero,
  One,
  Two,
  Three,
  Four,
  Five,
  Six,
  Seven,
  Eight,
  Nine
 }
\definedigits{greek}{
  \ensuremath{\alpha},
  \ensuremath{\beta},
  \ensuremath{\gamma},
  \ensuremath{\delta},
  \ensuremath{\varepsilon},
  \ensuremath{\zeta},
  \ensuremath{\eta},
  \ensuremath{\vartheta},
  \ensuremath{\iota},
  \ensuremath{\kappa}
}

\begin{document}

\overrideDigits{1234567890}

\overrideDigits[names]{0123456789}

\overrideDigits[greek]{0123456789}

\end{document}

enter image description here


Why did your attempt fail? Mainly because when you try doing \def0, 0 is not active, because it has been absorbed in the replacement text for \overrideDigits when it's not active.

A possible solution could be

\def\overrideDigits#1{%
  \begingroup
  \catcode`0=\active
  \begingroup\lccode`~=`0 \lowercase{\endgroup\let~}{\zero}%
  % and so on for the other digits
  \scantokens\expandafter{#1\empty}%
  \endgroup
}

Another possibility is

\begingroup
\catcode`0=\active \catcode`1=\active
\catcode`2=\active \catcode`3=\active
\catcode`4=\active \catcode`5=\active
\catcode`6=\active \catcode`7=\active
\catcode`8=\active \catcode`9=\active
\gdef\overrideDigits#1{%
  \catcode`0=\active \catcode`1=\active
  \catcode`2=\active \catcode`3=\active
  \catcode`4=\active \catcode`5=\active
  \catcode`6=\active \catcode`7=\active
  \catcode`8=\active \catcode`9=\active
  \let0\zero \let1\one \let2\two \let3\three \let4\four
  \let5\five \let6\six \let7\seven \let8\eight \let9\nine
  \scantokens{#1\empty}%
}
\endgroup
egreg
  • 1,121,712
  • I understood the question that 0 should be replaced by a macro named \zero, not by the verbal expression Zero (for example) –  Jun 05 '15 at 18:11
  • 1
    @ChristianHupfer Just change Zero into \zero and define \zero elsewhere. – egreg Jun 05 '15 at 18:14
  • @ChristianHupfer: You are right. I mean to override the macros to have different meanings at different locations in the document. – Nobody moving away from SE Jun 05 '15 at 18:16
  • @egreg: I presumed that catcodes would not be the best choice. As I see that you were the author of the answer that I linked to: Do you mind explaining why my adaption of your solution failed? – Nobody moving away from SE Jun 05 '15 at 18:18
  • @Nobody You can't do \def0{...} if 0 is not active; and it isn't at that point, because TeX doesn't execute commands when absorbing the replacement text for a macro. – egreg Jun 05 '15 at 18:20
  • Probably a stupid question especially since I asked for the opposite in the OP but: Is there a chance to enforce this substitution on a macro like: \def\number{12} and \specialOverrideDigits{\number} results in \one\two? I thought \expandafter\overridDigits{\number} might work but it doesn't (my understanding of \expandafter is probably wrong again). – Nobody moving away from SE Jun 05 '15 at 19:58
  • 2
    @Nobody Well, don't do \def\number{12}, because \number is an important TeX primitive. Do \newcommand\foo{12} and \expandafter\overrideDigits\expandafter{\foo}. – egreg Jun 05 '15 at 20:02
  • @egreg: Sorry, \def\number was just an example, I did not really use it but other names. I tried many combinations of \expandafter but this one I missed. Would you mind explaining why two of them are necessary? Does the second say expand { after \foo? – Nobody moving away from SE Jun 05 '15 at 20:12
  • @Nobody The first \expandafter expands the second one, which jumps over { and expands \foo. Avoid \def, unless you know precisely what you're doing. – egreg Jun 05 '15 at 20:14
7

Here's a LuaLaTeX-based solution. I deliberately didn't optimize the Lua-side code, to make it easy to modify the output of each substitution operation even if you're not at all not familiar with Lua.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode} % for "luacode" environment and "\luastring" macro

%% Lua-side code
\begin{luacode}
function override_digits ( s )  
    -- replace each digit with a macro
    s = string.gsub ( s, "0", "\\zero")
    s = string.gsub ( s, "1", "\\one")
    s = string.gsub ( s, "2", "\\two")
    s = string.gsub ( s, "3", "\\three")
    s = string.gsub ( s, "4", "\\four")
    s = string.gsub ( s, "5", "\\five")
    s = string.gsub ( s, "6", "\\six")
    s = string.gsub ( s, "7", "\\seven")
    s = string.gsub ( s, "8", "\\eight")
    s = string.gsub ( s, "9", "\\nine")
    tex.sprint ( s )
end
\end{luacode}

%% TeX-side code
\newcommand\overrideDigits[1]{%
    \directlua{ override_digits ( \luastring{#1} )}}

%% set up the macros \zero, \one, etc 
\def\zero{0}
\def\one{9}
\def\two{8}
\def\three{7}
\def\four{6}
\def\five{5}
\def\six{4}
\def\seven{3}
\def\eight{2}
\def\nine{1}

\def\otherMacro{9999}

\begin{document}
\overrideDigits{abc123}

\directlua{tex.sprint(\overrideDigits{4321} + \otherMacro)}
\end{document}
Mico
  • 506,678