考虑以下代码cwebmac.tex
:
{\setbox0=\hbox{\toksA={1.}\toksB={}\maketoks}\the\toksA}
宏有什么\maketoks
作用以及它如何工作?
以下是 pdftex 的完整示例(从 cwebmac.tex 简化而来):
\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD
\newcount\countB \newcount\countC
\def\makenote{%
\addtokens\toksB{\the\toksC}%
\toksC={}%
\global\countC=0
}
\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}%
\ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}}
\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|%
\ifnum\countB>`9 \countB=0 \fi
\ifnum\countB<`0
\ifnum0=\countC\else\makenote\fi
\ifx\first.%
\let\next=\maketoksdone
\else
\let\next=\maketoks
\addtokens\toksB{\the\toksD}
\ifx\first,\addtokens\toksB{\space}\fi
\fi
\else
\addtokens\toksC{\the\toksD}%
\global\countC=1
\let\next=\maketoks
\fi
\next
}
\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
{\setbox0=\hbox{\toksA={1.}\toksB={}\maketoks}\the\toksA}
\bye
编辑
考虑以下 test.w (我\startsection
故意禁用不设置目标标记):
\nosecs
\let\startsection\empty
@* Example.
@c
@<Something@>;
@ @<Something@>=
void main (void)
运行cweave test.w
,然后处理test.tex
1)通过 pdftex
pdftex test.tex
2)通过 dvipdfmx
tex '\let\pdf+ \input test.tex'
dvipdfmx test.dvi
我们得到以下错误:
1)
pdfTeX warning (dest): num1 has been referenced but does not exist, replaced by a fixed one
pdfTeX warning (dest): num2 has been referenced but does not exist, replaced by a fixed one
2)
dvipdfmx:warning: PDF destination "ii" not defined.
dvipdfmx:warning: PDF destination "i" not defined.
结论是,这个罗马数字只是在 dvipdfmx 中设置目标标记。也许它可以被替换为num #1
类似于 pdftex 那样,然后所有这些罗马数字的东西就没有必要了?
答案1
宏\maketoks
解析标记列表变量\toksA
并使用临时变量转换其内容\toksD
,同时\toksB
在交付到最终变量之前积累转换后的材料\toksA
(\toksB
也可能包含以前的内容)。此外,还使用了\toksC
其作用如下所述。
请注意,要进行分析,需要知道 的 catcode |ENDTOKS|
,我假设|
有 catcode other 和字母 catcode letter。 嗯,不,似乎循环必须在击中 之前结束|ENDTOKS|
,因此假设击中了出口点,这意味着(见下文)必须在某个点找到一个点。
进入循环。逐个检查遇到的标记。但请注意,代码本身并不是每个标记分析器的标记,而是将事物作为宏参数抓取,#1
因此似乎假设遇到的材料没有支撑材料,并且不会看到空格标记(并被修剪)。
如果标记是 catcode 12 的数字,则附加该数字
\toksC
并将\countC
标志设置为数值 1。如果 token 不是 catcode 12,或者是 catcode 12 但不是数字:
a. 如果之前已经遇到过某个数字,则将(连续的一系列)数字附加到该数字之后,
\toksB
并将其\toksC
重置为空并\countC
重置为零,然后继续如下所示。b. 否则,如果标记是点,
.
则退出循环,并将累积的数据放入\toksA
(不带点)。剩余的内容将被丢弃。请注意,这里有一个杂散的空格标记(%
后面没有\ifx\first.
),但显然整个过程是在临时框内执行的,因此不会影响排版。c. 否则将标记附加到
\toksB
。如果它是一个逗号,则会附加一个额外的空格。此处的代码也有一个虚假的空格标记(%
之后没有\addtokens\toksB{\the\toksD}
),但显然整个代码注定要在临时框内执行,因此这些空格无关紧要。然后继续循环。
简而言之,效果是\toksA
保留 直到遇到第一个点的部分(可能点甚至隐藏在 中{.}
),其余部分被丢弃,并在逗号后添加空格。关于数字,我不太明白,为什么它们会累积在 中\toksC
而不是直接累积在 中\toksB
,因为最终它们的命运似乎与其他标记(我假设主要为字符)没有什么不同。
Igor 在评论中澄清了发布的代码与 中的原始代码略有不同cwebmac.tex
。原始代码确实对解析的连续数字系列进行了特殊处理,特别是它似乎创建了 PDF 链接,如果编译通过 pdftex 或例如 tex+dvipdfmx,则语法会有所不同。
% standard macros for CWEB listings (in addition to plain.tex)
% Version 3.68 --- January 2016
我们发现:
\def\makenote{\addtokens\toksB
{\noexpand\pdflink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0}
并且:
\ifpdftex
\ifx\pdfannotlink\undefined\let\pdfannotlink\pdfstartlink\fi% for pdfTeX 0.14
\def\pdflink#1#2{\hbox{\pdfannotlink height\ht\strutbox depth\dp\strutbox
attr{/Border [0 0 0]} goto num #1 \BlueGreen #1\Black\pdfendlink}}
\else\def\pdflink#1#2{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
\special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
depth \thedepth << /Type /Annot /Subtype /Link
/Border [0 0 0] /A << /S /GoTo /D (#2) >> >>}\box0\relax}\fi
pdftex 案例使用\pdfannotlink=\pdfstartlink
来自 PDFTeX 并使用goto num
而不是#2
具有 TeX 的\romannumeral
,而 dvipdfmx 案例使用#2
将其转换为罗马数字。
在其他地方(的定义中\startsection
),可以找到如何创建目的地名称的示例:
\ifpdftex\smash{\raise\baselineskip\hbox to0pt{%
% \let\*=\empty\pdfdest num \secstar fith}} % bad space in versions < 3.68
\let\*=\empty\pdfdest num \secstar fith}}% changed in version 3.68
\else\ifpdf\smash{\raise\baselineskip\hbox to0pt{%
\let\*=\empty\special{%
pdf: dest (\romannumeral\secstar) [ @thispage /FitH @ypos ]}}}\fi\fi
所有这些都证实了原文对数字的解析\maketoks
确实不是无端的;-)
。
我不熟悉(这是委婉的说法)PDF 规范,我只能推测不能使用以数字开头或由数字组成的目标标识符,并且关键字num
或使用\romannumeral
是一种解决方法。这纯粹是猜测。