1
0
Fork 0
mirror of https://github.com/Reuh/candran.git synced 2025-10-27 09:59:29 +00:00

Added push, table comprehension, fixed stuff

* Implicit returns are now implicit pushes
* Candran keywords can be used as variable name
* Better handling of continue
* Moar newlines in output
This commit is contained in:
Étienne Fildadut 2017-08-25 17:09:52 +02:00
parent 20e33c279e
commit 6b95bfb698
6 changed files with 1331 additions and 425 deletions

View file

@ -56,27 +56,51 @@ return function(code, ast, options)
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 loop = { "While", "Repeat", "Fornum", "Forin" } -- loops tags
local func = { "Function", "TableCompr", "DoExpr", "WhileExpr", "RepeatExpr", "IfExpr", "FornumExpr", "ForinExpr" } -- function scope tags
-- Returns the first node or subnode from the list "list" which tag is in the list "tags", or nil if there were none.
-- Won't recursively follow nodes which have a tag in "nofollow".
local function any(list, tags, nofollow={})
local tagsCheck = {}
for _, tag in ipairs(tags) do
tagsCheck[tag] = true
end
local nofollowCheck = {}
for _, tag in ipairs(nofollow) do
nofollowCheck[tag] = true
end
for _, node in ipairs(list) do
if tagsCheck[node.tag] then
return node
if type(node) == "table" then
if tagsCheck[node.tag] then
return node
end
if not nofollowCheck[node.tag] then
local r = any(node, tags, nofollow)
if r then return r end
end
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
--- State stacks
-- Used for context-sensitive syntax.
local states = {
push = {} -- push stack variable names
}
-- Push a new value on top of the stack "name". Returns an empty string for chaining.
local function push(name, state)
table.insert(states[name], state)
return ""
end
-- Remove the value on top of the stack "name". Returns an empty string for chaining.
local function pop(name)
table.remove(states[name])
return ""
end
-- Returns the value on top of the stack "name".
local function peek(name)
return states[name][#states[name]]
end
--- Lua compiler
@ -88,25 +112,28 @@ return function(code, ast, options)
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 hasPush = not peek("push") and any(t, { "Push" }, func) -- push in block and push context not yet defined
if hasPush and hasPush == t[#t] then -- if the first push is the last statement, it's just a return
hasPush.tag = "Return"
hasPush = false
end
local r = ""
if hasPush then
r ..= push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline()
end
for i=1, #t-1, 1 do
r ..= lua(t[i]) .. newline()
end
if t[#t] then
r ..= lua(t[#t])
end
if hasPush and (t[#t] and t[#t].tag ~= "Return") then -- add return only if needed
r ..= newline() .. "return unpack(__PUSH__)" .. pop("push")
end
return r
end,
@ -146,11 +173,31 @@ return function(code, ast, options)
end,
-- While{ expr block }
While = (t)
return "while " .. lua(t[1]) .. " do" .. indent() .. namedLua("loop", t[2]) .. unindent() .. "end"
local hasContinue = any(t[2], { "Continue" }, loop)
local r = "while " .. lua(t[1]) .. " do" .. indent()
if hasContinue then
r ..= "repeat" .. indent()
end
r .. = lua(t[2])
if hasContinue then
r ..= unindent() .. "until true"
end
r ..= unindent() .. "end"
return r
end,
-- Repeat{ block expr }
Repeat = (t)
return "repeat".. indent() .. namedLua("loop", t[1]) .. unindent() .. "until " .. lua(t[2])
local hasContinue = any(t[2], { "Continue" }, loop)
local r = "repeat" .. indent()
if hasContinue then
r ..= "repeat" .. indent()
end
r .. = lua(t[1])
if hasContinue then
r ..= unindent() .. "until true"
end
r ..= unindent() .. "until " .. lua(t[2])
return r
end,
-- If{ (expr block)+ block? }
If = (t)
@ -167,14 +214,41 @@ return function(code, ast, options)
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"
local hasContinue = any(t[5], { "Continue" }, loop)
r ..= ", " .. lua(t[4]) .. " do" .. indent()
if hasContinue then
r ..= "repeat" .. indent()
end
r ..= lua(t[5])
if hasContinue then
r ..= "until true" .. unindent()
end
return r .. unindent() .. "end"
else
return r .. " do" .. indent() .. namedLua("loop", t[4]) .. unindent() .. "end"
local hasContinue = any(t[4], { "Continue" }, loop)
r ..= " do" .. indent()
if hasContinue then
r ..= "repeat" .. indent()
end
r ..= lua(t[4])
if hasContinue then
r ..= unindent() .. "until true"
end
return r .. 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"
local hasContinue = any(t[3], { "Continue" }, loop)
local r = "for " .. lua(t[1], "_lhs") .. " in " .. lua(t[2], "_lhs") .. " do" .. indent()
if hasContinue then
r ..= "repeat" .. indent()
end
r ..= lua(t[3])
if hasContinue then
r ..= "until true" .. unindent()
end
return r .. unindent() .. "end"
end,
-- Local{ {ident+} {expr+}? }
Local = (t)
@ -211,7 +285,21 @@ return function(code, ast, options)
end,
-- Return{ <expr*> }
Return = (t)
return "return "..lua(t, "_lhs")
local push = peek("push")
if push then
local r = ""
for _, val in ipairs(t) do
r ..= push .. "[#" .. push .. "+1] = " .. lua(val) .. newline()
end
return r .. "return unpack(" .. push .. ")"
else
return "return "..lua(t, "_lhs")
end
end,
-- Push{ <expr*> }
Push = (t)
local var = assert(peek("push"), "no context given for push")
return var .. "[#" .. var .. "+1] = " .. lua(t, "_lhs")
end,
-- Break
Break = ()
@ -219,7 +307,6 @@ return function(code, ast, options)
end,
-- Continue
Continue = ()
wrap("loop", "repeat", "until true")
return "break"
end,
-- apply (below)
@ -291,9 +378,13 @@ return function(code, ast, options)
elseif #t == 1 then
return "{ " .. lua(t, "_lhs") .. " }"
else
return "{" .. indent() .. lua(t, "_lhs") .. unindent() .. "}"
return "{" .. indent() .. lua(t, "_lhs", nil, true) .. unindent() .. "}"
end
end,
-- TableCompr{ block }
TableCompr = (t)
return push("push", "self") .. "(function()" .. indent() .. "local self = {}" .. newline() .. lua(t[1]) .. newline() .. "return self" .. unindent() .. "end)()" .. pop("push")
end,
-- Op{ opid expr expr? }
Op = (t)
local r
@ -316,32 +407,57 @@ return function(code, ast, options)
Paren = (t)
return "(" .. lua(t[1]) .. ")"
end,
-- statexpr (below)
-- apply (below)
-- lhs (below)
-- statexpr --
_statexpr = (t, stat)
local hasPush = any(t, { "Push" }, func)
local r = "(function()" .. indent()
if hasPush then
r ..= push("push", "__PUSH__") .. "local __PUSH__ = {}" .. newline()
end
r ..= lua(t, stat)
if hasPush then
r ..= newline() .. "return unpack(__PUSH__)" .. pop("push")
end
r ..= unindent() .. "end)()"
return r
end,
-- DoExpr{ stat* }
DoExpr = (t)
return "(function()" .. indent() .. lua(t, "Do") .. unindent() .. "end)()"
if t[#t].tag == "Push" then -- convert final push to return
t[#t].tag = "Return"
end
return lua(t, "_statexpr", "Do")
end,
-- WhileExpr{ expr block }
WhileExpr = (t)
return "(function()" .. indent() .. lua(t, "While") .. unindent() .. "end)()"
return lua(t, "_statexpr", "While")
end,
-- RepeatExpr{ expr block }
-- RepeatExpr{ block expr }
RepeatExpr = (t)
return "(function()" .. indent() .. lua(t, "Repeat") .. unindent() .. "end)()"
return lua(t, "_statexpr", "Repeat")
end,
-- IfExpr{ (expr block)+ block? }
IfExpr = (t)
return "(function()" .. indent() .. lua(t, "If") .. unindent() .. "end)()"
for i=2, #t do -- convert final pushes to returns
local block = t[i]
if block[#block].tag == "Push" then
block[#block].tag = "Return"
end
end
return lua(t, "_statexpr", "If")
end,
-- FornumExpr{ ident expr expr expr? block }
FornumExpr = (t)
return "(function()" .. indent() .. lua(t, "Fornum") .. unindent() .. "end)()"
return lua(t, "_statexpr", "Fornum")
end,
-- Forin{ {ident+} {expr+} block }
ForninExpr = (t)
return "(function()" .. indent() .. lua(t, "Forin") .. unindent() .. "end)()"
-- ForinExpr{ {ident+} {expr+} block }
ForinExpr = (t)
return lua(t, "_statexpr", "Forin")
end,
-- apply (below)
-- lhs (below)
-- apply --
@ -356,13 +472,12 @@ return function(code, ast, options)
end,
-- lhs --
_lhs = (t, start)
start = start or 1
_lhs = (t, start=1, newlines)
local r
if t[start] then
r = lua(t[start])
for i=start+1, #t, 1 do
r ..= ", "..lua(t[i])
r ..= "," .. (newlines and newline() or " ") .. lua(t[i])
end
else
r = ""