2

Next lualatex MWE emulates \newcommand\mySection{\section} on the lua side.

% !TEX TS-program = lualatex
\documentclass{article}
\begin{document}
\directlua{
  token.set_macro('mySection', '\\section')
}
\mySection{bar}
\end{document}

How to emulate a \let\mySection\section?

A poor man solution like

\directlua{
  token.set_macro(catcodetable, 'mySection', token.get_macro('section'))
}

would eventually work provided catcodetable is properly setup, but how?

  • Perhaps you might enlarge on the aim: whilst one can of course pass data between TeX and Lua, the model from ConTeXt is that when one moves code to Lua, you do as much there as you can. – Joseph Wright Jun 07 '21 at 15:16

2 Answers2

4

In general you can't. tex.set_macro is the only Lua function which allows to assign TeX macros from Lua and it is rather limited: It is not possible to use it to define macros with parameters and it does not allow you to define macros which contain the same character with different catcodes.

So the only reliable way to do the equivalent of \let from Lua is to pass \let\newname\oldname (or equivalent tokens) back to TeX and let TeX execute it.

Even if you want to limit yourself to parameterless macros with consistent catcodes, you can't get token.get_macro to tell you anything about catcodes either. So the only way to determine the catcodes used in a macro is again to pass it back to TeX, expand it there and then read back the tokens.

2

Just as a demonstration how you can do the task in LuaTeX (expandably!)

Of course it still involves passing the thing to TeX to execute.

%! TEX program = lualatex
\documentclass{article}
\begin{document}

% define some macros here. % NOTE \a and \b should not be redefined in normal code \def\a{123} \def\b{456}

% the old value of \a is 123 \directlua{print("========", token.get_macro("a"))}

\edef\c{ \directlua{

    --[[ the old value is 123 (use this comment style because new lines are stripped, this is directlua not luacode environment ]]
    print(token.get_macro("a"))

    --[[ create a table of tokens with content \let\a\b. Can use token.create as well ]]
    local tokens=token.scan_toks(false, false)

    --[[ run it ]]
    tex.runtoks(function()
        tex.sprint(tokens)
    end)

    --[[ look, the macro content really changed ]]
    print(token.get_macro("a"))

}{\let\a\b}

}

% and outside too \directlua{print(token.get_macro("a"))}

\end{document}

Some notes regarding this code from LuaTeX documentation:

  • runtoks

    You have to make sure that at the end TEX is in a sane state and this is not always trivial.

  • about scanning tokens in general

    When scanning for the next token you need to keep in mind that we’re not scanning like TeX does: expanding, changing modes and doing things as it goes. When we scan with Lua we just pick up tokens.


Alternatively copy the definition of \beginlocalcontrol macro from https://tex.stackexchange.com/a/628874/250119 we can do the following:

%! TEX program = lualatex
\documentclass{article}

\newluafunction\beginlocalcontrol \directlua{lua.get_functions_table()[\the\allocationnumber] = function() return tex.runtoks(token.get_next) end} % define its meaning \luadef\beginlocalcontrol\allocationnumber

\begin{document}

% define some macros here. % NOTE \a and \b should not be redefined in normal code \def\a{123} \def\b{456}

% the old value of \a is 123 \directlua{print("========", token.get_macro("a"))}

\edef\c{ \beginlocalcontrol\let\a\b\endlocalcontrol }

% the macro content changed \directlua{print("======== changed", token.get_macro("a"))}

\end{document}

As an alternative to using \newluafunction/\allocationnumber, it's possible to use #lft+1 such as seen in 1 2 3 but there's a risk of undefined behavior in Lua 5.2 when allocations are non-sequential (unlikely in practice however)

user202729
  • 7,143