Consider the MWE example.




Why the result of \meaning\toto is \toot{\textsc {1}} and not \toot{\textsc{1}} (that have an impact on Reledmac/Reledmac: duplicating index entries

  • 2
    That’s a hardwired feature of TeX: a space always follows a control word in TeX’s internal representation. – egreg Mar 17 '18 at 9:15
  • Did you mean \meaning\toot instead of \meaning\toto? – Solomon Ucko Mar 17 '18 at 12:24
  • Is the problem that you need to write \textsc{1} to an external file (rather than using it at that point in the document)? In which case, are you able to just do \string\textsc? – Nicola Talbot Mar 17 '18 at 13:38
  • @NicolaTalbot unfortunatly not, because \toot is (in reality) a user level command of reledmac (\Aendnote). So I can't ask to use \string. – Maïeul Mar 17 '18 at 15:39
  • @Maïeul Okay. Thought it worth mentioning just in case. – Nicola Talbot Mar 17 '18 at 15:51

TeX does not save character strings in its memory but tokens. When the parameter of \toot in the line of your example


is scanned, the tokenizer creates the tokens "textsc", "{", "1" and "}". When TeX is asked to print the tokens (using \meaning in your example) then it must solve the question how tokens of control-sequence type are printed. If the printed token control sequence name consists of letters then TeX prints a backslash in front of and a space after such token. This is decision of TeX’s author. The reason: the tokens "relax", "a" are printed as \relax a which is better than \relaxa.

Edit If you want to elaborate with catcodes, then an example follows. The parameter of the \toot macro is read (and tokenized) when catcodes of backslash and space are 12 (normal). So you can detect duplicate spaces and there are no control sequences. As soon as the parameter is read, the standard catcodes are set back (at \egroup). Then a second "normal" variant of the parameter is prepared using \scantokens because we expect that macro programmer needs to manipulate with both variants: "normal" and "space-respected". Finally the macro \tootB is started. It gets two parameters, first is "normal" variant of parameter and second is "space-respected" variant (where no control sequences are).

\def\toot{\bgroup \catcode`\\=12 \catcode`\ =12 \tootA}

  \message{normal: "\meaning\tmp"}
  \message{space-respected: "\meaning\tmp"}


\toot{\textsc{ a b   c \par a\par1 \textbf {a}a}}

\toot{\textsc{ a b c \hskip1em a\space 1 \textbf {a}a}}

| improve this answer | |
  • thank. So no workaround? – Maïeul Mar 17 '18 at 9:28
  • More precisely, in front of a control sequence the character with character code \escapechar is added (nothing if \escapechar is outside the range of character codes). There is no provision for an “after control word character” which is always a space character (charcode 32, catcode 10). – egreg Mar 17 '18 at 9:33
  • 1
    No workaround. The information if the space were typed or no is lost at tokenizer phase. You cannot know about it at expand processor. Only workaround: change catcode of the space when the parameter of \toot is read. I can show it, but catcode changing (especially of space) is very fragile. – wipet Mar 17 '18 at 9:33

With a macro which eats every space following a control sequence (so a macro name which consists of only alphabetic characters) which is not followed by an alphabetic character in its (once) expanded argument you can get what you want. It also eats the space if you typed it yourself, though.

EDIT: One could read the argument to the macro verbatim and then replace everything which should have been a macro with the macro. As a result \toottwo can't be used inside of an argument of another macro.

\tl_new:N \l_maieul_tl
\NewDocumentCommand \eatmacrospace { m }
    \tl_set:No \l_maieul_tl { #1 }
      { (\\[A-Za-z]+)\s([^A-Za-z]) } { \1\2 } \l_maieul_tl
\str_new:N \l_maieul_str
\int_new:N \l_maieul_int
\NewDocumentCommand \toottwo { +v }
    \tl_set:Nn \l_maieul_tl { #1 }
    \regex_replace_all:nnN { \\([A-Za-z]+) } { \c{\1} } \l_maieul_tl
    \regex_replace_all:nnN { \\([^A-Za-z]) } { \c{\1} } \l_maieul_tl
    \exp_args:NnV \regex_count:nnN { \{ } \l_maieul_tl \l_maieul_int
    \int_step_inline:nnnn { \c_one } { \c_one } { \l_maieul_int }
        \regex_replace_all:nnN { \cO{(.*)\cO} } { \cB{\1\cE} } \l_maieul_tl
    %\tl_show_analysis:N \l_maieul_tl % for debugging
    \str_set:Nn \l_maieul_str { #1 }

\toot{\textsc{ a b c \par a\par1 \textbf {a}a}}

\toottwo{\textsc{ a b c \hskip1em a\space 1 \textbf {a}a}}

\content% content is expandable



enter image description here

| improve this answer | |
  • thank. Unfortunetaly, in real word, it nos everxtime the first space ! – Maïeul Mar 17 '18 at 10:17
  • @Maïeul I know and am working on it :) – Skillmon Mar 17 '18 at 10:20
  • @Maïeul see my edit. – Skillmon Mar 17 '18 at 10:58
  • Your example clearly shows that there is no solution. When input inludes \par1 \textbf {} then you cannot restore this string because you have \par 1 \textbf {} XOR \par1 \textbf{}. – wipet Mar 17 '18 at 11:20
  • @wipet yes, it just shows that. It just converts the TeX internal representation in the equivalent form without spaces following control sequences when not necessary. – Skillmon Mar 17 '18 at 11:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.