使用__call创建带有lua的DSL,如何实现子部分?

时间:2013-12-25 11:28:49

标签: lua dsl

我是lua的新手,我正在尝试创建一个配置DSL,允许包含已经有默认值的部分。

因此,java表预定义了很多值

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

我有一个实现__call的Config原型,因此当使用表构造函数作为函数调用时,它只会覆盖默认值。某事(比如):

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

我们的想法是,您只能调用dsl来指定要覆盖的内容:

java {
  source = 1.5,
  directories {
    sources = "customsrcdir",
  }
}

有一个Config.new方法允许递归地将原型应用于表,以便所有都具有带有__call方法集的元表。

我的问题在于“目录”小节。它在全局上下文中进行评估,因此唯一可行的方法是:

java {
  source = 1.5,
    java.directories {
      sources = "customsrcdir",
  }
}

这是毫无意义的,因为这与做:

相同
java {
  source = 1.5
}

java.directories {
  sources = "customsrcdir",
}

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

我想知道有更多lua经验的人是否使用更高级的表/ metatable / _ENV魔法实现了这样的DSL。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

可以通过电话按照您的方式进行,但解决方案如此复杂,以至于遗漏=是不值得的。如果你仍然想要表合并/替换功能,那就不难了。

local function merge(t1, t2)
  for k, v in pairs(t2) do
    -- Merge tables with tables, unless the replacing table is an array,
    -- in which case, the array table overwrites the destination.
    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
}