我正在尝试用纯 TeX 编写一些宏用于交叉引用,类似于 LaTeX 的\label
和\ref
Eplain 的\xrdef
。\xref
我对 的定义\label
称为\label {foo}
,它在另一个文件中定义了一个控制序列\foo
。我的 版本\ref
称为\ref {foo}
,应该扩展到被调用的页码\label {foo}
;如果\foo
未定义,则\ref
通过写入控制台通知我。我\label
的 工作正常。但我的\ref
总是通知我我定义的标签未定义。
例子:
\input ref-label
foo\label {foo}\vfill \eject
bar (\ref {foo})%
\immediate \closeout \refs
\bye
这是 ref-label.tex。
\def \reffile {refs}%
\newwrite \refs
\immediate \openout \refs = \reffile
\def \label #1{\def \text {#1}\makelabel}%
\def \makelabel {\edef \writeit
{\write \refs
{\string \def \space \csname \text \endcsname
{\noexpand \number \pageno}}}%
\writeit
}%
\def \ref #1{\expandafter \ifx \csname #1\endcsname \relax
\immediate \write 16 {Undefined label ``#1''.}%
\else \csname #1\endcsname \fi
}%
\input \reffile\
在我的示例文件上运行一次 luatex 后,文件 refs.tex 包含\def \foo {1}
; 并且“未定义标签 ``foo''。” 被写入控制台,正如预期的那样。但是,即使在第二次运行 luatex 之后,\ref {foo}
控制台中仍会写入“未定义标签 ``foo''。”。因此,\foo
似乎仍然未定义,尽管它的定义实际上存在于 refs.tex 中。
注 1:我的\label
和\ref
是从 TeXbook 的\xref
[1] 和 Eplain 的\xrdef
, \xrefn
[2] 拼凑起来的;它们并不是真正“我的”。
注 2:我仅使用纯 TeX 编写所述宏。如果大家不建议我使用其他格式、引擎或软件包,我将不胜感激。
[1]:TeXbook,第 419-420 页。
[2]:缺乏耐心的 TeX,第 302-304 页。
答案1
您需要\reffile
在开始时输入 - 以收集上一次运行的值。正如您所做
\immediate \openout \refs = \reffile
它会在你执行之前清空文件
\input \reffile\
顺便说一下,那条线应该是
\input \reffile
否则您将强制在文档中出现空白。
\def \reffile {refs}%
\newwrite \refs
\newread\refcheck
\openin\refcheck=\reffile
\ifeof\refcheck\else
\input \reffile
\fi
\immediate \openout \refs = \reffile
\def \label #1{\def \text {#1}\makelabel}%
\def \makelabel {\edef \writeit
{\write \refs
{\def \expandafter\string\csname \text \endcsname
{\noexpand \number \pageno}}}%
\writeit
}%
\def \ref #1{\expandafter \ifx \csname #1\endcsname \relax
\immediate \write 16 {Undefined label ``#1''.}%
\else \csname #1\endcsname \fi
}%
答案2
主要问题是,正如 David Carlisle 所说,你输入的是\reffile
后已打开它以进行写入,但此操作会清除该文件。
你还可能犯一些错误;例如,关闭文件不应该是立即的,否则你可能会丢失材料。其次,你正在定义不合理的宏,这可能会覆盖现有的宏。如果你这样做\label{box}
,你最终会陷入非常严重的麻烦。所以最好用一些前缀来修饰宏。
您还拥有一个似乎不需要的间接层:如果您需要写入标记列表的某些部分,而这些部分应该立即展开,那么它很有用,但您的情况并非如此。定义毫无意义\text
:定义的宏越少,覆盖所需宏的风险就越小。
我添加了重复标签检查。我认为最好使用宏,而不是直接将宏写入辅助文件\newlabel
:它使编写变得更容易。
\edef\reffile{\jobname refs.tex}
% read the labels
\def\newlabel#1#2{%
\expandafter\show\csname ref@#1\endcsname
\expandafter\def\csname ref@#1\endcsname{#2}%
}
\newread\refcheck
\openin\refcheck=\reffile\relax % <--- important
\ifeof\refcheck % still no refs file
\else
\input\reffile
\fi
\closein\refcheck
\newwrite\refs
\immediate\openout\refs=\reffile % this clears the file
\def\label#1{%
\expandafter\ifx\csname label@#1\endcsname\relax
\write\refs{\string\newlabel{#1}{\the\pageno}}%
\global\expandafter\let\csname label@#1\endcsname\empty
\else
\immediate\write16{Duplicate label ``#1''.}%
\fi
}
\def\ref#1{%
\expandafter\ifx\csname ref@#1\endcsname\relax
\immediate\write16{Undefined label ``#1''.}%
\else
\csname ref@#1\endcsname
\fi
}
foo\label{foo}
\vfill\eject
bar (\ref{foo})%
\closeout\refs % not \immediate
\bye