10

Consider this MWE, modified from ifthenelse equal string comparison fails:

\documentclass{article}

\edef\test{german\relax}
\edef\curentry{\string german\relax}

\typeout{test: \meaning\test, curentry: \meaning\curentry}
\ifx\curentry\test
  \typeout{ equal}
\else
  \typeout{ unequal}
\fi

\begin{document}
\end{document}

If you compile this with pdflatex test.tex, you'll see this output in terminal:

test: macro:->german\relax , curentry: macro:->german\relax
 unequal

Now, the linked post explains why this is the case:

what happens is that \string is applied to the first token it sees, in this case a g. Comparing the two results, they are not the same: one has one non-letter then five letters, the second has six letters.

However, let's say I try to inspect some situation, in a package where I don't really know how the macros have been defined. So I decide to \typeout the \meaning of the macros - and I get the exact same contents printed, and the conditional still fails. What can I do to debug this kind of situation?

In other words, is there some sort of a function I could use, which (similar to Generating a catcode table in Latex (with \typeout to terminal)?) would output for the above macros, say:

% \typeoutComponents{\test}
g (catcode 11)
e (catcode 11)
r (catcode 11)
m (catcode 11)
a (catcode 11)
n (catcode 11)
\relax (catcode ?)

% \typeoutComponents{\curentry}
g (catcode 12)
e (catcode 11)
r (catcode 11)
m (catcode 11)
a (catcode 11)
n (catcode 11)
\relax (catcode ?)

... so that I'd have a chance of deducing myself why a "string" (macro) equality would fail?

(BTW, a subquestion: can a macro/command/token/control sequence (i.e. the thing starting with \) have a catcode or not?)

7

The command \tl_show_analysis:N does what you ask for.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\checktokenbytoken}{sm}
 {
  \IfBooleanTF{#1}
   {
    \tl_show_analysis:n { #2 }
   }
   {
    \tl_show_analysis:N #2
   }
 }
\ExplSyntaxOff

\edef\xgerman{\string german\relax}

\checktokenbytoken{\xgerman}
\checktokenbytoken*{german\relax}

You'll get, on the terminal,

The token list \xgerman contains the tokens:
>  g (the character g)
>  e (the letter e)
>  r (the letter r)
>  m (the letter m)
>  a (the letter a)
>  n (the letter n)
>  \relax (control sequence=\relax).
<recently read> }

l.19 \checktokenbytoken{\xgerman}

? 
The token list contains the tokens:
>  g (the letter g)
>  e (the letter e)
>  r (the letter r)
>  m (the letter m)
>  a (the letter a)
>  n (the letter n)
>  \relax (control sequence=\relax).
<recently read> }

l.20 \checktokenbytoken*{german\relax}

(For releases before TeX Live 2017, you will need \usepackage{l3tl-analysis} in addition to \usepackage{xparse} for this to work.)

  • Many thanks, @egreg - that looks great! Just to confirm: when it says the character g it means it's an "otherchar" or catcode 12; and when it says the letter g it means it's a "letter" catcode 11, correct? – sdaau Feb 19 '15 at 10:31
  • 1
    @sdaau Yes: character=12, letter=11 – egreg Feb 19 '15 at 10:33
  • @sdaau Note that my solution prints the catcode numbers (as mentioned in your question) but egreg's solution doesn't print this info. – wipet Feb 19 '15 at 16:01
7

My solution doesn't need any external package. The \showcat\macro is implemented. After

\def\test{ger{$##m}a~n \relax \foo}
\edef\curentry{\string german \relax}

\showcat\test
\showcat\curentry

we get the result:

\test -> 
  the letter g (catcode 11)   
  the letter e (catcode 11)
  the letter r (catcode 11)
  begin-group character { (catcode 1)
  math shift character $ (catcode 3)
  macro parameter character # (catcode 6)
  the letter m (catcode 11)
  end-group character } (catcode 2)
  the letter a (catcode 11)
  the token ~ (catcode 13)
  the letter n (catcode 11)
  blank space   (catcode 10)
  the token \relax (catcode 16)
  the token \foo (catcode 16)
\curentry -> 
  the character g (catcode 12)
  the letter e (catcode 11)
  the letter r (catcode 11)
  the letter m (catcode 11)
  the letter a (catcode 11)
  the letter n (catcode 11)
  blank space   (catcode 10)
  the token \relax (catcode 16)

And the implementation:

\def\showcat#1{\immediate\write16{\string#1 -> }\expandafter\showcatA#1\showcatA}
\def\showcatA{\futurelet\tmp\showcatB}
\def\showcatB{\let\next=\showcatE
   \ifx\tmp\bgroup \let\next=\showcatC \fi
   \ifx\tmp\egroup \let\next=\showcatC \fi
   \expandafter\ifx\space\tmp \let\next=\showcatC \fi
   \ifx\tmp\showcatA \let\next=\showcatF \fi
   \next
}
\def\showcatC{\afterassignment\showcatD \let\tmp= }
\def\showcatD{\showcatE\tmp}

\def\showcatE#1{\edef\next{\string#1}%
   \immediate\write16{\space\space 
       \ifnum\showcatG<13 \meaningtmp \else the token \next
       \fi \space (catcode \showcatG)}%
   \showcatA
}
\def\meaningtmp{\meaning\tmp}
\def\showcatF#1{}
\def\showcatG{\showcatH\bgroup1\showcatH\egroup2\showcatH$3\showcatH&4%
   \showcatH##6\showcatH^7\showcatH_8\showcatH{ }{10}%
   \showcatH A{11}\showcatH/{12}\showcatH~{13}16}
\def\showcatH#1#2{\ifcat\noexpand\tmp\noexpand#1#2\expandafter\showcatI\fi}
\def\showcatI#116{}

Of course, the tokens with category 0, 5, 9, 14, 15 never occur in the macro body. And the control sequences (tokens without category) are expressed here as category 16 in order to simple usage of \showcatG in \ifnum tests.

  • Thanks for that, @wipet - great to have an alternative; cheers! – sdaau Jun 12 '15 at 17:38

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.