LuaTeX and Going Mad With Power
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.