# Getting the catcode of a character assigned to a control sequence by \let

Given a control sequence \z that was defined like \let\z=z, how do you get the catcode the character z had at the time \z was defined?

The obvious solution

{\catcodez=12 \global\let\z=z}
\the\catcode\z


seems to work at the first glance, but it gives the wrong result 11 because '\z isn't interpreted as the previously defined control sequence but just another denotation of the character code of the letter z.

Using a different control sequence name makes it obvious

{\catcodez=12 \global\let\thez=z}
\the\catcode\thez


when it fails with

! Improper alphabetic constant.


This question deals with a similar problem, getting the character code of such a control sequence, but I don't see a way to use it for this problem as \meaning only returns characters of category 12 and 10.

• (In expl3 use \token_case_catcode.) Dec 16, 2021 at 14:54

An implicit character cannot be active, nor it can have catcode 0, 5, 9, 14 or 15. Check each possibility:

\def\catcodeofimplicitchar#1{%
\ifcat\noexpand#1\bgroup 1\else
\ifcat\noexpand#1\egroup 2\else
\ifcat\noexpand#1$3\else \ifcat\noexpand#1&4\else \ifcat\noexpand#1##6\else \ifcat\noexpand#1^7\else \ifcat\noexpand#1_8\else \ifcat\noexpand#1 10\else \ifcat\noexpand#1a11\else \ifcat\noexpand#1112\else -1\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi } \let\one={ \let\two=} \let\three=$
\let\four=& \let\six=# \let\seven=^
\let\eight=_ \let\eleven=z \let\twelve=0
\begingroup\def\\#1:{\global\let\ten= #1}\\ :\endgroup
\let\thirteen=~

{\tt\meaning\one}:
\catcodeofimplicitchar\one

{\tt\meaning\two}:
\catcodeofimplicitchar\two

{\tt\meaning\three}:
\catcodeofimplicitchar\three

{\tt\meaning\four}:
\catcodeofimplicitchar\four

{\tt\meaning\six}:
\catcodeofimplicitchar\six

{\tt\meaning\seven}:
\catcodeofimplicitchar\seven

{\tt\meaning\eight}:
\catcodeofimplicitchar\eight

{\tt\meaning\ten}:
\catcodeofimplicitchar\ten

{\tt\meaning\eleven}:
\catcodeofimplicitchar\eleven

{\tt\meaning\twelve}:
\catcodeofimplicitchar\twelve

{\tt\meaning\empty}:
\catcodeofimplicitchar\empty

\bye • I chose this answer because it's more direct and easier to understand Sep 28, 2016 at 15:45

You do want the same general idea as in How to get character code defined with \let (! Improper alphabetic constant), but you need to use the text of the \meaning. An example which covers three cases (space, letter, other), and which extends naturally to the other cases:

\long\def\getcatcode#1{%
\expandafter\getcatcodeaux\meaning#1\stop
}
\edef\getcatcodeaux#1\stop{%
\noexpand\getcatcodespace#1{}{}\detokenize{blank space}\noexpand\stop
\noexpand\getcatcodeletter#1{}{}\detokenize{the letter}\noexpand\stop
\noexpand\getcatcodeother#1{}{}\detokenize{the character}\noexpand\stop
}
\edef\temp{%
\def\noexpand\getcatcodespace##1\detokenize{blank space}##2\noexpand\stop{%
\noexpand\ifx\relax##1\relax
10%
\noexpand\fi
}%
\def\noexpand\getcatcodeletter##1\detokenize{the letter}##2\noexpand\stop{%
\noexpand\ifx\relax##1\relax
11%
\noexpand\fi
}%
\def\noexpand\getcatcodeother##1\detokenize{the character}##2\noexpand\stop{%
\noexpand\ifx\relax##1\relax
12%
\noexpand\fi
}%
}
\temp


This is all expandable so it can go into the argument of \catcode or similar. I've not added any defensive code in case \z here is a macro which might contain the test string, a chardef, etc., but one can extend the idea.