终端评估命令的交互式控制台

终端评估命令的交互式控制台

由于我使用的是 LaTeX(我使用的是 xelatex),因此经常会遇到一些小问题。作为一名使用过其他更函数式或过程式语言的程序员,我无法轻易弄清楚表达式求值的结果,这让我很恼火。所以我查看了 SE,但并没有找到很多。我确实找到了这个trace包,它非常有用。但我仍然不清楚如何解释输出,尤其是在评估多个级别的命令及其之间传递的值时。

我找到了\show命令,它非常适合在日志中进行一次性定义。我还发现texdef它很不错,我可以从终端快速使用它。我已经熟悉了\message\typeout等。我并不是在寻找任何可以在 latex 文档中使用的命令,然后编译它并检查日志文件。

有没有办法在交互式 REPL 模式下使用 latex?例如:

> \newcounter{index} % Input
# some output        % Output

> \theindex
# 0

> \stepcounter{index}
# some output, maybe '\c@theindex=1' or something

这有意义吗?这样,当我想检查命令时,我不需要设置复杂的跟踪序列,因为我只需要一点。这可以用 latex 实现吗?(这可以在大多数脚本语言中通过终端上的标志来实现,例如php -aruby irb​​。)

更新: 感谢@JosephWright,看起来如果 latex 文件是从命令行编译的,即latex file.tex,那么就有机会放弃我所说的交互模式。一旦完成,看起来我仍然必须使用命令。除非我遗漏了某些东西,否则trace前言命令将不起作用(例如)。\usepackage

我的下一个问题是,如果我按照给出的建议并停止命令\end{document},那么我将进入带有*提示的交互模式。当我输入命令或任何内容(启用跟踪)时,我可以看到它正在运行并处理我的输入。如果我输入错误导致错误的内容,我会收到一条消息,然后输入提示?。要返回提示*,我只需点击 。

问题是,如果我尝试在文档中的特定位置中断(例如,放入未定义的序列,如\BREAK),则会直接进入错误提示?,按回车键只会忽略错误并继续编译文档。它才不是将我送入输入提示*。如何实现?

答案1

当考虑调试或其他任何事情时,有两个关于 TeX 的重要因素需要牢记。

第一个包含输入:TeX 从文件或终端读取输入,它们是等效的。在大多数情况下,文件(或文件)设置为包含整个 TeX 运行,并且是自包含的(在 LaTeX 术语中包括\end{document})。但是,没有要求必须如此,您可以简单地启动 TeX 运行并在提示符下输入*命令或者使用部分文件(未达到\end{document}或等效的文件):TeX 将处理文件然后等待输入。因此,我们总是担心的是“输入流中的下一个”是什么,而不是“在文件中”或“在终端中”或其他什么。值得注意的是,这里没有像其他一些语言那样的“交互模式”:只有“TeX 等待更多输入”。

其次,TeX 是一种宏扩展语言,因此没有“求值”。当 TeX 找到控制序列时,可能会发生两件事。可扩展命令被扩展,即它们的定义被替换文本(对于宏)或扩展结果(对于可扩展原语)替换。您无法直接看到这一点,因为跟踪不会显示“即将到来的输入流”,但您可以显示扩展的结果:我将在下面详细说明。对于主要处理分配、排版和写入文件的非可扩展原语,结果可能是可见的,但同样没有“返回”可供查看。

TeX 是一种宏语言,其连锁效应是 LaTeX 被实现为一系列 TeX 宏(和设置),最终 LaTeX 命令解析为 TeX 构造。许多跟踪方法都会显示这一点,要看到正在发生的事情的“真实”结果,您需要了解其中的一些内容。

这些考虑的结果是,使用 (La)TeX 所使用的技术可能不为那些熟悉其他语言的人所熟悉。(当然,另一方面,对于主要使用 TeX 的人来说,其他语言似乎很奇怪!)正如问题所指出的,一个有用的能力是\show定义一个标记

\show\mymacro

例如这可能会给出

> \mymacro=macro:
#1->foo~#1.

如果我完成了\newcommand*\mymacro[1]{foo~#1}\def\mymacro#1{foo~#1}。当数据存储在 TeX 寄存器中而不是宏中时,这将不会太有帮助。这很重要,因为例如 LaTeX 计数器是作为寄存器实现的。因此

\newcounter{index}
\makeatletter
\show\c@index

我们得到

> \c@index=\count87.
l.9 \show\c@index

正如问题中提到的,\theindex打印计数器的值,我们可以使用它例如

\edef\shower{\theindex}
\show\shower

或者

\typeout{\theindex}

以查看是否可扩展的值\theindex\edef强制扩展与之前相同\typeout)。我们还可以使用以下命令直接查看寄存器的值\showthe

\showthe\count87

或者

\makeatletter
\showthe\c@index

均产生

> 0.

index(我在这里使用了我知道的 LaTeX 计数器是 TeX 计数寄存器的事实\c@index。)

为了更普遍地了解“发生了什么”,通常的方法是使用\tracingall。这可以插入到您想要的任何地方(在文档中,在终端中),因为正如已经指出的那样,两者是等效的。您可以选择在组中执行此操作,以查看某件事发生了什么

