Skeleton of the rule engine and type system.
This commit is contained in:
		
							parent
							
								
									df1372ab35
								
							
						
					
					
						commit
						cb0111b290
					
				
					 1 changed files with 153 additions and 17 deletions
				
			
		| 
						 | 
				
			
			@ -8,6 +8,10 @@
 | 
			
		|||
 | 
			
		||||
local environment = {}
 | 
			
		||||
local rules = {}
 | 
			
		||||
local targets = {}
 | 
			
		||||
local buildfiles = {}
 | 
			
		||||
local globals
 | 
			
		||||
local cwd = "."
 | 
			
		||||
 | 
			
		||||
local function subenv(old, cb)
 | 
			
		||||
	if not old then
 | 
			
		||||
| 
						 | 
				
			
			@ -49,37 +53,169 @@ local function emit(...)
 | 
			
		|||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function definerule(name, cb)
 | 
			
		||||
	if rules[name] then
 | 
			
		||||
		error(string.format("rule '%s' is already defined", name))
 | 
			
		||||
local function loadbuildfile(filename)
 | 
			
		||||
	local data, chunk, e
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	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                              --
 | 
			
		||||
-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
local globals = {
 | 
			
		||||
globals = {
 | 
			
		||||
	asstring = asstring,
 | 
			
		||||
	definerule = definerule,
 | 
			
		||||
	emit = emit,
 | 
			
		||||
	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")
 | 
			
		||||
for _, file in ipairs({...}) do
 | 
			
		||||
	local data, chunk, e
 | 
			
		||||
	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)
 | 
			
		||||
	loadbuildfile(file)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue