0. texio.closeinput()
Suggested by a comment
Code:
%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
\begin{document}
\def\F{\directlua{f()}}
\begin{luacode}
i=0
function f()
if i~=0 then
texio.closeinput()
end
if i<20000 then
i=i+1
tex.sprint(i .. [[, \F]])
end
end
\end{luacode}
\F
\end{document}
The manual explains...
This function that should be used with care. It acts as \endinput but at the Lua end. You can use it to (sort of) force a jump back to TeX.
which means that it drops the remaining content of the topmost "file".
Note/clarification of the manual,
- it doesn't really jump back to TeX, the following Lua content is still executed
tex.print() commands preceding it is also dropped
- unlike
\endinput the following content on the line is dropped
- but if there are some pending tokens put by
token.put_next() they will be kept as long as those after it
- only apply to real "file"/pseudofile instead of e.g. argument token list
1. Use token.put_next on the continuation token
%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
\begin{document}
\def\F{\directlua{f()}}
\begin{luacode}
i=0
function f()
if i<20000 then
i=i+1
tex.sprint(i .. [[, ]])
token.put_next(token.create("F"))
end
end
\end{luacode}
\F
\end{document}
(experimentally, all token.put_next() comes after all tex.*print(), regardless of their order in the code)
I think that the reason this method works is that while expanding a macro, unlike when token.get_next() or token.scan_toks() etc. is executed, TeX unwinds the input stack (even in case the macro does not have any argument such as in this case)
2. Use futurelet
%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{miscellaneous}
%\tracingmacros=1
\begin{document}
\def\F{\directlua{f()}}
\begin{luacode}
i=0
function f()
if i<20000 then
i=i+1
tex.sprint(i .. [[, \immediateassignment\futurelet\a\F]])
end
end
\end{luacode}
\expanded{\F}
\end{document}
\immediateassignment used to make it work in expansion-only context.
Note that if the following token is a notexpanded token, it will be changed.
3. (partially works only, do not use) Use token.put_next on another token and get_next it from inside Lua
%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{miscellaneous}
%\tracingmacros=1
\begin{document}
\def\F{\directlua{f()}}
\begin{luacode}
i=0
function f()
token.get_next() -- the relax token, either the original one or result of put_next
if i<3000 then
i=i+1
tex.sprint(i .. [[, \F]])
token.put_next(token.create("relax"))
end
end
\end{luacode}
\F\relax
\end{document}
This does not do proper tail-recursive (somehow token.get_next() does not eliminate the input level if it's exhausted),
so if 3000 is increased to a larger value you'll see
TeX capacity exceeded, sorry [input stack size=5000].
return. Lua doesn't know that you are tail calling as part oftex.sprint. – Henri Menke Apr 15 '22 at 19:22return" here? if you mean Lua'sreturnfromf()function, it already returns normally by reaching the end of the function – user202729 Apr 16 '22 at 03:40function f() ... return f() endwhich this one clearly isn't. – Henri Menke Apr 16 '22 at 07:48