测试 token 是否是控制序列

测试 token 是否是控制序列

有时,更复杂的 (La)TeX 宏会测试下一个输入标记并根据其类型进行分支。我知道如何测试 catcode 和字符代码,但有时我喜欢以特殊方式处理控制序列(宏、基元,即\后跟字母(catcode 11)以及的乘积\csname ...\endcsname)。

是否有合适的测试来判断作为宏参数读取的标记是否是控制序列?标记不能事先扩展。我喜欢避免使用\string和寻找前导\,因为这取决于 的值\escapechar可能在本地更改。使用\meaning和测试前导macro不适用于 这样的原语\relax

所以基本上我正在寻找一个\@ifcontrolsequence{<token>}{<true>}{<false>}宏,它可以提供真的例如\relax,对于\empty\custommacro对于任何其他标记,则为 false。活动字符可以被视为例外,并以任何方式处理。宏当然不应该在任何特殊标记上中断。

答案1

\makeatletter
\def\@ifismacro#1{%
  \begingroup\escapechar=-1
    \edef\x{\endgroup\def\noexpand\first{\string#1}}\x
  \begingroup\escapechar=`\\
    \edef\x{\endgroup\def\noexpand\second{\string#1}}\x
  \ifnum\pdfstrcmp{\first}{\second}=\z@
    \expandafter\@secondoftwo % no backslash in front
  \else
    \expandafter\@firstoftwo  % backslash in front
  \fi}
\def\report#1{\@ifismacro{#1}{\message{CS}}{\message{NON CS}}}
\makeatother

\report{A}
\report{\"}
\let\pippo=a
\report{\pippo}

这种方法的问题在于不是完全可扩展的,因为它依赖于对的分配\escapechar,同时与执行测试时的值无关。

此测试区分了最后一种情况,对于 来说这是不可能的\ifcat。对于\ifcsmacro来说也不可能电子工具箱, 它似乎。

答案2

无法通过扩展获得完整的解决方案。只要转义字符可打印(\escapechar介于 0 和 255 之间,含 0 和 255),下面的代码就会给出正确的结果。它依赖于这样一个事实:在这种情况下,应用于\string我们的参数将给出多个字符。事实上,我们\escapechar=32单独处理大小写(空格),因为 TeX 在抓取未分隔的参数时会忽略空格。

各个地方看起来怪异\noexpand的地方需要满足\outer宏的要求:它允许它们停留 \relax足够长的时间以在参数中抓取它们。请注意,#1永远不会出现在条件中可能被跳过的文本中(因为它可能是\outer)。

如果转义字符不可打印,并且\string#1只给出一个字符,那么我们需要进行更多的调查来区分通常的情况(但解决方案并不完整)。

\makeatletter
\newcommand{\ifcs}{\expandafter\ifcs@i\noexpand}
\newcommand{\ifcs@T}[3]{#2}
\newcommand{\ifcs@F}[3]{#3}

% "normal" escapechar
\newcommand{\ifcs@i}[1]
  {%
    \ifcat$\ifcat*\string#1\fi$%
      \expandafter \expandafter
      \expandafter \ifcs@test
    \else
      \expandafter \expandafter
      \expandafter \ifcs@T
    \fi
    \noexpand #1%
  }

如果我们不关心 的特殊情况,我们可以在这里完成\escapechar:只需将条件的结尾替换为 \expandafter \@secondoftwo \else \expandafter \@firstoftwo \fi (也删除\noexpand #1)。但区分各种转义​​字符并不太昂贵(我怀疑我在这里给出的测试是否接近最佳)。

\newcommand{\ifcs@test}
  {%
    \ifcase \expandafter\@gobble\string\2 % (space)
            \ifcat\@sptoken\string\1 \else 0 \fi
      \expandafter \ifcs@unprintable
    \or
      \expandafter \ifcs@space
    \else
      \expandafter \ifcs@F
    \fi
  }

% \escapechar=32
\newcommand{\ifcs@space}[1]
  {%
    \unless\ifcat\@sptoken\string#1%
      \expandafter \expandafter
      \expandafter \ifcs@F
    \else
      \expandafter \expandafter
      \expandafter \ifcs@space@i
    \fi
    \noexpand #1%
  }
\newcommand{\ifcs@space@i}[1]% \string#1 starts with space
  {%
    \ifcat$\romannumeral-`0\string#1$%
      \expandafter \@secondoftwo
    \else
      \expandafter \@firstoftwo
    \fi
  }

案例\escapechar < 0> 255。我对此没有进行太多测试,但无法提供完整的解决方案,因为例如控制序列\a和活动alet 彼此以及 let 到字符在扩展上无法区分。( \ifcat\if\ifx\meaning和相同\string

\newcommand{\ifcs@unprintable}[1]
  {%
    \ifcat\relax\noexpand#1%
      \expandafter \expandafter
      \expandafter \ifcs@T
    \else
      \expandafter \expandafter
      \expandafter \ifcs@unprintable@i
    \fi
    \noexpand #1%
  }
\newcommand{\ifcs@unprintable@i}[1]
  {%
    \expandafter\ifx\csname \string#1\endcsname#1%
      \expandafter \@firstoftwo % can be wrong
    \else
      \expandafter \@secondoftwo
    \fi
  }

\def\test{\expandafter\test@\noexpand}
\def\test@#1{\ifcs#1{\message{T}}{\message{F}}
\outer\def\foo{}

\escapechar=-1\relax
\test\foo
\test\a
\test\ %
\expandafter\test\csname \space a\endcsname
\test a
\test ^
\test $

答案3

看看电子工具箱包装及其\ifcsmacro测试。

\documentclass{article}

\usepackage{etoolbox}

\begin{document}

% \def\custommacro{foo}
% \def\custommacro{\empty}
% \def\custommacro{\relax}
% \let\custommacro\box

\ifcsmacro{box}{True}{False}

\ifcsmacro{custommacro}{True}{False}

\end{document}

答案4

你想要的\ifiscsetextools包。我发现有一个 bug 需要测试#

例子:

\documentclass{article}
\usepackage{etextools}

\begin{document}
% yes
\ifiscs{\fi}{yes}{no}\par
\ifiscs{\undefined}{yes}{no}\par
\ifiscs{\,}{yes}{no}\par
% no
\ifiscs{}{yes}{no}\par
\ifiscs{ }{yes}{no}\par
\ifiscs{
}{yes}{no}\par
\ifiscs{~}{yes}{no}\par
\ifiscs{_}{yes}{no}\par
\ifiscs{^}{yes}{no}\par
\ifiscs{&}{yes}{no}\par
\ifiscs{$}{yes}{no}\par
\ifiscs{汉}{yes}{no}\par
\ifiscs{foo}{yes}{no}

% wrong: should be no, but get yes
\ifiscs{#}{yes}{no} is wrong

% I cannot test braces { }, comment char % and escape char \

\makeatletter
% yes
\ifiscs{\@sptoken}{yes}{no}

\catcode`\@=0
% yes
\ifiscs{@asdf}{yes}{no}

\end{document}

相关内容