# Introspection and reflection with LaTeX/TeX macros

Below is a short program that can use \meaning and \string to let a combination of LaTeX and TeX commands to print out the listing of macros. Unsurprisingly (nothing can surprise me with TeX any more), the command can also list its own listing.

\makeatletter
\parindent0pt

\def\reflect{\@star@or@long\accommand}
\fboxrule=0.0pt

\def\accommand#1{\framebox[4cm][l]{\color{red}
{\tt\string#1:}} %
\parbox[t]{7cm}{\tt\expandafter\strip@prefix\meaning#1}}

\reflect{\reflect}\\
\reflect{\listoffigures}\\
\reflect{\section}\\
\reflect*{\thispagestyle}\\
\makeatother


I am contemplating to turn this into a package, where one can say

\reflect{command1, command2, command3} and obtain a listing of the control sequences. Am I duplicating any package you know? Is there another approach than the above? Code fails in mathmode for example \reflect{\gamma}.

-
Interesting. When I need to see the definition of a macro I just use \show to have it output in the console. –  Matthew Leingang Nov 1 '10 at 15:21
@Matthew Leingang I use \show also, but on large packages, I prefer to print as I go along for debugging. –  Yiannis Lazarides Nov 1 '10 at 15:25
cute. two comments: (1) i think of "reflect" as what happens in a mirror, and have used that name to mirror horizontally symbols or strings using the graphics package (which already has a \reflectbox command). i'd call this something different, maybe \unwrap or \unravel. (2) maybe i assumed too much, but i tried to run this directly, without wrapping it with a document class and \begin/\end{document}, ... and failed. guess i'm spoiled, but i like examples that run right out of the box. –  barbara beeton Nov 4 '10 at 21:13
I might not be in the target audience, but I don't see this being very useful to me. (It's certainly fun to experiment with, however.) I usually find that when I need to inspect macro definitions, the lack of indentation and line breaks in the output of \show and \meaning limit their utility in many cases. –  Will Robertson Nov 7 '10 at 14:08
I tend to concur with Will, as far as heavy debugging is concerned. And for quick debugging, I prefer to run TeX interactively and use \show to see the output directly on the terminal. –  mpg Nov 7 '10 at 14:58

EDIT: a few weeks ago, I wrote this "answer", wishing for something more powerful than Yiannis did, namely, the ability of executing step by step the LaTeX file. I have a prototype. See the bottom of this post for the full code.

EDIT 2,3,4: improved the code.

I am not sure that this should be an answer, but it is definitely too long for a comment.

I would find it neat if I could type \unravel{\section*{title}} and see the macro unravel: first, expand it once, then expand the first of these macros once and show me the resulting token list, and continue expanding in the order that TeX would do it, until there is a need to read something after the token list given as an argument to \unravel.

For instance, \unravel{\section*} should give

\@startsection {section}{1}{\z@ }{-3.5ex \@plus -1ex \@minus
-.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }*


notice the star at the end. Then it should give

\if@noskipsec \leavevmode \fi \par \@tempskipa -3.5ex \@plus -1ex
\@minus -.2ex\relax \@afterindenttrue \ifdim \@tempskipa <\z@
\@tempskipa -\@tempskipa \@afterindentfalse \fi \if@nobreak
\fi \@ifstar {
\@ssect {\z@ }{-3.5ex \@plus -1ex \@minus -.2ex}{2.3ex \@plus
.2ex}{\normalfont \Large \bfseries }
}{
\@dblarg {\@sect {section}{1}{\z@ }{-3.5ex \@plus -1ex \@minus
-.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }}
}*


etc. It would also be neat to be able to skip the expansion of some commands: if I already know very well what \@sect does, I should be able to happily do several steps of the expansion at that point.

EDIT (cont.): Sorry, the code is quite big (and has bugs). I tried to include some comments. In the end, you just write \debug@unravel{\textrm{Hello}} to see TeX at work.

\documentclass{article}
\usepackage{trace}
\makeatletter

% Storing a few primitives that we use, to avoid crashes if they are
% redefined by the user. [...]

% =========== Setup.
% "todo": tokens that are going to be read, expanded, etc.
% "modifiers": a place to save modifiers (so far, only "\global")
% "done": the output, which in the best possible world would contain
%   tokens that produce the same output as the original ones.
\newtoks\debug@toks@todo
\long\xdef\debug@modifiers@tl{}
\newtoks\debug@toks@done
\long\gdef\debug@afterassignment@token{}

\long\gdef\debug@toks@done@append#1{%
\global\debug@toks@done\expandafter{\the\debug@toks@done#1}%
}

% Various scratch objects.
\newcount\debug@tmp@count
\newskip\debug@tmp@skip
\newbox\debug@tmp@box

% A few parameters to control display:
\newcounter{debug@output@stream} \setcounter{debug@output@stream}{16}
\newcounter{debug@prompt@stream} \setcounter{debug@prompt@stream}{-1}
\newcounter{debug@noise}         \setcounter{debug@noise}{1}
\newcounter{debug@steps}         \setcounter{debug@steps}{0}
\newcounter{debug@nonstop}       \setcounter{debug@nonstop}{1}

% Spaces make \debug@unravel{...} choke!
%
% Explicit braces which start a group (rather than the argument
% of a macro) are not yet implemented.
%
% \noexpand, \afterassignments, \aftergroup,
% \unhbox, \unvbox, \unhcopy, \unvcopy,
% \write,
% ...
% need to be implemented

% The main command, \debug@unravel, simply sets the toks \debug@toks@todo,
% and repeatedly does one step of the expansion until we reach the
% end of the list.
\long\gdef\debug@unravel#1{%
\debug@print@welcome%
%
\global\debug@toks@todo{#1}%
\global\debug@toks@done{}%
%
\loop%
\debug@step%
\repeat%
%
\debug@print@outcome%
}

\long\gdef\debug@insurance#1{%
\begingroup\escapechar=\\\relax%
#1%
\endgroup%
}

