#!/usr/bin/env lua5.3 local _env = {} local directives = {} function directives.include(env, args) local path, err = args.get("string", 1) if (not path) then return false, err end local nenv = {code = ""} setmetatable(nenv, {__index=_env}) local data, err = nenv:process(path) if (not data) then return false, err end env.code = env.code .. "\n" .. data return true end local mods = {} function directives.loadmod(env, args) local path, err = args.get("string", 1) if (not path) then return false, err end local file = io.open(path, "rb") if (not file) then return false, "`"..path.."' not found." end local env = {} local copies = {{_G, env}} while #copies ~= 0 do local c = {} for i=1, #copies do for k, v in pairs(copies[i][1]) do if (type(v) == "table") then copies[i][2][k] = {} c[#c+1] = {v, copies[i][2][k]} else copies[i][2][k] = v end end end for i=1, #copies do copies[i] = nil end for i=1, #c do copies[i] = c[i] end end local dir2a = {} env.add_directive = function(name, func) do dir2a[#dir2a+1] = {name, func} end local func, err = load(file:read("*a"), "="..path, "t", env) if (not func) then return false, err end local name = func() if not name then return false, "Module did not return a name." end for i=1, #dir2a do directives[dir2a[i][1]] = dir2a[i][2] end return true end end function _env:process(path) print("PROC", path) local file = io.open(path, "rb") local f, err = load(file:read("*a"), "="..path) if not f then io.stderr:write("ERROR: "..err.."\n") os.exit(1) end file:seek("set", 0) local ln = 0 for line in file:lines() do ln = ln + 1 line = line:gsub("^%s+", "") if (line:sub(1, 3) == "--#") then --Process directive local dir = line:sub(4) local tmp = "" local open_quote = false local escape = false local cmd = nil local args = {} local pos = 0 dir = dir:gsub("^%s+", ""):gsub("%s+$", ""):gsub("%s+", " ") for i=1, #dir do local c = dir:sub(i, i) if (c == " " and not open_quote) then if (tmp ~= "") then if not cmd then cmd = tmp else args[#args+1] = tmp end end tmp = "" elseif (c == "\"" and not escape and not open_quote) then open_quote = true elseif (c == "\"" and not escape) then open_quote = false args[#args+1] = {"string", tmp} tmp = "" elseif (c == "\\" and not escape) then escape = true else if (escape) then escape = false end tmp = tmp .. c end end --Process arguments local rargs = {} for i=1, #args do if (type(args[i]) == "table") then if (args[i][1] == "string") then local str = args[i][2] local sp, ep = str:find("%$%([%w_]+%)") while sp do local var = str:sub(sp, ep) local st1, st2 = str:sub(1, sp-3), str:sub(ep+2) str = st1 .. var local nsp = #str str = str .. st2 sp, ep = str:find("%$%([%w_]+%)", nsp) end args[i][2] = str end rargs[#rargs+1] = args[i] elseif (tonumber(args[i])) then rargs[#rargs+1] = {"number", args[i]} elseif (args == "true" or args == "false") then rargs[#rargs+1] = {"boolean", args[i] == "true"} elseif (os.getenv(args[i])) then rargs[#rargs+1] = {"var", args[i]} else io.stderr:write("ERROR: "..path..":"..ln..": Undefined variable.\n") os.exit(1) end end if (not directives[cmd]) then io.stderr:write("ERROR: "..path..":"..ln..": Unknown directive.\n") os.exit(1) end local rtn, err = directives[cmd](self, {get=function(atype, i) if (type(atype) == "number") then return rargs[i][2], rargs[i][1] end if (rargs[i] == nil) then return false, "argument #"..i..": expected `"..atype.."', got nil" end if (rargs[i][1] ~= atype) then return false, "argument #"..i..": expected `"..atype.."', got `"..rargs[i][1].."'" end return rargs[i][2] end}) if (type(rtn) ~= "boolean") then io.stderr:write("ERROR: "..path..":"..ln..": Expected return type `boolean', got `"..type(rtn).."'.\n") os.exit(1) end if (not rtn) then err = err or "Unknown error" io.stderr:write("ERROR: "..path..":"..ln..": "..err..".\n") end else self.code = self.code .. line .. "\n" end end return self.code end local env = {code = ""} setmetatable(env, {__index=_env}) env:process(arg[1]) local tmpfile = os.tmpname() if tmpfile:sub(#tmpfile) == "." then tmpfile = tmpfile:sub(1, #tmpfile - 1) end local tmpf = io.open(tmpfile, "wb") env.code = env.code:gsub("%-%-.-\n","\n"):gsub("\n\n","\n") tmpf:write(env.code) tmpf:close() --if (os.execute("lua minify.lua "..tmpfile.." > "..arg[2])) then if (os.execute("cat "..tmpfile.." > "..arg[2])) then os.execute("stat -c \"Output: %s bytes\" "..arg[2]) else io.stderr:write("Error: ") os.execute("cat "..arg[2].." 1>&2") os.remove(arg[2]) end os.remove(tmpfile)