12

I have a very simple question. When I do this:

\catcode`\@=0
\catcode`\\=11
@mycommand{a}
@catcode`@\=0
\catcode`\@=11

It works perfectly and calls \mycommand correctly. However, when I try to define \mycommand with similar trickery:

\newcommand\mycommand[1]{
  \catcode`\@=0
  \catcode`\\=11
  @textbf{Hello #1}
  @catcode`@\=0
  \catcode`\@=11
}

compilation stalls and gives me the * prompt. If I give it Ctrl+D, it terminates without producing any output. Indeed, it seems that changing the catcodes inside a command has no effect whatsoever, because the following works perfectly (whereas, from my understanding, it shouldn't):

\newcommand\mycommand[1]{
  \catcode`\@=0
  \catcode`\\=11
  \textbf{Hello #1}
}

Can anybody please explain to me what's happening?

Joseph R.
  • 876
  • 3
    Does http://tex.stackexchange.com/questions/12638/how-to-change-catcode-in-a-macro?rq=1 help at all? – cfr Jan 05 '14 at 00:10
  • @cfr Thank you. This gave me the correct solution: moving the \catcode commands outside the \mycommand definition. egreg's answer gave me the explanation for what was tripping me up. – Joseph R. Jan 05 '14 at 00:48
  • related: http://tex.stackexchange.com/questions/146062/trouble-defining-a-macro-that-typesets-underscore-characters-as – jub0bs Jan 05 '14 at 01:20
  • Possibly related: http://tex.stackexchange.com/questions/47275/setting-catcodes-in-a-group – StrongBad Jan 05 '14 at 13:32

1 Answers1

11

When you say

\newcommand\mycommand[1]{
  \catcode`\@=0
  \catcode`\\=11
  @textbf{Hello #1}
  @catcode`@\=0
  \catcode`\@=11
}

TeX stores the following tokens as replacement text for \mycommand (the • bullet separates tokens and spaces are used for legibility, <sp> denotes a space token):

<sp> • \catcode • ` • \@ • = • 0 • <sp> •
\catcode • ` • \\ • = • 1 • 1 • <sp> •
@ • * t • e • x • t • b • f • { • H • e • l • l • o • <sp> • #1 • } • 
@ • c • a • t • c • o • d • e • ` • @ • \= • 0 • <sp> •
\catcode • ` • \@ • = • 1 • 1 • <sp>

where

  • @, `, 0, 1 and = have category code 12,
  • { and } have category code 1 and 2 respectively,
  • <sp> has category code 10,
  • the letters have category code 11.

The category codes are frozen once they have entered TeX's mouth. When the macro \mycommand{Worlds} is expanded, TeX will change the category code for all @ it will absorb from that point on (respecting groups), but this can't change in any way what has already been tokenized, for instance the sequence of tokens

@ • * t • e • x • t • b • f

that have already been stored.

Something will be printed, precisely

@textbfHello World @catcode‘@0

(the 0 will have a bar over it) and TeX won't have any character with category code 0 meaning it won't recognize commands.

I'll do an example in Plain TeX, which is easier to print, but essentially equivalent. I'll only add \csname bye\endcsname to end the game:

\def\mycommand#1{
  \catcode`\@=0
  \catcode`\\=11
  @textbf{Hello #1}
  @catcode`@\=0
  \catcode`\@=11
  \csname bye\endcsname
}

\mycommand{World}

enter image description here

Remember that when TeX executes \def, it completely disables macro expansion and command execution of the tokens it absorbs for the macro name, the parameter text and the replacement text. It will only expand the tokens in the replacement text but again not execute them when it's doing an \edef.

egreg
  • 1,121,712
  • In other words: This question here is a duplicate of the one linked by @cfr? – Speravir Jan 05 '14 at 00:36
  • @Speravir I don't think so. This one has different subtleties. – egreg Jan 05 '14 at 00:37
  • Thanks for the explanation. What I don't get, though, why would the @ in @textbf be "already tokenized" with catcode 12 when I have explicitly redefined its catcode to be 0 only 2 lines before? – Joseph R. Jan 05 '14 at 00:40
  • OK. I just thought so while reading your “respecting groups” and “category codes are _frozen_”. – Speravir Jan 05 '14 at 00:41
  • 2
    @JosephR. You don't have executed the category code change, just stored in a macro the instruction to do it; TeX never expands or executes tokens when it's absorbing the replacement text of a macro (with \def; it expands tokens but doesn't execute them with \edef). – egreg Jan 05 '14 at 00:42
  • @egreg Ah. There's the bit of knowledge I was missing. Thank you very much. – Joseph R. Jan 05 '14 at 00:42