{\tracingall\newcounter{index}}

给出

{vertical mode: \tracingrestores}
{\tracingassigns}
{into \tracingassigns=1}

\newcounter #1->\expandafter \@ifdefinable \csname c@#1\endcsname {\@definecoun
ter {#1}}\@ifnextchar [{\@newctr {#1}}{}
#1<-index
{\expandafter}
{\csname}
{changing \c@index=undefined}
{into \c@index=\relax}

\@ifdefinable #1#2->\edef \reserved@a {\expandafter \@gobble \string #1}\@ifund
efined \reserved@a {\edef \reserved@b {\expandafter \@carcube \reserved@a xxx\@
nil }\ifx \reserved@b \@qend \@notdefinable \else \ifx \reserved@a \@qrelax \@n
otdefinable \else #2\fi \fi }\@notdefinable 
#1<-\c@index 
#2<-\@definecounter {index}
{\edef}
{\expandafter}
{\string}

\@gobble #1->
#1<-\
{changing \reserved@a=macro:->\def \@currenvir {document}\edef \ETC.}
{into \reserved@a=macro:->c@index}

\@ifundefined #1->\expandafter \ifx \csname #1\endcsname \relax \expandafter \@
firstoftwo \else \expandafter \@secondoftwo \fi 
#1<-\reserved@a 
{\expandafter}
{\csname}

\reserved@a ->c@index
{\ifx: (level 1) entered on line 7}
{true}
{\expandafter}
{\else: \ifx (level 1) entered on line 7}
{\fi: \ifx (level 1) entered on line 7}

\@firstoftwo #1#2->#1
#1<-\edef \reserved@b {\expandafter \@carcube \reserved@a xxx\@nil }\ifx \reser
ved@b \@qend \@notdefinable \else \ifx \reserved@a \@qrelax \@notdefinable \els
e \@definecounter {index}\fi \fi 
#2<-\@notdefinable 
{\edef}
{\expandafter}

\reserved@a ->c@index

\@carcube #1#2#3#4\@nil ->#1#2#3
#1<-c
#2<-@
#3<-i
#4<-ndexxxx
{changing \reserved@b=\long macro:#1->}
{into \reserved@b=macro:->c@i}
{\ifx: (level 1) entered on line 7}
{false}
{\else: \ifx (level 1) entered on line 7}
{\ifx: (level 2) entered on line 7}
{false}
{\else: \ifx (level 2) entered on line 7}

\@definecounter #1->\expandafter \newcount \csname c@#1\endcsname \setcounter {
#1}\z@ \global \expandafter \let \csname cl@#1\endcsname \@empty \@addtoreset {
#1}{@ckpt}\global \expandafter \let \csname p@#1\endcsname \@empty \expandafter
 \gdef \csname the#1\expandafter \endcsname \expandafter {\expandafter \@arabic
 \csname c@#1\endcsname }
#1<-index
{\expandafter}
{\csname}

\newcount ->\e@alloc \count \countdef {\count 10}\insc@unt \float@count 

\e@alloc #1#2#3#4#5#6->\global \advance #3\@ne \e@ch@ck {#3}{#4}{#5}#1\allocati
onnumber #3\relax \global #2#6\allocationnumber \wlog {\string #6=\string #1\th
e \allocationnumber }
#1<-\count 
#2<-\countdef 
#3<-\count 10
#4<-\insc@unt 
#5<-\float@count 
#6<-\c@index 
{\global}
{\advance}
{globally changing \count10=86}
{into \count10=87}

\e@ch@ck #1#2#3#4->\ifnum #1<#2\else \ifnum #1=#2\relax \global #1\@cclvi \ifx 
\count #4\global \advance #1 10 \fi \fi \ifnum #1<#3\relax \else \errmessage {N
o room for a new \string #4}\fi \fi 
#1<-\count 10
#2<-\insc@unt 
#3<-\float@count 
#4<-\count 
{\ifnum: (level 3) entered on line 7}
{true}
{\else: \ifnum (level 3) entered on line 7}
{\fi: \ifnum (level 3) entered on line 7}
{\count21}
{changing \count21=102}
{into \count21=87}
{\relax}
{\global}
{\countdef}
{globally changing \c@index=\relax}
{into \c@index=\relax}
{globally changing \c@index=\relax}
{into \c@index=\count87}

\wlog ->\immediate \write \m@ne 
{\immediate}
\write->\string \c@index =\string \count \the \allocationnumber 
{no mode: \string}
{\string}

\setcounter #1#2->\@ifundefined {c@#1}{\@nocounterr {#1}}{\global \csname c@#1\
endcsname #2\relax }
#1<-index
#2<-\z@ 

\@ifundefined #1->\expandafter \ifx \csname #1\endcsname \relax \expandafter \@
firstoftwo \else \expandafter \@secondoftwo \fi 
#1<-c@index
{vertical mode: \expandafter}
{\csname}
{\ifx: (level 3) entered on line 7}
{false}
{\else: \ifx (level 3) entered on line 7}
{\expandafter}
{\fi: \ifx (level 3) entered on line 7}

\@secondoftwo #1#2->#2
#1<-\@nocounterr {index}
#2<-\global \csname c@index\endcsname \z@ \relax 
{\global}
{\csname}
{\count87}
{globally changing \count87=0}
{into \count87=0}
{\relax}
{\global}
{\expandafter}
{\csname}
{changing \cl@index=undefined}
{into \cl@index=\relax}
{\let}
{globally changing \cl@index=\relax}
{into \cl@index=macro:->}

\@addtoreset #1#2->\expandafter \@cons \csname cl@#2\endcsname {{#1}}
#1<-index
#2<-@ckpt
{\expandafter}
{\csname}

\@cons #1#2->\begingroup \let \@elt \relax \xdef #1{#1\@elt #2}\endgroup 
#1<-\cl@@ckpt 
#2<-{index}
{\begingroup}
{entering semi simple group (level 2) at line 7}
{\let}
{reassigning \@elt=\relax}
{\xdef}

\cl@@ckpt ->\@elt {page}\@elt {equation}\@elt {enumi}\@elt {enumii}\@elt {enumi
ii}\@elt {enumiv}\@elt {footnote}\@elt {mpfootnote}\@elt {part}\@elt {section}\
@elt {subsection}\@elt {subsubsection}\@elt {paragraph}\@elt {subparagraph}\@el
t {figure}\@elt {table}
{globally changing \cl@@ckpt=macro:->\@elt {page}\@elt {equation}\@elt \ETC.}
{into \cl@@ckpt=macro:->\@elt {page}\@elt {equation}\@elt \ETC.}
{\endgroup}
{leaving semi simple group (level 2) entered at line 7}
{\global}
{\expandafter}
{\csname}
{changing \p@index=undefined}
{into \p@index=\relax}
{\let}
{globally changing \p@index=\relax}
{into \p@index=macro:->}
{\expandafter}
{\csname}
{\expandafter}
{\expandafter}
{\expandafter}
{\csname}
{\gdef}
{globally changing \theindex=\long macro:->\if@twocolumn \@restonecolfalse \ETC
.}
{into \theindex=macro:->\@arabic \c@index }
{\fi: \ifx (level 2) entered on line 7}
{\fi: \ifx (level 1) entered on line 7}

\@ifnextchar #1#2#3->\let \reserved@d =#1\def \reserved@a {#2}\def \reserved@b 
{#3}\futurelet \@let@token \@ifnch 
#1<-[
#2<-\@newctr {index}
#3<-
{\let}
{reassigning \reserved@d=the character [}
{\def}
{changing \reserved@a=macro:->c@index}
{into \reserved@a=macro:->\@newctr {index}}
{\def}
{changing \reserved@b=macro:->c@i}
{into \reserved@b=macro:->}
{\futurelet}
{changing \@let@token=begin-group character {}
{into \@let@token=end-group character }}

\@ifnch ->\ifx \@let@token \@sptoken \let \reserved@c \@xifnch \else \ifx \@let
@token \reserved@d \let \reserved@c \reserved@a \else \let \reserved@c \reserve
d@b \fi \fi \reserved@c 
{\ifx: (level 1) entered on line 7}
{false}
{\else: \ifx (level 1) entered on line 7}
{\ifx: (level 2) entered on line 7}
{false}
{\else: \ifx (level 2) entered on line 7}
{\let}
{changing \reserved@c=macro:->\@argdef \abstractname [0]}
{into \reserved@c=macro:->}
{\fi: \ifx (level 2) entered on line 7}
{\fi: \ifx (level 1) entered on line 7}

\reserved@c ->
{end-group character }}
{restoring \reserved@c=macro:->\@argdef \abstractname [0]}
{restoring \@let@token=begin-group character {}
{retaining \p@index=macro:->}
{retaining \cl@index=macro:->}
{restoring \count21=102}
{restoring \reserved@b=\long macro:#1->}
{restoring \reserved@a=macro:->\def \@currenvir {document}\edef \ETC.}
{retaining \c@index=\count87}
{restoring \tracingassigns=0}

(没有人说跟踪很短!)您可以使用各种跟踪原语来仅拾取某些操作,例如设置

\tracingonline=1
\tracingassigns=1

将显示分配(并包括终端中的信息,而不仅仅是日志),但不提供其他数据\tracingall

就“中断运行”而言,通常的方法是放入一个未定义的控制序列(我使用\MARK)。这很方便查看“TeX 是否到达这里”。你然后按提示插入更多标记i?然后就可以使用它来结束读取当前文件。例如,对于文件

\documentclass{article}
\begin{document}

\MARK

\end{document}

如果我做

! Undefined control sequence.
l.4 \MARK

? i
insert>\endinput
)
*

我停止 TeX 的读取\end{document},可以进行交互工作。然而,虽然这在 TeX 每页排版可能需要 15 分钟的时代很有用,但现在却没那么有用了:我倾向于简单地终止运行并编辑我的文件。

相关内容