Some additional notes/demonstration about the "hash table" and the "equivalence table" etc.. as mentioned in some comments above.
Hash table
The hash table (or hash_extra
, it's complicated) maps each control sequence name to a number (for fast manipulation of tokens, etc.).
In LuaTeX it's possible to check whether a token is in the hash table using token.get_next()
or token.create()
.
In the example below, initially \undefineda
is not in the hash table, but after it's "seen" (even if it's never defined) it's in the hash table. (also you can see that \ifdefined
alone does not create new hash table entry even if the token is "read" by TeX)
%! TEX program = lualatex
\documentclass{article}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
print("\n\n\n\n")
function f()
print("f: csname =", token.get_next().csname)
end
\end{luacode*}
\ExplSyntaxOn
\directlua{f()} \undefineda % here the output is empty, so \undefineda is not in the hash table
\ifdefined\undefineda \directlua{print "defined"} \else \directlua{print "undefined"} \fi
\directlua{f()} \undefineda % special case: \ifdefined (alone) does not put the token into the hash table
\iffalse \undefineda \fi
\directlua{f()} \undefineda % special case 2: \iffalse ... \fi does not put the token into the hash table
\use_none:n {\undefineda} % the token appears, but \use_none:n "throws it away" immediately
\directlua{f()} \undefineda % now undefineda is in the hash table
\ExplSyntaxOff
\begin{luacode*}
print("\n\n\n\n")
\end{luacode*}
\end{document}
Relevant output:
f: csname =
undefined
f: csname =
f: csname =
f: csname = undefineda
Equivalence table
The "equivalence table" is an implementation details of TeX, in texdoc tex
it's called "The table of equivalents", part 17, section 220; and in the source code it's the variable eqtb
.
As the documentation states, it holds the current "equivalents" of things.
Note that (implementation details) this table is not called "macro definition table", as it holds more than just macro definitions, as
control sequences/active characters can be defined to be something other than macro (for example:
\chardef \% = `%
which makes \%
equivalent (not "expands to"! It remains unexpandable) to \char `%
when executed;
\let \c_math_toggle_token = $
;
- etc.)
the value of e.g. the baselineskip, are also put in this table.
In short, "not to have \directlua
in the equivalence table" is just a complicated way of saying "undefine \directlua
", if I understood correctly.
Note on \let \directlua \undefined
and the save stack
In this particular case, if you execute \begingroup
, do \csname directlua \endcsname
then execute \endgroup
it's similar to if you do \let \directlua \undefined
after a csname-endcsname
-- the only difference (as far as I know) is in the "save stack" i.e. if you do \let \directlua \undefined
alone it will leave an item in the save stack:
%! TEX program = lualatex
\documentclass{article}
\begin{document}
\ExplSyntaxOn
\begingroup
\tracingrestores=1~
\wlog{========~first~example}
\begingroup
\begingroup \expandafter \endgroup \expandafter \use_none:n \csname testtest \endcsname
\wlog{//there's~a~restore~above~because~of~the~endgroup,~but~no~restoration~below}
\endgroup
\wlog{========~second~example}
\begingroup
\expandafter \use_none:n \csname testtest \endcsname
\let \testtest \undefined
\wlog{//note~that~there's~still~a~restoration~below}
\endgroup
\endgroup
\ExplSyntaxOff
\end{document}
The relevant output of this program is:
======== first example
{restoring \testtest=undefined}
//there's a restore above because of the endgroup, but no restoration below
======== second example
//note that there's still a restoration below
{restoring \testtest=undefined}
{restoring \tracingrestores=0}
side note, \use_none:n is abused to gobble a single token here, but not a big problem
Addition
As shown in the comments under egreg's answer
I was a bit puzzled
why they don't just do \ifx\directlua\undefined
.
My conjecture is that, the LaTeX3 team knows that \csname...\endcsname
(and various other constructs using that) automatically defines the control sequence
to be \relax
when it was originally undefined, think that's error-prone, and decide to hide the behavior from the user
and treat \relax
and undefined the same way.
In particular, in this case, the code will consider \directlua
"nonexistent" even if the user accidentally does (something that internally does) \csname directlua \endcsname
somewhere before. Which I think is rather unlikely.
Compare this with how \cs_if_exist:NTF
or \cs_if_exist:cTF
works. While the \ifdefined
and \ifcsname
primitives
are true on \relax
and false on undefined
, both of the commands above are hard coded to be false on \relax
.
(worth noting that, unlike many other commands that take c-type argument which are implemented in terms of \exp_args:Nc
or \::c
,
this command does not implicitly define the control sequence to \relax
when it was undefined
because it's special-case implemented using \ifcsname
;
nevertheless this behavior (as well as the fact that most-but-not-all c-type argument implicitly converts undefined to \relax
, or in its LaTeX3 name, \scan_stop:
)
is as far as I can see never documented anywhere in interface3.pdf)
That having said, my opinion (not really related to this question however) is that this can never be completely abstracted away from the user,
as an undefined control sequence will create an error when expanded/executed while a \relax
control sequence will not.