Question

I am new to lua and I am trying to create a configuration DSL which allows to have sections that already have defaults.

So, the java table is predefined with lot of values

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

I have a Config prototype that implements __call so that when called as a function with a table constructor, it only overwrites the defaults. Something (like) this:

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

The idea is that you can call the dsl only to specify what you want to override:

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

There is a Config.new method that allows to apply the prototype recursively to the tables so that all have a metatable with the __call method set.

My problem is with the "directories" subsection. It is evaluated in the global context, so the only way this works is:

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

Which is pointless, as this is the same as doing:

java {
  source = 1.5
}

java.directories {
  sources = "customsrcdir",
}

I tried different approaches to have the desired DSL to work. One was setting a custom global environment with _ENV, but then I realized the table is evaluated before __call.

I wonder if someone with more lua experience has implemented a DSL like this using more advanced table/metatable/_ENV magic.

Was it helpful?

Solution

It's possible to do it your way with calls, but the solution's so convoluted that it's not worth the omission of the =. If you still want the table merge/replacement functionality, then that's not too difficult.

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)

Dumping data will result in:

java = {
  directories = {
    output = "build/classes",
    sources = "customsrcdir"
  }
  source = 1.5,
  target = 1.6
}

OTHER TIPS

Check out how premake does it... Might be a more elegant solution than what you have going right now. http://en.wikipedia.org/wiki/Premake#Sample_Script

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top