Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements for ast_to_src #30

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 129 additions & 59 deletions metalua/compiler/ast_to_src.mlua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

local M = { }
M.__index = M
M.__call = |self, ...| self:run(...)

local pp=require 'metalua.pprint'

Expand Down Expand Up @@ -126,7 +127,7 @@ local op_preprec = {
{ "concat" },
{ "add", "sub" },
{ "mul", "div", "mod" },
{ "unary", "not", "len" },
{ "unm", "not", "len" },
{ "pow" },
{ "index" } }

Expand All @@ -149,7 +150,9 @@ local op_symbol = {
div = " / ", mod = " % ", pow = " ^ ",
concat = " .. ", eq = " == ", ne = " ~= ",
lt = " < ", le = " <= ", ["and"] = " and ",
["or"] = " or ", ["not"] = "not ", len = "# " }
["or"] = " or ", ["not"] = "not ", len = "# ",
["unm"] = " - ",
}

--------------------------------------------------------------------------------
-- Accumulate the source representation of AST `node' in
Expand All @@ -160,24 +163,34 @@ local op_symbol = {
--------------------------------------------------------------------------------
function M:node (node)
assert (self~=M and self._acc)
if node==nil then self:acc'<<error>>'; return end
if not node.tag then -- tagless block.
self:list (node, self.nl)
else
local f = M[node.tag]
if type (f) == "function" then -- Delegate to tag method.
f (self, node, unpack (node))
elseif type (f) == "string" then -- tag string.
self:acc (f)
else -- No appropriate method, fall back to splice dumping.
-- This cannot happen in a plain Lua AST.
self:acc " -{ "
self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
self:acc " }"
if node==nil then self:acc'<<error>>'
elseif not self.custom_printer or not self.custom_printer (self, node) then
if not node.tag then -- tagless (henceunindented) block.
self:list (node, self.nl)
else
local f = M[node.tag]
if type (f) == "function" then -- Delegate to tag method.
f (self, node, unpack (node))
elseif type (f) == "string" then -- tag string.
self:acc (f)
else -- No appropriate method, fall back to splice dumping.
-- This cannot happen in a plain Lua AST.
self:acc " -{ "
self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1}), 80)
self:acc " }"
end
end
end
end

function M:block(body)
if not self.custom_printer or not self.custom_printer (self, body) then
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
end
end

--------------------------------------------------------------------------------
-- Convert every node in the AST list `list' passed as 1st arg.
-- `sep' is an optional separator to be accumulated between each list element,
Expand Down Expand Up @@ -225,9 +238,7 @@ end

function M:Do (node)
self:acc "do"
self:nlindent ()
self:list (node, self.nl)
self:nldedent ()
self:block (node)
self:acc "end"
end

Expand All @@ -244,9 +255,7 @@ function M:Set (node)
self:acc " ("
self:list (params, ", ", 2)
self:acc ")"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"

| `Set{ { lhs }, { `Function{ params, body } } } if is_idx_stack (lhs) ->
Expand All @@ -256,9 +265,7 @@ function M:Set (node)
self:acc " ("
self:list (params, ", ")
self:acc ")"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"

| `Set{ { `Id{ lhs1name } == lhs1, ... } == lhs, rhs }
Expand All @@ -267,16 +274,18 @@ function M:Set (node)
-- In that case, the spliced 1st variable must get parentheses,
-- to be distinguished from a statement splice.
-- This cannot happen in a plain Lua AST.
self:acc "("
self:node (lhs1)
self:acc ")"
if lhs[2] then -- more than one lhs variable
self:acc ", "
self:list (lhs, ", ", 2)
end
self:acc " = "
self:list (rhs, ", ")

| `Set{{`Paren{lhs}}, rhs} ->
self:Set(`Set{{lhs}, rhs})
| `Set{{`Paren{lhs}}, rhs, annot} ->
self:Set(`Set{{lhs}, rhs, annot})
| `Set{ lhs, rhs } ->
-- ``... = ...'', no syntax sugar --
self:list (lhs, ", ")
Expand All @@ -289,7 +298,7 @@ function M:Set (node)
local ell, a = lhs[i], annot[i]
self:node (ell)
if a then
self:acc ' #'
self:acc ' --'
self:node(a)
end
if i~=n then self:acc ', ' end
Expand All @@ -303,17 +312,13 @@ function M:While (node, cond, body)
self:acc "while "
self:node (cond)
self:acc " do"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"
end

function M:Repeat (node, body, cond)
self:acc "repeat"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "until "
self:node (cond)
end
Expand All @@ -325,16 +330,12 @@ function M:If (node)
self:acc (i==1 and "if " or "elseif ")
self:node (cond)
self:acc " then"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
end
-- odd number of children --> last one is an `else' clause --
if #node%2 == 1 then
self:acc "else"
self:nlindent ()
self:list (node[#node], self.nl)
self:nldedent ()
self:block (node[#node])
end
self:acc "end"
end
Expand All @@ -352,9 +353,7 @@ function M:Fornum (node, var, first, last)
self:node (node[4])
end
self:acc " do"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"
end

Expand All @@ -364,9 +363,7 @@ function M:Forin (node, vars, generators, body)
self:acc " in "
self:list (generators, ", ")
self:acc " do"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"
end

Expand All @@ -392,7 +389,7 @@ function M:Local (node, lhs, rhs, annots)
self:list (rhs, ", ")
end
else -- Can't create a local statement with 0 variables in plain Lua
self:acc (table.tostring (node, 'nohash', 80))
self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
end
end

Expand All @@ -406,43 +403,55 @@ function M:Localrec (node, lhs, rhs)
self:acc " ("
self:list (params, ", ")
self:acc ")"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"

| _ ->
-- Other localrec are unprintable ==> splice them --
-- This cannot happen in a plain Lua AST. --
self:acc "-{ "
self:acc (table.tostring (node, 'nohash', 80))
self:acc (pp.tostring (node, {metalua_tag=1, hide_hash=1, fix_indent=2}))
self:acc " }"
end
end

function M:Call (node, f)
-- single string or table literal arg ==> no need for parentheses. --
local parens
local parens, wrap
match node with
| `Call{`Function{...}, _} -> wrap = true
| `Call{`Function{...}} -> wrap = true
| _ -> wrap = false
end --[[ !! I doubt this should be here. Code that produces this AST should
produce a syntax error instead. ]]
match node with
| `Call{ _, `String{_} }
| `Call{ _, `Table{...}} -> parens = false
| _ -> parens = true
end
self:acc (wrap and "(" or "")
self:node (f)
self:acc (wrap and ")" or "")
self:acc (parens and " (" or " ")
self:list (node, ", ", 2) -- skip `f'.
self:acc (parens and ")")
end

function M:Invoke (node, f, method)
-- single string or table literal arg ==> no need for parentheses. --
local parens
local parens, wrap
match node with
| `Invoke{ `String{_}, ...} -> wrap = true
| _ -> wrap = false
end
match node with
| `Invoke{ _, _, `String{_} }
| `Invoke{ _, _, `Table{...}} -> parens = false
| _ -> parens = true
end
self:acc (wrap and "(" or "")
self:node (f)
self:acc (wrap and ")" or "")
self:acc ":"
self:acc (method[1])
self:acc (parens and " (" or " ")
Expand Down Expand Up @@ -488,9 +497,7 @@ function M:Function (node, params, body, annots)
self:list (params, ", ")
end
self:acc ")"
self:nlindent ()
self:list (body, self.nl)
self:nldedent ()
self:block (body)
self:acc "end"
end

Expand Down Expand Up @@ -602,11 +609,19 @@ function M:Index (node, table, key)
end
end

local function sanitize_name(name)
return name:gsub('%.', '__')
end

function M:Id (node, name)
if is_ident (name) then
self:acc (name)
else -- Unprintable identifier, fall back to splice representation.
-- This cannot happen in a plain Lua AST.
else -- Unprintable identifier
local sanitized_name = sanitize_name(name)
if is_ident(sanitized_name) then
self:acc(sanitized_name)
return nil
end
self:acc "-{`Id "
self:String (node, name)
self:acc "}"
Expand Down Expand Up @@ -690,4 +705,59 @@ for name, tag in pairs{ const='TConst', var='TVar', currently='TCurrently', just
end
end

return (|x| M.run(x))
function M:Label(node, name)
match name with
| `Id{n} -> self:Label(node, n)
| _ ->
if is_ident(name) then
self:acc "::"
self:acc(name)
self:acc "::"
else -- Unprintable identifier
local sanitized_name = sanitize_name(name)
if is_ident(sanitized_name) then
self:acc "::"
self:acc(sanitized_name)
self:acc "::"
return nil
end
self:acc "-{`Id "
self:String(node, name)
self:acc "}"
end
end
end

function M:Goto(node, name)
match name with
| `Id{n} -> self:Goto(node, n)
| _ ->
if is_ident(name) then
self:acc "goto "
self:acc(name)
else -- Unprintable identifier
local sanitized_name = sanitize_name(name)
if is_ident(sanitized_name) then
self:acc "goto "
self:acc(sanitized_name)
return nil
end
self:acc "-{`Goto "
self:String(node, name)
self:acc "}"
end
end
end

function M:Stat(node, body, ret)
self:acc "(function()"
self:nlindent()
self:list (body, self.nl)
self:nl ()
self:acc "return "
self:node (ret)
self:nldedent()
self:acc "end)()"
end

return M