將 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
函數可生成以下排版結果:
之所以是這個結果,是因爲 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
函數能輸出以下結果:
我傳給 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
函數能夠給出的輸出如下:
接下來,我希望 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
函數給出的輸出爲
基於上述參數解析原理,我想定義的 \keywords
宏就有着落了。
知道了原理的副作用是,懶得再去實踐了……