\long\gdef\debug@step{%
%
% Then, in a safe environment (\escapechar=92),
% - analyse the token,
% - print the token list
% - print/show the first token
% - figure out whether we should be using \debug@type or @iv
% - prepare the message on what we will be doing
\debug@insurance{%
\debug@print@done%
\debug@print@todo%
\debug@print@first%
\debug@prompt%
\debug@type@find@correct%
\debug@setup@print@wedid%
}%
%
% Finally, do what we should do.
\csname debug@do@\debug@type@correct\endcsname%
\debug@print@wedid% auto-insured
}

\long\gdef\debug@type@find@correct{%
\@ifundefined{debug@do@\debug@type}{%
\@ifundefined{debug@do@\debug@type@iv}{%
\debug@typeout{Warning: Unknown token \debug@solid@name.}%
\debug@typeout{\@spaces this dumps \debug@solid@name into the output,}%
\debug@typeout{\@spaces by inserting \noexpand\debug@primitive{\debug@type}{unsupp}}%
\debug@prompt@{\debug@type@find@correct@ifempty}%
\debug@type@find@correct%
}{%
\global\let\debug@type@correct\debug@type@iv%
}%
}{%
\global\let\debug@type@correct\debug@type%
}%
}

\long\gdef\debug@type@find@correct@ifempty#1{%
\ifx\debug@empty@short#1%
\debug@primitive{\debug@type}{unsupp}%
\fi
#1}

% short, not long!
\gdef\debug@empty@short{}
\long\gdef\debug@empty@long{}

% ========== Macros to read the beginning of \debug@toks@todo.
\long\gdef\debug@qstop{\debug@qstop}
\global\let\debug@varqstop\debug@qstop
\long\gdef\debug@to@stop#1\debug@qstop{}
\long\gdef\debug@to@stop@keep#1\debug@qstop{#1}
\expandafter\futurelet\expandafter#1%
\expandafter\debug@to@stop\the#2\debug@qstop}

% ==== below, "unsafe" means we do not check for explicit braces or #.
% \debug@toks@todo and stores them in \debug@read@x.
%
\debug@varqstop\debug@qstop%
}
\expandafter\long\expandafter\gdef%
\else%
\expandafter\debug@to@stop%
\fi%
}

% ========== Macros to understand the meaning of the head.
\long\gdef\debug@type@from@meaning#1{%
\expandafter\debug@meaning@aux\meaning#1 \debug@qstop%
\expandafter\debug@tfm@macro\meaning#1->\debug@qstop%
\long\xdef\debug@type@iv{%
\expandafter\debug@type@nondigits\debug@type\debug@qstop}%
}

