Pass macro to be parsed by lua function in directlua

I have something like

\directlua{X = "width=0.5*\linewidth"} <-- invalid escape sequence

\directlua{X = "width=0.5*\\linewidth"} <-- Error

! Undefined control sequence.
\\->\let \reserved@e
\relax \let \reserved@f \relax \@ifstar {\let \reserved...

l.254 ...X = "width=0.5*\\linewidth";}


X is used in a lua function like

function doX()
--X = "width=0.5*\\linewidth"; --works fine
tex.print("\includegraphics["..X.."]{...}");
end


So the problem isn't with the function or with what I am passing but the escape issue. Is this a catcode issue? Basically I want to pass LaTeX macros in strings to lua that eventually then get spit back out unmodified by the lua code but \l is interpreted as an escape sequence in Lua and \\, I guess, is weird by LaTeX.

The \directlua primitive carries out full expansion of its argument before passing the resulting string to Lua. In general, it should only be used for small 'snippets' or for loading a separate dedicated Lua file

\directlua{dofile("name-of-file")}


as this avoids needing to worry about the TeX<-> Lua issues.

Where you are using \directlua with anything other than 'text' you have to keep an eye on the TeX expansion which will happen. With

\directlua{X = "width=0.5*\linewidth"}


as \linewidth is not an expandable token, the literal string is passed through to Lua. It then sees \l, which as the error says is not a valid escape sequence. With

\directlua{X = "width=0.5*\\linewidth"}


you have the TeX control sequence \\: this normally means end-of-line in LaTeX and has a definition which will expand but not 'properly'. You therefore get an odd error in the same way you would if you tried

\edef\foo{\\}


The correct approach here is to ensure that Lua sees the value of \linewidth:

\directlua{X = "width=0.5*\the\linewidth"}


If you want the calculation doing here, you'll need to allow for the fact that \linewidth is a dimension so perhaps

\directlua{X = "width=\the\dimexpr0.5\linewidth\relax"}


might be better.

If you want to arrange that Lua can return a TeX control sequence, you need to put a literal \\ into the string: this is done using \noexpand. Thus if you aim to have X holding a string to return to TeX then

\directlua{X = "width=0.5*\noexpand\\linewidth"}


is the correct approach to take here.

For longer blocks one might consider using the luacode package. It defines an environment in which some catcode changes are made to make it 'easier' to put Lua code directly into a LaTeX document. There are a couple of things to watch out for

• Catcode changes and grouping make luacode non-expandable
• Like (almost) all LaTeX environments, luacode is a TeX group so any definitions at the TeX side need to be global to 'escape'

Using luacode one might have

\RequirePackage{luacode}
\begin{luacode}
X = "width=0.5*\\linewidth"
\end{luacode}

• Thanks, the noexpand didn't work as I got some issue with missing dimension but "width=\the\dimexpr0.5\linewidth\relax" worked! Thanks! – AbstractDissonance Dec 27 '16 at 7:57
• @AbstractDissonance Without more context it's hard to be sure, but I suspect you got the error as you are using width in a context where TeX isn't expecting a dimen. (As I've shown, inside \dimexpr or similar would be fine, though without the *.) – Joseph Wright Dec 27 '16 at 8:02