sprites/util/tup-ext.lua
2020-05-06 03:48:39 -04:00

163 lines
3.9 KiB
Lua

--
-- Configuration
--
-- getconfig returning an empty string on absence is inconvenient
function getconfig(str)
local v = tup.getconfig(str)
if v == "" then
return nil
else
return v
end
end
--
-- Globs
--
-- A glob pattern is either an interpolated string, or a table of glob patterns
--
-- A normalized glob pattern is a table of strings, with interpolation resolved.
-- globpat_normalize("foo/*") --> {"foo/*"}
-- globpat_normalize({"foo/*"}) --> {"foo/*"}
-- globpat_normalize({"foo/*", {"{bar}/*"}}) --> {"foo/*", "baz_value/*"}
local function globpat_normalize(pat)
if type(pat) == "string" then
return {expand(pat)}
elseif type(pat) == 'table' then
local result = {}
for x in iter(pat) do
result += globpat_normalize(x)
end
return result
else
error("bad globpat")
end
end
function glob(pat, opts)
local results = {}
local filter = opts and opts.filter
local key = opts and opts.key
local seen = {}
for pat in iter(globpat_normalize(pat)) do
local frame = {}
push_frame(frame)
for file in iter(tup.glob(pat)) do
-- Workaround a weird issue pre reported
file = file:gsub("//", "/")
frame.input = file
if key then
local k = expand(key)
if seen[k] then
goto continue
end
seen[k] = true
end
if filter then
if not filter() then
goto continue
end
end
table.insert(results, file)
::continue::
end
pop_frame()
end
return results
end
-- Convenience iterator so you can do
-- `for path in iglob{...}` instead of `for path in iter(glob{...})`
function iglob(pat, opts)
return iter(glob(pat, opts))
end
function glob_matches(pat, opts)
local matched = glob(pat, opts)
return #matched > 0
end
-- Commands
local function cspec2cmd(cmdSpec)
local cmd = ""
for newcmd in iter(flatten(astable(cmdSpec))) do
newcmd = trim(newcmd)
if cmd ~= "" then
cmd = cmd .. " &&\n"
end
cmd = cmd .. newcmd
end
return cmd
end
-- Abstract over tup's arcane display override
local function display_cmd(display, cmd)
if display == nil then
return cmd
else
return rep{"^ ${display}^ ${cmd}", display=display, cmd=cmd}
end
end
local function do_rule_frame(opts, foreach)
local cspec = opts.command
local cmd = display_cmd(opts.display, cspec2cmd(cspec))
if foreach then
local input = glob(opts.input, opts)
for i in iter(input) do
local frame = {}
push_frame(frame)
frame.input = i
local output = {}
for v in iter(astable(opts.output)) do
table.insert(output, expand(v))
end
frame.output = tostring(output)
tup.rule(i, expand(cmd), output)
pop_frame()
end
else
local frame = {}
push_frame(frame)
local input = glob(opts.input)
frame.input = tostring(input)
local output = {}
for v in iter(astable(opts.output)) do
table.insert(output, expand(v))
end
frame.output = tostring(output)
tup.rule(input, expand(cmd), output)
pop_frame()
end
end
local function do_rule(opts, foreach)
local dimensions = opts.dimensions or {}
local ret = {}
iter_rep(dimensions,
function()
for v in iter{do_rule_frame(opts, foreach, ret)} do
table.insert(ret, v)
end
end
)
return ret
end
function rule(opts)
return do_rule(opts, false)
end
function foreach_rule(opts)
return do_rule(opts, true)
end