使用 catcode 将宏拆分为单个标记/字符(以调试条件)?

使用 catcode 将宏拆分为单个标记/字符(以调试条件)?

考虑这个 MWE,修改自ifthenelse 相等字符串比较失败

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

如果你用 编译它pdflatex test.tex,你会在终端看到这个输出:

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

现在,链接的帖子解释了为什么会出现这种情况:

发生的情况是 \string 被应用于它看到的第一个标记,在本例中是 g。比较两个结果,它们是不一样的:一个有一个非字母然后有五个字母,第二个有六个字母。

但是,假设我尝试检查某种情况,在一个我不太清楚宏是如何定义的包中。所以我决定使用\typeout\meaning- 然后我得到了完全一样内容已打印,但条件仍然失败。我该如何调试这种情况?

换句话说,我可以使用某种函数吗?它(类似于在 Latex 中生成 catcode 表(使用 \typeout 到终端)?)将输出上述宏,例如:

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

...这样我就有机会自己推断为什么“字符串”(宏)相等会失败?

(顺便说一句,有一个子问题:宏/命令/标记/控制序列(即以 开头的东西\)是否可以有 catcode?)

答案1

该命令\tl_analysis_show:N按照您的要求执行。

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

您将在终端上看到

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}

(对于 TeX Live 2017 之前的版本,您还需要\usepackage{l3tl-analysis}才能\usepackage{xparse}使用此功能,并且\tl_show_analysis:N现已弃用)

答案2

我的解决方案不需要任何外部包。\showcat\macro已实现。之后

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

\showcat\test
\showcat\curentry

我们得到结果:

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

实施如下:

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

当然,类别为 0, 5, 9, 14, 15 的 token 在宏体中是不会出现的,而控制序列(没有类别的 token)为了\showcatG\ifnum测试中简单使用,这里表示为类别 16。

相关内容