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 and luacode*, and place your Lua code in either a luacode or a luacode* 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 the string.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.

enter image description here

\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

Tags:

Luatex