[texhax] Tracing a LaTeX macro

Doug McKenna doug at mathemaesthetics.com
Tue Feb 1 02:34:32 CET 2011


Greetings.

I have been trying to wrap my head around how a piece of LaTeX works 
under the hood, in particular the

  \DeclareRobustCommand

macro when it declares

  \DeclareRobustCommand\\{ ... }

soon after

  \DeclareRobustCommand{\GenericError}[4]{ ... }

all in the source distribution file "ltpar.dtx".  As the source notes 
say, there will be a difference in how this macro works, depending on 
whether the command name being defined is all letters or starts with a 
non-letter character.

So my strategy to understand how the macro works was to edit a copy of  
"latex.ltx", in which I placed a

  \tracingcommands=3\tracingmacros=1

just before the invocation of the macro, and

  \tracingcommands=0\tracingmacros=0

just after its invocation to turn tracing off.  I then ran the new file 
from the Unix terminal using the "tex" command (texlive 2008, installed 
on my Mac, though I doubt that matters).  I understand that "tex" is 
running a pre-formatted interpreter, as the LaTeX code mentions in a 
message right at the beginning after checking for CatCodes, but I don't 
care abut getting to the end of the entire thing.  I just wanted to see 
the execution trace of how this particular macro operates when asked to 
define \\.  It all seems to work okay, for example, for earlier 
invocations in the file, when declaring the various Generic... commands.

