Meta Lua

lua-users home
wiki

Metalua 是一種替代的 Lua 編譯器,支援編譯時期的元程式設計和語法延伸。它還包括一個純正的 Lua 函式庫,用於將 Lua 來源轉譯為 AST。

說明

Metalua 向下相容於 Lua 5.1。你可以動態地新增新的語句至語言中,並提供元程式,即在編譯器編譯另一個檔案時執行的程式,並且可以讀取 / 修改 / 寫入 / 編輯正在編譯的程式。

用途

它的實用興趣是你可以延伸語言,可以使用其他語言的概念進行匯入:你可以定義一個新語法,以及一個處理函式,將這個新語句轉換為一個有效的(儘管可能會比較長,比較難讀和維護)Lua 程式。範例包括適當地支援各種 OO 風格、ML 風格的結構性模式比對、契約設計、靜態或執行期間的類型檢查、惰性評估、解析式列表產生...

狀態

Metalua 的開發仍處於 alpha 階段。

抽象語法樹

有效地控制任何資料的祕訣,是要在適當的抽象層級中呈現它。程式可以用許多方式呈現:ASCII 來源檔案、符號串流、抽象語法樹、位元組碼、編譯的二進位檔案...然而,共識認為最實用的自動程式控制呈現法,是抽象語法樹 (AST)。由於人類使用者偏好純文字來源,Metalua 讓你使用 AST 和純文字,可以依照你看到的方式將它們混和在一起,並提供將一種格式轉譯成另一種格式的便利設施。

學習曲線

撰寫 metalua 延伸程式並非小事:你需要知道 AST 結構,清楚了解元層級是什麼,並能輕鬆地在不同元層級之間切換而不會搞混。顯而易見地,精通 Lua 是必須的。

然而,重要的是要瞭解,儘管設計延伸程式通常不容易,但使用設計良好的延伸程式應該是容易的。

此設計符合 Lua 的設計:即使是非開發人員,也可以輕易地撰寫基礎程式。但是如果你想執行進階的工作,你需要精通非基礎的概念,例如協同程式、環境、元資料表、弱資料表、高階函式等。Lua 並非不計代價地避免查看其本質,而是在其本質中保持乾淨,以使進階使用者可以自信地對其進行駭客入侵。請注意,如果這麼難以撰寫(例如,繞著使用者資料製作一個適當 API)的話,如果它被適當地設計了,使用它應該會很輕鬆。

這種「厚顏無恥地比撰寫更容易使用」的方法也類似於 C++ 的範本,儘管假裝 C++ 很容易使用會有點牽強:如果你真的這麼想的話,那麼精通 metalua 會讓你感覺像在公園散步一樣。

如果你覺得 metalua 的獨特性是你延伸程式設計中最困難的部分,那可能表示你低估了實作正確延伸程式的複雜性,其中包括你在撰寫的那一個。

Metalua 相較於 Lisp

儘管在技術上,Metalua 可寫入包含許多小特設巨集的程式,就像使用 Lisp 所做的那樣,這並非預期的目的,也不予鼓勵。儘管巨集很方便,尤其是在完全開發中單一編碼人員的專案中,但就可讀性和互操作性而言,付出的代價往往不值得。

Lisp 不惜一切代價追求自由,讓使用者幾乎可以隨意撰寫所需的內容,不必接受太多指導。如果你必須使用並非由傑出駭客編寫的程式碼,這可能會變成一個問題。在另一個極端,有些極其無聊的語言難以使用,但培養出大量的有用、健全且維護良好的函式庫。對於 metalua,其目標是

範例

由於 Metalua 會處理許多樹,因此像 ML 中那樣的模式比對非常方便。語言中提供了這類擴充功能,可寫入如下的字詞。請注意來源中存在 -{ extension 'foobar'} 宣告:它會在編譯器中載入擴充功能,而透過列出這些擴充功能,你可以知道所使用的語言是哪一種方言。此外,你也不需要記住如何編譯每個單獨的原始檔:此資訊會儲存在其中。

----------------------------------------------------
-- Lambda-Calculus evaluator (weak head normal form)
----------------------------------------------------
-{ extension "match" }

function replace(var, newval, term)
   match term with
   | `Var{ v } if v==var       -> return newval
   | `Var{ _ }                 -> return term
   | `Lambda{ v, _ } if v==var -> return term
   | `Lambda{ v, b }           -> return Lambda{ v, replace(var, newval, b) }
   | `Apply{ f, x }            -> return `Apply{ replace(var, newval, f), replace(var, newval, x) }
   end
end

function reduce_whnf(term)
   match term with
   | `Apply{ `Lambda { param, body }, arg } ->
      local x = replace (param, arg, body)
      return reduce_whnf(x)
   | _ -> return term
   end
end

有趣的是,上面的範例與 OCaml 非常接近,而模式比對的語法便取自 OCaml

(* Type declaration: OCaml is a statically typed language! *)
type term =
| Var    of string
| Apply  of term * term
| Lambda of string * term

let rec replace var newval term =
   match term with
   | Var(v) when v==var       -> newval
   | Var(_)                   -> term
   | Lambda(v, _) when v==var -> term
   | Lambda(v, b)             -> Lambda(v, replace var newval b)
   | Apply(f, x)              -> Apply(replace var newval f, replace var newval x)

let rec reduce_whnf term =
   match term with
   | Apply(Lambda(param, body), arg) ->
     let x = replace param arg body in
     reduce_whnf x
   | _ -> term

最後一個範例會說明擴充功能實作的樣貌。我們將介紹經常會用到的三元選擇運算子,又稱為 C「?:」條件式。由於 Lua 已使用「:」作為方法呼叫,因此我們會讓它變成「bool ? foo, bar」,而不是「bool ? foo : bar」。一種正確(儘管低效率)的編碼方式是將它放在一個函式中:「(function() if bool then return foo else return bar end end)()」。這的實作方式為

local function t_builder(bool, suffix)
   local foo, bar = unpack (suffix)
   return +{ (function() if -{bool} then -{foo} else -{bar} end end)() }
end

mlp.expr.suffix:add{ "?", mlp.expr, "," mlp.expr, builder=t_builder }

(記載中,一種更靈活且更有效率的實作是 與編譯器一起使用)。

作者

FabienFleutot

使用 Metalua 的相關專案

連結


RecentChanges · 偏好設定
編輯 · 歷程紀錄
上次編輯:2023 年 3 月 1 日 下午 5:36 GMT (diff)