更新至 LuaLaTeX 版本 2021-12-31 后出现宏扩展/列表问题

更新至 LuaLaTeX 版本 2021-12-31 后出现宏扩展/列表问题

我正在使用 Archlinux 和 texlive。在 2022-01-02 将我的安装更新为最新的 Archlinux texlive-packets 后,LuaLaTeX 的某些功能似乎发生了重大变化。例如,tex.sprint(STRING)在 的文件名参数中不再起作用\lstinputlisting{FILENAME}

更新之前,以下 MWE 正在运行:

\documentclass{minimal}

\usepackage{luacode}
\usepackage[procnames]{listings}

\begin{filecontents*}{before-file1_test_label.lst}
    \LaTeX~macros work
\end{filecontents*}

\newcommand{\lstSHAName}[2]{before-\directlua{
            local string = "#1"
            tex.print(string .. '_test')
        }_#2.lst}
    
\begin{document}
    %\lstinputlisting{\lstSHAName{file1}{label}} % was working before, now crashes
    \lstinputlisting{before-file1_test_label.lst} % still works, but requires setting path manually 
\end{document}

如果我使用新版本的 texlive 进行编译,编译将停止并显示以下错误消息:

! Missing \endcsname inserted.
<to be read again> 
global 
l.16    \lstinputlisting{\lstSHAName{file1}{label}}

更让人怀疑问题与更新有关的是,我的同事在 Windows 上使用 MikTeX 仍然可以毫无问题地编译旧代码。

解决方法

这个想法是使用tex.sprint(STRING)LuaLaTeX 的功能来打印\lstinputlisting带有所有选项的命令,然后让 LaTeX 执行它。

\documentclass{minimal}

\usepackage{luacode}
\usepackage[procnames]{listings}

\begin{filecontents*}{before-file1_test_label.lst}
    \LaTeX~macros work
\end{filecontents*}

\newcommand{\printListing}[3]{%
        \directlua{
            local strA = "#1"
            local strB = "#2"
            local strC = "#3"
            local string = "\\lstinputlisting[caption={" .. strC .. "}]{before-" .. strA  .. "_test_" .. strB .. ".lst}" 
            tex.sprint(string)
    }
}
    
\begin{document}
    \printListing{file1}{label}{Test caption} % works
    %\printListing{file1}{label}{Test caption with \LaTeX~macro} % throws an error due to expansion
    \printListing{file1}{label}{Test caption with \\LaTeX{} macro} % works, but tilde would have also to be escaped differently
\end{document}

传递 LaTeX 宏\directlua而不进行转义(第 22 行)会导致错误

! Undefined control sequence.
\S@10 ->\gdef \tf@size 
                       {10}\gdef \sf@size {7}\gdef \ssf@size {5}
l.22

手动转义在某种程度上是可行的。但是,最初的想法是为没有经验的 LaTeX 用户提供一个包装器宏。必须知道如何在 LaTeX 中转义,以便 lua 能够正确处理它,这违背了最初的目的。

问题

  1. 在将文件路径传输到 之前,可以通过类型转换/扩展/转义来解决此问题吗\lstinputlisting?如果可以,该如何解决(无法使其工作;总是出现相同的错误)?
  2. 有没有一种解决方法,expl3可以在将参数传递给之前对参数进行预处理\directlua,以便 lua 不会扩展宏(例如,传递\\LaTeX而不是\LaTeX)?

已安装 texlive 包

我认为与该问题相关的软件包:

  • texlive-bin 2021.5945-1,构建日期:2021-12-27
  • texlive-core 2021.61403-1,构建日期:2021-12-27
  • texlive-formats-extra 2021.57972-1,构建日期:2021-04-06
  • texlive-latexextra 2021.61405-1,构建日期:2021-12-27

答案1

1.

一般情况下,不会。根据 TeX 解析规则,如果你有

\lstinputlisting{\lstSHAName{file1}{label}}

然后 TeX 将\lstinputlisting首先扩展并将参数{\lstSHAName{file1}{label}}(删除括号后)传递给它,并且不能保证\lstinputlisting宏会扩展该参数。

但是仍然可以重新定义\lstinputlisting(可能很脆弱并且会干扰包装listings!)。

或者你也可以做类似的事情

\injectlstSHAName{
\lstinputlisting{\lstSHAName{file1}{label}}
}

外部宏读取参数(如果您想允许用户在内部使用 catcode 更改命令,则逐字读取),执行适当的替换,然后打印回内容。

2.

一种方法是逐字读取参数(参见下面代码的第 1 部分)。

另一种方式是detokenizeit。在这种情况下,无论如何它都是一样的,因为标题被当作参数来抓取(即\lstinputlisting[caption={a {\verb+123+} b}]{before-file1_test_label.lst}无论如何都行不通。cprotect但是,仍然可以让它与 一起工作。)

还有一种方法是根本不通过 Lua 进行往返,而是将其保留在 TeX 中。这种方法的优点是保留了标记 catcode,以防用户知识渊博并将奇怪的 catcode 组合传递到命令中。



%! TEX program = lualatex
\documentclass{minimal}

\usepackage{luacode}
\usepackage[procnames]{listings}

\begin{filecontents*}{before-file1_test_label.lst}
    \LaTeX~macros work
\end{filecontents*}

    
\begin{document}

    % ======== method 1
\NewDocumentCommand{\printListing}{mmv}{%
        \directlua{
            local strA = "#1"
            local strB = "#2"
            local strC = "\luaescapestring{#3}"
            local string = "\\lstinputlisting[caption={" .. strC .. "}]{before-" .. strA  .. "_test_" .. strB .. ".lst}" 
            tex.sprint(string)
    }%
}

    \printListing{file1}{label}{Test caption}
    \printListing{file1}{label}{Test caption with \LaTeX~macro}

    % ======== method 2
\RenewDocumentCommand{\printListing}{mmm}{%
        \directlua{
            local strA = "#1"
            local strB = "#2"
            local strC = "\luaescapestring{\detokenize{#3}}"
            local string = "\\lstinputlisting[caption={" .. strC .. "}]{before-" .. strA  .. "_test_" .. strB .. ".lst}" 
            tex.sprint(string)
    }%
}

    \printListing{file1}{label}{Test caption with \LaTeX~macro}

    % ======== method 3
\RenewDocumentCommand{\printListing}{mmm}{%
    \lstinputlisting[caption={#3}]{before-#1_test_#2.lst}
}

    \printListing{file1}{label}{Test caption with \LaTeX~macro}

\end{document}

事实证明方法 3 是最简单的。

相关内容