5

The \chardef primitive exhibits a peculiar behavior: the command

\chardef\controlSequence=<number>

behaves as though it were preceded by

\let\controlSequence=\relax

(see the answers to this question).

Is there any other similar TeX primitive, \p, that behaves as though some tokens have been (re)defined just before \p is expanded/executed?

* I'm looking for an exhaustive list, but a partial list is also welcome.

Evan Aad
  • 11,066
  • 1
    The list could be easily inferred from the quotation of tex.web at https://tex.stackexchange.com/a/376435/4427 – egreg Jun 24 '17 at 19:23

2 Answers2

10

\mathchardef for math characters behave in a similar way.

\tracingall
\chardef\foo=123
\mathchardef\baz=123
\csname @@end\endcsname\end

The .log contains:

{changing \foo=undefined}
{into \foo=\relax}

and

{changing \baz=undefined}
{into \baz=\relax}

Other commands:

\countdef
\dimendef
\skipdef
\muskipdef
\toksdef

The list comes from the source code tex.web (keyword shorthand_def):

@ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|;
a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|;
and the corresponding |chr| is the character code or math code. A \.{\\countdef}
or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control
sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the
corresponding |chr| is the |eqtb| location of the internal register in question.

@d char_def_code=0 {|shorthand_def| for .{\chardef}} @d math_char_def_code=1 {|shorthand_def| for .{\mathchardef}} @d count_def_code=2 {|shorthand_def| for .{\countdef}} @d dimen_def_code=3 {|shorthand_def| for .{\dimendef}} @d skip_def_code=4 {|shorthand_def| for .{\skipdef}} @d mu_skip_def_code=5 {|shorthand_def| for .{\muskipdef}} @d toks_def_code=6 {|shorthand_def| for .{\toksdef}}

@<Put each...@>= primitive("chardef",shorthand_def,char_def_code);@/ @!@:char_def_}{.{\chardef} primitive@> primitive("mathchardef",shorthand_def,math_char_def_code);@/ @!@:math_char_def_}{.{\mathchardef} primitive@> primitive("countdef",shorthand_def,count_def_code);@/ @!@:count_def_}{.{\countdef} primitive@> primitive("dimendef",shorthand_def,dimen_def_code);@/ @!@:dimen_def_}{.{\dimendef} primitive@> primitive("skipdef",shorthand_def,skip_def_code);@/ @!@:skip_def_}{.{\skipdef} primitive@> primitive("muskipdef",shorthand_def,mu_skip_def_code);@/ @!@:mu_skip_def_}{.{\muskipdef} primitive@> primitive("toksdef",shorthand_def,toks_def_code);@/ @!@:toks_def_}{.{\toksdef} primitive@>

@ @<Cases of |print_cmd_chr|...@>= shorthand_def: case chr_code of char_def_code: print_esc("chardef"); math_char_def_code: print_esc("mathchardef"); count_def_code: print_esc("countdef"); dimen_def_code: print_esc("dimendef"); skip_def_code: print_esc("skipdef"); mu_skip_def_code: print_esc("muskipdef"); othercases print_esc("toksdef") endcases; char_given: begin print_esc("char"); print_hex(chr_code); end; math_given: begin print_esc("mathchar"); print_hex(chr_code); end;

@ We temporarily define |p| to be |relax|, so that an occurrence of |p| while scanning the definition will simply stop the scanning instead of producing an ``undefined control sequence'' error or expanding the previous meaning. This allows, for instance, `.{\chardef\foo=123\foo}'.

@<Assignments@>= shorthand_def: begin n:=cur_chr; get_r_token; p:=cur_cs; define(p,relax,256); scan_optional_equals; case n of char_def_code: begin scan_char_num; define(p,char_given,cur_val); end; math_char_def_code: begin scan_fifteen_bit_int; define(p,math_given,cur_val); end; othercases begin scan_eight_bit_int; case n of count_def_code: define(p,assign_int,count_base+cur_val); dimen_def_code: define(p,assign_dimen,scaled_base+cur_val); skip_def_code: define(p,assign_glue,skip_base+cur_val); mu_skip_def_code: define(p,assign_mu_glue,mu_skip_base+cur_val); toks_def_code: define(p,assign_toks,toks_base+cur_val); end; {there are no other cases} end endcases; end;

There is another, but different case, where a control sequence gets assigned to \relax: If the command sequence constructed by \csname ...\endcsname is undefined, then it is assigned to \relax.

The case \font, see Joseph Wright's comment:

\tracingall
\font\foo\foo
\csname @@end\endcsname\end

From the .log, \foo is temporarily defined to an non-expandable command:

{changing \foo=undefined}
{into \foo=select font nullfont}
Heiko Oberdiek
  • 271,626
  • Is this an exhaustive list? – Evan Aad Jun 24 '17 at 18:40
  • 1
    @EvanAad I have added all cases, where macro define is used with meaning \relax in the source tex.web. – Heiko Oberdiek Jun 24 '17 at 18:56
  • 2
    Perhaps worth noting that \font is similar (though not identical, goes via \nullfont). – Joseph Wright Jun 24 '17 at 18:59
  • @JosephWright: Yes, worth noting! If you have any more like this, please don't hold back... – Evan Aad Jun 24 '17 at 19:00
  • Regarding your last sentence: you mean to say that the expression \csname name\endcsname behaves as though it were immediately followed by \let\name=\relax, correct? – Evan Aad Jun 24 '17 at 19:03
  • @EvanAad No, \name will have the meaning \relax instead of undefined only if it was undefined before. – Heiko Oberdiek Jun 24 '17 at 19:06
  • I've done some experimenting, and I think that the \csname ... \endcsname construct is different than the others, including \font, in that \csname foo\endcsname does not behave as though it were preceded by \let\foo=\relax. – Evan Aad Jun 24 '17 at 20:06
  • Indeed, consider the following TeX manuscript: \def\nick{nack} \expandafter\def\csname nick\nick\endcsname{pattiwhack} \nicknack \bye. If the \csname ... \endcsname construct behaved like \chardef, tex would throw an error, because the control sequence \nicknack would be undefined. However the manuscript compiles successfully, and outputs pattiwhack. – Evan Aad Jun 24 '17 at 20:06
5

The full list can be inferred from the quotation of tex.web I already pointed to in https://tex.stackexchange.com/a/376435/4427

enter image description here

There's also the case of \font that's slightly different in that the control sequence is assigned \nullfont instead of \relax.

egreg
  • 1,121,712