代码

代码

这是关于 TeX 中 token 处理的问题。我在实现时做错了什么\FutureLetNoSpace

这本书TEX 实践:第三卷:标记、宏Stephan v. Bechtolsheim 撰写的 确实很好地详细解释了 TeX。然而,我遇到了一个绊脚石,我无法独自解决\tracingall

有人能演示一下如何使用书中的代码吗?我还以为\FutureLetNoSpace是用户级/文档级命令。它似乎在模仿\futurelet

代码

在这里我写了文本“Hello there!”,并在“t”之后立即注入我的令牌检查器。它会检查令牌“h”。如果找到,它应该会在文档中产生一条不错的日志消息和“TRUE”。

\documentclass{article}
\usepackage{fontspec}% xelatex

%\tracingall % all hell breaks loose

\catcode`@=11 % or \makeatletter to change category code of @ to 11 and temporarily to access kernel macro \@tabularcr

\long\def\DoLongFutureLet #1#2#3#4{%
  \def\@FutureLetDecide{% hangs here
    #1#2\@FutureLetToken% becomes \ifx#2\@FutureLetToken, which compares two expanded tokens
      \def\@FutureLetNext{#3}%
    \else
      \def\@FutureLetNext{#4}%
    \fi% the \@FutureLetNext gets grabbed into \futurelet below
    \@FutureLetNext
  }%
    \futurelet\@FutureLetToken\@FutureLetDecide
}

\def\DoFutureLet #1#2#3#4{%
  \DoLongFutureLet{#1}{#2}{#3}{#4}
} % identical to \DoLongFutureLet

\def\FutureLetNoSpace #1#2{%
  \def\@FutureLetNoSpaceA{#1}% save arg
  \def\@FutureLetNoSpaceB{#2}% save arg
  \@FutureLetOne
}

\def\@FutureLetOne{%
  \DoFutureLet{\ifx}{ }%
    {\@FutureLetThree}{\@FutureLetOk}% \@FutureLetThree if it is a space token.
}

\edef\@FutureLetNoSpaceTemp{%
  \def\noexpand\@FutureLetThree\space{\noexpand\@FutureLetOne}% force expansion of space into a space token and recall \@FutureLetOne
}
\@FutureLetNoSpaceTemp% why are we calling this macro here?

\def\@FutureLetOk{% called when no space is found
  \expandafter\futurelet\@FutureLetTokenA\@FutureLetTokenB
}

\long\def\DoLongFutureLetNoSpace #1#2#3#4{%
  \def\@FutureLetDecideNoSpace{% \@FutureLetTokenNoSpace is self-contained by this macro
    #1#2\@FutureLetTokenNoSpace% becomes \ifx#2\@FutureLetTokenNoSpace, which compares two expanded tokens
      \def\@FutureLetNextNoSpace{#3}%
    \else
      \def\@FutureLetNextNoSpace{#4}% whatever should get executed on match
     \fi
     \@FutureLetNextNoSpace
    }
    \FutureLetNoSpace{\@FutureLetTokenNoSpace}%
      {\@FutureLetDecideNoSpace}% call \@FutureLetDecideNoSpace instead of \futurelet
}
\def\DoFutureLetNoSpace #1#2#3#4{%
  \DoLongFutureLetNoSpace{#1}{#2}{#3}{#4}%
}

\catcode`@=12 % or \makeatother to restore category code of @ to 12

\begin{document}
Hello t\DoFutureLetNoSpace{\ifx}{h}{\typeout{\noexpand\@FutureLetTokenNoSpace value: \meaning\@FutureLetTokenNoSpace}}TRUE}{\typeout{\noexpand\@FutureLetTokenNoSpace value: \meaning\@FutureLetTokenNoSpace}FALSE}here!
\end{document}

痕迹

控制台中跟踪的最后四行:

\DoFutureLet #1#2#3#4->\DoLongFutureLet {#1}{#2}{#3}{#4}
#1<-\ifx
#2<-
#3<-\@FutureLetThree

答案1

