Introduction to LuaTeX

LuaTeX is a TeX engine which integrates Lua as its scripting language. The combination of an editing language and programming language allows us to generate beautiful structural documents.

LuaTeX is usually included in TeXLive and MikTeX distributions.

Running Lua functions in TeX

LuaTeX introduces a new primitive \directlua, which allows one to run TeX and Lua code together. For example, if I define a TeX command called \texcmd, I can pass its value directly to a Lua variable with \directlua. The following code will leave some text in the output document.

\newcommand{\texcmd}{some text}

\directlua{
    local var = "\texcmd"
    tex.print(var)
}

This does create some trouble when we need to output special characters from Lua code. For example, as it is suggested by this question on StackExchange, if we want to print a table from Lua, the following code is not going to work. That is because LuaTex will first treat content in \directlua as if it is TeX and then send the expanded code to Lua interpreter. As a result, macros like \begin will result in errors.

% this code will not work
\directlua{
    tex.print("\\begin{tabular}{lll}")
    tex.print("1 & a & Test A \\\\")
    tex.print("2 & b & Test B \\\\")
    tex.print("\\end{tabular}")
}

A naive way to deal with this problem is to escape the backslash. But it is still troublesome.

% this code will work
\directlua{
    tex.print("\string\\begin{tabular}{lll}")
    tex.print("1 & a & Test A \string\\\string\\")
    tex.print("2 & b & Test B \string\\\string\\")
    tex.print("\string\\end{tabular}")
}

Fortunately, with the help of luacode package, the need of these tedious escaping can be eliminated. The luacode* environment executes code segments as if it is pure Lua. Of course, TeX macros cannot be used in this environment anymore.

% this code will work
\begin{luacode*}
    tex.print("\\begin{tabular}{lll}")
    tex.print("1 & a & Test A \\\\")
    tex.print("2 & b & Test B \\\\")
    tex.print("\\end{tabular}")
\end{luacode*}

Apart from luacode*, the luacode package also provides several different alternatives to \directlua, whose specifications are shown as below.

If the Lua function does not have any arguments, there is a way of calling it with much less parsing overhead. For example, we can put the table printing function into slot 1 of LuaTeX’s function table and call it with \luafunction1.

\directlua{
    % declare function
    function print_table()
        tex.print("\string\\begin{tabular}{lll}")
        tex.print("1 & a & Test A \string\\\string\\")
        tex.print("2 & b & Test B \string\\\string\\")
        tex.print("\string\\end{tabular}")
    end
    
    % get function table
    local t = lua.get_functions_table()
    % assign function in slot 1
    t[1] =  print_table()
}
% call function in slot 1
\luafunction1

Example: sqrt table

This is an example that I included in the pythontex article. The way pythontex works is to redirect stdout of Python to a file and use TeX to read the output afterwards. It is easy to see that this approach is much more indirect and slower than LuaTeX. With LuaTeX, I can do the following.

\documentclass{article}
\usepackage[a4paper]{geometry}
\usepackage{newtxtext, newtxmath}
\usepackage{amsmath}
\usepackage{luacode}
\usepackage{tasks}
\usepackage{datetime2}
\usepackage{expl3}

\begin{document}

% using luacode* because there are percentage signs
% declare the sqrt function in Lua
\begin{luacode*}
    function compute_sqrt(v)
        local s = math.sqrt(tonumber(v))
        local s_f = math.floor(s)
        local o
        if (math.abs(s_f * s_f - v) < 1.0e-5) then
            o = string.format("%.1f", s)
        else
            o = string.format("%.6f", s)
        end
        tex.print(o)
    end
\end{luacode*}

% declare a wrapper in TeX
\newcommand{\luasqrt}[1]{\directlua{compute_sqrt(#1)}}

\ExplSyntaxOn
\tl_clear:N \l_tmpa_tl

% construct tasks list in a token variable
\tl_put_right:Nn \l_tmpa_tl {\begin{tasks}[style=itemize](4)}
\int_set:Nn \l_tmpa_int {0}
\int_do_while:nNnn {\l_tmpa_int} < {31} {
    % use a loop to construct values
    \tl_put_right:Nx \l_tmpa_tl {\exp_not:N \task $\exp_not:N\sqrt{\int_use:N \l_tmpa_int}=\luasqrt{\int_use:N \l_tmpa_int}$ \space}
    \int_incr:N \l_tmpa_int
} 
\tl_put_right:Nn \l_tmpa_tl {\end{tasks}}

% show the constructed token
\str_set:NV \l_tmpa_str \l_tmpa_tl
\par \str_use:N \l_tmpa_str

% show the constructed table
\tl_use:N \l_tmpa_tl

\ExplSyntaxOff

\luasqrt{5}

\par\DTMNow

\end{document} 

I declared a function compute_sqrt in Lua and created a LaTeX wrapper \luasqrt for it. Then, I can use \luasqrt to put the square root of any number into TeX input stream. To construct a table output, I use the tasks package and constructed the entire table with loops in LaTeX3. The constructed token list is:

\begin {tasks}[style=itemize](4)\task $\sqrt {0}=0.0$ \task $\sqrt {1}=1.0$ \task $\sqrt {2}=1.414214$
\task $\sqrt {3}=1.732051$ \task $\sqrt {4}=2.0$ \task $\sqrt {5}=2.236068$ \task $\sqrt {6}=2.449490$
\task $\sqrt {7}=2.645751$ \task $\sqrt {8}=2.828427$ \task $\sqrt {9}=3.0$ \task $\sqrt {10}=3.162278$
\task $\sqrt {11}=3.316625$ \task $\sqrt {12}=3.464102$ \task $\sqrt {13}=3.605551$ \task $\sqrt
{14}=3.741657$ \task $\sqrt {15}=3.872983$ \task $\sqrt {16}=4.0$ \task $\sqrt {17}=4.123106$ \task
$\sqrt {18}=4.242641$ \task $\sqrt {19}=4.358899$ \task $\sqrt {20}=4.472136$ \task $\sqrt {21}=4.582576$
\task $\sqrt {22}=4.690416$ \task $\sqrt {23}=4.795832$ \task $\sqrt {24}=4.898979$ \task $\sqrt
{25}=5.0$ \task $\sqrt {26}=5.099020$ \task $\sqrt {27}=5.196152$ \task $\sqrt {28}=5.291503$ \task
$\sqrt {29}=5.385165$ \task $\sqrt {30}=5.477226$ \end {tasks}

And the table looks like this: