《Lua in ConTeXt》12:參數表解析

將 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 宏就有着落了。

知道了原理的副作用是,懶得再去實踐了……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章