[comp.text.tex] Catcodes Explained

marcel@cs.caltech.edu (Marcel van der Goot) (05/16/91)

[Note: This posting is not so easy. However, it is very worthwhile to
 try and understand it, both for plain TeX users and for LaTeX users
 who want to write their own macros. Category codes are rather essential
 in TeX, and the source of many problems with macros.
]

Binod K. Taterway (lubkt@spectrum.CC.Lehigh.EDU) writes
> I want to place a box around the verbatim environment. Unfortunately,
> when I do the following,
>
>	% Make boxes. Inspired from Exercise 21.3 from TeXbook.
>	\def\boxit#1{\vbox{\hrule\hbox{\vrule \kern1.2pt
>	 \vbox{\kern1.2pt{ \parindent 0pt
>	 \tt #1}\kern1.2pt}\kern1.2pt\vrule}
>	 \hrule}}
>	
>	\boxit{
>	\begin{verbatim}
>	this better come out in boxed verbatim mode ...
>	\end{verbatim}
>	}
>
> I get the following error message:
>   [...]

First the reason your solution doesn't work (this is the hard part):

As you are probably aware, character tokens in TeX have the form
<character code, category code>. The character code is just the ascii
code, the catcode (which is the culprit in this case) is the catcode
of that character when TeX first read it. E.g., the catcode of @ is
normally 12, but if you set \catcode`@=11, then from that point on
all @ characters \em{that are read from the file} will be assigned catcode 11.
However, each character token gets its catcode only once, namely when
it is read from the file. Once a character is read, its catcode cannot
be changed.

Now, for your problem: When you do \begin{verbatim}, a lot of catcodes are
changed. Basically, the catcodes of all special characters (such as \, $,
and #) are set to 12 (which indicates a normal character rather than a
special one). Then the text that must be set is read, which now consists
of normal characters only.

However, in your case, that text is already read, namely as argument
of the \boxit macro. Therefore, all the catcodes of that text are already
fixed, and cannot be changed. Hence, nothing happens when the \begin{verbatim}
is executed.


The solution:

We have to prevent reading the verbatim text before we do \begin{verbatim}.
Therefore, the \boxit macro is split into two macros: \Beginboxit for
the part that comes before the boxed text, and \Endboxit for the
part that comes after the boxed text. That way the boxed text is not read
too early. To split the definition of \boxit, every { is replaced by \bgroup,
every } by \egroup. (The definition was also reformatted ...)

%%%%%%%%%%%%%%%%%%%%
\def\Beginboxit
   {\vbox\bgroup
	   \hrule
	   \hbox\bgroup
		  \vrule \kern1.2pt %
		  \vbox\bgroup\kern1.2pt
   }

\def\Endboxit{%
			      \kern1.2pt
		       \egroup
		  \kern1.2pt\vrule
		\egroup
	   \hrule
	 \egroup
   }	

\newenvironment{boxit}{\Beginboxit}{\Endboxit}

%%%%%%%%%%%%%%%%%%%%

Now you can say:

\begin{boxit}
\begin{verbatim}
this better come out in boxed verbatim mode ...
\end{verbatim}
\end{boxit}

This does not change the margin; you'll have to do that yourself
(or find someone else to do it).


An interesting experiment for LaTeX hackers:
In the above macro definition, change \Beginboxit to \beginboxit,
and \Endboxit to \endboxit.


                                          Marcel van der Goot
 .----------------------------------------------------------------
 | Blauw de viooltjes,                    marcel@vlsi.cs.caltech.edu
 |    Rood zijn de rozen;
 | Een rijm kan gezet
 |    Met plaksel en dozen.
 |