1
0
mirror of https://github.com/Adorable-Catgirl/LuaComp.git synced 2024-12-23 08:48:05 +11:00

I have no clue what I've done so far.

This commit is contained in:
Jane Roxanne 2020-03-18 13:08:15 -05:00
parent a62731e0bf
commit fb244ee786
17 changed files with 15332 additions and 2 deletions

0
application.lua Normal file
View File

2044
build/luacomp-5.1-static.lua Normal file

File diff suppressed because it is too large Load Diff

514
build/luacomp-5.1.lua Normal file
View File

@ -0,0 +1,514 @@
#!/usr/bin/env lua5.1
--[[
init.lua - Main file of LuaComp
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 _sv(k, v)
_G[k] = v
--os.setenv(k, tostring(v))
end
_sv("LUACOMP_V_MAJ", 1)
_sv("LUACOMP_V_MIN", 1)
_sv("LUACOMP_V_PAT", 0)
_sv("LUACOMP_VERSION", LUACOMP_V_MAJ.."."..LUACOMP_V_MIN.."."..LUACOMP_V_PAT)
_sv("LUACOMP_NAME", "LuaComp")
--[[
ast.lua - Generates a structure for use in preprocessing.
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 nextc(f, c)
c = c or 1
return f:read(c)
end
local function peek(f, c)
c = c or 1
local z = f:read(c)
f:seek("cur", -c)
return z
end
local function skip(f, c)
c = c or 1
return f:seek("cur", c)
end
local ws = {
["\t"] = true,
[" "] = true
}
local function parse_hex(f)
local lc = " "
local hex = ""
while (48 <= lc:byte() and lc:byte() <= 57) or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte()) <= 57 or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) then
hex = hex .. lc
end
end
return tonumber(hex, 16)
end
local function parse_number(f, c)
local lc = " "
local num = c
while 48 <= lc:byte() and lc:byte() <= 57 do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte() <= 57) then
num = num .. lc
end
end
return tonumber(hex, 10)
end
local esct = {
["t"] = "\t",
["n"] = "\n",
["r"] = "\r",
["\\"] = "\\\\"
}
for i=0, 9 do
esct[tostring(i)] = string.char(i)
end
local function parse_dblquote(f)
local val = ""
while peek(f) ~= "\"" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_snglquote(f)
local val = ""
while peek(f) ~= "\'" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_envarg(f)
local val = ""
while peek(f) ~= ")" do
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
val = val .. nextc(f)
end
skip(f)
return val
end
local function parse_directive(f)
local lc = "_"
local name = ""
local args = {}
local carg = ""
while not ws[lc] do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name}
elseif not ws[lc] then
name = name .. lc
end
end
while true do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name, args=args}
elseif lc == "0" and peek(f) == "x" then
skip(f)
local val = parse_hex(f)
args[#args+1] = val
elseif 48 <= lc:byte() and lc:byte() <= 57 then
local val = parse_number(f, lc)
args[#args+1] = val
elseif lc == "\"" then
local val, e = parse_dblquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "\'" then
local val, e = parse_snglquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "$" and peek(f) == "(" then
skip(f)
local val = parse_envarg(f)
if not os.getenv(val) then return nil, "Enviroment variable `"..val.."' does not exist." end
args[#args+1] = os.getenv(val)
elseif not ws[lc] then
return nil, "Syntax error"
end
end
end
local function mkast(f, n)
io.stderr:write("PROC\t",n,"\n")
local lc = " "
local lpos = 1
local ilpos = 1
local tree = {}
local code = ""
local branches = {}
local function add_code()
tree[#tree+1] = {type="code", data=code, file=n, line=lpos}
code = ""
end
local function parse_error(e)
io.stderr:write("ERROR:"..n..":"..lpos..": "..e.."\n")
os.exit(1)
end
while lc and lc ~= "" do
lc = nextc(f)
if (lc == "-" and ilpos == 1) then
if (peek(f, 2) == "-#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "/" and ilpos == 1) then
if (peek(f, 2) == "/#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "$" and peek(f) == "(") then
add_code()
skip(f)
local val, e = parse_envarg(f)
if not val then
parse_error(e)
end
tree[#tree+1] = {type="envvar", var=val, file=n, line=lpos}
elseif (lc == "@" and peek(f, 2) == "[[") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "]]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "@" and peek(f, 2) == "[{") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "}]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua_r", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "\r" or lc == "\n") then
if (lc == "\r" and peek(f) == "\n") then
skip(f)
end
lpos = lpos+1
ilpos = 1
code = code .. "\n"
else
code = code .. (lc or "")
ilpos = ilpos+1
end
end
add_code()
return tree
end
--[[
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 directives = {}
local function generate(ast)
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)
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.."))"
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 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 = os.getenv(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 fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
func()
return env.code
end
--[[
directive_provider.lua - Provides preprocessor directives
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.
]]
---#include "directives/define.lua"
function directives.include(env, file)
if (not os.execute("stat "..file..">/dev/null")) then
return false, "File `"..file.."' does not exist!"
end
local f = io.open(file, "r")
local fast = mkast(f, file)
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true
end
local warned = false
function directives.loadmod(env, mod)
if not warned then
io.stderr:write("Warning: loadmod is depreciated and unsafe. The API differs from luapreproc.\n")
warned = true
end
if (not os.execute("stat "..file..">/dev/null")) then
return false, "Module `"..file.."' does not exist!"
end
local modname, func = dofile(mod)
directives[modname] = func
return true
end
--[[
cfg/minifier_providers.lua - Provides minifier providers.
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 providers = {}
function providers.luamin(cin)
local fn = os.tmpname()
local fh = io.open(fn, "w")
fh:write(cin)
fh:close()
local lmh = io.popen("luamin -f "..fn.." 2>&1", "r")
local dat = lmh:read("*a")
local stat, _, code = lmh:close()
if (code ~= 0) then
return false, dat
end
return dat
end
function providers.none(cin)
return cin
end
local argparse = require("argparse")
local parser = argparse(arg[0], "LuaComp v"..LUACOMP_VERSION.."\nA Lua preprocessor+postprocessor.")
parser:argument("input", "Input file (- for STDIN)")
parser:option("-O --output", "Output file. (- for STDOUT)", "-")
parser:option("-m --minifier", "Sets the minifier", "none")
parser:option("-x --executable", "Makes the script an executable (default: current lua version)"):args "?"
local args = parser:parse()
local file = args.input
local f
if (file ~= "-") then
f = io.open(file, "r")
if not f then
io.stderr:write("ERROR: File `"..file.."' does not exist!\n")
os.exit(1)
end
else
f = io.stdin
end
local ast = mkast(f, file)
local ocode = generate(ast)
local minifier = providers[args.minifier]
local rcode = minifier(ocode)
local of
if (args.output == "-") then
of = io.stdout
else
of = io.open(args.output, "w")
end
local ver = _VERSION:lower():gsub(" ", "")
if jit then
ver = "luajit"
end
if (args.executable) then
of:write("#!/usr/bin/env ", args.executable[1] or ver, "\n")
end
of:write(rcode)
of:close()
f:close()

2044
build/luacomp-5.2-static.lua Normal file

File diff suppressed because it is too large Load Diff

514
build/luacomp-5.2.lua Normal file
View File

@ -0,0 +1,514 @@
#!/usr/bin/env lua5.2
--[[
init.lua - Main file of LuaComp
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 _sv(k, v)
_G[k] = v
--os.setenv(k, tostring(v))
end
_sv("LUACOMP_V_MAJ", 1)
_sv("LUACOMP_V_MIN", 1)
_sv("LUACOMP_V_PAT", 0)
_sv("LUACOMP_VERSION", LUACOMP_V_MAJ.."."..LUACOMP_V_MIN.."."..LUACOMP_V_PAT)
_sv("LUACOMP_NAME", "LuaComp")
--[[
ast.lua - Generates a structure for use in preprocessing.
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 nextc(f, c)
c = c or 1
return f:read(c)
end
local function peek(f, c)
c = c or 1
local z = f:read(c)
f:seek("cur", -c)
return z
end
local function skip(f, c)
c = c or 1
return f:seek("cur", c)
end
local ws = {
["\t"] = true,
[" "] = true
}
local function parse_hex(f)
local lc = " "
local hex = ""
while (48 <= lc:byte() and lc:byte() <= 57) or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte()) <= 57 or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) then
hex = hex .. lc
end
end
return tonumber(hex, 16)
end
local function parse_number(f, c)
local lc = " "
local num = c
while 48 <= lc:byte() and lc:byte() <= 57 do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte() <= 57) then
num = num .. lc
end
end
return tonumber(hex, 10)
end
local esct = {
["t"] = "\t",
["n"] = "\n",
["r"] = "\r",
["\\"] = "\\\\"
}
for i=0, 9 do
esct[tostring(i)] = string.char(i)
end
local function parse_dblquote(f)
local val = ""
while peek(f) ~= "\"" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_snglquote(f)
local val = ""
while peek(f) ~= "\'" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_envarg(f)
local val = ""
while peek(f) ~= ")" do
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
val = val .. nextc(f)
end
skip(f)
return val
end
local function parse_directive(f)
local lc = "_"
local name = ""
local args = {}
local carg = ""
while not ws[lc] do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name}
elseif not ws[lc] then
name = name .. lc
end
end
while true do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name, args=args}
elseif lc == "0" and peek(f) == "x" then
skip(f)
local val = parse_hex(f)
args[#args+1] = val
elseif 48 <= lc:byte() and lc:byte() <= 57 then
local val = parse_number(f, lc)
args[#args+1] = val
elseif lc == "\"" then
local val, e = parse_dblquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "\'" then
local val, e = parse_snglquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "$" and peek(f) == "(" then
skip(f)
local val = parse_envarg(f)
if not os.getenv(val) then return nil, "Enviroment variable `"..val.."' does not exist." end
args[#args+1] = os.getenv(val)
elseif not ws[lc] then
return nil, "Syntax error"
end
end
end
local function mkast(f, n)
io.stderr:write("PROC\t",n,"\n")
local lc = " "
local lpos = 1
local ilpos = 1
local tree = {}
local code = ""
local branches = {}
local function add_code()
tree[#tree+1] = {type="code", data=code, file=n, line=lpos}
code = ""
end
local function parse_error(e)
io.stderr:write("ERROR:"..n..":"..lpos..": "..e.."\n")
os.exit(1)
end
while lc and lc ~= "" do
lc = nextc(f)
if (lc == "-" and ilpos == 1) then
if (peek(f, 2) == "-#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "/" and ilpos == 1) then
if (peek(f, 2) == "/#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "$" and peek(f) == "(") then
add_code()
skip(f)
local val, e = parse_envarg(f)
if not val then
parse_error(e)
end
tree[#tree+1] = {type="envvar", var=val, file=n, line=lpos}
elseif (lc == "@" and peek(f, 2) == "[[") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "]]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "@" and peek(f, 2) == "[{") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "}]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua_r", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "\r" or lc == "\n") then
if (lc == "\r" and peek(f) == "\n") then
skip(f)
end
lpos = lpos+1
ilpos = 1
code = code .. "\n"
else
code = code .. (lc or "")
ilpos = ilpos+1
end
end
add_code()
return tree
end
--[[
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 directives = {}
local function generate(ast)
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)
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.."))"
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 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 = os.getenv(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 fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
func()
return env.code
end
--[[
directive_provider.lua - Provides preprocessor directives
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.
]]
---#include "directives/define.lua"
function directives.include(env, file)
if (not os.execute("stat "..file..">/dev/null")) then
return false, "File `"..file.."' does not exist!"
end
local f = io.open(file, "r")
local fast = mkast(f, file)
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true
end
local warned = false
function directives.loadmod(env, mod)
if not warned then
io.stderr:write("Warning: loadmod is depreciated and unsafe. The API differs from luapreproc.\n")
warned = true
end
if (not os.execute("stat "..file..">/dev/null")) then
return false, "Module `"..file.."' does not exist!"
end
local modname, func = dofile(mod)
directives[modname] = func
return true
end
--[[
cfg/minifier_providers.lua - Provides minifier providers.
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 providers = {}
function providers.luamin(cin)
local fn = os.tmpname()
local fh = io.open(fn, "w")
fh:write(cin)
fh:close()
local lmh = io.popen("luamin -f "..fn.." 2>&1", "r")
local dat = lmh:read("*a")
local stat, _, code = lmh:close()
if (code ~= 0) then
return false, dat
end
return dat
end
function providers.none(cin)
return cin
end
local argparse = require("argparse")
local parser = argparse(arg[0], "LuaComp v"..LUACOMP_VERSION.."\nA Lua preprocessor+postprocessor.")
parser:argument("input", "Input file (- for STDIN)")
parser:option("-O --output", "Output file. (- for STDOUT)", "-")
parser:option("-m --minifier", "Sets the minifier", "none")
parser:option("-x --executable", "Makes the script an executable (default: current lua version)"):args "?"
local args = parser:parse()
local file = args.input
local f
if (file ~= "-") then
f = io.open(file, "r")
if not f then
io.stderr:write("ERROR: File `"..file.."' does not exist!\n")
os.exit(1)
end
else
f = io.stdin
end
local ast = mkast(f, file)
local ocode = generate(ast)
local minifier = providers[args.minifier]
local rcode = minifier(ocode)
local of
if (args.output == "-") then
of = io.stdout
else
of = io.open(args.output, "w")
end
local ver = _VERSION:lower():gsub(" ", "")
if jit then
ver = "luajit"
end
if (args.executable) then
of:write("#!/usr/bin/env ", args.executable[1] or ver, "\n")
end
of:write(rcode)
of:close()
f:close()

2044
build/luacomp-5.3-static.lua Normal file

File diff suppressed because it is too large Load Diff

514
build/luacomp-5.3.lua Normal file
View File

@ -0,0 +1,514 @@
#!/usr/bin/env lua5.3
--[[
init.lua - Main file of LuaComp
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 _sv(k, v)
_G[k] = v
--os.setenv(k, tostring(v))
end
_sv("LUACOMP_V_MAJ", 1)
_sv("LUACOMP_V_MIN", 1)
_sv("LUACOMP_V_PAT", 0)
_sv("LUACOMP_VERSION", LUACOMP_V_MAJ.."."..LUACOMP_V_MIN.."."..LUACOMP_V_PAT)
_sv("LUACOMP_NAME", "LuaComp")
--[[
ast.lua - Generates a structure for use in preprocessing.
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 nextc(f, c)
c = c or 1
return f:read(c)
end
local function peek(f, c)
c = c or 1
local z = f:read(c)
f:seek("cur", -c)
return z
end
local function skip(f, c)
c = c or 1
return f:seek("cur", c)
end
local ws = {
["\t"] = true,
[" "] = true
}
local function parse_hex(f)
local lc = " "
local hex = ""
while (48 <= lc:byte() and lc:byte() <= 57) or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte()) <= 57 or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) then
hex = hex .. lc
end
end
return tonumber(hex, 16)
end
local function parse_number(f, c)
local lc = " "
local num = c
while 48 <= lc:byte() and lc:byte() <= 57 do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte() <= 57) then
num = num .. lc
end
end
return tonumber(hex, 10)
end
local esct = {
["t"] = "\t",
["n"] = "\n",
["r"] = "\r",
["\\"] = "\\\\"
}
for i=0, 9 do
esct[tostring(i)] = string.char(i)
end
local function parse_dblquote(f)
local val = ""
while peek(f) ~= "\"" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_snglquote(f)
local val = ""
while peek(f) ~= "\'" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_envarg(f)
local val = ""
while peek(f) ~= ")" do
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
val = val .. nextc(f)
end
skip(f)
return val
end
local function parse_directive(f)
local lc = "_"
local name = ""
local args = {}
local carg = ""
while not ws[lc] do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name}
elseif not ws[lc] then
name = name .. lc
end
end
while true do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name, args=args}
elseif lc == "0" and peek(f) == "x" then
skip(f)
local val = parse_hex(f)
args[#args+1] = val
elseif 48 <= lc:byte() and lc:byte() <= 57 then
local val = parse_number(f, lc)
args[#args+1] = val
elseif lc == "\"" then
local val, e = parse_dblquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "\'" then
local val, e = parse_snglquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "$" and peek(f) == "(" then
skip(f)
local val = parse_envarg(f)
if not os.getenv(val) then return nil, "Enviroment variable `"..val.."' does not exist." end
args[#args+1] = os.getenv(val)
elseif not ws[lc] then
return nil, "Syntax error"
end
end
end
local function mkast(f, n)
io.stderr:write("PROC\t",n,"\n")
local lc = " "
local lpos = 1
local ilpos = 1
local tree = {}
local code = ""
local branches = {}
local function add_code()
tree[#tree+1] = {type="code", data=code, file=n, line=lpos}
code = ""
end
local function parse_error(e)
io.stderr:write("ERROR:"..n..":"..lpos..": "..e.."\n")
os.exit(1)
end
while lc and lc ~= "" do
lc = nextc(f)
if (lc == "-" and ilpos == 1) then
if (peek(f, 2) == "-#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "/" and ilpos == 1) then
if (peek(f, 2) == "/#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "$" and peek(f) == "(") then
add_code()
skip(f)
local val, e = parse_envarg(f)
if not val then
parse_error(e)
end
tree[#tree+1] = {type="envvar", var=val, file=n, line=lpos}
elseif (lc == "@" and peek(f, 2) == "[[") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "]]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "@" and peek(f, 2) == "[{") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "}]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua_r", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "\r" or lc == "\n") then
if (lc == "\r" and peek(f) == "\n") then
skip(f)
end
lpos = lpos+1
ilpos = 1
code = code .. "\n"
else
code = code .. (lc or "")
ilpos = ilpos+1
end
end
add_code()
return tree
end
--[[
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 directives = {}
local function generate(ast)
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)
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.."))"
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 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 = os.getenv(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 fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
func()
return env.code
end
--[[
directive_provider.lua - Provides preprocessor directives
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.
]]
---#include "directives/define.lua"
function directives.include(env, file)
if (not os.execute("stat "..file..">/dev/null")) then
return false, "File `"..file.."' does not exist!"
end
local f = io.open(file, "r")
local fast = mkast(f, file)
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true
end
local warned = false
function directives.loadmod(env, mod)
if not warned then
io.stderr:write("Warning: loadmod is depreciated and unsafe. The API differs from luapreproc.\n")
warned = true
end
if (not os.execute("stat "..file..">/dev/null")) then
return false, "Module `"..file.."' does not exist!"
end
local modname, func = dofile(mod)
directives[modname] = func
return true
end
--[[
cfg/minifier_providers.lua - Provides minifier providers.
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 providers = {}
function providers.luamin(cin)
local fn = os.tmpname()
local fh = io.open(fn, "w")
fh:write(cin)
fh:close()
local lmh = io.popen("luamin -f "..fn.." 2>&1", "r")
local dat = lmh:read("*a")
local stat, _, code = lmh:close()
if (code ~= 0) then
return false, dat
end
return dat
end
function providers.none(cin)
return cin
end
local argparse = require("argparse")
local parser = argparse(arg[0], "LuaComp v"..LUACOMP_VERSION.."\nA Lua preprocessor+postprocessor.")
parser:argument("input", "Input file (- for STDIN)")
parser:option("-O --output", "Output file. (- for STDOUT)", "-")
parser:option("-m --minifier", "Sets the minifier", "none")
parser:option("-x --executable", "Makes the script an executable (default: current lua version)"):args "?"
local args = parser:parse()
local file = args.input
local f
if (file ~= "-") then
f = io.open(file, "r")
if not f then
io.stderr:write("ERROR: File `"..file.."' does not exist!\n")
os.exit(1)
end
else
f = io.stdin
end
local ast = mkast(f, file)
local ocode = generate(ast)
local minifier = providers[args.minifier]
local rcode = minifier(ocode)
local of
if (args.output == "-") then
of = io.stdout
else
of = io.open(args.output, "w")
end
local ver = _VERSION:lower():gsub(" ", "")
if jit then
ver = "luajit"
end
if (args.executable) then
of:write("#!/usr/bin/env ", args.executable[1] or ver, "\n")
end
of:write(rcode)
of:close()
f:close()

File diff suppressed because it is too large Load Diff

514
build/luacomp-generic.lua Normal file
View File

@ -0,0 +1,514 @@
#!/usr/bin/env lua
--[[
init.lua - Main file of LuaComp
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 _sv(k, v)
_G[k] = v
--os.setenv(k, tostring(v))
end
_sv("LUACOMP_V_MAJ", 1)
_sv("LUACOMP_V_MIN", 1)
_sv("LUACOMP_V_PAT", 0)
_sv("LUACOMP_VERSION", LUACOMP_V_MAJ.."."..LUACOMP_V_MIN.."."..LUACOMP_V_PAT)
_sv("LUACOMP_NAME", "LuaComp")
--[[
ast.lua - Generates a structure for use in preprocessing.
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 nextc(f, c)
c = c or 1
return f:read(c)
end
local function peek(f, c)
c = c or 1
local z = f:read(c)
f:seek("cur", -c)
return z
end
local function skip(f, c)
c = c or 1
return f:seek("cur", c)
end
local ws = {
["\t"] = true,
[" "] = true
}
local function parse_hex(f)
local lc = " "
local hex = ""
while (48 <= lc:byte() and lc:byte() <= 57) or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte()) <= 57 or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) then
hex = hex .. lc
end
end
return tonumber(hex, 16)
end
local function parse_number(f, c)
local lc = " "
local num = c
while 48 <= lc:byte() and lc:byte() <= 57 do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte() <= 57) then
num = num .. lc
end
end
return tonumber(hex, 10)
end
local esct = {
["t"] = "\t",
["n"] = "\n",
["r"] = "\r",
["\\"] = "\\\\"
}
for i=0, 9 do
esct[tostring(i)] = string.char(i)
end
local function parse_dblquote(f)
local val = ""
while peek(f) ~= "\"" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_snglquote(f)
local val = ""
while peek(f) ~= "\'" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_envarg(f)
local val = ""
while peek(f) ~= ")" do
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
val = val .. nextc(f)
end
skip(f)
return val
end
local function parse_directive(f)
local lc = "_"
local name = ""
local args = {}
local carg = ""
while not ws[lc] do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name}
elseif not ws[lc] then
name = name .. lc
end
end
while true do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name, args=args}
elseif lc == "0" and peek(f) == "x" then
skip(f)
local val = parse_hex(f)
args[#args+1] = val
elseif 48 <= lc:byte() and lc:byte() <= 57 then
local val = parse_number(f, lc)
args[#args+1] = val
elseif lc == "\"" then
local val, e = parse_dblquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "\'" then
local val, e = parse_snglquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "$" and peek(f) == "(" then
skip(f)
local val = parse_envarg(f)
if not os.getenv(val) then return nil, "Enviroment variable `"..val.."' does not exist." end
args[#args+1] = os.getenv(val)
elseif not ws[lc] then
return nil, "Syntax error"
end
end
end
local function mkast(f, n)
io.stderr:write("PROC\t",n,"\n")
local lc = " "
local lpos = 1
local ilpos = 1
local tree = {}
local code = ""
local branches = {}
local function add_code()
tree[#tree+1] = {type="code", data=code, file=n, line=lpos}
code = ""
end
local function parse_error(e)
io.stderr:write("ERROR:"..n..":"..lpos..": "..e.."\n")
os.exit(1)
end
while lc and lc ~= "" do
lc = nextc(f)
if (lc == "-" and ilpos == 1) then
if (peek(f, 2) == "-#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "/" and ilpos == 1) then
if (peek(f, 2) == "/#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "$" and peek(f) == "(") then
add_code()
skip(f)
local val, e = parse_envarg(f)
if not val then
parse_error(e)
end
tree[#tree+1] = {type="envvar", var=val, file=n, line=lpos}
elseif (lc == "@" and peek(f, 2) == "[[") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "]]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "@" and peek(f, 2) == "[{") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "}]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua_r", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "\r" or lc == "\n") then
if (lc == "\r" and peek(f) == "\n") then
skip(f)
end
lpos = lpos+1
ilpos = 1
code = code .. "\n"
else
code = code .. (lc or "")
ilpos = ilpos+1
end
end
add_code()
return tree
end
--[[
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 directives = {}
local function generate(ast)
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)
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.."))"
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 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 = os.getenv(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 fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
func()
return env.code
end
--[[
directive_provider.lua - Provides preprocessor directives
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.
]]
---#include "directives/define.lua"
function directives.include(env, file)
if (not os.execute("stat "..file..">/dev/null")) then
return false, "File `"..file.."' does not exist!"
end
local f = io.open(file, "r")
local fast = mkast(f, file)
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true
end
local warned = false
function directives.loadmod(env, mod)
if not warned then
io.stderr:write("Warning: loadmod is depreciated and unsafe. The API differs from luapreproc.\n")
warned = true
end
if (not os.execute("stat "..file..">/dev/null")) then
return false, "Module `"..file.."' does not exist!"
end
local modname, func = dofile(mod)
directives[modname] = func
return true
end
--[[
cfg/minifier_providers.lua - Provides minifier providers.
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 providers = {}
function providers.luamin(cin)
local fn = os.tmpname()
local fh = io.open(fn, "w")
fh:write(cin)
fh:close()
local lmh = io.popen("luamin -f "..fn.." 2>&1", "r")
local dat = lmh:read("*a")
local stat, _, code = lmh:close()
if (code ~= 0) then
return false, dat
end
return dat
end
function providers.none(cin)
return cin
end
local argparse = require("argparse")
local parser = argparse(arg[0], "LuaComp v"..LUACOMP_VERSION.."\nA Lua preprocessor+postprocessor.")
parser:argument("input", "Input file (- for STDIN)")
parser:option("-O --output", "Output file. (- for STDOUT)", "-")
parser:option("-m --minifier", "Sets the minifier", "none")
parser:option("-x --executable", "Makes the script an executable (default: current lua version)"):args "?"
local args = parser:parse()
local file = args.input
local f
if (file ~= "-") then
f = io.open(file, "r")
if not f then
io.stderr:write("ERROR: File `"..file.."' does not exist!\n")
os.exit(1)
end
else
f = io.stdin
end
local ast = mkast(f, file)
local ocode = generate(ast)
local minifier = providers[args.minifier]
local rcode = minifier(ocode)
local of
if (args.output == "-") then
of = io.stdout
else
of = io.open(args.output, "w")
end
local ver = _VERSION:lower():gsub(" ", "")
if jit then
ver = "luajit"
end
if (args.executable) then
of:write("#!/usr/bin/env ", args.executable[1] or ver, "\n")
end
of:write(rcode)
of:close()
f:close()

2044
build/luacomp-jit-static.lua Normal file

File diff suppressed because it is too large Load Diff

514
build/luacomp-jit.lua Normal file
View File

@ -0,0 +1,514 @@
#!/usr/bin/env luajit
--[[
init.lua - Main file of LuaComp
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 _sv(k, v)
_G[k] = v
--os.setenv(k, tostring(v))
end
_sv("LUACOMP_V_MAJ", 1)
_sv("LUACOMP_V_MIN", 1)
_sv("LUACOMP_V_PAT", 0)
_sv("LUACOMP_VERSION", LUACOMP_V_MAJ.."."..LUACOMP_V_MIN.."."..LUACOMP_V_PAT)
_sv("LUACOMP_NAME", "LuaComp")
--[[
ast.lua - Generates a structure for use in preprocessing.
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 nextc(f, c)
c = c or 1
return f:read(c)
end
local function peek(f, c)
c = c or 1
local z = f:read(c)
f:seek("cur", -c)
return z
end
local function skip(f, c)
c = c or 1
return f:seek("cur", c)
end
local ws = {
["\t"] = true,
[" "] = true
}
local function parse_hex(f)
local lc = " "
local hex = ""
while (48 <= lc:byte() and lc:byte() <= 57) or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte()) <= 57 or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) then
hex = hex .. lc
end
end
return tonumber(hex, 16)
end
local function parse_number(f, c)
local lc = " "
local num = c
while 48 <= lc:byte() and lc:byte() <= 57 do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte() <= 57) then
num = num .. lc
end
end
return tonumber(hex, 10)
end
local esct = {
["t"] = "\t",
["n"] = "\n",
["r"] = "\r",
["\\"] = "\\\\"
}
for i=0, 9 do
esct[tostring(i)] = string.char(i)
end
local function parse_dblquote(f)
local val = ""
while peek(f) ~= "\"" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_snglquote(f)
local val = ""
while peek(f) ~= "\'" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_envarg(f)
local val = ""
while peek(f) ~= ")" do
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
val = val .. nextc(f)
end
skip(f)
return val
end
local function parse_directive(f)
local lc = "_"
local name = ""
local args = {}
local carg = ""
while not ws[lc] do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name}
elseif not ws[lc] then
name = name .. lc
end
end
while true do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name, args=args}
elseif lc == "0" and peek(f) == "x" then
skip(f)
local val = parse_hex(f)
args[#args+1] = val
elseif 48 <= lc:byte() and lc:byte() <= 57 then
local val = parse_number(f, lc)
args[#args+1] = val
elseif lc == "\"" then
local val, e = parse_dblquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "\'" then
local val, e = parse_snglquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "$" and peek(f) == "(" then
skip(f)
local val = parse_envarg(f)
if not os.getenv(val) then return nil, "Enviroment variable `"..val.."' does not exist." end
args[#args+1] = os.getenv(val)
elseif not ws[lc] then
return nil, "Syntax error"
end
end
end
local function mkast(f, n)
io.stderr:write("PROC\t",n,"\n")
local lc = " "
local lpos = 1
local ilpos = 1
local tree = {}
local code = ""
local branches = {}
local function add_code()
tree[#tree+1] = {type="code", data=code, file=n, line=lpos}
code = ""
end
local function parse_error(e)
io.stderr:write("ERROR:"..n..":"..lpos..": "..e.."\n")
os.exit(1)
end
while lc and lc ~= "" do
lc = nextc(f)
if (lc == "-" and ilpos == 1) then
if (peek(f, 2) == "-#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "/" and ilpos == 1) then
if (peek(f, 2) == "/#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "$" and peek(f) == "(") then
add_code()
skip(f)
local val, e = parse_envarg(f)
if not val then
parse_error(e)
end
tree[#tree+1] = {type="envvar", var=val, file=n, line=lpos}
elseif (lc == "@" and peek(f, 2) == "[[") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "]]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "@" and peek(f, 2) == "[{") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "}]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua_r", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "\r" or lc == "\n") then
if (lc == "\r" and peek(f) == "\n") then
skip(f)
end
lpos = lpos+1
ilpos = 1
code = code .. "\n"
else
code = code .. (lc or "")
ilpos = ilpos+1
end
end
add_code()
return tree
end
--[[
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 directives = {}
local function generate(ast)
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)
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.."))"
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 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 = os.getenv(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 fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
func()
return env.code
end
--[[
directive_provider.lua - Provides preprocessor directives
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.
]]
---#include "directives/define.lua"
function directives.include(env, file)
if (not os.execute("stat "..file..">/dev/null")) then
return false, "File `"..file.."' does not exist!"
end
local f = io.open(file, "r")
local fast = mkast(f, file)
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true
end
local warned = false
function directives.loadmod(env, mod)
if not warned then
io.stderr:write("Warning: loadmod is depreciated and unsafe. The API differs from luapreproc.\n")
warned = true
end
if (not os.execute("stat "..file..">/dev/null")) then
return false, "Module `"..file.."' does not exist!"
end
local modname, func = dofile(mod)
directives[modname] = func
return true
end
--[[
cfg/minifier_providers.lua - Provides minifier providers.
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 providers = {}
function providers.luamin(cin)
local fn = os.tmpname()
local fh = io.open(fn, "w")
fh:write(cin)
fh:close()
local lmh = io.popen("luamin -f "..fn.." 2>&1", "r")
local dat = lmh:read("*a")
local stat, _, code = lmh:close()
if (code ~= 0) then
return false, dat
end
return dat
end
function providers.none(cin)
return cin
end
local argparse = require("argparse")
local parser = argparse(arg[0], "LuaComp v"..LUACOMP_VERSION.."\nA Lua preprocessor+postprocessor.")
parser:argument("input", "Input file (- for STDIN)")
parser:option("-O --output", "Output file. (- for STDOUT)", "-")
parser:option("-m --minifier", "Sets the minifier", "none")
parser:option("-x --executable", "Makes the script an executable (default: current lua version)"):args "?"
local args = parser:parse()
local file = args.input
local f
if (file ~= "-") then
f = io.open(file, "r")
if not f then
io.stderr:write("ERROR: File `"..file.."' does not exist!\n")
os.exit(1)
end
else
f = io.stdin
end
local ast = mkast(f, file)
local ocode = generate(ast)
local minifier = providers[args.minifier]
local rcode = minifier(ocode)
local of
if (args.output == "-") then
of = io.stdout
else
of = io.open(args.output, "w")
end
local ver = _VERSION:lower():gsub(" ", "")
if jit then
ver = "luajit"
end
if (args.executable) then
of:write("#!/usr/bin/env ", args.executable[1] or ver, "\n")
end
of:write(rcode)
of:close()
f:close()

494
luacomp2.lua Normal file
View File

@ -0,0 +1,494 @@
#!/usr/bin/env lua5.3
--[[
init.lua - Main file of LuaComp
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 _sv(k, v)
_G[k] = v
--os.setenv(k, tostring(v))
end
_sv("LUACOMP_V_MAJ", 1)
_sv("LUACOMP_V_MIN", 0)
_sv("LUACOMP_V_PAT", 0)
_sv("LUACOMP_VERSION", LUACOMP_V_MAJ.."."..LUACOMP_V_MIN.."."..LUACOMP_V_PAT)
_sv("LUACOMP_NAME", "LuaComp")
--[[
ast.lua - Generates a structure for use in preprocessing.
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 nextc(f, c)
c = c or 1
return f:read(c)
end
local function peek(f, c)
c = c or 1
local z = f:read(c)
f:seek("cur", -c)
return z
end
local function skip(f, c)
c = c or 1
return f:seek("cur", c)
end
local ws = {
["\t"] = true,
[" "] = true
}
local function parse_hex(f)
local lc = " "
local hex = ""
while (48 <= lc:byte() and lc:byte() <= 57) or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte()) <= 57 or (97 <= lc:byte() and lc:byte() <= 102) or (65 <= lc:byte() and lc:byte() <= 70) then
hex = hex .. lc
end
end
return tonumber(hex, 16)
end
local function parse_number(f, c)
local lc = " "
local num = c
while 48 <= lc:byte() and lc:byte() <= 57 do
lc = nextc(f)
if (48 <= lc:byte() and lc:byte() <= 57) then
num = num .. lc
end
end
return tonumber(hex, 10)
end
local esct = {
["t"] = "\t",
["n"] = "\n",
["r"] = "\r",
["\\"] = "\\\\"
}
for i=0, 9 do
esct[tostring(i)] = string.char(i)
end
local function parse_dblquote(f)
local val = ""
while peek(f) ~= "\"" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_snglquote(f)
local val = ""
while peek(f) ~= "\'" do
local c = nextc(f)
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
if (c == "\\") then
if (esct[peek(f)]) then
c = esct[peek(f)]
skip(f)
else
c = nextc(f)
end
end
val = val .. c
end
skip(f)
return val
end
local function parse_envarg(f)
local val = ""
while peek(f) ~= ")" do
if (peek(f) == "\n" or peek(f) == "\r") then
return nil, "Unexpected end of line"
end
val = val .. nextc(f)
end
skip(f)
return val
end
local function parse_directive(f)
local lc = "_"
local name = ""
local args = {}
local carg = ""
while not ws[lc] do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name}
elseif not ws[lc] then
name = name .. lc
end
end
while true do
lc = nextc(f)
if (lc == "\n" or lc == "\r") then
if (lc == "\r" and peek(f) == "\n") then skip(f) end
return {type="directive", name=name, args=args}
elseif lc == "0" and peek(f) == "x" then
skip(f)
local val = parse_hex(f)
args[#args+1] = val
elseif 48 <= lc:byte() and lc:byte() <= 57 then
local val = parse_number(f, lc)
args[#args+1] = val
elseif lc == "\"" then
local val, e = parse_dblquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "\'" then
local val, e = parse_snglquote(f)
if not val then return val, e end
args[#args+1] = val
elseif lc == "$" and peek(f) == "(" then
skip(f)
local val = parse_envarg(f)
if not os.getenv(val) then return nil, "Enviroment variable `"..val.."' does not exist." end
args[#args+1] = os.getenv(val)
elseif not ws[lc] then
return nil, "Syntax error"
end
end
end
local function mkast(f, n)
io.stderr:write("PROC\t",n,"\n")
local lc = " "
local lpos = 1
local ilpos = 1
local tree = {}
local code = ""
local branches = {}
local function add_code()
tree[#tree+1] = {type="code", data=code, file=n, line=lpos}
code = ""
end
local function parse_error(e)
io.stderr:write("ERROR:"..n..":"..lpos..": "..e.."\n")
os.exit(1)
end
while lc and lc ~= "" do
lc = nextc(f)
if (lc == "-" and ilpos == 1) then
if (peek(f, 2) == "-#") then --Directive
add_code()
skip(f, 2)
local d, r = parse_directive(f)
if not d then
parse_error(r)
end
d.line = lpos
d.file = n
lpos = lpos+1
tree[#tree+1] = d
else
code = code .. lc
ilpos = ilpos+1
end
elseif (lc == "$" and peek(f) == "(") then
add_code()
skip(f)
local val, e = parse_envarg(f)
if not val then
parse_error(e)
end
tree[#tree+1] = {type="envvar", var=val, file=n, line=lpos}
elseif (lc == "@" and peek(f, 2) == "[[") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "]]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "@" and peek(f, 2) == "[{") then
add_code()
skip(f, 2)
local val = ""
while peek(f, 2) ~= "}]" do
val = val .. nextc(f)
end
tree[#tree+1] = {type="lua_r", code=val, file=n, line=lpos}
skip(f, 2)
elseif (lc == "\r" or lc == "\n") then
if (lc == "\r" and peek(f) == "\n") then
skip(f)
end
lpos = lpos+1
ilpos = 1
code = code .. "\n"
else
code = code .. (lc or "")
ilpos = ilpos+1
end
end
add_code()
return tree
end
--[[
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 directives = {}
local function generate(ast)
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)
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.."))"
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 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 = os.getenv(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 fenv = {}
for k, v in pairs(_G) do
fenv[k] = v
end
fenv._G = fenv
fenv._ENV = fenv
fenv.call_directive = call_directive
fenv.put_code = put_code
fenv.put_env = put_env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
func()
return env.code
end
--[[
directive_provider.lua - Provides preprocessor directives
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.
]]
---#include "directives/define.lua"
function directives.include(env, file)
if (not os.execute("stat "..file..">/dev/null")) then
return false, "File `"..file.."' does not exist!"
end
local f = io.open(file, "r")
local fast = mkast(f, file)
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true
end
local warned = false
function directives.loadmod(env, mod)
if not warned then
io.stderr:write("Warning: loadmod is depreciated and unsafe. The API differs from luapreproc.\n")
warned = true
end
if (not os.execute("stat "..file..">/dev/null")) then
return false, "Module `"..file.."' does not exist!"
end
local modname, func = dofile(mod)
directives[modname] = func
return true
end
--[[
cfg/minifier_providers.lua - Provides minifier providers.
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 providers = {}
function providers.luamin(cin)
local fn = os.tmpname()
local fh = io.open(fn, "w")
fh:write(cin)
fh:close()
local lmh = io.popen("luamin -f "..fn.." 2>&1", "r")
local dat = lmh:read("*a")
local stat, _, code = lmh:close()
if (code ~= 0) then
return false, dat
end
return dat
end
function providers.none(cin)
return cin
end
local argparse = require("argparse")
local parser = argparse(arg[0], "LuaComp v"..LUACOMP_VERSION.."\nA Lua preprocessor+postprocessor.")
parser:argument("input", "Input file (- for STDIN)")
parser:option("-O --output", "Output file. (- for STDOUT)", "-")
parser:option("-m --minifier", "Sets the minifier", "none")
parser:flag("-x --executable", "Makes the script an executable")
local args = parser:parse()
local file = args.input
local f
if (file ~= "-") then
if (not os.execute("stat "..file..">/dev/null")) then
io.stderr:write("ERROR: File `"..file.."' does not exist!\n")
os.exit(1)
end
f = io.open(file, "r")
else
file = io.stdin
end
local ast = mkast(f, file)
local ocode = generate(ast)
local minifier = providers[args.minifier]
local rcode = minifier(ocode)
local of
if (args.output == "-") then
of = io.stdout
else
of = io.open(args.output, "w")
end
if (args.executable) then
of:write("#!/usr/bin/env lua5.3\n")
end
of:write(rcode)
of:close()
f:close()

2044
luacomp_test.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,7 @@ else
end
dprint("Generating AST...")
local ast = mkast(f, file)
ast.file = f
dprint("Generating code...")
local ocode = generate(ast, args.generator_code)

View File

@ -4,6 +4,7 @@ function directives.include(env, file)
end
local f = io.open(file, "r")
local fast = mkast(f, file)
fast.file = file
local code = generate(fast)
env.code = env.code .. "\n" .. code .. "\n"
return true

View File

@ -142,7 +142,7 @@ local function generate(ast, gencode)
fenv.put_svar = put_svar
fenv.put_shell_out = put_shell_out
fenv._GENERATOR=env
local func = assert(load(lua_code, "=(generated code)", "t", fenv))
local func = assert(load(lua_code, "=(generated code: "..ast.file..")", "t", fenv))
func()
return env.code
end

View File

@ -1,4 +1,4 @@
local svar = {}
svar = {}
local svars = {}