\long\gdef\debug@tfm@macro#1->#2\debug@qstop{%
\ifx\debug@qstop#2\debug@qstop%
\else%
\long\gdef\debug@type{macro}%
\fi}

\begingroup
\lccode\*\\
\lowercase{%
\endgroup%
\let\debug@char@bs*}

% \long\gdef\debug@iv#1#2#3#4#5\debug@qstop{#1#2#3#4}

\long\gdef\debug@type@nondigits#1{%
\ifx\debug@qstop#1%
\expandafter\debug@use@TF%
\else%
\expandafter\debug@use@FT%
\fi%
{}{%
\ifnum9<1#1 % Critical space
\expandafter\debug@use@TF%
\else%
\expandafter\debug@use@FT%
\fi%
{\debug@to@stop}%
{#1\debug@type@nondigits}%
}%
}

\long\gdef\debug@use@TF#1#2{#1}
\long\gdef\debug@use@FT#1#2{#2}
\long\gdef\debug@use@FF#1#2{}
\long\gdef\debug@use@F#1{}
\long\gdef\debug@gobble#1{}

\long\gdef\debug@meaning@aux#1#2 #3\debug@qstop{%
\ifx\debug@char@bs#1%
\long\gdef\debug@type{#2}%
\else%
\long\gdef\debug@type{character}%
\fi%
}

% ============= Let us now act! =======================

% For "macros", we expand once, whether it is protected or not.
\long\gdef\debug@do@expand{%
\debug@toks@expandfirst\debug@toks@todo}

\long\gdef\debug@toks@expandfirst#1{%
\expandafter\expandafter\expandafter\expandafter%
\expandafter\expandafter\expandafter\global%
\expandafter\expandafter\expandafter\expandafter%
\expandafter\expandafter\expandafter#1%
\expandafter\expandafter\expandafter\expandafter%
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter\empty%
\the#1%
}%
}

% Special type of expansion: conditionals, for which we wish to also
% output the outcome of the test.
\long\gdef\debug@do@expand@if#1{%
\debug@insurance{%
\long\xdef\debug@print@wedid@tl{\space%
}%
}%
\debug@do@expand%
}

% For a few other cases, we remove the head of \debug@toks@todo
% and append it to \debug@toks@done
\long\gdef\debug@do@keep{%
\debug@save@first\debug@use@first\debug@remove@first}
\long\gdef\debug@do@character{%
\debug@save@first\debug@use@first\debug@remove@first}
\long\gdef\debug@do@unsupp{%
\debug@save@first\debug@remove@first}
\long\gdef\debug@do@throw{\debug@remove@first}
\long\gdef\debug@do@justdo{%
\expandafter\expandafter\expandafter\global%
\expandafter\expandafter\expandafter\debug@toks@todo%
\expandafter\expandafter\expandafter{%
\expandafter\debug@use@F\the\debug@toks@todo}%
}
\long\gdef\debug@do@relax{%
\expandafter\expandafter\expandafter\global%
\expandafter\expandafter\expandafter\debug@toks@todo%
\expandafter\expandafter\expandafter{%
\expandafter\debug@use@F\the\debug@toks@todo}%
}

% For assignments, we use \afterassignment\debug@do@assign@aux
% to regain control after letting TeX do the assignment.
\long\gdef\debug@do@assign@after{%
\long\xdef\debug@modifiers@tl{}%
\let\debug@tmpa\debug@afterassignment@token%
\let\debug@afterassignment@token\debug@empty@long%
\global\debug@toks@todo\expandafter\expandafter\expandafter{%
\expandafter\debug@tmpa\iffalse}\fi%
}
\long\gdef\debug@do@assign{%
\afterassignment\debug@do@assign@after%
\iffalse{\fi%
\expandafter\debug@modifiers@tl%
\the\debug@toks@todo}%
}

