共计 2100 个字符,预计需要花费 6 分钟才能阅读完成。
上一篇:工夫治理
将 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", "!"}
。
上述的 TeX 宏向 Lua 函数传递参数的办法蕴含的技能是偷梁换柱。尽管奇妙,然而 \foo
的调用语句里曾经有了 Lua 代码的痕迹。\foo
承受的参数里含有 3 个 Lua 字符串常量,亦即三段文本,然而在 TeX 源文件里,所有皆文本,无需引号。换言之,为了向 Lua 函数传递数据,TeX 源文件不再是纯正的 TeX 语法了。从后者角度看,\foo
该当像上面这样调用:
\foo{Hello, world, !}
该如何实现这样的宏呢?
首先,将 \foo
从新定义为
\def\foo#1{\ctxlua{bar([[#1]])}}
亦即,将 \foo
所承受的参数以长字串的模式作为 Lua 函数 bar
的参数。
而后从新定义 bar
函数:
function bar(x)
context(x)
end
此时,
\foo{Hello, world, !}
的排版后果变为
该结果表明,bar
函数接到的参数确实是一个字符串。接下来,只须要对该字符串予以解析,将解析后果存为 Lua 表构造。持续从新定义 bar
函数:
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
utilities.parsers.settings_to_array
是 ConTeXt 开发者实现的 Lua 库里的函数,其作用是以逗号作为分隔符对字符串进行宰割,后果存 Lua 表,于是便解决了下面提出的问题。
将上述思路利用于上一篇定义的 \task
宏,便可将其两个参数变为 1 个:
% 待办事项
\definextable[todolist]
\setupxtable[todolist][frame=off]
\startluacode
my = my or {}
local ctx = context
local dim = number.todimen
local textwidth = tex.dimen.textwidth
function my.task(task)
local x = utilities.parsers.settings_to_array(task)
ctx.startxrow()
-- 第一列
ctx.startxcell{width=dim(tex.sp("1.5em"))};
context([[$\circ$]]);
ctx.stopxcell()
-- 第二列
ctx.startxcell{width=dim(0.9 * textwidth)};
context([[%s]], x[1]);
ctx.stopxcell()
-- 第三列
ctx.startxcell{width=dim(0.1 * textwidth),align="{middle,lohi}"};
if x[2] then
context(x[2])
else
context([[\strut]])
end
ctx.stopxcell()
ctx.stopxrow()
end
\stopluacode
\def\task#1{\ctxlua{my.task([[#1]])}}
\task
的用法如下:
\environment card-env
\starttext
\timestamp{2023 年 01 月 31 日}
\startxtable[todolist]
\task{晒太阳, $\checkmark$}
\task{包饺子, $\checkmark$}
\task{拖地板}
\stopxtable
\stoptext