¿Dividir macros en tokens/caracteres individuales, con catcode (para depurar condicionales)?

¿Dividir macros en tokens/caracteres individuales, con catcode (para depurar condicionales)?

Considere este MWE, modificado deSi la comparación de cadenas iguales falla:

\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}

Si compila esto con pdflatex test.tex, verá este resultado en la terminal:

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

Ahora, la publicación vinculada explica por qué este es el caso:

lo que sucede es que \string se aplica al primer token que ve, en este caso una g. Comparando los dos resultados, no son iguales: uno tiene una no letra, luego cinco letras, el segundo tiene seis letras.

Sin embargo, digamos que intento inspeccionar alguna situación, en un paquete donde no sé realmente cómo se han definido las macros. Así \typeoutque decido\meaning macros y obtengo elexactamente el mismoEl contenido se imprime y el condicional aún falla. ¿Qué puedo hacer para depurar este tipo de situación?

En otras palabras, ¿hay algún tipo de función que pueda usar y que (similar a¿Generando una tabla catcode en Latex (con \typeout al terminal)?) generaría para las macros anteriores, digamos:

% \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 ?)

... ¿para tener la oportunidad de deducir por qué fallaría una igualdad de "cadena" (macro)?

(Por cierto, una subpregunta: ¿puede una macro/comando/token/secuencia de control (es decir, lo que comienza con \) tener un código cat o no?)

Respuesta1

El comando \tl_analysis_show:Nhace lo que pides.

\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}

Obtendrás, en la 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}

(Para versiones anteriores a TeX Live 2017, necesitará \usepackage{l3tl-analysis}además \usepackage{xparse}para que esto funcione y \tl_show_analysis:N, ahora obsoleto)

Respuesta2

Mi solución no necesita ningún paquete externo. El \showcat\macroestá implementado. Después

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

\showcat\test
\showcat\curentry

obtenemos el resultado:

\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)

Y la implementación:

\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{}

Por supuesto, los tokens con categoría 0, 5, 9, 14, 15 nunca aparecen en el cuerpo de la macro. Y las secuencias de control (tokens sin categoría) se expresan aquí como categoría 16 para facilitar su uso \showcatGen \ifnumlas pruebas.

información relacionada