problem with string.format, \directlua and tex.sprint
The %
(percent) symbol is special to both (La)TeX and Lua, but in very different ways. In (La)TeX, it's the default comment character. In Lua, it's one of the "magic" characters in pattern matching operations. (Lua's other "magic" characters are ^$().[]*+-?
.)
The \directlua
directive is expandable, in the TeX-specific sense of the word. This means that TeX scans the argument of \directlua
for any macros; if macros are found, TeX will try to expand them. In your second example, when (La)TeX scans
\directlua{tex.sprint(string.format(' %.3f', math.pi))}
it doesn't come across any LaTeX macros, but it does encounter the %
character, which it interprets as a comment character. Hence, everything through the end of the input line is ignored, including the two closing parentheses and the single closing curly brace. That's why you're getting an error message.
How to keep these two, rather incompatible, uses of %
from interfering with each other? I can think of four strategies.
Load the Lua code in such a way that it's not processed by LaTeX inappropriately.
Place the Lua code in an external file (generally with extension
.lua
, e.g.,file.lua
) and load the code into LuaTeX with a\directlua{dofile("file.lua")}
instruction.In your main tex file, load the luacode package, which provides environments called
luacode
andluacode*
, and place your Lua code in either aluacode
or aluacode*
environment.
Instead of \directlua
, use an instruction which can handle \%
as a substitute for %
while executing Lua code.
Load the
luacode
package, which provides the macro\luaexec
(to be used instead of\directlua
), and "escape" all instances of%
with a backslash, i.e., rewrite them as\%
. E.g.,\luaexec{tex.sprint(string.format('\%.3f', math.pi))}
Define a macro called, say, \percentchar
which expands to %
without this character being interpreted by TeX as a comment character. This allows continued use of \directlua
. (Many thanks to Joseph Wright for providing this suggestion and to @egreg for providing a pointer to an even more parsimonious definition of \percentchar
!)
The fourth method works by making the
%
character non-special to TeX's eyes. Place the following instruction --\@percentchar
is defined by the LaTeX "kernel"; it outputs a catcode-12 ("other") form of%
-- in the preamble:\makeatletter\let\percentchar\@percentchar\makeatother %% or: \begingroup\catcode`\%=12\relax\gdef\percentchar{%}\endgroup
Then, execute this instruction in the body of the document:
\directlua{tex.sprint(string.format('\percentchar.3f', math.pi))}
Recall that
\directlua
is expandable, i.e., it'll expand\percentchar
to%
, which is the character Lua needs to see in thestring.format
function. And, because this form of%
has been given catcode 12 ("other"), it won't be misinterpreted as a comment character by LaTeX -- and all is well. :-)While this fourth approach may seem a bit cumbersome at first, it enjoys the undeniable virtue of being implementable without having to load any packages (such as the
luacode
package) or having to place the Lua code in an external file.
Which of these four methods one should use depends crucially on what one needs to accomplish when making a Lua call. If it's just a single instance of a simple instruction, the third and fourth methods may be easiest. If the Lua code is long and complex, the first and second methods are almost certainly to be preferred.
The first three possibilities are illustrated in the following example.
\RequirePackage{filecontents}
%% create an external file, to store Lua code
\begin{filecontents*}{aux.lua}
function printpi (n) -- n: number of decimal digits to be shown
tex.sprint ( string.format ( '%.'..n..'f' , math.pi ))
end
\end{filecontents*}
\documentclass{article}
\directlua{ dofile ( "aux.lua" ) } % load Lua code from external file
\usepackage{luacode} % load 'luacode' package
\begin{luacode}
function pi3() -- this function doesn't take an argument
tex.sprint ( string.format ( '%.3f' , math.pi ) )
end
\end{luacode}
\begin{document}
$\pi \approx \directlua{printpi(3)}$
$\pi \approx \directlua{pi3()}$
$\pi \approx \luaexec{tex.sprint(string.format('\%.3f', math.pi))}$
\end{document}
Here is another idea. Simply round of math.pi
and show the result. Lua does not have a function that rounds up to n
decimal places, but it is relatively simply to create one. The code below is in ConTeXt, but should be straight forward to translate to LaTeX as well:
\startluacode
local floor, ceil = math.floor, math.ceil
math.Round = function(x, n)
local factor = math.pow(10, n or 0)
local y = x * factor
if x >= 0 then
y = floor(y + 0.5)
else
y = ceil(y - 0.5)
end
return y / factor
end
\stopluacode
\starttext
$π \approx \ctxlua{context(math.Round(math.pi, 3))}$
\stoptext