以下代码是将参数从 TeX 传递到 Lua 的简单示例。直接传递参数(不带 的版本\luastringN
)会出错。
(./luafunction.aux)./luafunction.lua:2: attempt to concatenate local 's' (a nil value)
stack traceback:
./luafunction.lua:2: in function 'joinstring'
[\directlua]:1: in main chunk.
\joinstring #1#2-> \directlua {joinstring(#1, #2)}
l.19 \joinstring{foo}{bar}
\luastringN
我注意到其他代码在将参数从 TeX 传递到 Lua 时使用了宏,所以我也这么做了。但这完全是 Cargo Cult 编程,因为我不知道为什么。luacode
手册中的文档不是很有启发性。在 v1.2a 手册第 3 页第 1.2 节中\luastringN
,有文档记录,它讨论了转义特殊字符。但我的示例中没有特殊字符。我不明白从 TeX 传递到 Lua 需要什么特殊处理。感谢您的解释 - 请随意简化它。
此外,这是对“LuaLaTeX 在 lua 中设置路径”问题的回答提到应该使用token.get_macro
。v 1.10 手册第 10.6.4 页上的文档luatex
说:
get_macro 函数可用于获取宏的内容
但我不确定这是什么意思。另外,我在 TeX SE 中搜索了token.get_macro
,得到了两个结果。
那么这是一个更好的选择吗?如果是,为什么?该get_macro
函数到底有什么用?
\documentclass{article}
\usepackage{luacode}
\usepackage{filecontents}
\begin{filecontents*}{luafunction2.lua}
function joinstring (s, t)
tex.sprint(s .. t)
end
\end{filecontents*}
\directlua{require "luafunction2.lua"}
\begin{document}
\newcommand\joinstring[2]
{
% \directlua{joinstring(#1, #2)}
\directlua{joinstring(\luastringN{#1}, \luastringN{#2})}
}
\joinstring{foo}{bar}
\end{document}
聊天室讨论开始于这里
答案1
这里所做\luastringN
的只是提供一个包装器,\luaescapestring
加上一些皱纹。我只会把它写出来,至少对于“练习”文件来说
\documentclass{article}
\usepackage{filecontents}
\begin{filecontents*}{luafunction2.lua}
function joinstring (s, t)
tex.sprint(s .. t)
end
\end{filecontents*}
\directlua{require "luafunction2.lua"}
\begin{document}
\newcommand\joinstring[2]
{%
\directlua{joinstring(
"\luaescapestring{\unexpanded{#1}}",
"\luaescapestring{\unexpanded{#2}}")
}%
}
\joinstring{foo}{bar}
\end{document}
请注意,\luaescapestring
执行 ( e
-type) 扩展,因此我们需要\unexpanded
避免发生奇怪的事情。还要注意"
字符,这些字符是告诉 Lua 我们正在传递一个字符串所必需的。我们不必担心"
TeX 输入中的任何内容,因为\luaescapestring
它们是安全的。
答案2
由于 Lua 函数tex.sprint
需要对字符串进行操作(字符串常量、类型变量string
或可以string
动态强制转换为类型的内容),因此将 LaTeX 端宏定义为
\newcommand\joinstring[2]{\directlua{joinstring(#1,#2)}}
然后运行
\joinstring{foo}{bar}
是错误的,因为foo
和bar
不会自动转换为 Lua 识别的字符串。事实上,您重现的错误消息表明 Lua 认为foo
和bar
属于 类型nil
。 类型的变量nil
肯定会使指令..
中的字符串连接操作出错tex.sprint ( s .. t )
。
两个明显的补救措施是
将 的参数放在显式引号中
\joinstring
,即运行\joinstring{"foo"}{"bar"}
将 LaTeX 宏定义为
\newcommand\joinstring[2]{\directlua{joinstring("#1","#2")}}
我想有人会说,
\luastring{#1}
尤其是对该方法的\luastringN{#1}
详细说明(例如,禁止扩展) 。它们还更强大,因为它们可以处理参数中不平衡的(单引号和双引号);不平衡的双引号会破坏该方法。#1
"#1"
"#1"
实现第二个想法的完整 MWE —— 请注意,对于手头的简单代码,无需加载包luacode
:
\documentclass{article}
\directlua{function joinstring (s,t) tex.sprint (s..t) end}
\newcommand\joinstring[2]{\directlua{joinstring("#1","#2")}}
\begin{document}
\joinstring{foo}{bar}
\end{document}
答案3
\luaescapestring
作为其他答案中基于解决方案的替代方案,您还可以使用token
库将参数从 TeX 传递到 Lua。这会导致速度略有提高,因为 LuaTeX 不必转义字符串以便 Lua 再次解析它。因此,您可能会考虑它,尤其是当您期望长字符串时。此外,这避免了混合 TeX 和 Lua 代码,如果您以后决定使用\luafunction
(或\luadef
) 的代码,这会有所帮助。
无论如何,你首先必须决定是否需要 ( e
-type) 扩展。token
函数总是会扩展参数,所以\unexpanded
如果你不想这样做,你必须应用。然后你可以使用token.scan_argument
它来读取直接给出的参数后整个 Lua 代码。如果您决定读取扩展参数,您也可以完全通过 Lua 处理参数读取,但适用略有不同的扫描规则。特别是在简单情况下,字符串参数不需要括号。
以下是一些示例,可查看不同选项的实际效果:
\documentclass{article}
\usepackage{luacode}
\usepackage{filecontents}
\begin{filecontents*}{luafunction2.lua}
function joinstring (s, t)
tex.write(s, ', ' , t, ': ')
tex.sprint(s .. t .. '.')
end
\end{filecontents*}
\directlua{require "luafunction2.lua"}
\begin{document}
\newcommand\joinstring[2]{
\directlua{joinstring(token.scan_argument(), token.scan_argument())}%
{\unexpanded{#1}}{\unexpanded{#2}}
}
\newcommand\expandedjoinstring[2]
{
\directlua{joinstring(token.scan_argument(), token.scan_argument())}%
{#1}{#2}%
}
\newcommand\otherexpandedjoinstring
{
\directlua{joinstring(token.scan_argument(), token.scan_argument())}%
}
\newcommand\foo{foo}
\newcommand\Bar{bar}
\verb|\joinstring{foo}{bar}|: \joinstring{foo}{bar}\\
\verb|\joinstring{\foo}{\Bar}|: \joinstring{\foo}{\Bar}\\
\verb|\joinstring foo bar|: \joinstring foo bar\\
\verb|\expandedjoinstring{foo}{bar}|: \expandedjoinstring{foo}{bar}\\
\verb|\expandedjoinstring{\foo}{\Bar}|: \expandedjoinstring{\foo}{\Bar}\\
\verb|\expandedjoinstring foo bar|: \expandedjoinstring foo bar\\
\verb|\otherexpandedjoinstring{foo}{bar}|: \otherexpandedjoinstring{foo}{bar}\\
\verb|\otherexpandedjoinstring{\foo}{\Bar}|: \otherexpandedjoinstring{\foo}{\Bar}\\
\verb|\otherexpandedjoinstring foo bar|: \otherexpandedjoinstring foo bar\\
\end{document}