diff --git a/moonscript/compile.lua b/moonscript/compile.lua index 947e6f2..d759f9a 100644 --- a/moonscript/compile.lua +++ b/moonscript/compile.lua @@ -69,6 +69,9 @@ end)() local Block_ Block_ = (function(_parent_0) local _base_0 = { + header = "do", + footer = "end", + delim = "\n", line_table = function(self) return self._posmap end, set = function(self, name, value) self._state[name] = value end, get = function(self, name) return self._state[name] end, @@ -145,7 +148,16 @@ Block_ = (function(_parent_0) local footer = flatten(self.footer) return(concat({ header, footer }, " ")) end - local body = pretty(self._lines, indent_char:rep(self.indent)) + local indent = indent_char:rep(self.indent) + if not self.delim then + for i = 1, #self._lines - 1 do + local left, right = self._lines[i], self._lines[i + 1] + if left:sub(-1) == ")" and right:sub(1, 1) == "(" then + self._lines[i] = self._lines[i] .. ";" + end + end + end + local body = indent .. concat(self._lines, (self.delim or "") .. "\n" .. indent) return concat({ header, body, indent_char:rep(self.indent - 1) .. (function() if self.next then return self.next:render() @@ -181,15 +193,19 @@ Block_ = (function(_parent_0) end, values = function(self, values, delim) delim = delim or ', ' - return concat((function() - local _moon_0 = {} - local _item_0 = values - for _index_0=1,#_item_0 do - local v = _item_0[_index_0] - table.insert(_moon_0, self:value(v)) - end - return _moon_0 - end)(), delim) + do + local _with_0 = Line() + _with_0:append_list((function() + local _moon_0 = {} + local _item_0 = values + for _index_0=1,#_item_0 do + local v = _item_0[_index_0] + table.insert(_moon_0, self:value(v)) + end + return _moon_0 + end)(), delim) + return _with_0 + end end, stm = function(self, node, ...) local fn = line_compile[ntype(node)] @@ -248,12 +264,6 @@ Block_ = (function(_parent_0) _base_0.__index = _base_0 local _class_0 = setmetatable({ __init = function(self, parent, header, footer) self.parent, self.header, self.footer = parent, header, footer - if not self.header then - self.header = "do" - end - if not self.footer then - self.footer = "end" - end self.line_offset = 1 self._lines = { } self._posmap = { } diff --git a/moonscript/compile.moon b/moonscript/compile.moon index f6b917e..8832ccc 100644 --- a/moonscript/compile.moon +++ b/moonscript/compile.moon @@ -43,10 +43,11 @@ class Line concat buff class Block_ - new: (@parent, @header, @footer) => - @header = "do" if not @header - @footer = "end" if not @footer + header: "do" + footer: "end" + delim: "\n" + new: (@parent, @header, @footer) => @line_offset = 1 @_lines = {} @@ -138,7 +139,16 @@ class Block_ footer = flatten @footer return concat {header, footer}, " " - body = pretty @_lines, indent_char\rep @indent + indent = indent_char\rep @indent + + -- inject semicolons for ambiguous lines + if not @delim + for i = 1, #@_lines - 1 + left, right = @_lines[i], @_lines[i+1] + if left\sub(-1) == ")" and right\sub(1,1) == "(" + @_lines[i] = @_lines[i]..";" + + body = indent .. concat @_lines, (@delim or "") .. "\n" .. indent concat { header, @@ -171,7 +181,8 @@ class Block_ values: (values, delim) => delim = delim or ', ' - concat [@value v for v in *values], delim + with Line! + \append_list [@value v for v in *values], delim stm: (node, ...) => fn = line_compile[ntype(node)] diff --git a/moonscript/compile/format.lua b/moonscript/compile/format.lua index 6631168..f3192ce 100644 --- a/moonscript/compile/format.lua +++ b/moonscript/compile/format.lua @@ -5,36 +5,6 @@ local itwos = util.itwos local Set, ntype = data.Set, data.ntype local concat, insert = table.concat, table.insert indent_char = " " -pretty = function(lines, indent) - indent = indent or "" - local render - render = function(line) - if type(line) == "table" then - return indent_char .. pretty(line, indent .. indent_char) - else - return line - end - end - lines = (function() - local _moon_0 = {} - local _item_0 = lines - for _index_0=1,#_item_0 do - local line = _item_0[_index_0] - table.insert(_moon_0, render(line)) - end - return _moon_0 - end)() - local fix - fix = function(i, left, k, right) - if left:sub(-1) == ")" and right:sub(1, 1) == "(" then - lines[i] = lines[i] .. ";" - end - end - for i, l, k, r in itwos(lines) do - fix(i, l, k, r) - end - return indent .. concat(lines, "\n" .. indent) -end returner = function(exp) if ntype(exp) == "chain" and exp[2] == "return" then local items = { "explist" } diff --git a/moonscript/compile/format.moon b/moonscript/compile/format.moon index 2772e72..01e447e 100644 --- a/moonscript/compile/format.moon +++ b/moonscript/compile/format.moon @@ -7,27 +7,10 @@ import itwos from util import Set, ntype from data import concat, insert from table -export indent_char, pretty, returner, moonlib, cascading, non_atomic, has_value, is_non_atomic +export indent_char, returner, moonlib, cascading, non_atomic, has_value, is_non_atomic export count_lines indent_char = " " -pretty = (lines, indent) -> - indent = indent or "" - render = (line) -> - if type(line) == "table" - indent_char..pretty(line, indent..indent_char) - else - line - - lines = [render line for line in *lines] - - -- add semicolons for ambiguities - fix = (i, left, k, right) -> - if left\sub(-1) == ")" and right\sub(1,1) == "(" - lines[i] = lines[i]..";" - fix(i,l, k,r) for i,l,k,r in itwos lines - - indent .. concat lines, "\n"..indent returner = (exp) -> if ntype(exp) == "chain" and exp[2] == "return" diff --git a/moonscript/compile/line.lua b/moonscript/compile/line.lua index 369f2f5..d38413d 100644 --- a/moonscript/compile/line.lua +++ b/moonscript/compile/line.lua @@ -14,6 +14,17 @@ line_compile = { local _, text = unpack(node) return self:add(text) end, + declare = function(self, node) + local _, names = unpack(node) + local undeclared = self:declare(names) + if #undeclared > 0 then + do + local _with_0 = self:line("local ") + _with_0:append_list(names, ", ") + return _with_0 + end + end + end, assign = function(self, node) local _, names, values = unpack(node) local undeclared = self:declare(names) @@ -234,27 +245,6 @@ line_compile = { find_special(unpack(entry)) end tbl[2] = final_properties - local def_scope = self:block() - local parent_loc = def_scope:free_name("parent") - def_scope:set("super", function(block, chain) - local calling_name = block:get("current_block") - local slice = (function() - local _moon_0 = {} - local _item_0 = chain - for _index_0=3,#_item_0 do - local item = _item_0[_index_0] - table.insert(_moon_0, item) - end - return _moon_0 - end)() - slice[1] = { "call", { "self", unpack(slice[1][2]) } } - return { - "chain", - parent_loc, - { "dot", calling_name }, - unpack(slice) - } - end) if not constructor then constructor = { "fndef", @@ -295,35 +285,57 @@ line_compile = { if #self_args > 0 then insert(body, 1, { "assign", dests, self_args }) end - local base_name = def_scope:free_name("base") - def_scope:add_line(("local %s ="):format(base_name), def_scope:value(tbl)) - def_scope:add_line(("%s.__index = %s"):format(base_name, base_name)) - local cls = def_scope:value({ "table", { { "__init", constructor } } }) - local cls_mt = def_scope:value({ "table", { { "__index", base_name }, { "__call", { - "fndef", - { "mt", "..." }, - "slim", - { { "raw", ("local self = setmetatable({}, %s)"):format(base_name) }, { "chain", "mt.__init", { "call", { "self", "..." } } }, "self" } - } } } }) - if parent_val ~= "" then - def_scope:stm({ "if", parent_loc, { { "chain", "setmetatable", { "call", { base_name, { + local parent_loc = self:free_name("parent", false) + local def_scope + do + local _with_0 = self:block() + if parent_val ~= "" then + local parent = self:value(parent_val) + end + _with_0:put_name(parent_loc) + _with_0.header = self:line("(function(", parent_loc, ")") + _with_0.footer = self:line("end)(", parent, ")") + _with_0:set("super", function(block, chain) + local calling_name = block:get("current_block") + local slice = (function() + local _moon_0 = {} + local _item_0 = chain + for _index_0=3,#_item_0 do + local item = _item_0[_index_0] + table.insert(_moon_0, item) + end + return _moon_0 + end)() + slice[1] = { "call", { "self", unpack(slice[1][2]) } } + return { + "chain", + parent_loc, + { "dot", calling_name }, + unpack(slice) + } + end) + local base_name = _with_0:init_free_var("base", tbl) + _with_0:stm({ "assign", { { "chain", base_name, { "dot", "__index" } } }, { base_name } }) + _with_0:stm({ "if", parent_loc, { { "chain", "setmetatable", { "call", { base_name, { "chain", "getmetatable", { "call", { parent_loc } }, { "dot", "__index" } } } } } } }) + local cls = { "table", { { "__init", constructor } } } + local cls_mt = { "table", { { "__index", base_name }, { "__call", { + "fndef", + { "mt", "..." }, + "slim", + { { "raw", ("local self = setmetatable({}, %s)"):format(base_name) }, { "chain", "mt.__init", { "call", { "self", "..." } } }, "self" } + } } } } + local cls_name = _with_0:init_free_var("class", { "chain", "setmetatable", { "call", { cls, cls_mt } } }) + _with_0:stm({ "assign", { { "chain", base_name, { "dot", "__class" } } }, { cls_name } }) + _with_0:stm({ "return", cls_name }) + def_scope = _with_0 end - local cls_name = def_scope:free_name("class") - def_scope:add_line(("local %s = setmetatable(%s, %s)"):format(cls_name, cls, cls_mt)) - def_scope:add_line(("%s.__class = %s"):format(base_name, cls_name)) - def_scope:add_line("return", cls_name) - if parent_val ~= "" then - parent_val = self:value(parent_val) - end - local def = concat({ ("(function(%s)\n"):format(parent_loc), def_scope:render(), ("\nend)(%s)"):format(parent_val) }) - self:add_line("local", name) - self:put_name(name) - return self:stm({ "assign", { name }, { def } }) + self:stm({ "declare", { name } }) + return self:line(name, " = ", def_scope) end, comprehension = function(self, node, action) local _, exp, clauses = unpack(node) diff --git a/moonscript/compile/line.moon b/moonscript/compile/line.moon index 715e61d..d64ee86 100644 --- a/moonscript/compile/line.moon +++ b/moonscript/compile/line.moon @@ -22,6 +22,13 @@ line_compile = _, text = unpack node @add text + declare: (node) => + _, names = unpack node + undeclared = @declare names + if #undeclared > 0 + with @line "local " + \append_list names, ", " + assign: (node) => _, names, values = unpack node @@ -146,7 +153,7 @@ line_compile = @put_name name for name in *names when type(name) == "string" nil - -- TODO convert this + -- fix newlines class: (node) => _, name, parent_val, tbl = unpack node @@ -163,17 +170,6 @@ line_compile = find_special unpack entry for entry in *tbl[2] tbl[2] = final_properties - def_scope = @block! - parent_loc = def_scope\free_name "parent" - - def_scope\set "super" (block, chain) -> - calling_name = block\get"current_block" - slice = [item for item in *chain[3:]] - -- inject self - slice[1] = {"call", {"self", unpack slice[1][2]}} - - {"chain", parent_loc, {"dot", calling_name}, unpack slice} - -- synthesize constructor if needed if not constructor constructor = {"fndef", {"..."}, "fat", { @@ -182,7 +178,7 @@ line_compile = }} }} - -- organize constructor + -- organize constructor arguments -- extract self arguments self_args = {} get_initializers = (arg) -> @@ -199,45 +195,61 @@ line_compile = dests = [{"self", name} for name in *self_args] insert body, 1, {"assign", dests, self_args} if #self_args > 0 - base_name = def_scope\free_name "base" - def_scope\add_line ("local %s =")\format(base_name), def_scope\value tbl - def_scope\add_line ("%s.__index = %s")\format(base_name, base_name) + -- now create the class's initialization block + parent_loc = @free_name "parent", false - cls = def_scope\value {"table", { - {"__init", constructor} - }} + def_scope = with @block! + parent = @value parent_val if parent_val != "" + \put_name parent_loc - cls_mt = def_scope\value {"table", { - {"__index", base_name} - {"__call", {"fndef", {"mt", "..."}, "slim", { - {"raw", ("local self = setmetatable({}, %s)")\format(base_name)} - {"chain", "mt.__init", {"call", {"self", "..."}}} - "self" - }}} - }} + .header = @line "(function(", parent_loc, ")" + .footer = @line "end)(", parent, ")" - if parent_val != "" - def_scope\stm {"if", parent_loc, + \set "super", (block, chain) -> + calling_name = block\get"current_block" + slice = [item for item in *chain[3:]] + -- inject self + slice[1] = {"call", {"self", unpack slice[1][2]}} + {"chain", parent_loc, {"dot", calling_name}, unpack slice} + + -- the metatable holding all the class methods + base_name = \init_free_var "base", tbl + \stm {"assign", { {"chain", base_name, {"dot", "__index"}} }, { base_name }} + + -- handle super class if there is one + \stm {"if", parent_loc, {{"chain", "setmetatable", {"call", {base_name, {"chain", "getmetatable", {"call", {parent_loc}}, {"dot", "__index"}}}}}}} - cls_name = def_scope\free_name "class" - def_scope\add_line ("local %s = setmetatable(%s, %s)")\format(cls_name, cls, cls_mt) - def_scope\add_line ("%s.__class = %s")\format base_name, cls_name - def_scope\add_line "return", cls_name + -- the class object that is returned + cls = {"table", { + {"__init", constructor} + }} - parent_val = @value parent_val if parent_val != "" + -- the class's meta table, gives us call and access to base methods + cls_mt = {"table", { + {"__index", base_name} + {"__call", {"fndef", {"mt", "..."}, "slim", { + {"raw", ("local self = setmetatable({}, %s)")\format(base_name)} + {"chain", "mt.__init", {"call", {"self", "..."}}} + "self" + }}} + }} - def = concat { - ("(function(%s)\n")\format(parent_loc) - def_scope\render() - ("\nend)(%s)")\format(parent_val) - } + cls_name = \init_free_var "class", { + "chain", "setmetatable", {"call", {cls, cls_mt}} + } - @add_line "local", name - @put_name name - @stm {"assign", {name}, {def}} + \stm {"assign" + {{"chain", base_name, {"dot", "__class"}}} + {cls_name} + } + + \stm {"return", cls_name} + + @stm {"declare", {name}} + @line name, " = ", def_scope comprehension: (node, action) => _, exp, clauses = unpack node diff --git a/moonscript/compile/value.lua b/moonscript/compile/value.lua index 5b675e4..654e748 100644 --- a/moonscript/compile/value.lua +++ b/moonscript/compile/value.lua @@ -50,20 +50,27 @@ value_compile = { return delim .. inner .. (delim_end or delim) end, ["if"] = function(self, node) - local func = self:block() - func:stm(node, returner) - return self:format("(function()", func:render(), "end)()") + do + local _with_0 = self:block("(function()", "end)()") + _with_0:stm(node, returner) + return _with_0 + end end, comprehension = function(self, node) local exp = node[2] - local func = self:block() - local tmp_name = func:free_name() - func:add_line("local", tmp_name, "= {}") - local action = func:block() - action:add_line(("table.insert(%s, %s)"):format(tmp_name, func:value(exp))) - func:stm(node, action) - func:add_line("return", tmp_name) - return self:format("(function()", func:render(), "end)()") + do + local _with_0 = self:block("(function()", "end()") + local tmp_name = _with_0:init_free_var("accum", { "table" }) + local action + do + local _with_1 = _with_1:block() + _with_1:stm({ "chain", "table.insert", { "call", { tmp_name, _with_1:value(exp) } } }) + action = _with_1 + end + _with_1:stm(node, action) + _with_1:stm({ "return", tmp_name }) + return _with_0 + end end, chain = function(self, node) local callee = node[2] @@ -81,34 +88,37 @@ value_compile = { chain_item = function(node) local t, arg = unpack(node) if t == "call" then - return "(" .. (self:values(arg)) .. ")" + return "(", self:values(arg), ")" elseif t == "index" then - return "[" .. (self:value(arg)) .. "]" + return "[", self:value(arg), "]" elseif t == "dot" then - return "." .. arg + return ".", arg elseif t == "colon" then - return ":" .. arg .. (chain_item(node[3])) + return ":", arg, chain_item(node[3]) + elseif t == "colon_stub" then + return error("Uncalled colon stub") else return error("Unknown chain action: " .. t) end end - local actions = (function() - local _moon_0 = {} + local actions + do + local _with_0 = self:line() local _item_0 = node for _index_0=3,#_item_0 do - local act = _item_0[_index_0] - table.insert(_moon_0, chain_item(act)) + local action = _item_0[_index_0] + _with_0:append(chain_item(action)) end - return _moon_0 - end)() + actions = _with_0 + end if ntype(callee) == "self" and node[3] and ntype(node[3]) == "call" then callee[1] = "self_colon" end local callee_value = self:name(callee) if ntype(callee) == "exp" then - callee_value = "(" .. callee_value .. ")" + callee_value = self:line("(", callee_value, ")") end - return self:name(callee) .. concat(actions) + return self:line(callee, actions) end, fndef = function(self, node) local _, args, arrow, block = unpack(node) @@ -128,50 +138,43 @@ value_compile = { end, table = function(self, node) local _, items = unpack(node) - local inner = self:block() - local _comp - _comp = function(i, tuple) - local out - if #tuple == 2 then - local key, value = unpack(tuple) - if type(key) == "string" and data.lua_keywords[key] then - key = { "string", '"', key } - end - local key_val = self:value(key) - if type(key) ~= "string" then - key = ("[%s]"):format(key_val) + do + local _with_0 = self:block("{", "}") + _with_0.delim = "," + local format_line + format_line = function(tuple) + if #tuple == 2 then + local key, value = unpack(tuple) + if type(key) == "string" and data.lua_keywords[key] then + key = { "string", '"', key } + end + local assign + if type(key) ~= "string" then + assign = self:line("[", _with_0:value(key), "]") + else + assign = key + end + _with_0:set("current_block", key) + local out = self:line(assign, " = ", _with_0:value(value)) + _with_0:set("current_block", nil) + return out else - key = key_val + return self:line(_with_0:value(tuple[1])) end - inner:set("current_block", key_val) - value = inner:value(value) - inner:set("current_block", nil) - out = ("%s = %s"):format(key, value) - else - out = inner:value(tuple[1]) end - if i ~= #items then - return out .. "," - else - return out + if items then + local _item_0 = items + for _index_0=1,#_item_0 do + local line = _item_0[_index_0] + _with_0:add(format_line(line)) + end end - end - local values = (function() - local _moon_0 = {} - for i, v in ipairs(items) do - table.insert(_moon_0, _comp(i, v)) - end - return _moon_0 - end)() - if #values > 3 then - return self:format("{", values, "}") - else - return "{ " .. (concat(values, " ")) .. " }" + return _with_0 end end, - minus = function(self, node) return "-" .. self:value(node[2]) end, - length = function(self, node) return "#" .. self:value(node[2]) end, - ["not"] = function(self, node) return "not " .. self:value(node[2]) end, + minus = function(self, node) return self:line("-", self:value(node[2])) end, + length = function(self, node) return self:line("#", self:value(node[2])) end, + ["not"] = function(self, node) return self:line("not ", self:value(node[2])) end, self = function(self, node) return "self." .. self:value(node[2]) end, self_colon = function(self, node) return "self:" .. self:value(node[2]) end } diff --git a/moonscript/compile/value.moon b/moonscript/compile/value.moon index 3943fa5..73553f5 100644 --- a/moonscript/compile/value.moon +++ b/moonscript/compile/value.moon @@ -39,31 +39,28 @@ value_compile = delim..inner..(delim_end or delim) if: (node) => - func = @block! - func\stm node, returner - @format "(function()", func\render!, "end)()" + with @block "(function()", "end)()" + \stm node, returner comprehension: (node) => exp = node[2] - func = @block! - tmp_name = func\free_name! - func\add_line "local", tmp_name, "= {}" + with @block "(function()", "end()" + tmp_name = \init_free_var "accum", {"table"} - action = func\block! - action\add_line ("table.insert(%s, %s)")\format(tmp_name, func\value exp) - func\stm node, action + -- TODO change this to function + action = with \block! + \stm {"chain", "table.insert", {"call", {tmp_name, \value exp}}} - func\add_line "return", tmp_name - - @format "(function()", func\render!, "end)()" + \stm node, action + \stm {"return", tmp_name} chain: (node) => callee = node[2] if callee == -1 - callee = @get"scope_var" - if not callee then error"Short-dot syntax must be called within a with block" + callee = @get "scope_var" + if not callee then error "Short-dot syntax must be called within a with block" sup = @get "super" if callee == "super" and sup @@ -72,25 +69,28 @@ value_compile = chain_item = (node) -> t, arg = unpack node if t == "call" - "("..(@values arg)..")" + "(", @values(arg), ")" elseif t == "index" - "["..(@value arg).."]" + "[", @value(arg), "]" elseif t == "dot" - "."..arg + ".", arg elseif t == "colon" - ":"..arg..(chain_item node[3]) + ":", arg, chain_item(node[3]) + elseif t == "colon_stub" + error "Uncalled colon stub" else error "Unknown chain action: "..t - actions = [chain_item act for act in *node[3:]] + actions = with @line! + \append chain_item action for action in *node[3:] if ntype(callee) == "self" and node[3] and ntype(node[3]) == "call" callee[1] = "self_colon" callee_value = @name callee - callee_value = "("..callee_value..")" if ntype(callee) == "exp" + callee_value = @line "(", callee_value, ")" if ntype(callee) == "exp" - return @name(callee)..concat(actions) + @line callee, actions fndef: (node) => _, args, arrow, block = unpack node @@ -104,46 +104,39 @@ value_compile = table: (node) => _, items = unpack node + with @block "{", "}" + .delim = "," - inner = @block! -- handle indent - _comp = (i, tuple) -> - out = if #tuple == 2 - key, value = unpack tuple + format_line = (tuple) -> + if #tuple == 2 + key, value = unpack tuple - if type(key) == "string" and data.lua_keywords[key] - key = {"string", '"', key} + if type(key) == "string" and data.lua_keywords[key] + key = {"string", '"', key} - key_val = @value key - key = if type(key) != "string" - ("[%s]")\format key_val + assign = if type(key) != "string" + @line "[", \value(key), "]" + else + key + + \set "current_block", key + out = @line assign, " = ", \value(value) + \set "current_block", nil + out else - key_val + @line \value tuple[1] - inner\set "current_block", key_val - value = inner\value value - inner\set "current_block", nil - - ("%s = %s")\format key, value - else - inner\value tuple[1] - - out.."," if i != #items else out - - values = [_comp i,v for i,v in ipairs items] - - if #values > 3 - @format "{", values, "}" - else - "{ "..(concat values, " ").." }" + if items + \add format_line line for line in *items minus: (node) => - "-"..@value node[2] + @line "-", @value node[2] length: (node) => - "#"..@value node[2] + @line "#", @value node[2] not: (node) => - "not "..@value node[2] + @line "not ", @value node[2] self: (node) => "self."..@value node[2] diff --git a/todo b/todo index b165479..1489070 100644 --- a/todo +++ b/todo @@ -10,6 +10,7 @@ differentiate root block and normal block - use `with` as an arugment to function (normal expression) - varargs that get put in a nested generated function aren't valid anymor: - variable declartion for classes is messed up (always local) +- class expressions, x = class extends Hello do new: => print "hello" - add all lines through Block