% Todo: reset modifiers after any step
\long\gdef\debug@do@modifier{%
\long\xdef\debug@modifiers@tl{%
\debug@remove@first%
}

% Experimental \afterassignment.
\long\gdef\debug@do@afterassignment{%
\debug@remove@first%
\debug@remove@first%
}

% When we want to keep things like skips, penalties, etc., and
% particularly cleanup after them, we assign their "argument" to
% an internal skip or count, and regain control \afterassignment.
% We use \debug@do@keep@skip and \debug@do@keep@count.
\long\gdef\debug@remember#1{\long\gdef\debug@remembered{#1}}

\long\gdef\debug@do@keep@after{%
\long\xdef\debug@modifiers@tl{}%
\global\debug@toks@todo\expandafter{\iffalse}\fi%
}

\long\gdef\debug@do@keep@#1{%
\long\gdef\debug@tmpa{\afterassignment\debug@do@keep@after%
\csname debug@tmp@#1\endcsname}%
\afterassignment\debug@tmpa%
\iffalse{\fi\expandafter\debug@remember\the\debug@toks@todo}%
%
\expandafter\debug@remembered\the\csname debug@tmp@#1\endcsname%
%
\expandafter\expandafter\expandafter\debug@toks@done@append%
\expandafter\expandafter\expandafter{%
\expandafter\debug@remembered%
\the\csname debug@tmp@#1\endcsname%
\relax}%
}

\long\gdef\debug@do@keep@count{\debug@do@keep@{count}}
\long\gdef\debug@do@keep@skip{\debug@do@keep@{skip}}

\long\gdef\debug@do@keep@count@edef{%
\long\gdef\debug@tmpa{% captures the rest of the toks
\global\debug@toks@todo\expandafter{\iffalse}\fi%
}%
\long\gdef\debug@tmpb{% captures the text to write, then proceed
\afterassignment\debug@tmpa%
\edef\debug@tmpw% will get the text to write
}%
\long\gdef\debug@tmpc{% captures the stream number, then proceed
\afterassignment\debug@tmpb%
\global\debug@tmp@count% will get the stream.
}%
\afterassignment\debug@tmpc%
\iffalse{\fi\expandafter\debug@remember\the\debug@toks@todo}%
%
\long\xdef\debug@tmpa{%
\debug@modifiers@tl%
\expandafter\noexpand\debug@remembered%
\the\debug@tmp@count%
{\unexpanded\expandafter{\debug@tmpw}}%
}%
\debug@tmpa%
\expandafter\debug@toks@done@append\expandafter{\debug@tmpa}%
\long\xdef\debug@modifiers@tl{}%
}

% For \setbox, the situation is more complicated: \afterassignment only
% brings us inside the box. So we \afterassignment a macro which expands
% to \aftergroup \<regain_control>.

\long\gdef\debug@do@assign@after@afgr{\aftergroup\debug@do@assign@after}
\long\gdef\debug@do@assign@box{%
\afterassignment\debug@do@assign@after@afgr%
\iffalse{\fi%
\the\debug@toks@todo}%
}

% For \hbox, \vbox, \vtop, we cheat by storing them inside the dummy
% box \debug@tmp@box (using \savebox that we have just obtained).
\long\gdef\debug@do@any@box{%
\global\debug@toks@todo\expandafter{%
\expandafter\setbox\expandafter\debug@tmp@box\expandafter=%
\the\debug@toks@todo}%
}

% ===== Moving the first from \debug@toks@todo to \debug@toks@done.

\long\gdef\debug@remove@first{%
\expandafter\expandafter\expandafter\global%
\expandafter\expandafter\expandafter\debug@toks@todo%
\expandafter\expandafter\expandafter{%
\expandafter\debug@use@F\the\debug@toks@todo}%
}

\long\gdef\debug@save@first{%
\expandafter\debug@save@first@\the\debug@toks@todo\debug@qstop}
\long\gdef\debug@save@first@#1{%
\debug@toks@done@append{#1}%
\debug@to@stop}

\long\gdef\debug@use@first{%
\expandafter\debug@use@first@\the\debug@toks@todo\debug@qstop}
\long\gdef\debug@use@first@#1{%
\expandafter#1\debug@to@stop}

% ============ Setup one macro per primitive

\long\gdef\debug@primitive#1{%
\long\xdef\debug@tmpa@tl{#1}%
\debug@primitive@aux%
}
\long\gdef\debug@primitive@iv#1{%
\long\xdef\debug@tmpa@tl{\debug@type@nondigits #1\debug@qstop}%
\debug@primitive@aux%
}
\long\gdef\debug@primitive@aux#1{%
\long\xdef\debug@tmpb@tl{%
\unexpanded\expandafter\expandafter\expandafter{%
\csname debug@text@do@#1\endcsname}}%
\expandafter\global\expandafter\let%
\csname debug@do@\debug@tmpa@tl\expandafter\endcsname%
\csname debug@do@#1\endcsname%
\expandafter\long\expandafter\gdef\csname debug@text@do@\debug@tmpa@tl%
\expandafter\endcsname\expandafter{\debug@tmpb@tl}%
}

\long\gdef\debug@text@do@character{Output the character \debug@solid@name. }
\long\gdef\debug@text@do@expand{Expanded \debug@solid@name once. }
\long\gdef\debug@text@do@keep{Sent \debug@solid@name to the output. }
\long\gdef\debug@text@do@justdo{Just did \debug@solid@name! }
\long\gdef\debug@text@do@throw{Ignored \debug@solid@name! }
\long\gdef\debug@text@do@relax{Relaxed a bit with \debug@solid@name. }
\long\gdef\debug@text@do@keep@skip{Output \debug@solid@name and its skip. }
\long\gdef\debug@text@do@keep@count{Output \debug@solid@name and its count. }
\long\gdef\debug@text@do@keep@count@edef{Maybe it was a write? \debug@solid@name. }
\long\gdef\debug@text@do@unsupp{Unsupported \debug@solid@name dumped into the output. }
\long\gdef\debug@text@do@assign{Assigned using %
\detokenize\expandafter{\debug@modifiers@tl}\debug@solid@name... }
\long\gdef\debug@text@do@assign@box{Assigned a box using \debug@solid@name... }
\long\gdef\debug@text@do@any@box{Preparing to store the \debug@solid@name in our box. }
\long\gdef\debug@text@do@modifier{Remembering the modifier \debug@solid@name. }
\long\gdef\debug@text@do@afterassignment{Attempting to take care of afterassignment.}

% In the current implementation, macros are mis-recognized as
% their modifiers, so we need to cheat and pretend that those
% modifiers should just be expanded. This will fail if there
% is a genuine \long or \protected or \outer out there.

\debug@primitive{immediate}{modifier}
\debug@primitive{global}{modifier}
%\debug@primitive{outer}{modifier}%disabled because it will break me.
\debug@primitive{long}{modifier}
\debug@primitive{protected}{modifier}

\debug@primitive{macro}{expand}
\debug@primitive{expandafter}{expand}
\debug@primitive{noexpand}{expand}
\debug@primitive{detokenize}{expand}
\debug@primitive{string}{expand}
\debug@primitive{csname}{expand}
\debug@primitive{lowercase}{expand}
\debug@primitive{uppercase}{expand}

\debug@primitive{relax}{relax}
\debug@primitive{par}{keep}
\debug@primitive{indent}{keep}
\debug@primitive{unskip}{keep}
\debug@primitive{unhbox}{keep@count}
\debug@primitive{unhcopy}{keep@count}
\debug@primitive{penalty}{keep@count}
\debug@primitive{vskip}{keep@skip}
\debug@primitive{hskip}{keep@skip}
\let\debug@do@muskipAA\debug@do@keep@muskip% not yet supported!

\debug@primitive{begingroup}{keep}
\debug@primitive{endgroup}{keep}
\debug@primitive{write}{keep@count@edef}

\debug@primitive{ignorespaces}{unsupp}
\debug@primitive{unpenalty}{unsupp}
\debug@primitive{/}{unsupp}
\debug@primitive{hbox}{unsupp}
\debug@primitive{vbox}{unsupp}
\debug@primitive{afterassignment}{afterassignment}
\debug@primitive{aftergroup}{throw}

\debug@primitive{futurelet}{assign}
\debug@primitive{let}{assign}
\debug@primitive{def}{assign}
\debug@primitive{edef}{assign}
\debug@primitive{gdef}{assign}
\debug@primitive{xdef}{assign}
\debug@primitive@iv{toks}{assign}
\debug@primitive@iv{skip}{assign}
\debug@primitive@iv{font}{assign}
\debug@primitive@iv{muskip}{assign}
\debug@primitive@iv{dimen}{assign}
\debug@primitive@iv{count}{assign}
\debug@primitive{everypar}{assign}
\debug@primitive{baselineskip}{assign}
\debug@primitive{parfillskip}{assign}
\debug@primitive{parskip}{assign}
\debug@primitive{leftskip}{assign}
\debug@primitive{rightskip}{assign}
\debug@primitive{parindent}{assign}
\debug@primitive{parshape}{assign}
\debug@primitive{escapechar}{assign}
\debug@primitive{hyphenchar}{assign}
\debug@primitive{catcode}{assign}
\debug@primitive{lccode}{assign}
\debug@primitive{uccode}{assign}
\debug@primitive{accent}{assign}%??
\debug@primitive{tracingassigns}{assign}
\debug@primitive{tracingcommands}{assign}
\debug@primitive{tracinggroups}{assign}
\debug@primitive{tracinglostchars}{assign}
\debug@primitive{tracingmacros}{assign}
\debug@primitive{tracingonline}{assign}
\debug@primitive{tracingoutput}{assign}
\debug@primitive{tracingpages}{assign}
\debug@primitive{tracingparagraphs}{assign}
\debug@primitive{tracingrestores}{assign}
\debug@primitive{tracingstats}{assign}
\debug@primitive{showboxdepth}{assign}

\debug@primitive{setbox}{assign@box}
\debug@primitive{hbox}{any@box}
\debug@primitive{vbox}{any@box}
\debug@primitive{vtop}{any@box}

\debug@primitive{if}{expand}
\debug@primitive{ifdim}{expand}
\debug@primitive{ifnum}{expand}
\debug@primitive{else}{expand}
\debug@primitive{fi}{expand}
\long\gdef\debug@do@ifx{\debug@do@expand@if{3}}
\long\gdef\debug@do@ifvmode{\debug@do@expand@if{1}}
\long\gdef\debug@do@ifhmode{\debug@do@expand@if{1}}
\long\gdef\debug@do@ifmmode{\debug@do@expand@if{1}}
\long\gdef\debug@do@iffalse{\debug@do@expand@if{1}}
\long\gdef\debug@do@iftrue{\debug@do@expand@if{1}}

% ========== Messages.
\usepackage{hardwrap}

\long\gdef\debug@typeout#1{\immediate\write\c@debug@output@stream{#1}}

\long\gdef\debug@print@welcome{%
\debug@typeout{}%
\debug@typeout{======== Welcome to the debug package ========}%
\debug@typeout{\@spaces "|>" denotes tokens that we will act on.}%
\debug@typeout{\@spaces "<|" denotes the output to TeX's stomach.}%
\debug@typeout{\@spaces Type "\string\step{2}" to do two steps [...]}%
\debug@typeout{}%
}

\long\gdef\debug@print@outcome{%
\debug@typeout{Step \the\c@debug@steps\space===== The end!}%
\debug@print@done%
}

\long\gdef\debug@message@aux#1#2#3{%
\begingroup%
\lccode\*=\newlinechar\relax%
\lowercase{\def\debug@newline{*}}%
\def\MessageBreak{%
\debug@newline #1\space\space}%
\set@display@protect%
\debug@typeout{#2\MessageBreak #3}%
\debug@typeout{}%
\endgroup}%

\long\gdef\debug@message#1#2#3{%
\HardWrap{\debug@message@aux{#1}{#2}}{72}{\HardWrapSetup}%
{\MessageBreak}{#3}}

\long\gdef\debug@print@todo{%
\ifnum\the\c@debug@noise>-1\relax%
\debug@message{|>}{\expandafter\debug@use@F}{%
\detokenize\expandafter{\the\debug@toks@todo}}%
\fi}

\long\gdef\debug@print@done{%
\debug@insurance{%
\debug@message{<|}{\expandafter\debug@use@F}{%
\detokenize\expandafter{\the\debug@toks@done}}}}

\long\gdef\debug@setup@print@wedid{%
\long\xdef\debug@print@wedid@tl{\space%
\csname debug@text@do@\debug@type@correct\endcsname}}

\long\gdef\debug@print@wedid{%
\debug@insurance{%
\lccode\*=\newlinechar%
\lowercase{\long\gdef\MessageBreak{*\@spaces\@spaces\@spaces}}%
\debug@typeout{Step \the\c@debug@steps\space%
=====\debug@print@wedid@tl}%
\ifnum\c@debug@noise>-1\relax%
\debug@typeout{}%
\fi%
}%
}

% ============= To show the first token.

\long\gdef\debug@print@first{%
\ifnum\the\c@debug@noise>0\relax
\debug@message{\debug@use@FF}{\debug@use@F}{%
\debug@first@string=\debug@first@meaning}%
\fi%
}

% ============= The prompt
\long\def\debug@hop@else#1\else#2\fi{\fi#1}
\long\def\debug@hop@fi#1\fi{\fi#1}

\long\gdef\debug@prompt{%
\ifnum\the\c@debug@nonstop=1\relax%
\debug@hop@else\debug@prompt@{\debug@prompt@treat}%
\else%
\fi%
}

\long\gdef\debug@prompt@treat#1{%
\expandafter\debug@prompt@treat@aux#1\debug@qstop}
\long\gdef\debug@prompt@treat@aux#1{%
\ifcat A\noexpand#1\relax%
\expandafter\debug@use@TF%
\else%
\expandafter\debug@use@FT%
\fi%
{\csname debug@prompt@treat@#1\endcsname%
\debug@prompt@treat@aux}%
{\debug@to@stop@keep#1}%
}
\long\gdef\debug@prompt@treat@q{\noise{-1}\step{0}}
\long\gdef\debug@prompt@treat@x{\endgroup\endgroup\fi\iffalse}
\long\gdef\debug@prompt@treat@s#1#2{\silentstep{#2}#1}
\long\gdef\debug@prompt@treat@o#1#2{%
\ifnum#2<0\relax%
\else\ifnum#2=0\relax%
\global\c@debug@output@stream-1\relax%
\else%
\global\c@debug@output@stream16\relax%
\fi\fi%
#1}

\long\xdef\debug@prompt@tl#1{%
\long\gdef\debug@prompt@before@tl{}
\long\gdef\debug@prompt@#1{%
\begingroup\escapechar=-1\relax%
\def\step##1{\global\c@debug@nonstop##1\relax}%
\def\noise##1{\global\c@debug@noise##1\relax}%
\def\silentstep##1{%
\noise{-1}%
\step{##1}%
\long\gdef\debug@prompt@before@tl{\noise{1}}%
}%
\endlinechar=-1%
\debug@prompt@before@tl%
\long\gdef\debug@prompt@before@tl{}%
\debug@prompt@tl{#1}%
\endgroup}

\usepackage{amsmath}
\begin{document}

\section{Normal-section}

Title above for comparison purposes.

\debug@unravel{\section{Unravelled-section}}%

The section title above was typeset as we unravelled.

\the\debug@toks@done%

That third section is made using what is stored at
the end of the unravelling.

Only trivial maths works: $\debug@unravel{xy = z}$,
and it is stored in our ouput token list, $\the\debug@toks@done$.

Some more complex maths symbols fail, complain, cheat:
$\debug@unravel{\int\mathrm{d}x\sin(\pi)=0}$
$\the\debug@toks@done.$

And sub- and super-scripts are totally unsupported so far.

\end{document}

-
I agree! I think that you could theoretically generate this information from parsing the log file with \tracingall. Anyone game? :) –  Will Robertson Jan 20 '11 at 11:36
Sounds like a nice idea. If can easily do it if there are only expandable tokens, but in your example, you get a \par after 3 expansions, and even worse, \@tempskipa -3.5ex \@plus -1ex \@minus -.2ex\relax after the \par. I'm not sure how to handle this. –  Hendrik Vogt Jan 20 '11 at 12:12
@Bruno It sounds like a good idea, but is probably best done by parsing the log file with an external script. –  Yiannis Lazarides Jan 20 '11 at 15:19
@Hendrik: see the code that I added above for a way to have unexpandable tokens without too much trouble (still plenty of primitives are not implemented, but e.g., \xdef, \let, \expandafter, TeX dimen and count assignments, \setbox, etc. are). I would be glad to have some feedback :). Should I post this on comp.text.tex for example? –  Bruno Le Floch Jan 31 '11 at 14:19
@Bruno: I haven't had time to look at it in details, but it seems to be a great start. I can't say anything about comp.text.tex; never been there. –  Hendrik Vogt Jan 31 '11 at 15:57

Charles has asked me to post a summary of the comments above; here's a quick synopsis.

Will:

I might not be in the target audience, but I don't see this being very useful to me. (It's certainly fun to experiment with, however.) I usually find that when I need to inspect macro definitions, the lack of indentation and line breaks in the output of \show and \meaning limit their utility in many cases.

Barbara:

For quick debugging, I prefer to run TeX interactively and use \show to see the output directly on the terminal.