将 TeX 宏接到的参数传递于 Lua 函数,略含艺术性。
例如,将 \foo
接受的 Lua 表数据传递给 bar
函数,
\environment card-env
\startluacode
function bar(x)
context.startitemize{"n", "broad"}
for _, v in ipairs(x) do
context.item(v)
end
context.stopitemize()
end
\stopluacode
\def\foo#1{\ctxlua{bar({#1})}}
\starttext
\foo{"Hello", "world", "!"}
\stoptext
\foo
接到的参数,并非真正的 Lua 表,而是一段文本 "Hello", "world", "!"
。
宏调用语句
\foo{"Hello", "world", "!"}
里的这对花括号 {}
,它是 TeX 的编组(Group)符号,用于囊括一段文本并将其作为 \foo
的参数 #1
。换言之,对于上述宏调用语句而言,\foo
的定义里的参数 #1
是 "Hello", "world", "!"
,而非 {"Hello", "world", "!"}
。
在 \foo
的定义里,将 #1
的值传递给 Lua 函数 bar
时,我又给 #1
穿上了 {}
,此时,对于 Lua 解释器而言,bar
函数的参数是一个表 {#1}
。由于在上例里,#1
的值是 "Hello", "world", "!"
,所以 Lua 解释器便认为 bar
函数的参数是 {"Hello", "world", "!"}
。
bar
函数可生成以下排版结果:
![](https://img.haomeiwen.com/i11203728/d0c76bc22bc25f9a.png)
之所以是这个结果,是因为 bar
函数在 ConTeXt 源文件里输出了以下代码的缘故:
\startitemize[n,broad]
\item Hello
\item world
\item !
\stopitemize
上述的 TeX 宏向 Lua 函数传递参数的方法蕴含的技艺是移花接木。虽然巧妙,但是 \foo
的调用语句里已经有了 Lua 代码的痕迹。\foo
接受的参数里含有 3 个 Lua 字符串常量,亦即三段文本,然而在 TeX 源文件里,一切皆文本,无需引号。换言之,为了向 Lua 函数传递数据,TeX 源文件不再是纯粹的 TeX 语法了。
倘若想在 TeX 源文件里消除宏参数里的引号,同时又能够向 Lua 函数传递正确的参数,有一个直拙的办法。这个办法,人类用了几千年了。例如,我将我所知道的有关 ConTeXt 的一切传授给一个人,那么我能想到的最好的办法是,写一份文档送给他。他通过阅读这份文档,从而在他的思想里构造出对 ConTeXt 的认识。
现在,重新定义 \foo
:
\def\foo#1{\ctxlua{bar("#1")}}
再重新定义 bar
:
function bar(x)
context(x)
end
然后,像下面这样调用 \foo
:
\foo{x1::x2::x3, y1::y2::y3, z1::z2}
bar
函数能输出以下结果:
![](https://img.haomeiwen.com/i11203728/31190099b9ac7ea3.png)
我传给 bar
函数的参数虽然是字符串 "x1::x2::x3, y1::y2::y3, z1::z2"
,但是我希望 bar
函数能够想办法理解,我传给它的是一个表,只是迫于维护 TeX 世界的清净无为,我用字符串表示了这个表。
ConTeXt 说,可用 utilities.parsers.settings_to_array
……
试试看:
function bar(x)
local y = utilities.parsers.settings_to_array(x)
context.startitemize{"n", "broad"}
for _, v in ipairs(y) do
context.item(v)
end
context.stopitemize()
end
现在,bar
函数能够给出的输出如下:
![](https://img.haomeiwen.com/i11203728/ce51f415580aaaa5.png)
接下来,我希望 bar
函数能够进一步理解,x1::x2::x3
这样的字符串也是表,即 {"x1", "x2", "x3"}
。
ConTeXt 觉得,此事也不难,用他实现的 string.split
函数就能解决。例如
local a = "x1::y1::z1"
local b = string.split(a, "::")
b
的值便是 {"x1", "y1", "z1"}
。好的……于是我将 bar
函数重新定义为
function bar(x)
local y = utilities.parsers.settings_to_array(x)
context.startitemize{"n", "broad"}
for _, v in ipairs(y) do
local z = string.split(v, lpeg.P("::"))
context.item(v)
context.startitemize{"n", "broad"}
for _, w in ipairs(z) do
context.item(w)
end
context.stopitemize()
end
context.stopitemize()
end
对于宏调用
\foo{x1::x2::x3, y1::y2::y3, z1::z2}
现在的 bar
函数给出的输出为
![](https://img.haomeiwen.com/i11203728/484a0b130b22fc4a.png)
![](https://img.haomeiwen.com/i11203728/f8d33d5ca3ce18f0.png)
基于上述参数解析原理,我想定义的 \keywords
宏就有着落了。
知道了原理的副作用是,懒得再去实践了……
网友评论