From c591ea1560705d1f0c78e590cd181068a58fcd83 Mon Sep 17 00:00:00 2001 From: Jane Roxanne Date: Tue, 27 Aug 2019 16:52:48 -0500 Subject: [PATCH] Zorya config compiler and lua preprocessor added. --- luapreproc.lua | 192 +++++++++++++++++++++++++++++++++++++++++ zcfg-compiler/init.lua | 71 +++++++++++++++ 2 files changed, 263 insertions(+) create mode 100755 luapreproc.lua create mode 100644 zcfg-compiler/init.lua diff --git a/luapreproc.lua b/luapreproc.lua new file mode 100755 index 0000000..92527ed --- /dev/null +++ b/luapreproc.lua @@ -0,0 +1,192 @@ +#!/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() +local tmpf = io.open(tmpfile, "wb") +tmpf:write(env.code) +tmpf:close() +if (os.execute("luamin -f "..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) \ No newline at end of file diff --git a/zcfg-compiler/init.lua b/zcfg-compiler/init.lua new file mode 100644 index 0000000..079d326 --- /dev/null +++ b/zcfg-compiler/init.lua @@ -0,0 +1,71 @@ +local file = arg[1] +if (file == "-") then + file = io.stdin +else + file = io.open(file, "r") +end + +local ast = {} + +local current_node = ast + +for line in file:lines() do + if (line:sub(1, 6) ~= "entry " and line ~= "") then + --Parse arguments + local tmp = "" + local open_quote = false + local escape = false + local cmd = nil + local args = {} + local pos = 0 + line = line:gsub("^%s+", ""):gsub("%s+$", ""):gsub("%s+", " ") + for i=1, #line do + local c = line: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] = "\""..tmp.."\"" + tmp = "" + elseif (c == "\\" and not escape) then + escape = true + else + if (escape) then escape = false end + tmp = tmp .. c + end + end + if (tmp ~= "") then + if not cmd then + cmd = tmp + else + args[#args+1] = tmp + end + end + current_node[#current_node+1] = {type="call", call = cmd, args = args} + elseif (line ~= "") then + ast[#ast+1] = {type = "entry", name = line:sub(7)} + current_node = ast[#ast] + end +end + +print("-- WARNING: Do not edit this file. This file is autogenerated by the zcfg-compiler") +for i=1, #ast do + if (ast[i].type == "entry") then + print("menu.entry(\""..ast[i].name.."\", function(env)") + for j=1, #ast[i] do + print("\tenv:"..ast[i][j].call.."("..table.concat(ast[i][j].args, ", ")..")") + end + print("end)") + else + print("menu."..ast[i].call.."("..table.concat(ast[i].args, ", ")..")") + end +end \ No newline at end of file