使用 LaTeX/TeX 宏进行自省和反射

使用 LaTeX/TeX 宏进行自省和反射

下面是一个简短的程序,可以使用\meaning\string让 LaTeX 和 TeX 命令组合打印出宏列表。不出所料(TeX 再也不会让我感到惊讶了),该命令还可以列出自己的列表。

\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

我正在考虑把它变成一个包,这样就可以说

\reflect{command1, command2, command3}并获得控制序列的列表。我是否重复了您知道的任何包?除了上述方法之外还有其他方法吗?例如,代码在数学模式下失败\reflect{\gamma}

答案1

编辑:几周前,我写了这个“答案”,希望能有比 Yiannis 更强大的东西,即能够逐步执行 LaTeX 文件。我有一个原型。请参阅本文底部的完整代码。

编辑 2、3、4:改进了代码。

我不确定这是否应该是一个答案,但对于评论来说它肯定太长了。

如果我可以输入并看到宏的解开,我会觉得很简洁\unravel{\section*{title}}:首先,展开一次,然后展开第一个宏一次并向我显示结果标记列表,然后按照 TeX 执行的顺序继续展开,直到需要在作为参数给出的标记列表之后读取一些内容\unravel

例如,\unravel{\section*}应该给予

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

注意末尾的星号。然后它应该给出

\if@noskipsec \leavevmode \fi \par \@tempskipa -3.5ex \@plus -1ex 
\@minus -.2ex\relax \@afterindenttrue \ifdim \@tempskipa <\z@ 
\@tempskipa -\@tempskipa \@afterindentfalse \fi \if@nobreak 
\everypar {}\else \addpenalty \@secpenalty \addvspace \@tempskipa 
\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 }}
}*

等等。如果能够跳过某些命令的扩展就好了:如果我已经非常清楚该做什么\@sect,我应该能够愉快地执行扩展的几个步骤。

编辑(续):抱歉,代码太大了(而且有 bug)。我尝试添加一些注释。最后,你只需要写代码\debug@unravel{\textrm{Hello}}就能看到 TeX 是如何工作的。

\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{}%
  \global\let\debug@head\relax%
  % 
  \loop%
  \debug@set@to@head\debug@head\debug@toks@todo%
  \unless\ifx\debug@head\debug@qstop%
  \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@type@from@meaning\debug@head%
    \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@read@unsafe{1}%
      \debug@typeout{Warning: Unknown token \debug@solid@name.}%
      \debug@typeout{\@spaces Input code to insert here. Or press return to proceed:}%
      \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}
\long\gdef\debug@set@to@head#1#2{%
  \expandafter\futurelet\expandafter#1%
  \expandafter\debug@to@stop\the#2\debug@qstop}


% ==== below, "unsafe" means we do not check for explicit braces or #.
% \debug@read@unsafe{3} reads the 3 first (at least 1) tokens of 
% \debug@toks@todo and stores them in \debug@read@x.
\newcount\debug@read@count%
%
\long\gdef\debug@read@unsafe#1{%
  \debug@read@count #1\relax%
  \long\gdef\debug@read@x{}%
  \expandafter\debug@read@unsafe@aux\the\debug@toks@todo%
  \debug@varqstop\debug@qstop%
}
\long\gdef\debug@read@unsafe@aux#1{%
  \expandafter\long\expandafter\gdef%
  \expandafter\debug@read@x\expandafter{\debug@read@x#1}%
  \advance \debug@read@count by \m@ne%
  \ifnum\debug@read@count>0\relax%
  \expandafter\debug@read@unsafe@aux%
  \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{%
    \debug@read@unsafe{#1}%
    \long\xdef\debug@print@wedid@tl{\space%
      Test: \detokenize\expandafter{\debug@read@x}= %
      \debug@read@x true \else false \fi%
    }%
  }%
  \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}% 
  \debug@head%
}
\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{%
  \debug@read@unsafe{1}%
  \long\xdef\debug@modifiers@tl{%
    \debug@modifiers@tl\debug@read@x}%
  \debug@remove@first%
}

% Experimental \afterassignment.
\long\gdef\debug@do@afterassignment{%
  \debug@remove@first%
  \debug@read@unsafe{1}%
  \debug@remove@first%
  \let\debug@afterassignment@token\debug@read@x%
}


% 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@solid@name{\detokenize\expandafter{\debug@read@x}}
\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{advance}{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{showboxbreadth}{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{%
  \debug@read@unsafe{1}%
  \long\xdef\debug@print@wedid@tl{\space%
    \csname debug@text@do@\debug@type@correct\endcsname}}

\long\gdef\debug@print@wedid{%
  \debug@insurance{%
    \global\advance \c@debug@steps by 1\relax%
    \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@read@unsafe{1}%
  \long\xdef\debug@first@meaning{\expandafter\meaning\debug@read@x}%
  \long\xdef\debug@first@string{\expandafter\string\debug@read@x}%
  \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%
  \global\advance\c@debug@nonstop by -1\relax%
  \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{%
  \noexpand\read\noexpand\c@debug@prompt@stream to%
  \expandafter\noexpand\csname Your input \endcsname%
  #1\expandafter\noexpand\csname Your input \endcsname}
\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}

答案2

查尔斯要求我发布上述评论的摘要;这里有一个简短的概要。

将要

我可能不是目标受众,但我认为这对我来说没什么用。(不过,尝试一下肯定很有趣。)我通常发现,当我需要检查宏定义时,输出中缺少缩进和换行符,这\show\meaning很多情况下限制了它们的实用性。

芭芭拉

很可爱。2 条评论:

  1. 我认为“反射”就像镜子中发生的事情,并使用该名称通过图形包(其中已经有一个 \reflectbox 命令)水平镜像符号或字符串。我会称之为不同的东西,也许是 \unwrap 或 \unravel。
  2. 也许我假设太多,但是我尝试直接运行它,而不使用文档类和 \begin/\end{document} 来包装它,...并且失败了。我想我被宠坏了,但是我喜欢开箱即用的例子。

曼努埃尔

为了快速调试,我更喜欢以交互方式运行 TeX 并\show直接在终端上查看输出。

相关内容