\makeatletter
\def\futureletignorespaces#1#2{\def\nextaction{#2}%
  \def\checkspace{\ifx\nexttoken\@sptoken
    \expandafter\@firstoftwo
   \else
    \expandafter\@secondoftwo
   \fi
   {\afterassignment\futureletagain\let\nexttoken= }
   {\futurelet#1\nextaction}}%
  \futurelet\nexttoken\checkspace}
\def\futureletagain{\futurelet\nexttoken\checkspace}
\makeatother

这开始只是\futurelet\nexttoken\action忽略空格的替代品\futureletignorespaces\nexttoken\action。但我稍微修改了代码,这样就可以动态定义第二个参数:

\futureletignorespaces\nexttoken\action % or you could define \action in the argument
\futureletignorespaces\nexttoken{\ifx\nexttoken!yes\fi}

答案2

虽然这个问题已经很老了,而且已经解决了,但我想看看我是否可以让我的tokcycle包执行此功能。我使用了我最近引入的包功能,即从输入流弹出、推送和查看(未来)信息。我甚至用它将循环结束推\endtokcycraw送到输入流,这允许使用所需的无限制参数方法\futurelet

在 MWE 中,我\let曾一度将活动标记Q置于\mytest,以便我可以轻松地在 之后\mytest、但在我们想要的标记之前引入空格标记。在 MWE 中,允许执行输入流,然后在行末,我在括号中显示使用 偷看的标记\futureletignorespaces

逻辑如下,\futureletignorespaces调用时,吸收一个参数(#1),即要放置 的标记\futurelet。然后它启动标记循环(最终寻找 来\endtokcycraw结束循环)。它将输入流中的下一个标记吸收到循环中,这是 Futurelet 完成后要执行的标记。如果这个被吸收的标记是 cat-0,它将作为\Macrodirective#1”(或在本例中为####1)传递给 。如果被吸收的标记是一个字符,它将被传递给\Characterdirective。无论哪种情况,我都希望执行相同的操作,这些操作在我的 定义中具有特征\mydirective

它吸收空白并将其结果放入。然后它通过\spacesbsorbed执行(传递给 的参数在哪里,futurelet 的指定目标)。然后它使这个 token-let 成为全局的。\futurelet\tcpeek#1#1\futureletignorespaces

现在,它开始以 LIFO 方式重新组装输入流:它将先前吸收的任何空白推回。然后,它将推回####1,这是在过程结束时要执行的标记。最后,它将其推送到输入流上,以便当它在标记循环的下一次迭代中被吸收时,它将终止循环。在其中两次推送中\endtokcycraw使用是因为期望推送先前弹出到宏中的内容(需要一次扩展才能恢复替换文本),而我希望它推送文字标记。因此, 得到扩展,留下所需的文字标记要推送。\empty\tcpush\empty

这样定义了循环指令后,标记循环就通过 释放\tokencyclexpress,它不接受任何指令参数,而是使用我上面描述的预定义指令。

\documentclass{article}
\usepackage{tokcycle}
\newcommand\futureletignorespaces[1]{%
 \def\mydirective{%
  \tcpopwhitespace\spaceabsorbed
  \tcpeek#1%
  \global\let#1#1%
  \tcpush\spaceabsorbed
  \tcpush{\empty####1}%
  \tcpush{\empty\endtokcycraw}}%
 \expandafter\Characterdirective\expandafter{\mydirective}%
 \expandafter\Macrodirective\expandafter{\mydirective}%
 \tokencyclexpress
}
\def\mytest{\ifx*\nexttok\expandafter\mytestSTAR
  \else\expandafter\mytestNOSTAR\fi}
\def\mytestSTAR#1#2{STAR VERSION, ARGUMENT ``#2''}
\def\mytestNOSTAR#1{NOSTAR VERSION, ARGUMENT ``#1''}
\begin{document}
\catcode`\Q=\active
\letQ\mytest
\futureletignorespaces\nexttok Q{xyz} (\ifx\bgroup\nexttok\{\else\nexttok\fi)

\futureletignorespaces\nexttok Q {xyz} (\ifx\bgroup\nexttok\{\else\nexttok\fi)

\futureletignorespaces\nexttok Q*{xyz} (\ifx\bgroup\nexttok\{\else\nexttok\fi)

\futureletignorespaces\nexttok Q *{xyz} (\ifx\bgroup\nexttok\{\else\nexttok\fi)

\futureletignorespaces\nexttok Xy (\nexttok)

\futureletignorespaces\nexttok X y (\nexttok)

\futureletignorespaces\nexttok Xxy (\nexttok)

\futureletignorespaces\nexttok X xy (\nexttok)
\end{document}

在此处输入图片描述

\futureletignorespaces为了进行直接比较,这是在文档中用替换时的输出\futurelet。偶数行有所不同,因为\futurelet看到的是空格,而不是空格后面的标记。

在此处输入图片描述

补充

由于问题被标记tex-core,我可以证明该tokcycle方法在纯文本中也有效:

\input tokcycle
\def\futureletignorespaces#1{%
 \def\mydirective{%
  \tcpopwhitespace\spaceabsorbed
  \tcpeek#1%
  \global\let#1#1%
  \tcpush\spaceabsorbed
  \tcpush{\empty####1}%
  \tcpush{\empty\endtokcycraw}}%
 \expandafter\Characterdirective\expandafter{\mydirective}%
 \expandafter\Macrodirective\expandafter{\mydirective}%
 \tokencyclexpress
}
\def\mytest{\ifx*\nexttok\expandafter\mytestSTAR
  \else\expandafter\mytestNOSTAR\fi}
\def\mytestSTAR#1#2{STAR VERSION, ARGUMENT ``#2''}
\def\mytestNOSTAR#1{NOSTAR VERSION, ARGUMENT ``#1''}

\catcode`\Q=\active
\letQ\mytest
\futureletignorespaces\nexttok Q{xyz} (\ifx\bgroup\nexttok\string{\else\nexttok\fi)

\futureletignorespaces\nexttok Q {xyz} (\ifx\bgroup\nexttok\string{\else\nexttok\fi)

\futureletignorespaces\nexttok Q*{xyz} (\ifx\bgroup\nexttok\string{\else\nexttok\fi)

\futureletignorespaces\nexttok Q *{xyz} (\ifx\bgroup\nexttok\string{\else\nexttok\fi)

\futureletignorespaces\nexttok Xy (\nexttok)

\futureletignorespaces\nexttok X y (\nexttok)

\futureletignorespaces\nexttok Xxy (\nexttok)

\futureletignorespaces\nexttok X xy (\nexttok)
\bye

相关内容