Limited Interest

LuaTeX and Going Mad With Power

Technical

LuaTeX is a TeX engine that enables the use of Lua inside of your documents. This is something to be explored, for sure.

Dates from a directory

The first use of this that I came up with is to import all files from a directory and automatically insert some date title. The takeaway from this section is that LuaTeX comes with the Lua File System package (or lfs) by default.

The code for this is included verbatim, despite the fact that I wrote this to see if it would work rather than to do it right…

local lfs = require("lfs")

local function files_in_dir(dir)
    local t = {}
    for file in lfs.dir(dir) do
        table.insert(t, file)
    end
    return t
end

local function ends_with(str, suffix)
    return suffix == "" or str:sub(-#suffix) == suffix
end

local function remove_suffix(str, suffix)
    if ends_with(str, suffix) then
        return str:sub(1, #str - #suffix)
    end
    return str
end

local function write_files(dir)
    local files = files_in_dir(dir)
    table.sort(files)
    for _, file in ipairs(files) do
        if ends_with(file, ".tex") then
            local d =  remove_suffix(file, ".tex")
            tex.print([[\par]])
            tex.print(string.format([[\entry\{ %s }]], d))
            tex.print(string.format([[\input\{ %s/%s }]], dir, d))
        end
    end
end

return write_files

which I have saved in a file named include_directory.lua. I can then use this in some tex file,

\usepackage{xparse}

\newcommand{\entry}[1]{\hspace*{\fill}\textbf{#1}\par\noindent}

\NewDocumentCommand{\inputDirectory}{v}{
    \directlua{
        local f = require("include_directory")
        local s = [[#1]]
        f(s)
    }
}

which, if the files are named following an ISO 8601 date format, will include the files in date order with a nice little timestamp aligned to the left of the start of the section. The command to include a directory in this way is defined as \inputDirectory, and the dates can be formatted without it using the newly defined \entry command.

Equation Evaluation

Have you ever had a simple equation that you didn’t want to evaluate by hand? Can’t a computer do it for you at compile time? Yes, yes it can. And with LuaTeX it’s not too bad!

Using lua code in a file named evaluate.lua

local env = {
    sin = math.sin,
    sinh = math.sinh,
    cos = math.cos,
    cosh = math.cosh,
    tan = math.tan,
    tanh = math.tanh,
    asin = math.asin,
    acos = math.acos,
    atan = math.atan,
    asinh = math.asinh,
    acosh = math.acosh,
    atanh = math.atanh,
    pi = math.pi,
    e = math.e,
    exp = math.exp,
    log = math.log10,
    ln = math.log,
    sqrt = math.sqrt,
}

return function(str)
    print("Evaluating")
    print(str)
    local brackets, _ = str:gsub("{", "("):gsub("}", ")")
    local func = load("return " .. brackets, "evaluate", "t", env)
    return func()
end

This defines a function that takes a string and evaluates it using the built-in maths functions. and the TeX code is

\usepackage{xparse}
\usepackage{siunitx}
\usepackage{amsmath}

\NewDocumentCommand{\printeval}{ m }{#1=\begingroup
    \def\left({(}%
    \def\right){)}%
    \def\times{*}%
    \RenewExpandableDocumentCommand\sqrt{ O{2} m }{(##2)^(1/(##1))}%
    \def\frac##1##2{(##1)/(##2)}%
    \def\sin##1{sin(##1)}%
    \def\sinh##1{sinh(##1)}%
    \def\cos##1{cos(##1)}%
    \def\cosh##1{cosh(##1)}%
    \def\tan##1{tan(##1)}%
    \def\tanh##1{tanh(##1)}%
    \def\arcsin##1{asin(##1)}%
    \def\arccos##1{ascos(##1)}%
    \def\arctan##1{atan(##1)}%
    \def\arcsinh##1{asinh(##1)}%
    \def\arccosh##1{acosh(##1)}%
    \def\arctanh##1{atanh(##1)}%
    \def\pi{pi}%
    \def\e{e}%
    \def\exp##1{exp(##1)}%
    \def\log##1{log10(##1)}%
    \def\ln##1{ln(##1)}%
    \edef\x{\directlua{tex.write(require("evaluate")([[#1]]))}}%
    \expandafter\endgroup\expandafter\num\expandafter{\x}
}

This defines a command \printeval that can be used in a maths environment. This prints and evaluates the maths. It works by using macros to convert the TeX formulation into a Lua formulation, and then evaluates using the function defined in the lua code from before.

EDIT: 2020-11-04

Today I noticed that some escaping did not work as expected in the first lua code block, and would not actually work! This has been corrected.