Skeleton of the rule engine and type system.

This commit is contained in:
David Given 2016-06-06 20:50:48 +02:00
parent df1372ab35
commit cb0111b290

View file

@ -8,6 +8,10 @@
local environment = {} local environment = {}
local rules = {} local rules = {}
local targets = {}
local buildfiles = {}
local globals
local cwd = "."
local function subenv(old, cb) local function subenv(old, cb)
if not old then if not old then
@ -49,37 +53,169 @@ local function emit(...)
end end
end end
local function definerule(name, cb) local function loadbuildfile(filename)
if rules[name] then local data, chunk, e
error(string.format("rule '%s' is already defined", name)) data = io.open(filename):read("*a")
if not e then
local thisglobals = {_G = thisglobals}
setmetatable(thisglobals, {__index = globals})
chunk, e = loadstring(data, filename, "text", thisglobals)
end
if e then
error(string.format("couldn't load '%s': %s", filename, e))
end
chunk()
end
local function loadtarget(targetname)
if targets[target] then
return targets[targetname]
end
local target
if not target:find(":") then
target = {
outs = {targetname},
is = {
__implicitfile = true
}
}
targets[targetname] = target
else
local _, _, filepart, targetpart = targetname:find("^([^:]+):(%w+)$")
if not filepart or not targetpart then
error(string.format("malformed target name '%s'", targetname))
end
if not buildfiles[filepart] then
buildfiles[filepart] = true
local oldcwd = cwd
cwd = filepart
loadbuildfile(filepart.."/build.lua")
cwd = oldcwd
end
target = targets[targetname]
if not target then
error(string.format("build file '%s' contains no rule '%s'",
filepart, targetpart))
end end
end end
return target
end
local typeconverters = {
targets = function(propname, i)
if (type(i) == "string") then
i = {i}
elseif (type(i) ~= "table") then
error(string.format("property '%s' must be a target list", propname))
end
local o = {}
for _, s in ipairs(i) do
if (type(s) ~= "string") then
error(string.format("member of target list '%s' is not a string", propname))
end
if s:find("^//") then
s = s:gsub("^//", "")
elseif s:find("^[/]") then
s = concatpath(cwd, s)
end
o[#o+1] = s
end
end,
strings = function(propname, i)
if (type(i) == "string") then
i = {i}
elseif (type(i) ~= "table") then
error(string.format("property '%s' must be a string list", propname))
end
return i
end,
string = function(propname, i)
if (type(i) ~= "string") then
error(string.format("property '%s' must be a string", propname))
end
end,
}
local function definerule(rulename, types, cb)
if rules[rulename] then
error(string.format("rule '%s' is already defined", rulename))
end
types.name = { type="string" }
for propname, typespec in pairs(types) do
if not typeconverters[typespec.type] then
error(string.format("property '%s' has unrecognised type '%s'",
propname, typespec.type))
end
end
rules[rulename] = function(e)
local args = {}
for propname, typespec in pairs(types) do
if not e[propname] and not typespec.optional then
error(string.format("missing mandatory property '%s'", propname))
end
args[propname] = typeconverters[typespec.type](propname, e[propname])
e[propname] = nil
end
local propname, _ = next(e)
if propname then
error(string.format("don't know what to do with property '%s'", propname))
end
end
end
-----------------------------------------------------------------------------
-- DEFAULT RULES --
-----------------------------------------------------------------------------
definerule("simplerule",
{
ins = { type="targets" },
outs = { type="strings" },
},
function (e)
end
)
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- MAIN PROGRAM -- -- MAIN PROGRAM --
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
local globals = { globals = {
asstring = asstring, asstring = asstring,
definerule = definerule, definerule = definerule,
emit = emit, emit = emit,
environment = environment, environment = environment,
} }
setmetatable(globals, {__index = _G}) setmetatable(globals,
{
__index = function(self, k)
local rule = rules[k]
if rule then
return rule
else
return _G[k]
end
end
}
)
emit("hide=@\n") emit("hide=@\n")
for _, file in ipairs({...}) do for _, file in ipairs({...}) do
local data, chunk, e loadbuildfile(file)
data = io.open(file):read("*a")
if not e then
local thisglobals = {_G = thisglobals}
setmetatable(thisglobals, {__index = globals})
chunk, e = loadstring(data, file, "text", thisglobals)
end
if e then
error(string.format("couldn't load '%s': %s", file, e))
end
local _, e = pcall(chunk)
end end