5

I am trying to use lualatex to generate tikz images. However, the the compiler gets stuck producing no output. Included below is a minimal example

\documentclass{standalone}
\usepackage{luacode}
\usepackage{tikz}

\newcommand{\drawaxes}{%
  \directlua{%
     tex.sprint("\draw[thick,red] (-1,0) -- (1,0)")
}
}

\begin{document}
\begin{tikzpicture}
  %\draw[thick,red] (-1,0) -- (1,0);
  \drawaxes;
\end{tikzpicture}
\end{document}

compilation gets stuck at:

[Loading MPS to PDF converter (version 2006.09.02).]
) (/usr/share/texlive/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty
(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/grfext.sty
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/kvdefinekeys.sty))
(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/kvoptions.sty
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/kvsetkeys.sty
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/etexcmds.sty)))
(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
^C))
! Interruption.
\path ^^@-\let \tikz@signal@path 
                               =\tikz@signal@path \pgfutil@ifnextchar [{\tik...
l.14   \drawaxes
              ;
? 

Note that hen I use the \draw directly it works fine. Any hint on how to debug this is appreciated.

  • Welcome to TeX.SX! \directlua tries to fully expand its argument and \draw gets so expanded at the wrong time. – egreg Mar 2 '18 at 23:18
  • Welcome! This is an amusing problem; this is why it is always advisable, when using LuaTeX, to put all Lua code in a separate file. :-) – ShreevatsaR Mar 2 '18 at 23:25
5

The argument to \directlua is always fully expanded, so \draw is expanded to \path, which expands to the code in your error message. This goes into an endless loop to protect against using it in an expansion context.

To fix this, you have to avoid the expansion of \draw during the full expansion of the \directlua argument. For this, you can use for example \unexpanded:

\newcommand{\drawaxes}{%
  \directlua{\unexpanded{%
    tex.sprint([[\draw[thick,red] (-1,0) -- (1,0)]])
  }}
}

You can see that I also had to replace the quotes with [[]]. Inside quotes, the backslash starts an escape sequence, so "\draw" is the special character \d followed by raw, which would lead to another TeX error.

In general this is great example why writing Lua in LuaTeX inline with \directlua leads to a lot of problems. Most of these problems can be avoided by moving the Lua code into another file or into a luacode* environment provided by the luacode package.

The luacode package also provides the \luaexec command if you prefer an inline solution. This allows you to include TeX commands in a regular Lua string by using a double backslash. So you could use

\newcommand{\drawaxes}{%
  \luaexec{
    tex.sprint("\\draw[thick,red] (-1,0) -- (1,0)")
  }
}
  • Moving all Lua code into separate seems to be the cleanest solution... I solemnly swear to put all lua code in a separate file! – Haaput Mar 2 '18 at 23:48
5

So you've defined

\newcommand{\drawaxes}{%
  \directlua{%
     tex.sprint("\draw[thick,red] (-1,0) -- (1,0)")
}
}

Later, when TeX encounters \drawaxes, it replaces it with

\directlua {tex.sprint("\draw [thick,red] (-1,0) -- (1,0)") }

and goes to work again. It now encounters \directlua. If you look up the documentation of the \directlua primitive in the LuaTeX manual, it says:

The primitive \directlua is used to execute Lua code immediately. The syntax is

\directlua ⟨general text⟩
\directlua ⟨16-bit number⟩ ⟨general text⟩

The ⟨general text⟩ is expanded fully, and then fed into the Lua interpreter. …

So TeX takes the sequence of tokens tex.sprint("\draw [thick,red] (-1,0) -- (1,0)") and starts expanding it. Each token up to tex.sprint(" remains as it is (does not expand), but the \draw expands to \path [draw]. And \path expands to

\let \tikz@signal@path=\tikz@signal@path \pgfutil@ifnextchar [{\tikz@check@earg }{\pgfutil@ifnextchar <{\tikz@doopt }{\tikz@@command@path }}

in which \tikz@signal@path is defined as a macro that expands to… \tikz@signal@path, itself! By the time you interrupt, TeX is still repeatedly expanding \tikz@signal@path.

If you like, you could see all this if you add something like \errorcontextlines=100 in your .tex file, to see more context in the error message than you saw:

! Interruption.
\path ->\let \tikz@signal@path 
                               =\tikz@signal@path \pgfutil@ifnextchar [{\tikz@check@earg }{\pgfutil@ifnextchar <{\tikz@doopt }{\tikz@@command@path }}
\draw ->\path 
              [draw]
\drawaxes ->\directlua {tex.sprint("\draw 
                                          [thick,red] (-1,0) -- (1,0)") } 
l.16   \drawaxes
              ;
?     

(The convention is that in each pair of lines, the first line is everything that TeX has read, and the second line is what is yet to come. It's a dump of TeX's internal input stack.)

Add \tracingmacros=1 \tracingonline=1 to see TeX working hard.

You can reproduce this error without LuaTeX, with a document like:

\documentclass{standalone}
\usepackage{tikz}
\makeatletter
\edef\haha{\tikz@signal@path}

on any other engine (pdfTeX or XeTeX) too.

(I don't know why \tikz@signal@path is defined to expand to itself; it's come up before.)


Anyway, the solution is to make sure that \directlua gets the actual Lua code you want. There are some tricks with TeX macros, but the easiest is to put as much Lua code as possible in a separate file. (My rule of thumb is: the contents of \directlua{...} should contain only alphanumeric characters, (), and single quotes.)

For example, put the following in draw.lua:

function drawaxes()
   tex.sprint([[ \draw[thick,red] (-1,0) -- (1,0) ]])
end

(note the [[ ... ]] to treat the string literally, without having to escape backslashes)

and your .tex file can be:

\documentclass{standalone}
\usepackage{luatex85}
\usepackage{tikz}

\directlua{dofile('draw.lua')}
\newcommand{\drawaxes}{\directlua{drawline()}}

\begin{document}
\begin{tikzpicture}
  \drawaxes;
\end{tikzpicture}
\end{document}

(Or you could put just the tex.sprint line in the draw.lua file and have

\newcommand{\drawaxes}{\directlua{dofile('draw.lua')}}

but I imagine you'd like to put multiple functions in the same file.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.