3

I am trying to write the contents of \foo to a file using LuaLatex. Consider the following example:

\newcommand{\abcd}{test}
\newcommand{\foo}{XXX \abcd}
\directlua{
   local f = {}
   f.foo = token.get_macro 'foo'
(...)

When writing the contents of f.foo to a file later, the content will contain the string XXX \\abcd. If instead we change this to the following:

   f.foo = "\luaescapestring{\foo}"

The result will be the intended XXX test.

When the command \foo contains 'special' commands like ~, the result will be XXX \\protect \\unhbox \\voidb@x \\penalty \\@M \\ {} test. Now this is ugly, but can be parsed/is workable. However, when commands like \\ or \xspace are used, the compilation will fail, such as:

! Undefined control sequence.
\GenericError  ...                                
                                                    #4  \errhelp \@err@     ...

l.180 }

The error message seems to differ based on the command that was used.

Is there a way to expand the command \foo such that recursive expansion is performed and arbitrary special commands are protected/escaped or at the least compilation does not break?

EDIT 1

File main.tex:

\documentclass[british]{report}
\usepackage{luacode}

\begin{document}
Test text
\newcommand{\abcd}{test}
\newcommand{\foo}{XXX \\ \abcd}
\directlua{
    local f = {}
    f.foo = "\luaescapestring{\foo}"
    local fd = io.open("test.out", "a")
    fd:write(f.foo)
    fd:flush()
    fd:close()
}
\end{document}

Compiled with latexmk -pdf -pdflatex="lualatex" main.tex

Above throws an error, but works with \newcommand{\foo}{XXX \abcd}.

5
  • Welcome to TeX.SE.
    – Mico
    Commented Feb 25, 2020 at 19:41
  • it would easier to answer if you provided a test file rather than unconnected fragments/ handling tex expansion from Lua is tricky it would be easiest if you use \protected@edef first to expand everything as much as is safe then wrote the resulting tokens to the file. Commented Feb 25, 2020 at 19:45
  • Thanks for your time and suggestion, I will look into \protected@edef. In the mean time I have added an example to the original post.
    – Tom
    Commented Feb 25, 2020 at 20:08
  • with \newcommand{\foo}{XXX \abcd} I get [\di rectlua]:1: bad argument #1 to 'close' (FILE* expected, got no value) stack traceback: [C]: in field 'close' [\directlua]:1: in main chunk. l.15 } Commented Feb 25, 2020 at 20:11
  • @Tom the error in the previous comment is as you have .close not :close as the last line of Lua. Commented Feb 25, 2020 at 20:32

1 Answer 1

2

In order to make "fragile commands safe in a moving argument" LaTex sets \protect to a suitable value, here the expansion happens while passing to Lua, but is otherwise similar to the expansion that happens in a TeX \write \string is a suitable value here so

\documentclass[british]{report}


\begin{document}
Test text
\newcommand{\abcd}{test}
\newcommand{\foo}{XXX \\ \abcd}

{\let\protect\string
\directlua{
    local f = {}
    f.foo = "\luaescapestring{\foo}"
    local fd = io.open("test.out", "a")
    fd:write(f.foo)
    fd:flush()
    fd:close()
}}

\end{document}

which writes a file with content

XXX \\ test
3
  • You are a genius, this works! I just applied it to my original project and it expands most commands. Some commands like \bullet \cmd \acs are not expanded but escaped. I guess they can not be interpreted/stripped to a string and will have to be parsed manually.
    – Tom
    Commented Feb 25, 2020 at 20:49
  • @Tom we have some expl3 code to "purify" such strings to pure text (eg for use in pdf bookmarks) er but I'm not sure if it is released yet, let me check.... Commented Feb 25, 2020 at 20:51
  • @Tom don't really have parse anything just make your macro that sets this up give expandable definitions so inside the group with \let\protect\string add \def\bullet{•} then \bullet will expand to • during the write. Commented Feb 25, 2020 at 22:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .