2

Consider the following MWE. If I pass some TeX code to Lua (via \directlua) and Lua tries to pass it back to TeX (via tex.sprint), the following problem becomes apparent.

The code below prints

text is  \newcommand {\foo }{##1} \foo  end

As you can see, the # doubles once passed to Lua, and of course this breaks the macro \foo.

It's necessary to wrap the text argument in \detokenize or similar, in order to stop TeX expanding it before passing to to Lua, but both \detokenize and \unexpanded have this issue.

This question: Macro char # doubles when made letter? is probably the same phenomenon. And also How do I expand exactly once a macro which takes an argument.

Suggestions on how to work around this would be appreciated. I'd obviously prefer an approach whereby I wrap the argument passed to luafun inside \NewDocumentCommand in something, rather than adding extra special code to every occurence of # in my text.

And in this context, are there any other unpleasant surprises lurking in the wings with other special TeX characters? If so, please warn me now.

\documentclass[12pt]{article}
\usepackage{xparse}
\usepackage{filecontents}
\begin{filecontents*}{fn.lua}
function luafun (text)
   texio.write_nl(string.format([[text is %s end]], text))
   tex.sprint(text)
end
\end{filecontents*}
\directlua{local scratch = require "fn.lua"}

\NewDocumentCommand{\luafun}{+m}
{
  \directlua{luafun([[\detokenize{#1}]])}
  %\directlua{luafun([[\unexpanded{#1}]])}
}
\begin{document}

\luafun
{
  \newcommand{\foo}[1]{#1}
  %\foo{foo}
}

\end{document}
Faheem Mitha
  • 7,778
  • The correct way to pass TeX tokens to Lua is with \directlua{luafun("\luaescapestring{\unexpanded{#1}}")}. Everything else will suffer from things like the hash doubling problem. – Henri Menke Jun 28 '19 at 06:42
  • @HenriMenke I was just posting exactly the same as an answer! – Joseph Wright Jun 28 '19 at 06:43

1 Answers1

4

You need \unexpanded here because of it's toks-like behaviour. You also need to properly escape the tokens, so [[ .. ]] won't work. Try instead

\NewDocumentCommand{\luafun}{+m}
  {%
    \directlua{luafun("\luaescapestring{\unexpanded{#1}}")}%
  }
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036