元 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 vs. 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 的相关项目

链接


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2023 年 3 月 1 日下午 5:36 GMT (差异)