diff --git a/moonscript/compile/line.lua b/moonscript/compile/line.lua index f211991..1c4951f 100644 --- a/moonscript/compile/line.lua +++ b/moonscript/compile/line.lua @@ -210,21 +210,12 @@ line_compile = { end, ["class"] = function(self, node) local _, name, tbl = unpack(node) - local mt_name = "_" .. name .. "_mt" - self:add_line("local", concat(self:declare({ name, mt_name }), ", ")) local constructor = nil - local meta_methods = { } local final_properties = { } - local overloaded_index = value local find_special find_special = function(name, value) if name == constructor_name then constructor = value - elseif name:match("^__%a") then - insert(meta_methods, { name, value }) - if name == "__index" then - overloaded_index = value - end else return insert(final_properties, { name, value }) end @@ -234,15 +225,12 @@ line_compile = { local entry = _item_0[_index_0] find_special(unpack(entry)) end - if not overloaded_index then - insert(meta_methods, { "__index", { "table", final_properties } }) - end - self:stm({ "assign", { mt_name }, { { "table", meta_methods } } }) + tbl[2] = final_properties if not constructor then constructor = { "fndef", { }, - "slim", + "fat", { } } end @@ -264,7 +252,7 @@ line_compile = { end return _moon_0 end)() - constructor[3] = "slim" + constructor[3] = "fat" local body = constructor[4] local dests = (function() local _moon_0 = {} @@ -278,9 +266,20 @@ line_compile = { if #self_args > 0 then insert(body, 1, { "assign", dests, self_args }) end - insert(body, 1, { "raw", ("local self = setmetatable({}, %s)"):format(mt_name) }) - insert(body, { "return", "self" }) - return self:stm({ "assign", { name }, { constructor } }) + local def_scope = self:block() + 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" } + } } } }) + def_scope:add_line(("return setmetatable(%s, %s)"):format(cls, cls_mt)) + local def = concat({ "(function()\n", (def_scope:render()), "\nend)()" }) + return self:stm({ "assign", { name }, { def } }) end, comprehension = function(self, node, action) local _, exp, clauses = unpack(node) diff --git a/moonscript/compile/line.moon b/moonscript/compile/line.moon index e20ebea..92f54ca 100644 --- a/moonscript/compile/line.moon +++ b/moonscript/compile/line.moon @@ -162,35 +162,24 @@ line_compile = ["class"]: (node) => _, name, tbl = unpack node - mt_name = "_"..name.."_mt" - @add_line "local", concat @declare({ name, mt_name }), ", " constructor = nil - meta_methods = {} final_properties = {} - overloaded_index = value - find_special = (name, value) -> if name == constructor_name constructor = value - elseif name:match("^__%a") - insert meta_methods, {name, value} - overloaded_index = value if name == "__index" else insert final_properties, {name, value} find_special unpack entry for entry in *tbl[2] - - if not overloaded_index - insert meta_methods, {"__index", {"table", final_properties}} - - @stm {"assign", {mt_name}, {{"table", meta_methods}}} + tbl[2] = final_properties -- synthesize constructor if not constructor - constructor = {"fndef", {}, "slim", {}} + constructor = {"fndef", {}, "fat", {}} + -- organize constructor -- extract self arguments self_args = {} get_initializers = (arg) -> @@ -200,15 +189,37 @@ line_compile = arg constructor[2] = [get_initializers arg for arg in *constructor[2]] - constructor[3] = "slim" + constructor[3] = "fat" body = constructor[4] dests = [{"self", name} for name in *self_args] insert body, 1, {"assign", dests, self_args} if #self_args > 0 - insert body, 1, {"raw", ("local self = setmetatable({}, %s)"):format(mt_name)} - insert body, {"return", "self"} - @stm {"assign", {name}, {constructor}} + -- insert body, 1, {"raw", ("local self = setmetatable({}, %s)"):format(mt_name)} + -- insert body, {"return", "self"} + + def_scope = @block() + 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) + + cls = def_scope:value {"table", { + {"__init", constructor} + }} + + 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" + }}} + }} + + def_scope:add_line ("return setmetatable(%s, %s)"):format(cls, cls_mt) + + def = concat { "(function()\n", (def_scope:render()), "\nend)()" } + @stm {"assign", {name}, {def}} comprehension: (node, action) => _, exp, clauses = unpack node diff --git a/moonscript/parse.lua b/moonscript/parse.lua index 7228152..b8d2cc6 100644 --- a/moonscript/parse.lua +++ b/moonscript/parse.lua @@ -340,7 +340,7 @@ local build_grammar = wrap(function() TableBlock = Break * #Cmt(Indent, advance_indent) * TableBlockInner * OutBlock / mark"table", - ClassDecl = key"class" * Name * TableBlock / mark"class", + ClassDecl = key"class" * Name * (key"extends" * Name)^-1 * TableBlock / mark"class", Export = key"export" * Ct(NameList) / mark"export", diff --git a/tests/outputs/class.lua b/tests/outputs/class.lua index e65d0e7..44421f4 100644 --- a/tests/outputs/class.lua +++ b/tests/outputs/class.lua @@ -1,11 +1,15 @@ -local Hello, _Hello_mt -_Hello_mt = { __tostring = function(self) return "hello world" end, __index = { hello = function(self) return print(self.test, self.world) end } } -Hello = function(test, world) - local self = setmetatable({}, _Hello_mt) - self.test, self.world = test, world - print("creating object..") - return self -end +local Hello = (function() + local _base_0 = { hello = function(self) return print(self.test, self.world) end, __tostring = function(self) return "hello world" end } + _base_0.__index = _base_0 + return setmetatable({ __init = function(self, test, world) + self.test, self.world = test, world + return print("creating object..") + end }, { __index = _base_0, __call = function(mt, ...) + local self = setmetatable({}, _base_0) + mt.__init(self, ...) + return self + end }) +end)() local x = Hello(1, 2) x:hello() print(x) \ No newline at end of file