LuaComp/src/generator.lua

148 lines
5.1 KiB
Lua

--[[
generator.lua - Generates the code.
Copyright 2019 Adorable-Catgirl
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]
local function lua_escape(code)
return code:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("\n", "\\n")
end
local function shell_escape(code)
return code:gsub("\\%[", "[")
end
local function svar_escape(code)
return code:gsub("\"", "\\\""):gsub("\'", "\\\'"):gsub("`", "\\`")
end
local directives = {}
local function generate(ast, gencode)
local lua_code = ""
for i=1, #ast do
local leaf = ast[i]
if (leaf.type == "lua") then
lua_code = lua_code .. leaf.code
elseif (leaf.type == "directive") then
local stargs = {}
for i=1, #leaf.args do
local arg = leaf.args[i]
if (type(arg) == "string") then
stargs[i] = "\""..lua_escape(arg).."\""
elseif (type(arg) == "number") then
stargs[i] = tostring(arg)
elseif (type(arg) == "table" and arg.type=="lua_var") then
stargs[i] = arg[1]
end
end
lua_code = lua_code .. "call_directive(\""..leaf.file..":"..tostring(leaf.line).."\",\""..leaf.name.."\","..table.concat(stargs, ",")..")"
elseif (leaf.type == "envvar") then
lua_code = lua_code .. "put_env(\""..leaf.file..":"..tostring(leaf.line).."\",\""..leaf.var.."\")"
elseif (leaf.type == "code") then
lua_code = lua_code .. "put_code(\""..leaf.file..":"..tostring(leaf.line).."\",\"" .. lua_escape(leaf.data) .. "\")"
elseif (leaf.type == "lua_r") then
lua_code = lua_code .. "put_code(\""..leaf.file..":"..tostring(leaf.line).."\",tostring("..leaf.code.."))"
elseif (leaf.type == "shell") then
lua_code = lua_code .. "put_shell_out(\""..leaf.file..":"..tostring(leaf.line).."\",\""..lua_escape(leaf.code).."\")"
elseif (leaf.type == "shell_r") then
lua_code = lua_code .. "put_svar(\""..leaf.file..":"..tostring(leaf.line).."\",\""..leaf.code.."\")"
else
io.stderr:write("ERROR: Internal catastrophic failure, unknown type "..leaf.type.."\n")
os.exit(1)
end
lua_code = lua_code .. "\n"
end
local env = {code = ""}
local function run_away_screaming(fpos, err)
io.stdout:write("ERROR: "..fpos..": "..err.."\n")
os.exit(1)
end
local function bitch(fpos, err)
io.stdout:write("WARNING: "..fpos..": "..err.."\n")
end
local function call_directive(fpos, dname, ...)
if (not directives[dname]) then
run_away_screaming(fpos, "Invalid directive name `"..dname.."'")
end
local r, er = directives[dname](env, ...)
if (not r) then
run_away_screaming(fpos, er)
end
end
local function put_env(fpos, evar)
local e = svar.get(evar)
if not e then
run_away_screaming(fpos, "Enviroment variable `"..evar.."' does not exist!")
end
env.code = env.code .. "\""..lua_escape(e).."\""
end
local function put_code(fpos, code)
env.code = env.code .. code --not much that can fail here...
end
local function put_shell_out(fpos, code)
local tname = os.tmpname()
local f = os.tmpname()
local fh = io.open(f, "w")
fh:write(code)
fh:close()
os.execute("chmod +x "..f)
local vars = svar.get_all()
local vstr = ""
for k, v in pairs(vars) do
vstr = vstr .. k.."=".."\""..svar_escape(v).."\" "
end
dprint("Shell", vstr .. f.." "..tname)
local h = io.popen(vstr .. f.." "..tname, "r")
local output = h:read("*a"):gsub("\n$", "")
local ok, sig, code = h:close()
fh = io.open(tname, "r")
local stderr = fh:read("*a"):gsub("\n$", "")
fh:close()
os.remove(f)
os.remove(tname)
if not ok then
run_away_screaming(fpos, "Shell exit code "..code..", SIG_"..sig:upper().."\n"..stderr)
elseif #stderr > 0 then
bitch(fpos, stderr)
end
env.code = env.code .. output
end
local function put_svar(fpos, evar)
local e = svar.get(evar)
if not e then
run_away_screaming(fpos, "Enviroment variable `"..evar.."' does not exist!")
end
env.code = env.code .. e
end
local fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
if gencode then
return lua_code
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
fenv.put_svar = put_svar
fenv.put_shell_out = put_shell_out
fenv._GENERATOR=env
local func = assert(load(lua_code, "=(generated code: "..ast.file..")", "t", fenv))
func()
return env.code
end