如何使用Lua中的__call创建DSL,在其中实现子节?

我不熟悉lua,正在尝试创建一个配置DSL,它允许具有默认值的部分。

因此,java表格是预定义的,具有许多值

“`Java = { Source = 1.6, 目标=1.6, 目录={ 源头="src/main/java", 输出="build/classes", }, }“

我有一个Config原型,实现了__call,因此,当将其作为带表构造函数的函数调用时,它仅覆盖默认值。类似于此:

Config.__call = function(t, props) for k,v in pairs(props) do t[k] = v end end“

这个想法是你只能调用dsl来指定你想要覆盖的内容:

Java={ Source = 1.5, 目录{ 源码="customsrcdir", } }“

存在一个Config.new方法,可以递归应用原型到表格上,从而使所有元表都具有设置__call方法的元表。

我的问题是“目录”子节。它在全局上下文中评估,因此唯一的方法使其工作是:

Java={ Source = 1.5, Java.directories{ 源码="customsrcdir", } }“

这是毫无意义的,因为这与执行以下操作相同:

“`Java={ Source = 1.5 }

Java.directories{ 源码="customsrcdir", }“`

我尝试了不同的方法来使所需的DSL起作用。其中一个是使用_ENV设置自定义全局环境,但是后来我意识到在__call之前评估了表格。

我想知道是否有更多lua经验的人使用更高级的表/元表/_ENV技巧实现了此类DSL。

点赞
用户2752296
用户2752296

请查看 premake 是如何做的......可能比你现在所做的更优雅的解决方案。 http://en.wikipedia.org/wiki/Premake#Sample_Script

2013-12-25 12:23:40
用户828255
用户828255

可以使用调用的方式来实现,但解决方案非常复杂,不值得省略 =。如果仍想要表格合并或替换功能,则不太困难。

local function merge(t1, t2)
  for k, v in pairs(t2) do
    -- 合并表格与表格,除非替换表格是一个数组,
    -- 在这种情况下,数组表格会覆盖目标。
    if type(t1[k]) == 'table' and type(v) == 'table' and #v == 0 then
      merge(t1[k], v)
    else
      t1[k] = v
    end
  end
end

local data = {
  java = {
    source = 1.6,
    target = 1.6,
    directories = {
      sources = "src/main/java",
      output = "build/classes",
    },
  }
}

local dsl = {}
load( [[
  java = {
    source = 1.5,
    directories = {
      sources = "customsrcdir",
    },
  }
]], 'dsl-config', 't', dsl)()

merge(data, dsl)

倾印 data 将导致:

java = {
  directories = {
    output = "build/classes",
    sources = "customsrcdir"
  }
  source = 1.5,
  target = 1.6
}
2013-12-25 22:45:48