这是关于 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