转义字符串

转义字符串

TeX 和 Lua 之间的交互有时会非常微妙。前者有类别代码和标记,而后者有转义序列和数据类型。将这两个世界的不同概念混合在一起可能会导致意外行为,从而导致常见错误(称为陷阱)。

在下面的社区维基答案中,您可以找到人们在掌握 LuaTeX 时常犯的错误概述。

答案1

转义字符串

当将 TeX 标记作为 Lua 字符串传递时,Lua 中的特殊字符必须进行转义。此类错误的一个简单示例是

\newcommand\justprint[1]{\directlua{tex.sprint("#1")}}
\justprint{"hello"}

导致错误

[\directlua]:1: ')' expected near 'hello'.
\justprint #1->\directlua {tex.sprint("#1")}

因为 #1 已正式被替换,"hello"这会导致无效的 lua 语法tex.sprint(""hello"")。这可以通过应用轻松修复\luaescapestring

\newcommand\justprint[1]{\directlua{tex.sprint("\luaescapestring{#1}")}}
\justprint{"hello"}

如果luacode包已加载,则可以\directlua{tex.sprint("\luaescapestring{#1}")}}用替换\directlua{tex.sprint(\luastring{#1})}}

打印数字

让我们尝试将数字 42 和字符串打印foo到文档中。

\directlua{tex.sprint(42, "foo")}

foo但只显示出字符串,发生了什么?

这里的陷阱是,它tex.sprint接受可变数量的参数,并根据参数类型执行不同的操作。如果有多个参数,并且第一个参数是数字,则它将被解释为类别代码表编号,而不会打印到文档中。因此,将要打印的任何内容明确转换为字符串始终是一个好主意。

\directlua{tex.sprint(tostring(42), tostring("foo"))}

在这种情况下,第二种方法tostring是多余的,但你应该始终使用tostring函数返回值。此外,如果你忘记将整数转换为字符串,LuaTeX 可能会默默地为你完成此操作,但请注意,这个未记录的功能(自 1.13 版起)将来可能不受支持。

..只要一个操作数是字符串,就可以使用连接运算符

\directlua{tex.sprint((42).."foo")}

抑制扩张

\directlua语会完全展开其参数,如果存在不可扩展的内容,则会导致错误。例如,如果我们要执行\section{Title}

\directlua{tex.sprint("\section{Title}")}

由于命令执行得太早,因此不起作用\section。为了解决这个问题,可以使用以下常用的习惯用法:

\directlua{tex.sprint("\luaescapestring{\unexpanded{\section{Title}}}")}

\unexpanded原语将抑制扩展并\luaescapestring确保\s的前导\section不会被解释为转义序列。

如果luacode包已加载,则可以tex.sprint("\luaescapestring{\unexpanded{\section{Title}}}")用替换tex.sprint(\luastringN{\section{Title}})

另一种直接的可能性是\string将原语与长 lua 字符串一起使用:

\directlua{tex.sprint([[\string\section{Title}]])}

之后\string\section不再被视为命令,并且长的 lua 字符串不再被视为\控制字符。

对结果进行排版,即 Lua 函数返回的结果

在设置包含\directlua实时执行某些操作的语句的 LaTeX 宏时,初学者可能没有意识到,仅仅将正确的对象“返回”到 LaTeX 控件并不足以对其进行排版;tex.sprint还需要一个明确的指令。例如,

\directlua{function modify ( s )
    return ( s:gsub ( "Hello World" , "Hola Mundo" ) )
  end
}
\newcommand\modify[1]{\directlua{modify("#1")}}

输入时How to say \modify{Hello World} in Spanish?输出将是“如何用西班牙语说[两个空格]?”

为了获得所需的输出,有必要写入,比如说,\newcommand\modify[1]{\directlua{tex.sprint(modify("#1"))}}

评论范围之内\directlua

正如已经看到的,的参数\directlua首先根据 TeX 规则进行管理。特别是,%启动 TeX 注释:

\directlua{tex.sprint("this is printed% but not this comment"
   (up to the real closing quote)"
)}

相当于

\directlua{tex.sprint("this is printed(up to the real closing quote)" )}

我们观察到,它只\directlua用一行指令就可以完成。结果是,lua 短注释适用于比预期更多的指令:

\directlua{
  tex.sprint("One"); -- One is printed
  tex.sprint("Two"); -- but not Two
}

你可以使用长注释来代替

\directlua{
  tex.sprint("One"); --[[ One is printed ]]
  tex.sprint("Two"); --[[ Two is printed as well ]]
}

luacode环境

\begin{luacode}
  tex.sprint("One"); -- One is printed
  tex.sprint("Two"); -- Two is print as well
\end{luacode}

共享数据

lua 可以对许多 TeX 变量进行读写访问。例如,下面的 LaTeX 代码定义了一个计数器,从 lua 端打印并修改它,然后从 LaTeX 端打印其更新后的值:

\newcounter{abc}
\setcounter{abc}{421}
\directlua{
  tex.sprint(tostring(tex.getcount('c@abc')));
  tex.setcount('c@abc', 666);
}
\arabic{abc}

输出为“421 666”。这对于尺寸、框等也同样适用。LuaTeX 手册第 10.3 章对这个主题很有帮助。

对于宏来说,这并不那么透明。我们无法从 lua 端看到类别代码,因为token.get_macro('foo')是 的去标记内容\foo。我们也不能用 定义带有参数的宏token.set_macro,为此我们必须使用tex.print。尽管如此,宏可用于在 TeX 和 lua 之间共享信息(请参阅 LuaTeX 手册第 10.6 章)。我们将在下面说明这一点。

相反,TeX 无法直接访问 lua 变量。

TeX 和 lua 世界之间的一个主要区别是 lua 变量不遵循 TeX 分组机制。例如,下一个 LaTeX 环境在关闭时会打印包含以下内容的行号\begin

\newenvironment{Demo}{
  \directlua{ begin_lineno = tex.inputlineno }
} {
  \directlua{ tex.sprint(begin_lineno) }
}

当我们将 Demo 环境嵌入到另一个环境中时,这种方法就行不通了,因为begin_lineno只考虑了最后一个\begin。改用 LaTeX 计数器可以解决这个问题。更一般地说,遵循 TeX 组的数据应该存储在 TeX 端。

在 TeX 和 Lua 之间来回切换

\directlua允许从 TeX 端执行 lua 指令,而tex.sprint允许从 lua 端执行 TeX 指令。我们已经看到了喂食\directlua可能很棘手,现在我们看到打印是异步的,不知何故

\directlua参数中,各种tex.print函数向 TeX 发出指令。但这些指令只在结束时执行\directlua:此后宏的内容\foo交替为“白天”和“夜晚”,

\directlua{
  token.set_macro('foo', 'day');   --[[ same as \string\def\string\foo{day} ]]
  tex.sprint([[\string\foo]]);
  token.set_macro('foo', 'night'); --[[ same as \string\def\string\foo{night} ]]
  tex.sprint([[\string\foo]]);
}

结果并不是我们预期的“白天黑夜”,而是“夜晚黑夜”!这就像把所有的打印指令都集中在最后一样:

\directlua{
  token.set_macro('foo', 'day');
  token.set_macro('foo', 'night');
  tex.sprint([[\string\foo]]);
  tex.sprint([[\string\foo]]);
}

我们看到第一行是完全没用的。为了得到“白天夜晚”,我们必须使用两个单独的\directlua

\directlua{
  token.set_macro('foo', 'day');
  tex.sprint([[\string\foo]]);
}
\directlua{
  token.set_macro('foo', 'night');
  tex.sprint([[\string\foo]]);
}

由此推论,合并连续的\directlua指令并不总是安全的。此外,当 TeX 和 lua 之间的讨论涉及更复杂的指令时,某些回调模式可能会有所帮助:

\direclua{
  <lua instructions>
  tex.print(<tex instructions>)
  function callback()
    <to be performed after the tex instructions>
  end
  tex.print([[\string\directlua{callback()}]])
}

tex.runtoks或 lua 协程图示这个答案在这种情况下可能会有帮助,但这是高级用法。

一些未分类的事情(需要写出来)

  • 查找库
  • 错误报告(行号可能在\directlua调用内)
  • 百分比和其他“有趣”的字符和 luacode 包

相关内容