One possible way to get is to use the expand capability of scan_toks() to expand/execute some following tokens, similar to this answer.
In the example below,
- function
f does a simple token.get_next() (which does not handle the case the following token is not in the hash table)
- function
g uses scan_toks(false, true) to expand the input, and \immediateassignment combined with \futurelet to force TeX to tokenize the next token.
- function
h uses coroutine trick (although instead of a coroutine, auxiliary functions are used) to call the \futurelet instead. \immediateassignment can be added if it needs to work in an expansion-only context; nevertheless in an o-expansion context it will still fail to work.
%! TEX program = lualatex
\documentclass{article}
\usepackage{luacode}
\ExplSyntaxOn
\use_none:n {__unused} % put the token __unused into the hash table
\cs_new_protected:Npn __h_aux { \directlua{h_aux()} }
\ExplSyntaxOff
\begin{luacode*}
print("\n\n\n\n")
function f()
print("f: csname =", token.get_next().csname)
end
function g()
token.put_next {
token.create(string.byte "{", 1),
token.create "immediateassignment",
token.create "futurelet",
token.create "__unused",
token.create(string.byte "}", 2),
}
token.scan_toks(false, true)
print("g: csname =", token.get_next().csname)
end
function h()
tex.sprint {
token.create "futurelet",
token.create "__unused",
token.create "__h_aux",
}
end
function h_aux()
print("h: csname =", token.get_next().csname)
end
\end{luacode*}
% normal behavior: tokens never seen before (not in the hash table) results in empty csname
\directlua{f()}\par
\directlua{f()}\undefined
\directlua{f()}\undefineda
\directlua{f()}\undefineda
\directlua{f()}\undefinedb
% use function g instead
\directlua{g()}\par
\directlua{g()}\undefined
\directlua{g()}\undefineda
\directlua{g()}\undefineda
% just checking, undefinedb is still not in the hash table
\directlua{f()}\undefinedb
% use function h instead
\directlua{h()}\par
\directlua{h()}\undefined
\directlua{h()}\undefinedb
\directlua{h()}\undefinedb
\begin{luacode*}
print("\n\n\n\n")
\end{luacode*}
\begin{document}
\end{document}
The output is
f: csname = par
f: csname = undefined
f: csname =
f: csname =
f: csname =
g: csname = par
g: csname = undefined
g: csname = undefineda
g: csname = undefineda
f: csname =
h: csname = par
h: csname = undefined
h: csname = undefinedb
h: csname = undefinedb
You can see that function g() and h() gets the csname correctly, being \undefineda and \undefinedb.
token.get_next().csnamereturns, rather the csname linked to this input where it exists. – Joseph Wright Oct 26 '16 at 11:16\detokenizeequivalent won't work as you can't ell if a token was an active char (due to dropping of the escape char):~and\~yield the same fromtoken.get_next().csname, for example. – Joseph Wright Oct 26 '16 at 12:48\directlua{tex.sprint(token.get_next().active and "active character" or "control sequence")}– user3840170 Oct 26 '16 at 16:36token.create("UNDEFINED").csnameis empty, whiletoken.create("par").csnameis"par". – user3840170 Oct 27 '16 at 14:05