讓 Lua 與語言 L 互動的顯然方法是在 L 中實作 Lua API,但這對實作者來說很困難,而且會讓 L 程式設計師負擔冗長的語法。一個更簡單的解決方案是在 L 中實作 lua_open
、lua_close
、lua_dobuffer
和 lua_register
,並擴充 lua_register
以執行跨語言封送。然後可以在 Lua 中提供其他功能。
Lua API 允許從 C 完全控制 Lua 狀態。但是,如果您想與另一種語言 L 互動怎麼辦?假設 L 可以與 C 互動,顯然的方法是將 C API 反映到 L 中。然而,這是一個相當令人生畏的前景,因為 Lua API 很大,而且有一些隱藏的細微差別。此外,雖然它是一個編寫 Lua 擴充程式庫和工具的良好媒介,但它並不會自然而然地產生方便 L 程式設計師的語法;Lua 手冊顯示(請參閱 4.0 版第 26 頁的 5.12 節)Lua 陳述式
a,b = f("how", t.x, 4)
會變成對 Lua API 的十次呼叫。
有一個更簡單的方法:使用 Lua 的第一條規則(「在 Lua 中執行」)
lua_dostring(S, "a,b = f(\"how\", t.x, 4)");
其中 S
是要執行程式碼的狀態。事實上,lua_dostring
唯一做不到的事情是從 Lua 取回值,並允許 Lua 呼叫 L。這兩個都可以使用 lua_register
來達成。
因此,語言互動所需的一切就是 lua_dostring
和 lua_register
,加上 lua_open
和 lua_close
以允許建立和銷毀 Lua 狀態。此外,最好使用 lua_dobuffer
而不是 lua_dostring
,因為它也可以處理預編譯的程式碼。
但是等等!在 C API 中,lua_register
沒有說明要註冊的函式的引數或結果類型;這些必須透過檢查和操作 Lua 堆疊來處理。一個殘酷但簡單的解決方案是讓 lua_register
指定引數和回傳值的類型和數量,並且只允許自然對應到 L 的類型。
精簡 API 的函式最終清單是
lua_open
和 lua_close
,允許建立和銷毀 Lua 狀態
lua_dobuffer
,允許 Lua 從 L 呼叫
lua_register
(適當專門化),允許 L 從 Lua 呼叫
將 Lua 移植到 EPOC 時,Symbian 的行動裝置作業系統,例如 PDA,我想提供掛鉤到作業系統功能,例如 Eikon GUI。EPOC 是以 C++ 為基礎,看起來很有希望,但由於空間因素,其函式庫不包含任何符號資訊,因此無法依據名稱進行執行時期動態連結。不想要使用 tolua,我決定將 Lua 繫結到 OPL,EPOC 的解譯 BASIC 類似 RAD 語言,它同時支援 EPOC,包括廣泛的 OPX(以 C++ 實作的 OPL 函式庫),並允許依據名稱動態呼叫程序。
OPL 有四個基本類型:16 位元和 32 位元整數、64 位元浮點數和字串。16 位元整數表示為 %
、32 位元整數表示為 &
、字串表示為 $
,浮點數則不表示。OPL 支援 C 類似函式原型,例如
foo&:(a,b%,c$)
foo
是函式的名稱。&
表示它傳回 32 位元整數(所有 OPL 函式傳回一個值,如果沒有明確的 RETURN
陳述式,預設為零或空字串)。冒號表示 foo
是函式。接下來是選用的引數清單;在這個案例中,有三個引數:浮點數 a
、16 位元整數 b%
和字串 c$
。(字串長度最多 255 個字元;在此 API 中,較長的字串無法直接與 Lua 交換。)
因此,我建立一個小型 OPX,提供下列 OPL 函式
LuaOpen&:
傳回新狀態的指標
LuaClose:(state&)
關閉指定的狀態 Lua&:(state&,chunk$)
在指定的狀態中執行指定的區塊(可能已預先編譯,但這不太可能有用,因為它最多只能有 255 個位元組長)
LuaRegister:(state&,func$,name$)
在指定的 Lua 狀態中,以 Lua 名稱 name$
註冊 OPL 函式,其原型由 func$
指定
Lua&:
看起來比 LuaDoBuffer&:
更好的名稱,因為它既貼切(Lua&:
是執行一些 Lua 的函式),又是四個程序中很可能使用最廣泛的簡短好名稱。當從 Lua 呼叫由 LuaRegister:
註冊的 OPL 函式時,引數會自動轉換為 OPL 類型,而結果類型會轉換回來。檢查整數引數是否在範圍內是程式設計師的責任。
乍看之下,這個介面似乎非常受限。例如,沒有簡單的方法可以評估 Lua 表達式並將其結果傳回 OPL,也無法在 OPL 中遍歷 Lua 表格。這是故意的:加入這些功能會使 API 複雜化,而省略這些功能會鼓勵程式設計師僅使用 OPL 提供函式庫常式給 Lua。畢竟,將 Lua 連結到 OPL 的主要動機是能夠存取 EPOC,而無需先為 Lua 編寫大量 C++ 函式庫。
然而,在某些情況下,我可能想要使用另一種語言撰寫大部分應用程式,因為它的應用程式網域屬性(例如 SQL 或 Prolog)。此外,我似乎將 Lua 從其預期的用途(應用程式擴充語言)提升為撰寫應用程式的主要語言。
實際上,這裡沒有衝突。不要將 Lua 視為應用程式擴充語言,而是將它視為膠水語言,將以其他語言撰寫的程式片段結合在一起。應用程式功能的核心通常會以其他語言 L 實作,可能是為了速度而使用 C,或使用某些特定網域語言。透過將此核心結構化為函式庫,L 程式設計人員可以專注於在 L 中提供應用程式原語,而不用擔心將它們綁在一起;L 可能不適合這樣做。然後,應用程式可以實作為 Lua 層,位於一系列函式庫之上;這將程式設計特定網域原語與設定特定應用程式的不同考量分開,這使得應用程式更容易撰寫,並促進 Lua 和 L 程式碼的重複使用。
如果真的有必要在 L 中實作 Lua API 的其他部分,那麼,只要不是為了效能考量,仍然可以使用 Lua 搭配 L 回呼來實作必要的功能。事實上,可以撰寫 Lua API 的完整 Lua 實作,然後與 Lua 透過精簡 API 介接的任何語言一起使用。
Lua 可以透過非常簡單的 API 連接到其他語言,這個 API 主要為標準 C API 的子集。只要目標語言可以與 C 互通,就可以快速實作,並提供所有必要的功能,以便使用 Lua 和目標語言的混合來撰寫應用程式。精簡 API 中的一些看似限制,實際上有助於撰寫更可重複使用的程式碼。