第 23.7 节source3.pdf
列出了所有可能的标记,比如常见的字符和控制序列,以及一些 TeX 的内部标记。
- 扩展
\the\font
结果产生的标记看起来与用于选择当前字体的命令(例如\tenrm
)相同,但形状不同。- 当在评估条件之前遇到条件的结束符时,会插入一个“frozen”
\relax
,它的形状与原语不同(但含义相同) 。\fi
- 扩展
\noexpand
<令牌>(当。。。的时候<令牌>是可扩展的)会产生一个内部标记,暂时显示为\notexpanded:
<令牌>,其形状与<令牌>并且其含义不同于\relax
。\outer endtemplate:
当预览下一个标记时可能会遇到;这会扩展为另一个内部标记,即对齐模板的结尾。- 棘手的编程可能会访问冻结的
\endwrite
。- 一些冻结的代币只能在交互式会话中访问:
\cr
,,,,,。\right
\endgroup
\fi
\inaccessible
我怎样才能生成这些标记,是通过\let
将它们与另一个控制序列、包含一个控制序列的宏进行关联,还是仅仅以某种形式“看到”它们?
答案1
更新:这个问题在新版本的 (PDF)TeX 中已经修复(通过\noexpand
对 进行无操作\endwrite
),但可能存在另一个分段错误。详情请参阅https://topanswers.xyz/tex?q=5286#a5394。
公平地说,也许 Bruno Le Floch(LaTeX3 团队的成员,可能也是在 interface3.pdf 中写了那一段的人)比我先发现了这一点。
错误消息 - 引发 SIGSEGV 的最短代码 - Code Golf Stack Exchange
准确地说,纠正下面的评论:\show ⟨\endwrite⟩
两者\meaning ⟨\endwrite⟩
都会不是Knuth TeX 中的分段错误——前者显示
> \endwrite=\outer macro:
.
但在 PDFTeX 中却如此。只有\let ⟨some assignable thing⟩ ⟨\endwrite⟩
(或⟨\endwrite⟩
直接执行)分段错误 Knuth TeX。
不知道为什么没有人发布有关该\endwrite
令牌的答案,但它就在这里。
%! TEX program = pdflatex
\documentclass{article}
\begin{document}
\ExplSyntaxOn
% starting state of the input stream:
% \weird } \endwrite ...
\def \weird { \expandafter \weirda \expandafter { \iffalse } \fi }
% → \weirda { } \endwrite ...
\def \weirda #1 { \expandafter \weirdb \noexpand }
% \endwrite cannot be grabbed as #2 so we \noexpand it
% → \weirdb <safe \endwrite token>
\def \weirdb #1 { \iffalse { \fi } #1 \edef \mycontainendwrite { \noexpand #1 } }
% after it's grabbed once it becomes "unsafe" again
% → } \endwrite \edef \mycontainendwrite { \noexpand \endwrite } ...
% we need to restore the `} \endwrite` tokens otherwise it's an error
\setbox0=\vbox{
\write 16{\weird}
}
\tex_shipout:D\box0
\nonstopmode
\show \mycontainendwrite
\errorstopmode
\expandafter \let \expandafter \myendwrite \mycontainendwrite
\nonstopmode
\show \myendwrite % no output in LuaLaTeX, segmentation fault in pdflatex and XeLaTeX and latex
\errorstopmode
\ExplSyntaxOff
\end{document}
基本上,如果您这样做\write <some number> {<some token list>}
,令牌列表将未扩展地存储在“whatsit”中,并且当盒子被运出时,例如,会执行\shipout \box 0 \somemoretokens ...
类似于的操作。\immediate \write <the number> { <the token list> } \endwrite \somemoretokens ...
(*)如下所述,\immediate\write
这也是这样。上次我尝试时没有发生这种情况——可能是我的一些失误。
要将endwrite存储在某个地方,请在之后注入它\write
(因为里面的内容是纯可扩展的,所以不能存储它)。
有趣的是,它会产生分段错误。(我不知道它是否可被利用。)它只是一个空指针取消引用,在正常环境下它必然会导致 SIGSEGV,因此与最近修复的任意位置内存读取月份值相比,它没那么“有趣”。
LuaLaTeX 上的输出是
> \mycontainendwrite=macro:
->\endwrite .
l.37 \show \mycontainendwrite
> \myendwrite=\outer macro:
.
l.43 \show \myendwrite
其余的是
> \mycontainendwrite=macro:
->\endwrite .
l.37 \show \mycontainendwrite
Segmentation fault (core dumped)
(分段错误是由 引起的\let
,而不是\show
。)
其余标记的附注。您可以阅读texdoc tex
并稍作调整以弄清楚它们。
首先触发一个导致生成 token 的错误。然后“猜测”输入流中接下来的 token 是什么。
例如,对于\inaccessible
可以通过执行\def
后跟非控制序列来访问的令牌。
$ pdflatex
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022/Arch Linux) (preloaded format=pdflatex)
restricted \write18 enabled.
**\def a
entering extended mode
LaTeX2e patch level 1
L3 programming layer
! Missing control sequence inserted.
\inaccessible
\def a
? I\a{}\show
> \inaccessible=undefined.
\inaccessible
\def a
? I\show
> the letter a.
a
\def a
?
这下划线部分是输入的文本。其余部分是从 pdflatex 输出的。
您可以推断,当\def a
执行时,TeX 会打印一条错误消息,将用户带到命令提示符,然后继续执行,就好像\def
刚刚执行并\inaccessible a
在输入流中跟随一样。
如果你I\a{}\show
在这里输入的话,执行的内容就会变成\def\a{}\show\inaccessible a
你所期望的那样。
这个令牌在 lualatex 中具有.tok
值0x20010001
(或536936449
),但我认为无论如何都没有任何方法可以根据其 tok 值创建令牌。
或者(出于学术目的),也可以使用 Lua 中的错误钩子来故意触发错误以创建无法访问的令牌,但是如果你在同一个段落中重复这个操作 100 次,TeX 就会停止。
\directlua{
luatexbase.add_to_callback("show_error_hook",
function()
luatexbase.remove_from_callback("show_error_hook", "get inaccessible token")
inaccessible_token=token.get_next()
token.put_next(inaccessible_token)
end,
"get inaccessible token"
)
}
\ExplSyntaxOn
\batchmode
\def a{}
\errorstopmode
在线尝试一下! (链接到一些在线代码执行服务进行演示并查看结果。)
补充说明。
第四点 代\endtemplate
币
如果您执行以下操作(类似于获取\endwrite
令牌的技巧,但不需要大括号破解)
\def\weird{\expandafter \weirda\noexpand}
\def\weirda #1{\global\edef\container{\noexpand #1}#1}
{\setbox0=\vbox{\halign{#\cr\noexpand\weird\cr}}}
你可以获取外部 endtemplate 标记本身,而不仅仅是其含义。它具有属性
- 字符串表示也
\endtemplate
与内部标记一样 - 意思是
\outer endtemplate:
,或者[unknown command code! (151, 4)]:
在 LuaTeX 上。(另一个错误……?) - 在外面。
- 是可扩展的。
- 如果
\noexpand
将其应用于它,其含义就变成\relax
,就像普通的可扩展标记一样。
如果它被扩展一次,它就会变成具有以下属性的内部标记
- 字符串表示法
\endtemplate
如上所述 - 意思是
end of alignment template
- 不在外面。
- 不可扩展。
第 3 点 notexpanded
书中提到,它的含义并不等同于正常的 的含义\relax
。值得注意的是,它的含义可以分配给另一个 token,而新的 token 将无法扩展。
\noexpand
此外,当应用于未定义的标记时,也会获得此含义。
当\noexpand
应用于不可扩展的 token(包括冻结的放松)时,它不是一个无操作,但只有在你
- 将其重新定义为其他东西,那么输入流上的标记的含义就变成
\relax
- 应用
\show
,然后\notexpanded
打印在控制台上。(即使不可扩展,也会\expandafter\show \noexpand\let
显示)\notexpanded: let
\let
演示。
% ======== assign meaning \a = \noexpand of y
\def\y {}
\expandafter \let \expandafter \a \noexpand \y
% ======== assign meaning \b = \noexpand of z
\expandafter \let \expandafter \b \noexpand \z
% ======== assign meaning \c = \noexpand of \let
\expandafter \let \expandafter \c \noexpand \let
% ======== \a is different from \relax
\ifx\a\relax \errmessage{this cannot happen} \fi
% ======== \a expanded once is different from \relax
\expandafter\ifx\a\relax \errmessage{this cannot happen} \fi
% ======== \a is different from \y
\ifx\a\y \errmessage{this cannot happen} \fi
% ======== \a expanded once remains the same i.e. it's not expandable, unlike the notexpanded token
\expandafter\ifx\a\a \else \errmessage{this cannot happen} \fi
% ======== \a and \b is the same
\ifx\a\b \else \errmessage{this cannot happen} \fi
% ======== \c meaning is \let
\ifx\c\let \else \errmessage{this cannot happen} \fi
\bye
答案2
DRF 等人同意 \endwrite 访问至少是一个错误,我们会将其报告给 Knuth(最终)。最终归结为访问 mem[0],如果 mem_in>0,这将超出范围,就像在 trip 测试中一样。无论如何,与此同时,我将其添加到https://tug.org/texmfbug/newbug.html#B155endwrite
DRF 奇迹:
我(目前)还不能确信没有类似的技巧可以让 \let 或 \futurelet 应用于 end_write_token,当 eq_destroy 认为旧值是 outer_call 时,这会导致麻烦(因此我们需要 another_null_list 修复)。但我对 TeX 的嘴和胃的复杂性还不够熟悉,无法判断 \let 和 \futurelet 是否存在类似的问题;所以也许值得向 [这里] 的超级黑客核实一下,他们一开始就想出了这个魔法?
如果有人想尝试一下并报告,请这样做。最好[电子邮件保护]以及这里。
此外,我不清楚这里讨论的其他内容是否可能是错误或只是奇怪之处。如果 TeX 没有崩溃,Knuth 不太可能想要改变任何东西,但很难说...
答案3
在我的实验中,我只能够产生 1、2、(部分通过\show
)3、4,但不能产生最后两个。
1 号可以简单地生成
\font\tenrm=cmr10
{\tenrm \xdef\tmpa{\the\font}}
\show\tmpa % macro:->\tenrm
\expandafter\ifx\tmpa\tenrm y\fi % \ifx equivalent
\expandafter\def\expandafter\tmpb\tmpa{}
\expandafter\tmpb\tmpa
% \tmpb\tenrm % produces error: different shape
可以使用相同的测试生成数字 2 并验证其是否为异常:
\edef\tmpa{\ifnum0=0\else\fi}
\show\tmpa % macro:->\relax
对于 3,我只能在 just 'd 标记上\notexpanded:
使用时看到。也许还有更多方法可以尝试它们\show
\noexpand
\expandafter\show\noexpand\stuff
% > \stuff=\relax.
% <recently read> \notexpanded: \stuff
(这已经失控了:现在至少有两种异常变体\relax
!)
数字 4 可以作为\futurelet
h/valign 内的控制序列
\halign{#\cr\global\futurelet\tmpa\relax\cr}
\show\tmpa % =\outer endtemplate:
这可以再次扩展以获得\endtemplate
。
\expandafter\let\expandafter\tmpb\tmpa
\show\tmpb % =end of alignment template
\expandafter\def\expandafter\tmpc\expandafter{\tmpa}
\show\tmpc % macro:->\endtemplate