1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2025-10-27 17:59:30 +00:00
candran/compiler/lua53.can
2017-08-24 20:28:07 +02:00

399 lines
11 KiB
Text

return function(code, ast, options)
--- Line mapping
local lastInputPos = 1 -- last token position in the input code
local prevLinePos = 1 -- last token position in the previous line of code in the input code
local lastSource = "nil" -- last found code source name (from the original file)
local lastLine = 1 -- last found line number (from the original file)
--- Newline management
local indentLevel = 0
-- Returns a newline.
local function newline()
local r = options.newline .. string.rep(options.indentation, indentLevel)
if options.mapLines then
local sub = code:sub(lastInputPos)
local source, line = sub:sub(1, sub:find("\n")):match("%-%- (.-)%:(%d+)\n")
if source and line then
lastSource = source
lastLine = tonumber(line)
else
for _ in code:sub(prevLinePos, lastInputPos):gmatch("\n") do
lastLine += 1
end
end
prevLinePos = lastInputPos
r = " -- " .. lastSource .. ":" .. lastLine .. r
end
return r
end
-- Returns a newline and add one level of indentation.
local function indent()
indentLevel += 1
return newline()
end
-- Returns a newline and remove one level of indentation.
local function unindent()
indentLevel -= 1
return newline()
end
--- Module management
local required = {} -- { ["module"] = true, ... }
local requireStr = ""
-- Add the module "mod" to the list of modules to require, and load its field "field" (or the whole module if nil) into the variable "name".
local function addRequire(mod, name, field)
if not required[mod] then
requireStr ..= "local " .. options.requirePrefix .. name .. (" = require(%q)"):format(mod) .. (field and "."..field or "") .. options.newline
required[mod] = true
end
end
-- Returns the required module variable name.
local function getRequire(name)
return options.requirePrefix .. name
end
--- AST traversal helpers
-- Returns the first node from the list "list" which tag is in the list "tags", or nil if there were none.
local function any(list, tags)
local tagsCheck = {}
for _, tag in ipairs(tags) do
tagsCheck[tag] = true
end
for _, node in ipairs(list) do
if tagsCheck[node.tag] then
return node
end
end
return nil
end
--- Modification helpers
local namedNodes = {} -- { ["name"] = ast, ... }
-- Wrap the compiled code of a named node with suffix and prefix.
local function wrap(name, prefix, suffix)
local node = namedNodes[name]
if not node then error("not inside a " .. name) end
node.prefix, node.suffix = prefix .. newline(), newline() .. suffix
end
--- Lua compiler
local tags
-- Recursively returns the compiled AST Lua code, set "forceTag" to override the tag type and pass additional arguments to the tag constructor if needed.
local function lua(ast, forceTag, ...)
if options.mapLines and ast.pos then
lastInputPos = ast.pos
end
return tags[forceTag or ast.tag](ast, ...)
end
-- Same as lua(), but gives the AST node a name which can be used for further access & modification.
local function namedLua(name, ast, ...)
local old = namedNodes[name]
namedNodes[name] = ast
local code = lua(ast, ...)
namedNodes[name] = old
return (ast.prefix or "") .. code .. (ast.suffix or "")
end
-- Tag constructors
tags = setmetatable({
-- block: { stat* } --
Block = (t)
local r = ""
for i=1, #t-1, 1 do
r ..= lua(t[i]) .. newline()
end
if t[#t] then
r ..= lua(t[#t])
end
return r
end,
-- stat --
-- Do{ stat* }
Do = (t)
return "do" .. indent() .. lua(t, "Block") .. unindent() .. "end"
end,
-- Set{ {lhs+} (opid? = opid?)? {expr+} }
Set = (t)
if #t == 2 then
return lua(t[1], "_lhs") .. " = " .. lua(t[2], "_lhs")
elseif #t == 3 then
return lua(t[1], "_lhs") .. " = " .. lua(t[3], "_lhs")
elseif #t == 4 then
if t[3] == "=" then
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], { tag = "Paren", t[4][1] } }, "Op")
for i=2, math.min(#t[4], #t[1]), 1 do
r ..= ", " .. lua({ t[2], t[1][i], { tag = "Paren", t[4][i] } }, "Op")
end
return r
else
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[3], { tag = "Paren", t[4][1] }, t[1][1] }, "Op")
for i=2, math.min(#t[4], #t[1]), 1 do
r ..= ", " .. lua({ t[3], { tag = "Paren", t[4][i] }, t[1][i] }, "Op")
end
return r
end
else -- You are mad.
local r = lua(t[1], "_lhs") .. " = " .. lua({ t[2], t[1][1], { tag = "Op", t[4], { tag = "Paren", t[5][1] }, t[1][1] } }, "Op")
for i=2, math.min(#t[5], #t[1]), 1 do
r ..= ", " .. lua({ t[2], t[1][i], { tag = "Op", t[4], { tag = "Paren", t[5][i] }, t[1][i] } }, "Op")
end
return r
end
end,
-- While{ expr block }
While = (t)
return "while " .. lua(t[1]) .. " do" .. indent() .. namedLua("loop", t[2]) .. unindent() .. "end"
end,
-- Repeat{ block expr }
Repeat = (t)
return "repeat".. indent() .. namedLua("loop", t[1]) .. unindent() .. "until " .. lua(t[2])
end,
-- If{ (expr block)+ block? }
If = (t)
local r = "if " .. lua(t[1]) .. " then" .. indent() .. lua(t[2]) .. unindent()
for i=3, #t-1, 2 do
r ..= "elseif " .. lua(t[i]) .. " then" .. indent() .. lua(t[i+1]) .. unindent()
end
if #t % 2 == 1 then
r ..= "else" .. indent() .. lua(t[#t]) .. unindent()
end
return r .. "end"
end,
-- Fornum{ ident expr expr expr? block }
Fornum = (t)
local r = "for " .. lua(t[1]) .. " = " .. lua(t[2]) .. ", " .. lua(t[3])
if #t == 5 then
return r .. ", " .. lua(t[4]) .. " do" .. indent() .. lua(t[5]) .. unindent() .. "end"
else
return r .. " do" .. indent() .. namedLua("loop", t[4]) .. unindent() .. "end"
end
end,
-- Forin{ {ident+} {expr+} block }
Forin = (t)
return "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent() .. namedLua("loop", t[3]) .. unindent() .. "end"
end,
-- Local{ {ident+} {expr+}? }
Local = (t)
local r = "local "..lua(t[1], "_lhs")
if t[2][1] then
r ..= " = "..lua(t[2], "_lhs")
end
return r
end,
-- Let{ {ident+} {expr+}? }
Let = (t)
local nameList = lua(t[1], "_lhs")
local r = "local " .. nameList
if t[2][1] then
if any(t[2], { "Function", "Table", "Paren" }) then -- predeclaration doesn't matter otherwise
r ..= newline() .. nameList .. " = " .. lua(t[2], "_lhs")
else
r ..= " = " .. lua(t[2], "_lhs")
end
end
return r
end,
-- Localrec{ ident expr }
Localrec = (t)
return "local function "..lua(t[1][1])..lua(t[2][1], "_functionWithoutKeyword")
end,
-- Goto{ <string> }
Goto = (t)
return "goto " .. lua(t[1], "Id")
end,
-- Label{ <string> }
Label = (t)
return "::" .. lua(t[1], "Id") .. "::"
end,
-- Return{ <expr*> }
Return = (t)
return "return "..lua(t, "_lhs")
end,
-- Break
Break = ()
return "break"
end,
-- Continue
Continue = ()
wrap("loop", "repeat", "until true")
return "break"
end,
-- apply (below)
-- expr --
-- Nil
Nil = ()
return "nil"
end,
-- Dots
Dots = ()
return "..."
end,
-- Boolean{ <boolean> }
Boolean = (t)
return tostring(t[1])
end,
-- Number{ <number> }
Number = (t)
return tostring(t[1])
end,
-- String{ <string> }
String = (t)
return ("%q"):format(t[1])
end,
-- Function{ { ( `ParPair{ Id expr } | `Id{ <string> } )* `Dots? } block }
_functionWithoutKeyword = (t)
local r = "("
local decl = {}
if t[1][1] then
if t[1][1].tag == "ParPair" then
local id = lua(t[1][1][1])
indentLevel += 1
table.insert(decl, id .. " = " .. id .. " == nil and " .. lua(t[1][1][2]) .. " or " .. id)
indentLevel -= 1
r ..= id
else
r ..= lua(t[1][1])
end
for i=2, #t[1], 1 do
if t[1][i].tag == "ParPair" then
local id = lua(t[1][i][1])
indentLevel += 1
table.insert(decl, "if " .. id .. " == nil then " .. id .. " = " .. lua(t[1][i][2]) .. " end")
indentLevel -= 1
r ..= ", " ..id
else
r ..= ", " .. lua(t[1][i])
end
end
end
r ..= ")" .. indent()
for _, d in ipairs(decl) do
r ..= d .. newline()
end
return r .. lua(t[2]) .. unindent() .. "end"
end,
Function = (t)
return "function" .. lua(t, "_functionWithoutKeyword")
end,
-- Table{ ( `Pair{ expr expr } | expr )* }
Pair = (t)
return "[" .. lua(t[1]) .. "] = " .. lua(t[2])
end,
Table = (t)
if #t == 0 then
return "{}"
elseif #t == 1 then
return "{ " .. lua(t, "_lhs") .. " }"
else
return "{" .. indent() .. lua(t, "_lhs") .. unindent() .. "}"
end
end,
-- Op{ opid expr expr? }
Op = (t)
local r
if #t == 2 then
if type(tags._opid[t[1]]) == "string" then
r = tags._opid[t[1]] .. " " .. lua(t[2])
else
r = tags._opid[t[1]](t[2])
end
else
if type(tags._opid[t[1]]) == "string" then
r = lua(t[2]) .. " " .. tags._opid[t[1]] .. " " .. lua(t[3])
else
r = tags._opid[t[1]](t[2], t[3])
end
end
return r
end,
-- Paren{ expr }
Paren = (t)
return "(" .. lua(t[1]) .. ")"
end,
-- DoExpr{ stat* }
DoExpr = (t)
return "(function()" .. indent() .. lua(t, "Do") .. unindent() .. "end)()"
end,
-- WhileExpr{ expr block }
WhileExpr = (t)
return "(function()" .. indent() .. lua(t, "While") .. unindent() .. "end)()"
end,
-- RepeatExpr{ expr block }
RepeatExpr = (t)
return "(function()" .. indent() .. lua(t, "Repeat") .. unindent() .. "end)()"
end,
-- IfExpr{ (expr block)+ block? }
IfExpr = (t)
return "(function()" .. indent() .. lua(t, "If") .. unindent() .. "end)()"
end,
-- FornumExpr{ ident expr expr expr? block }
FornumExpr = (t)
return "(function()" .. indent() .. lua(t, "Fornum") .. unindent() .. "end)()"
end,
-- Forin{ {ident+} {expr+} block }
ForninExpr = (t)
return "(function()" .. indent() .. lua(t, "Forin") .. unindent() .. "end)()"
end,
-- apply (below)
-- lhs (below)
-- apply --
-- Call{ expr expr* }
Call = (t)
return lua(t[1]) .. "(" .. lua(t, "_lhs", 2) .. ")"
end,
-- Invoke{ expr `String{ <string> } expr* }
Invoke = (t)
return lua(t[1])..":"..lua(t[2], "Id").."("..lua(t, "_lhs", 3)..")"
end,
-- lhs --
_lhs = (t, start)
start = start or 1
local r
if t[start] then
r = lua(t[start])
for i=start+1, #t, 1 do
r ..= ", "..lua(t[i])
end
else
r = ""
end
return r
end,
-- Id{ <string> }
Id = (t)
return t[1]
end,
-- Index{ expr expr }
Index = (t)
return lua(t[1]).."["..lua(t[2]).."]"
end,
-- opid --
_opid = {
add = "+", sub = "-", mul = "*", div = "/",
idiv = "//", mod = "%", pow = "^", concat = "..",
band = "&", bor = "|", bxor = "~", shl = "<<", shr = ">>",
eq = "==", ne = "~=", lt = "<", gt = ">", le = "<=", ge = ">=",
["and"] = "and", ["or"] = "or", unm = "-", len = "#", bnot = "~", ["not"] = "not"
}
}, {
__index = (self, key)
error("don't know how to compile a "..tostring(key).." to Lua 5.3")
end
})
#placeholder("patch")
local code = lua(ast) .. newline()
return requireStr .. code
end