But when it comes to calling

   DeclareRobustCommand\\{ ...

the parse goes to pieces and I keep getting lost as to what's 
transpiring.  TeX issues an error message about \number not finding an 
integer to convert (because there's a '{' as the next token that causes 
\number to be unhappy).  This occurs while executing the \\@yargdef 
macro, called from \@ifdefinable, called from @argdef.  A cascade of 
further errors involving illegal '#' characters on the input ensues.

Since one can only assume this error does not occur when building LaTeX, 
I'm wondering what I'm missing, especially given that the macro seems to 
manage fine with the earlier all-letter Generic... commands.

Any help would be much appreciated, especially as I've spent a whole lot 
of time trying to figure this out, and am at my Witt's End.  After 
removing nearly all extraneous stuff from the input file, I can reproduce 
the situation with just the following excerpt from the LaTeX code (I've 
inserted commented blank lines for a touch of clarity):

\catcode`\{=1
\catcode`\}=2
\catcode`\#=6
\catcode`\^=7
\chardef\active=13
\catcode`\@=11
\countdef\count@=255
\let\bgroup={ \let\egroup=}%
%
\def\makeatletter{\catcode`\@11\relax}%
\makeatletter
\def\makeatother{\catcode`\@12\relax}%
\def\strip at prefix#1>{}%
\long\def \@gobble #1{}%
\long\def\@firstoftwo#1#2{#1}%
\long\def\@secondoftwo#1#2{#2}%
\def\@carcube#1#2#3#4\@nil{#1#2#3}%
%
\def\@ifundefined#1{%
   \expandafter\ifx\csname#1\endcsname\relax
     \expandafter\@firstoftwo
   \else
     \expandafter\@secondoftwo
   \fi}%
%
\let\@typeset at protect\relax
%
\long\def\@ifdefinable #1#2{%
       \edef\reserved at a{\expandafter\@gobble\string #1}%
      \@ifundefined\reserved at a
          {\edef\reserved at b{\expandafter\@carcube \reserved at a xxx\@nil}%
           \ifx \reserved at b\@qend \@notdefinable\else
             \ifx \reserved at a\@qrelax \@notdefinable\else
               #2%
             \fi
           \fi}%
          \@notdefinable}%
\let\@@ifdefinable\@ifdefinable
%
\def\x at protect#1{%
    \ifx\protect\@typeset at protect\else
       \@x at protect#1%
    \fi
}%
\def\@x at protect#1\fi#2#3{%
    \fi\protect#1%
}%
\long\def\@rc at ifdefinable#1#2{%
   \let\@ifdefinable\@@ifdefinable
   #2}%
%
\def\@star at or@long#1{%
   \@ifstar
    {\let\l at ngrel@x\relax#1}%
    {\let\l at ngrel@x\long#1}}%
%
\long\def\@ifnextchar#1#2#3{%
   \let\reserved at d=#1%
   \def\reserved at a{#2}%
   \def\reserved at b{#3}%
   \futurelet\@let at token\@ifnch}%
\let\kernel at ifnextchar\@ifnextchar
\def\@ifnch{%
   \ifx\@let at token\@sptoken
     \let\reserved at c\@xifnch
   \else
     \ifx\@let at token\reserved at d
       \let\reserved at c\reserved at a
     \else
       \let\reserved at c\reserved at b
     \fi
   \fi
   \reserved at c}%
%
\def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}}%
%
\def\:{\let\@sptoken= } \:  % this makes \@sptoken a space token
\def\:{\@xifnch} \expandafter\def\: {\futurelet\@let at token\@ifnch}%
%
\def\newcommand{\@star at or@long\new at command}%
\def\@newcommand#1[#2]{%
   \kernel at ifnextchar [{\@xargdef#1[#2]}%
                 {\@argdef#1[#2]}}%
\def\new at command#1{%
   \@testopt{\@newcommand#1}0}%
%
\long\def\@argdef#1[#2]#3{%
    \@ifdefinable #1{\@yargdef#1\@ne{#2}{#3}}}
\long\def\@xargdef#1[#2][#3]#4{%
   \@ifdefinable#1{%
      \expandafter\def\expandafter#1\expandafter{%
           \expandafter
           \@protected at testopt
           \expandafter
           #1%
           \csname\string#1\endcsname
           {#3}}%
        \expandafter\@yargdef
           \csname\string#1\endcsname
            \tw@
            {#2}%
            {#4}}}%
%
\long\def\@xargdef#1[#2][#3]#4{%
   \@ifdefinable#1{%
      \expandafter\def\expandafter#1\expandafter{%
           \expandafter
           \@protected at testopt
           \expandafter
           #1%
           \csname\string#1\endcsname
           {#3}}%
        \expandafter\@yargdef
           \csname\string#1\endcsname
            \tw@
            {#2}%
            {#4}}}%
%
\long \def \@yargdef #1#2#3{%
   \ifx#2\tw@
     \def\reserved at b##11{[####1]}%
   \else
     \let\reserved at b\@gobble
   \fi
   \expandafter
     \@yargd at f \expandafter{\number #3}#1%  <-- Where things go bad!
}
\long \def \@yargd at f#1#2{%
   \def \reserved at a ##1#1##2##{%
     \expandafter\def\expandafter#2\reserved at b ##1#1%
     }%
   \l at ngrel@x \reserved at a 0##1##2##3##4##5##6##7##8##9###1%
}%
%
\long\def\@testopt#1#2{%
   \kernel at ifnextchar[{#1}{#1[{#2}]}}%
%
\def\DeclareRobustCommand{\@star at or@long\declare at robustcommand}%
%
\def\declare at robustcommand#1{%
    \ifx#1\@undefined\else\ifx#1\relax\else
       \@latex at info{Redefining \string#1}%
    \fi\fi
    \edef\reserved at a{\string#1}%
    \def\reserved at b{#1}%
    \edef\reserved at b{\expandafter\strip at prefix\meaning\reserved at b}%
    \edef#1{%
       \ifx\reserved at a\reserved at b
          \noexpand\x at protect
          \noexpand#1%
       \fi
       \noexpand\protect
       \expandafter\noexpand\csname
          \expandafter\@gobble\string#1 \endcsname
    }%
    \let\@ifdefinable\@rc at ifdefinable
    \expandafter\new at command\csname
       \expandafter\@gobble\string#1 \endcsname
}%
%
\tracingcommands=3\tracingmacros=2%
\DeclareRobustCommand\\{%
   \let \reserved at e \relax
   \let \reserved at f \relax
   \@ifstar{\let \reserved at e \vadjust \let \reserved at f \nobreak
              \@xnewline}%
           \@xnewline}%
\tracingcommands=0\tracingmacros=0%


Thanks for any guru guidance!


Doug McKenna



More information about the texhax mailing list