503 lines
12 KiB
Lua
503 lines
12 KiB
Lua
local local_path = LuaVenusCompiler.path or ""
|
|
local vc_util = dofile(local_path.."vc_util.lua")
|
|
|
|
local compiler = {}
|
|
|
|
local elements = {
|
|
names = "^(%a%w*)$",
|
|
spaces = "(%s+)",
|
|
special = "[%%%(%)%{%}%;%,]",
|
|
strings = "[\"']",
|
|
special_combined = "([%+%-%*/%^#=~<>%[%]:%.][%+%-/#=>%[%]:%.]?[%.]?)",
|
|
lambda_args = "[,%(%)]"
|
|
}
|
|
|
|
local non_space_elements = {elements.special_combined,elements.special,elements.strings}
|
|
|
|
function compiler.warn(msg)
|
|
print("LuaVenusCompiler warning: " .. msg)
|
|
end
|
|
|
|
--TODO: check if spaces before a curly brace were already added
|
|
|
|
--TODO: make some functions handling each group of commands
|
|
local function parse_element(el,pc)
|
|
if el == "" then
|
|
return el
|
|
end
|
|
local prefix
|
|
local precurlch = false
|
|
if el == "elseif" then
|
|
if pc.ifend then
|
|
pc.ifend = false
|
|
end
|
|
pc.curlyopt = "if"
|
|
elseif el == "else" then
|
|
pc.ifend = false
|
|
pc.curlyopt = true
|
|
pc.precurly = el
|
|
precurlch = true
|
|
elseif pc.ifend then
|
|
if pc.linestart then
|
|
prefix = pc.ifend
|
|
else
|
|
prefix = " "..pc.ifend
|
|
end
|
|
pc.ifend = false
|
|
end
|
|
|
|
if pc.deccheck then
|
|
local cpos = pc.deccheck
|
|
pc.deccheck = false
|
|
pc.optassign = false
|
|
if cpos == true then
|
|
pc.slcomm = true
|
|
return "--"..el
|
|
else
|
|
pc.slcomm = true
|
|
return "--"..cpos..el
|
|
end
|
|
end
|
|
|
|
if el == "=>" then
|
|
if not pc.lambargs then
|
|
compiler.warn(("invalid lambda in line %i"):format(pc.line))
|
|
return el
|
|
end
|
|
local larg = pc.lambargs
|
|
pc.lambargs = false
|
|
pc.lambend = false
|
|
pc.precurly = "function"
|
|
pc.curlyopt = true
|
|
return "function" .. larg .. " "
|
|
elseif pc.lambend then
|
|
if prefix then
|
|
compiler.warn(("end statement and lambda match end may be mixed in line %i"):format(pc.line))
|
|
prefix = pc.lambargs .. prefix
|
|
else
|
|
prefix = pc.lambargs
|
|
end
|
|
pc.lambargs = false
|
|
pc.lambend = false
|
|
end
|
|
|
|
if el == '"' or el == "'" then
|
|
if not pc.instring then
|
|
pc.instring = el
|
|
elseif pc.instring == el then
|
|
pc.instring = false
|
|
end
|
|
elseif el == "[[" then
|
|
if not pc.instring then
|
|
pc.instring = el
|
|
end
|
|
elseif el == "]]" then
|
|
if pc.instring == "[[" then
|
|
pc.instring = false
|
|
end
|
|
elseif pc.instring then
|
|
return el,prefix
|
|
end
|
|
|
|
if pc.foreach == 2 then
|
|
pc.foreach = 3
|
|
if el == "{" then
|
|
table.insert(pc.opencurly, "table")
|
|
end
|
|
return "pairs("..el,prefix
|
|
elseif el == "{" then
|
|
if pc.foreach == 3 then
|
|
pc.foreach = 0
|
|
table.insert(pc.opencurly, "for")
|
|
pc.curlyopt = false
|
|
return ") do ",prefix
|
|
elseif not pc.curlyopt then
|
|
table.insert(pc.opencurly, "table")
|
|
return el,prefix
|
|
elseif pc.curlyopt == true then
|
|
if pc.precurly == "function" or pc.precurly == "repeat" or
|
|
pc.precurly == "else" or pc.precurly == "do" then
|
|
table.insert(pc.opencurly, pc.precurly)
|
|
pc.precurly = false
|
|
pc.curlyopt = false
|
|
return "",prefix
|
|
end
|
|
elseif pc.curlyopt == "for" or pc.curlyopt == "while" then
|
|
table.insert(pc.opencurly, pc.curlyopt)
|
|
pc.curlyopt = false
|
|
return " do ",prefix
|
|
elseif pc.curlyopt == "if" then
|
|
table.insert(pc.opencurly, pc.curlyopt)
|
|
pc.curlyopt = false
|
|
return " then ",prefix
|
|
end
|
|
elseif pc.precurly and not precurlch then
|
|
pc.precurly = false
|
|
pc.curlyopt = false
|
|
end
|
|
|
|
if el == "}" then
|
|
local closecurly = table.remove(pc.opencurly)
|
|
if closecurly == "table" then
|
|
return el,prefix
|
|
elseif closecurly == "repeat" then
|
|
return "",prefix
|
|
elseif closecurly == "for" or closecurly == "while" or
|
|
closecurly == "function" or closecurly == "repeat" or
|
|
closecurly == "do" or closecurly == "else" then
|
|
if pc.linestart then
|
|
return "end",prefix
|
|
else
|
|
return " end",prefix
|
|
end
|
|
elseif closecurly == "if" then
|
|
pc.ifend = "end"
|
|
return "",prefix
|
|
else
|
|
compiler.warn(("closing curly bracket in line %i could not be matched to an opening one"):format(pc.line))
|
|
return el,prefix
|
|
end
|
|
elseif el == "foreach" then
|
|
pc.curlyopt = "for"
|
|
pc.foreach = 1
|
|
return "for _,",prefix
|
|
elseif el == "for" then
|
|
pc.curlyopt = el
|
|
pc.foreach = 0
|
|
elseif el == "in" then
|
|
if pc.foreach == 1 then
|
|
pc.foreach = 2
|
|
end
|
|
elseif el == "do" then
|
|
pc.curlyopt = true
|
|
pc.precurly = "do"
|
|
if pc.foreach == 3 then
|
|
pc.foreach = 0
|
|
return ") " .. el,prefix
|
|
end
|
|
elseif el == "while" then
|
|
pc.curlyopt = el
|
|
elseif el == "repeat" then
|
|
pc.precurly = el
|
|
pc.curlyopt = true
|
|
elseif el == "if" then
|
|
pc.curlyopt = el
|
|
elseif el == "then" then
|
|
pc.curlyopt = false
|
|
elseif el == "fn" then
|
|
pc.curlyopt = "function"
|
|
return "function",prefix
|
|
elseif el == "function" then
|
|
pc.curlyopt = el
|
|
elseif el == "(" then
|
|
pc.newlamb = el
|
|
pc.lambend = false
|
|
return "",prefix
|
|
elseif el == ")" then
|
|
if pc.curlyopt == "function" then
|
|
pc.precurly = pc.curlyopt
|
|
pc.curlyopt = true
|
|
end
|
|
if pc.lambargs then
|
|
pc.lambend = true
|
|
end
|
|
elseif el=="##" then
|
|
if not pc.instring then
|
|
pc.slcomm = true
|
|
return "--",prefix
|
|
end
|
|
elseif el == "--" then
|
|
if pc.optassign then
|
|
pc.deccheck = true
|
|
return "",prefix
|
|
else
|
|
pc.slcomm = true
|
|
return el,prefix
|
|
end
|
|
elseif el == "++" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return " = " .. nam .. " + 1"
|
|
else
|
|
compiler.warn(("empty increment in line %i"):format(pc.line))
|
|
return el, prefix
|
|
end
|
|
elseif el == "+=" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return "= " .. nam .. "+"
|
|
else
|
|
compiler.warn(("empty increment assignment in line %i"):format(pc.line))
|
|
end
|
|
elseif el == "-=" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return "= " .. nam .. "-"
|
|
else
|
|
compiler.warn(("empty decrement assignment in line %i"):format(pc.line))
|
|
end
|
|
elseif el == "*=" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return "= " .. nam .. "*"
|
|
else
|
|
compiler.warn(("empty multiply assignment in line %i"):format(pc.line))
|
|
end
|
|
elseif el == "/=" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return "= " .. nam .. "/"
|
|
else
|
|
compiler.warn(("empty divide assignment in line %i"):format(pc.line))
|
|
end
|
|
elseif el == "^=" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return "= " .. nam .. "^"
|
|
else
|
|
compiler.warn(("empty power assignment in line %i"):format(pc.line))
|
|
end
|
|
elseif el == ".=" then
|
|
if pc.optassign then
|
|
local nam = pc.optassign
|
|
pc.optassign = false
|
|
return "= " .. nam .. ".."
|
|
else
|
|
compiler.warn(("empty concatenation assignment in line %i"):format(pc.line))
|
|
end
|
|
end
|
|
--print(el,pc.instring and "in string" or "")
|
|
return el, prefix
|
|
end
|
|
|
|
local function store_space(sp,pc)
|
|
if pc.optassign then
|
|
if pc.optassign ~= true then
|
|
pc.optassign = pc.optassign .. sp
|
|
end
|
|
end
|
|
if pc.lambargs then
|
|
pc.lambargs = pc.lambargs .. sp
|
|
return false
|
|
elseif pc.deccheck then
|
|
if pc.deccheck == true then
|
|
pc.deccheck = sp
|
|
return false
|
|
else
|
|
pc.deccheck = pc.deccheck .. sp
|
|
return false
|
|
end
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function handle_prefix(el,p)
|
|
local pre = p
|
|
local lpre
|
|
if pre then
|
|
while pre:match("\n") do
|
|
if lpre then
|
|
lpre = lpre .. pre:sub(1,pre:find("\n"))
|
|
else
|
|
lpre = pre:sub(1,pre:find("\n"))
|
|
end
|
|
pre = pre:sub(pre:find("\n")+1)
|
|
end
|
|
local pres = pre:match("^%s*") or ""
|
|
if lpre then
|
|
lpre = lpre .. pres
|
|
else
|
|
lpre = pres
|
|
end
|
|
pre = pre:sub(#pres+1)
|
|
--[[
|
|
if (pre ~= "") then
|
|
print("pre:".. pre..":")
|
|
else
|
|
print("prel:" .. el)
|
|
end
|
|
--]]
|
|
return vc_util.concat_optnil(pre,el," "),lpre
|
|
end
|
|
return el
|
|
end
|
|
|
|
local function store_lambargs(e,pc)
|
|
local el = e
|
|
if pc.newlamb then
|
|
if pc.lambargs then
|
|
el = pc.lambargs .. el
|
|
end
|
|
pc.lambargs = pc.newlamb
|
|
pc.newlamb = false
|
|
--print("newl:", pc.lambargs, el)
|
|
elseif pc.lambargs then
|
|
if el:match(elements.names) or el:match(elements.lambda_args) then
|
|
pc.lambargs = pc.lambargs .. el
|
|
el = ""
|
|
elseif el ~= "" then
|
|
el = pc.lambargs .. el
|
|
pc.lambargs = false
|
|
pc.lambend = false
|
|
--print("notl:", el)
|
|
end
|
|
end
|
|
return el
|
|
end
|
|
|
|
local function store_optassign(el,pc)
|
|
if pc.optassign and el ~= "" then
|
|
if (pc.linestart and el:match(elements.names)) or
|
|
(pc.optassign and el == ".") or
|
|
(pc.optassign and pc.optassign ~= true and
|
|
pc.optassign:sub(#pc.optassign) == "." and el:match(elements.names)) then
|
|
if pc.optassign == true then
|
|
pc.optassign = el
|
|
else
|
|
pc.optassign = pc.optassign .. el
|
|
end
|
|
elseif el ~= "--" then
|
|
pc.optassign = false
|
|
end
|
|
end
|
|
end
|
|
|
|
local function parse_line(l,pc)
|
|
local pl = ""
|
|
for sp,s in vc_util.optmatch(l,elements.spaces) do
|
|
if s then
|
|
if store_space(sp,pc) then
|
|
pl = pl .. sp
|
|
end
|
|
else
|
|
for st in vc_util.optmatch(sp,non_space_elements) do
|
|
if pc.slcomm then
|
|
pl = pl .. st
|
|
else
|
|
local el,lpre = handle_prefix(parse_element(st,pc))
|
|
el = store_lambargs(el,pc)
|
|
store_optassign(el,pc)
|
|
if lpre then
|
|
pl = pl .. lpre .. el
|
|
else
|
|
pl = pl .. el
|
|
end
|
|
if pc.linestart and el ~= "" then
|
|
pc.linestart = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return pl
|
|
end
|
|
|
|
local function handle_linestart(pc)
|
|
pc.line = pc.line + 1
|
|
pc.linestart = true
|
|
pc.optassign = true
|
|
end
|
|
|
|
local function handle_lineend_curly(pc)
|
|
if pc.ifend then
|
|
local ret
|
|
if pc.linestart then
|
|
ret = pc.ifend
|
|
else
|
|
ret = " " .. pc.ifend
|
|
end
|
|
pc.ifend = false
|
|
return ret
|
|
end
|
|
return ""
|
|
end
|
|
|
|
local function handle_lineend_decrement(pc)
|
|
if pc.deccheck then
|
|
if pc.optassign == false then
|
|
pc.deccheck = false
|
|
elseif pc.optassign == true then
|
|
pc.deccheck = false
|
|
else
|
|
local ret = " = " .. pc.optassign .. " - 1"
|
|
pc.deccheck = false
|
|
pc.optassign = false
|
|
return ret
|
|
end
|
|
end
|
|
return ""
|
|
end
|
|
|
|
local function handle_lineend_lambargs(pc)
|
|
if pc.lambargs then
|
|
pc.lambargs = pc.lambargs .. "\n"
|
|
else
|
|
return "\n"
|
|
end
|
|
return ""
|
|
end
|
|
|
|
function compiler.tl_venus_string(str)
|
|
local fc = ""
|
|
local pc = {instring = false, opencurly = {}, line = 0}
|
|
for l,e in vc_util.optmatch(str,"\n") do
|
|
if e then
|
|
if pc.slcomm then
|
|
pc.slcomm = false
|
|
fc = fc .. "\n"
|
|
else
|
|
fc = fc .. handle_lineend_curly(pc)
|
|
fc = fc .. handle_lineend_decrement(pc)
|
|
fc = fc .. handle_lineend_lambargs(pc)
|
|
end
|
|
else
|
|
handle_linestart(pc)
|
|
fc = fc .. parse_line(l,pc)
|
|
end
|
|
end
|
|
if (#pc.opencurly > 0) then
|
|
compiler.warn("not all curly brackets were closed")
|
|
end
|
|
return fc
|
|
end
|
|
|
|
function compiler.tl_venus_file(file)
|
|
local f = io.open(file)
|
|
local ret = compiler.tl_venus_string(f:read("*a"))
|
|
f:close()
|
|
return ret
|
|
end
|
|
|
|
function compiler.loadvenus(file,env)
|
|
local fc = compiler.tl_venus_file(file)
|
|
if env then
|
|
return loadstring(fc,"@"..file,"t",env)
|
|
else
|
|
return loadstring(fc,"@"..file)
|
|
end
|
|
end
|
|
|
|
function compiler.dovenus(file)
|
|
local ff, err = compiler.loadvenus(file)
|
|
if ff == nil then
|
|
error(err,2)
|
|
end
|
|
return ff()
|
|
end
|
|
|
|
function compiler.convert_venus_file(venus_file_in,lua_file_out)
|
|
local s = compiler.tl_venus_file(venus_file_in)
|
|
local f = io.open(lua_file_out,"w")
|
|
f:write(s)
|
|
f:close()
|
|
end
|
|
|
|
return compiler
|