8

As I'm using LaTeX (in my case xelatex), I often run into little gotchas. As a programmer from other more functional or procedural languages, it really grates me that I can't easily figure out what an expression evaluates to. So I looked here at SE, and I didn't find a whole lot. I did find the trace package, which is very helpful. But I'm still confused as to how to interpret the output, especially when evaluating several levels of commands and the values passed between them.

I found the \show command, which is nice for a one-off definition in the log. I also found texdef which is nice that I can use it quickly from the terminal. I'm already familiar with \message, \typeout, etc. I'm not looking for any commands to use in a latex document then compile it and inspect the log file.

Is there any way to use latex in an interactive REPL mode? An example could be:

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

> \theindex
# 0

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

Does that make sense? This way when I want to inspect commands, I don't have to set up elaborate tracing sequences when I need just a bit at a time. Is this possible with latex? (This can be done in most scripted languages with a flag at the terminal, e.g. php -a or irb for ruby.)

UPDATE: Thanks to @JosephWright, it looks like if the latex file is compiled from the command line, i.e. latex file.tex, then there's the chance to drop the interactive mode I was talking about. Once this is accomplished, it looks like I still have to use trace commands. Preamble commands won't work (like \usepackage) unless I'm missing something.

My next problem is if I follow the advice given and leave off the \end{document} command, then I'm launched into interactive mode with a * prompt. When I enter commands or anything (with tracing on) I can see it's working and processing my input. If I mistype something that causes an error, I'll get a message, and enter the ? prompt. To return to the * prompt, I just need to hit .

The issue is if I try to break in the document at a specific spot (like putting an undefined sequence like \BREAK), then I'm launched directly into the error prompt ?, and hitting return just ignores the error and continues compiling the document. It does not send me into the input prompt *. How can this be achieved?

  • 1
    Not interactive, but you can print the definition etc. of something with \meaning\foo in the document body, and of course \typeout{...}, and perhaps xelatex --interaction=, where a specific mode is to be specified – user31729 May 28 '16 at 10:26
  • 2
    it will be a step for humanity. – touhami May 28 '16 at 10:39
  • \tracingall first, perhaps? I'm not sure otherwise what you expect to see. (Okay, you might just trace assignments but it's not so helpful) – Joseph Wright May 28 '16 at 18:28
  • I'm not interested in commands that are used to trace from within a document. I know that this method already exists. I'm looking for if this other type of interaction exists or not. – tralston May 28 '16 at 18:54
  • 1
    you don't need the * prompt that is just if there is no more file to run, to interactively insert commands at the ? prompt, use i – David Carlisle May 29 '16 at 9:33
8

There are two important factors to bear in mind about TeX when thinking about debugging or indeed anything else.

The first contains input: TeX reads input either from a file or from the terminal, and these are equivalent. In most cases, a file (or files) is set up to contain the entire TeX run and is self-contained (in LaTeX terms including \end{document}). However, there is no requirement that this is the case, and one can simply start a TeX run and type in commands at the * prompt or use a partial file (one that does not reach \end{document} or equivalent): TeX will process the file then wait for input. Thus what we are always worried about is what is 'next in the input stream' not 'in a file' or 'in the terminal' or whatever. Notable here, there is no 'interactive mode' in the same way as some other languages have: there's just 'TeX waiting for more input'.

Secondly, TeX is a macro expansion language, so there is no 'evaluation'. Two things can happen when TeX finds a control sequence. Expandable commands are expanded, that is their definition is replaced by their replacement text (for a macro) or the result of expansion (for an expandable primitive). You can't see this directly, in the sense that tracing doesn't show the 'upcoming input stream', though you can show the result of expansion: I'll detail that below. For non-expandable primitives, which deal primarily with assignments, typesetting and writing to files, the result may be viewable but again there is no 'return' to look at.

A knock-on effect of TeX being a macro language is that LaTeX is implemented as a series of TeX macros (and settings) and that in the end LaTeX commands resolve into TeX constructs. A lot of tracing approaches will show this up and to see the 'real' outcome of what is happening you need to know some of this.

The result of these considerations is that working with (La)TeX uses techniques which may be unfamiliar to those experienced with other languages. (Of course, on the flip side other languages seem odd to people primarily experienced with TeX!) As the question notes, a useful ability is to \show the definition of a token

\show\mymacro

which might give for example

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

if I've done \newcommand*\mymacro[1]{foo~#1} or \def\mymacro#1{foo~#1}. Where data is stored in TeX registers, rather than in macros, this won't be so helpful. That's important as for example LaTeX counters are implemented as registers. So with

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

we get

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

As noted in the question, \theindex prints the value of the counter, and we could use that for example in

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

or

\typeout{\theindex}

to see the value as \theindex is expandable (\edef forces expansion in the same was \typeout does). We can also look directly at the value of the register with \showthe:

\showthe\count87

or

\makeatletter
\showthe\c@index

both yield

> 0.

(I've used here the fact that I know that the LaTeX counter index is the TeX count register \c@index.)

For more generally picking up 'what is going on', the usual method is to use \tracingall. This can be inserted wherever you want (in a document, at the terminal) as, as already noted, the two are equivalent. You might choose to do this in a group to see just what happens for one thing

{\tracingall\newcounter{index}}

gives

{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}

(No one said tracing was short!) There are various tracing primitives you might use to pick up only some operations, for example setting

\tracingonline=1
\tracingassigns=1

will show assignments (and include the information in the terminal, not just the log), but none of the other data \tracingall gives.

In terms of 'interrupting the run', the usual approach is to put in an undefined control sequence (I use \MARK). This is handy to see 'did TeX get here'. You can then insert more tokens by pressing i a the the ? prompt, and you can use that to end reading the current file. For example, with a file

\documentclass{article}
\begin{document}

\MARK

\end{document}

if I do

! Undefined control sequence.
l.4 \MARK

? i
insert>\endinput
)
*

I stop TeX ever reading \end{document} and can work interactively. However, whilst this was useful in the days when TeX might take 15 minutes per page of typesetting, it's less useful today: I tend to favour simply killing the run and editing my file.

  • 1
    \tracingall is your friend: I find the lack of this in say Lua extremely frustrating! – Joseph Wright May 29 '16 at 9:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.