LuaTeX 中的 Lua 代码按什么顺序进行处理

LuaTeX 中的 Lua 代码按什么顺序进行处理

有人能帮助我了解 LuaTeX 文档的处理顺序吗?

据我了解,顺序如下:

  • 代码正在被读取,从上到下
  • 当编译器找到一些 Lua 代码的开头时,它会吞下它,扩展它能找到的任何 TeX 命令,并将结果传递给 Lua 解释器
  • Lua 解释器解释代码并将其输出放回到文档中

  • 输出是否被再次解析?是否直接放入文档中?

为了说明我的问题:

\newcommand{\compare}[2]{
#1
\directlua{
    testNumber=#2
    if #1<testNumber then
        tex.sprint("is bigger")
    else
        tex.sprint("is smaller")    
    end
    tex.sprint(" than "..testNumber..".")
    }
}

\compare{1}{5}被调用时,Lua 解释器被输入:

testNumber=5
if 1<testNumber then
    tex.sprint("is bigger")
else
    tex.sprint("is smaller")    
end
tex.sprint(" than "..testNumber..".")

并且输出将会被粘贴到调用directlua命令的位置的文档中。

现在假设我添加另一个命令:

\newcommand{\iterate}[2]{
    \directlua{
        for i=1,#1,1 do
        tex.print(\\compare{i}{#2})
        end
    }
}

并通过 \iterate{5}{5} 调用它

根据我有限的理解,这应该按如下方式提供给 Lua 解释器:

for i=1,10,1 do
tex.print(\compare{i}{5})
end

输出结果为:

\compare{1}{5}
\compare{2}{5}
\compare{3}{5}
\compare{4}{5}
\compare{5}{5}

这个输出是否再次被评估?我似乎无法运行我的代码,因此我想我可能误解了一些东西。

这个过程是否可以从预处理器-处理器关系的角度来理解,比如说,一个 PHP 应用程序输出 JS 代码,而该代码本身正在由客户端解释?

答案1

当你写作时

\newcommand{\iterate}[2]{
    \directlua{
        for i=1,#1,1 do
        tex.print(\\compare{i}{#2})
        end
    }
}

Lua 进程看到

for i=1,#1,1 do
  tex.print(<the result of \compare>)
end

你需要做的是这样的:

\newcommand\iterate[2]{%
  \luaexec{
    for i=1,#1 do
        tex.print(string.format("\\compare{\%d}{#2}",i))
    end
 }
}

\iterate{3}{2}

(luaexec 来自 luacode 包)这会将“ \compare{1}{2} \compare{2}{2} \compare{3}{2}”打印到 TeX 的输入字符串中,并对其进行评估。这有效,因为\\它会生成一个反斜杠,并且它与单词“compare”分开。这是一种 hack,我不建议这样做。请参阅如何在 LuaTeX 中执行“printline”了解更多信息。

这是一个关于“重新评估”的实验:

例如:如果你写

\documentclass[a4paper]{article}
\usepackage{luacode}
\begin{document}

\begin{luacode*}
tex.sprint("%hello")
\end{luacode*}

\end{document}

TeX 将会把%hello它看作和解释为注释,因此不会生成输出文件。

当你这样写的时候(将 -2 视为 tex.print() 的第一个参数)

\documentclass[a4paper]{article}
\usepackage{luacode}
\begin{document}

\begin{luacode*}
tex.sprint(-2,"%hello")
\end{luacode*}

\end{document}

tex 看到 %hello,但 % 有一个“安全”类别代码。如果 tex.print 的第一个参数是数字,它将被视为 catcode 表。我想要展示的是:TeX 读取调用的结果\directlua{}

答案2

值得一提的是,ConTeXt 提供了一个 lua 表context,可以访问在 TeX 级别定义的所有宏。因此,您可以使用它context.compare来调用在 TeX 端定义的宏\compare。因此,可以按如下方式将您的宏转换为 ConTeXt(我遵循与您的宏相同的定义。编写 lua 函数的正常 ConTeXt 风格有所不同)。

\def\compare#1#2%
    {\directlua{
        local testNumber=#2
        if #1<testNumber then
            context("#1 is bigger")
        else
            context("#1 is not bigger")    
        end
        context(" than "..testNumber..".")
    }}

\def\iterate#1#2%
    {\directlua{
        for i=1,#1,1 do
          context.compare(tostring(i) , #2)
        end}}

\starttext
\iterate{5}{2}
\stoptext

这使用了一些我不理解的 Lua 魔法。从内部来看,它类似于 rdhs 给出的答案;只是您不必手动进行管道操作。请参阅ConTeXt Lua 文档手册了解更多详细信息。

答案3

首先,这是您的代码的工作版本:

\newcommand{\compare}[2]{
#1
\directlua{
    testNumber=#2
    if #1<testNumber then
        tex.sprint("is bigger")
    else
        tex.sprint("is not bigger")    
    end
    tex.sprint(" than "..testNumber..".")
    }
}
\newcommand{\iterate}[2]{
    \directlua{
        for i=1,#1,1 do
        tex.print("\noexpand\\compare{" .. i .. "}{#2}")
        end
    }
}
\iterate{5}{5}

现在,基本事件顺序如下:

  1. \newcommand将您的代码存储在 的定义中\iterate。到目前为止,尚未执行任何操作。
  2. 在某个时候,\iterate会遇到并替换您的定义。TeX 会看到\directlua并尝试扩展它。
  3. TeX (via \directlua) 扩展括号内的所有内容。这意味着参数 ( #1) 以及\\,因为\\是控制序列。因此当您说 时"\\compare",TeX尝试将 的定义传递\\给 Lua。为了防止这种情况,您需要使用"\noexpand\\compare"。您也可以使用[[\@backslashchar]] .. "compare"或 ,如果脑损伤严重,请尝试"\@backslashchar\noexpand\compare"
  4. \directlua将扩展的代码传递给Lua.,使其"\\compare"成为\compare
  5. TeX 现在看到\directlua扩展为五个\compare序列(及其参数)。它会替换您的定义并看到另一个\directlua,它会尝试将其扩展确切地与步骤2相同。

您可以通过将 Lua 代码放入单独的文件或使用软件包提供的功能来“关闭”步骤 3。您可以luacode对步骤 5 执行相同操作,tex.print(-2,"")因为这会使 Lua 的输出变成 TeX 不会尝试继续扩展的字符串。

FWIW,这是最干净的实现方式\iterate

\begin{filecontents*}{myiterate.lua}
function mycompare(num1,num2)
    tex.sprint(num1 .. " ")
    if num1<num2 then
        tex.sprint("is bigger")
    else
        tex.sprint("is not bigger")    
    end
    tex.sprint(" than " .. num2 .. ".")
end

function myiterate(reps,targ)
    for i=1,reps,1 do
        tex.sprint([[\compare{]] .. i .. "}{" .. targ .. [[}\\]])
    end
end
\end{filecontents*}

...

\directlua{require("myiterate")}
\newcommand\compare[2]{\directlua{mycompare(#1,#2)}}
\newcommand\iterate[2]{\directlua{myiterate(#1,#2)}}
\iterate{5}{5}

相关内容