diff --git a/moonscript/compile.lua b/moonscript/compile.lua index 06c2183..a0a02e8 100644 --- a/moonscript/compile.lua +++ b/moonscript/compile.lua @@ -358,6 +358,7 @@ Block_ = (function(_parent_0) end end, stm = function(self, node, ...) + node = transform.node(node) local fn = line_compile[ntype(node)] if not fn then if has_value(node) then diff --git a/moonscript/compile.moon b/moonscript/compile.moon index 035e625..47b4bc1 100644 --- a/moonscript/compile.moon +++ b/moonscript/compile.moon @@ -238,6 +238,7 @@ class Block_ \append_list [@value v for v in *values], delim stm: (node, ...) => + node = transform.node node fn = line_compile[ntype(node)] if not fn -- coerce value into statement diff --git a/moonscript/compile/line.lua b/moonscript/compile/line.lua index 1cf2a8f..7e2a2b0 100644 --- a/moonscript/compile/line.lua +++ b/moonscript/compile/line.lua @@ -70,8 +70,12 @@ line_compile = { end do local _with_0 = self:line() + local skip_values = false if #undeclared == #names and not has_fndef then _with_0:append(declare) + if #values == 0 then + skip_values = true + end else if #undeclared > 0 then self:add(declare) @@ -90,20 +94,22 @@ line_compile = { return _accum_0 end)(), ", ") end - _with_0:append(" = ") - _with_0:append_list((function() - local _accum_0 = { } - local _len_0 = 0 - do - local _item_0 = values - for _index_0 = 1, #_item_0 do - local v = _item_0[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = self:value(v) + if not skip_values then + _with_0:append(" = ") + _with_0:append_list((function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = values + for _index_0 = 1, #_item_0 do + local v = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:value(v) + end end - end - return _accum_0 - end)(), ", ") + return _accum_0 + end)(), ", ") + end return _with_0 end end @@ -412,242 +418,6 @@ line_compile = { self:declare(names) return nil end, - class = function(self, node) - local _, name, parent_val, tbl = unpack(node) - local constructor = nil - local final_properties = { } - do - local _item_0 = tbl[2] - for _index_0 = 1, #_item_0 do - local entry = _item_0[_index_0] - if entry[1] == constructor_name then - constructor = entry[2] - else - insert(final_properties, entry) - end - end - end - tbl[2] = final_properties - local parent_loc = self:free_name("parent", true) - if not constructor then - constructor = { - "fndef", - { - { - "..." - } - }, - { }, - "fat", - { - { - "if", - parent_loc, - { - { - "chain", - "super", - { - "call", - { - "..." - } - } - } - } - } - } - } - end - smart_node(constructor) - constructor.arrow = "fat" - local def_scope - do - local _with_0 = self:block() - if parent_val ~= "" then - parent_val = 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_val, ")") - _with_0:set("super", function(block, chain) - local calling_name = block:get("current_block") - local slice = (function() - local _accum_0 = { } - local _len_0 = 0 - do - local _item_0 = chain - for _index_0 = 3, #_item_0 do - local item = _item_0[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = item - end - end - return _accum_0 - end)() - slice[1] = { - "call", - { - "self", - unpack(slice[1][2]) - } - } - local act - if ntype(calling_name) ~= "value" then - act = "index" - else - act = "dot" - end - return { - "chain", - parent_loc, - { - act, - 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 - self:stm({ - "declare", - { - name - } - }) - return self:line(name, " = ", def_scope) - end, comprehension = function(self, node, action) local _, exp, clauses = unpack(node) if not action then @@ -698,5 +468,12 @@ line_compile = { end return _with_0 end + end, + run = function(self, code) + code:call(self) + return nil + end, + group = function(self, node) + return self:stms(node[2]) end } diff --git a/moonscript/compile/line.moon b/moonscript/compile/line.moon index 8998d94..bf04553 100644 --- a/moonscript/compile/line.moon +++ b/moonscript/compile/line.moon @@ -48,14 +48,17 @@ line_compile = i = i +1 with @line! + skip_values = false if #undeclared == #names and not has_fndef \append declare + skip_values = true if #values == 0 else @add declare if #undeclared > 0 \append_list [@value name for name in *names], ", " - \append " = " - \append_list [@value v for v in *values], ", " + if not skip_values + \append " = " + \append_list [@value v for v in *values], ", " update: (node) => _, name, op, exp = unpack node @@ -197,90 +200,6 @@ line_compile = @declare names nil - class: (node) => - _, name, parent_val, tbl = unpack node - - constructor = nil - final_properties = {} - - -- organize constructor and everything else - for entry in *tbl[2] - if entry[1] == constructor_name - constructor = entry[2] - else - insert final_properties, entry - - tbl[2] = final_properties - - -- now create the class's initialization block - parent_loc = @free_name "parent", true - - -- synthesize constructor if needed - if not constructor - constructor = {"fndef", {{"..."}}, {}, "fat", { - {"if", parent_loc, { - {"chain", "super", {"call", {"..."}}} - }} - }} - - smart_node constructor - constructor.arrow = "fat" - - def_scope = with @block! - parent_val = @value parent_val if parent_val != "" - \put_name parent_loc - - .header = @line "(function(", parent_loc, ")" - .footer = @line "end)(", parent_val, ")" - - \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]}} - - act = if ntype(calling_name) != "value" then "index" else "dot" - {"chain", parent_loc, {act, 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"}}}}}}} - - -- the class object that is returned - cls = {"table", { - {"__init", constructor} - }} - - -- 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" - }}} - }} - - cls_name = \init_free_var "class", { - "chain", "setmetatable", {"call", {cls, cls_mt}} - } - - \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 @@ -312,3 +231,10 @@ line_compile = \stms block \stm ret var if ret + run: (code) => + code\call self + nil + + group: (node) => + @stms node[2] + diff --git a/moonscript/transform.lua b/moonscript/transform.lua index d790248..86b23d2 100644 --- a/moonscript/transform.lua +++ b/moonscript/transform.lua @@ -2,7 +2,8 @@ module("moonscript.transform", package.seeall) local types = require("moonscript.types") local util = require("moonscript.util") local data = require("moonscript.data") -local ntype, build = types.ntype, types.build +local ntype, build, smart_node = types.ntype, types.build, types.smart_node +local insert = table.insert NameProxy = (function(_parent_0) local _base_0 = { get_name = function(self, scope) @@ -11,6 +12,39 @@ NameProxy = (function(_parent_0) end return self.name end, + chain = function(self, ...) + local items = { + ... + } + items = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = items + for _index_0 = 1, #_item_0 do + local i = _item_0[_index_0] + local _value_0 + if type(i) == "string" then + _value_0 = { + "dot", + i + } + else + _value_0 = i + end + if _value_0 ~= nil then + _len_0 = _len_0 + 1 + _accum_0[_len_0] = _value_0 + end + end + end + return _accum_0 + end)() + return build.chain({ + base = self, + unpack(items) + }) + end, __tostring = function(self) if self.name then return ("name<%s>"):format(self.name) @@ -39,7 +73,244 @@ NameProxy = (function(_parent_0) _base_0.__class = _class_0 return _class_0 end)() +local Run +Run = (function(_parent_0) + local _base_0 = { + call = function(self, state) + return self.fn(state) + end + } + _base_0.__index = _base_0 + if _parent_0 then + setmetatable(_base_0, getmetatable(_parent_0).__index) + end + local _class_0 = setmetatable({ + __init = function(self, fn) + self.fn = fn + self[1] = "run" + end + }, { + __index = _base_0, + __call = function(mt, ...) + local self = setmetatable({}, _base_0) + mt.__init(self, ...) + return self + end + }) + _base_0.__class = _class_0 + return _class_0 +end)() +local constructor_name = "new" local transformers = { + class = function(node) + local _, name, parent_val, tbl = unpack(node) + local constructor = nil + local properties = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = tbl[2] + for _index_0 = 1, #_item_0 do + local entry = _item_0[_index_0] + local _value_0 + if entry[1] == constructor_name then + constructor = entry[2] + _value_0 = nil + else + _value_0 = entry + end + if _value_0 ~= nil then + _len_0 = _len_0 + 1 + _accum_0[_len_0] = _value_0 + end + end + end + return _accum_0 + end)() + tbl[2] = properties + local parent_cls_name = NameProxy("parent") + local base_name = NameProxy("base") + local self_name = NameProxy("self") + local cls_name = NameProxy("class") + if not constructor then + constructor = build.fndef({ + args = { + { + "..." + } + }, + arrow = "fat", + body = { + build["if"]({ + cond = parent_cls_name, + ["then"] = { + build.chain({ + base = "super", + { + "call", + { + "..." + } + } + }) + } + }) + } + }) + else + smart_node(constructor) + constructor.arrow = "fat" + end + local cls = build.table({ + { + "__init", + constructor + } + }) + local cls_mt = build.table({ + { + "__index", + base_name + }, + { + "__call", + build.fndef({ + args = { + { + "cls" + }, + { + "..." + } + }, + body = { + build.assign_one(self_name, build.chain({ + base = "setmetatable", + { + "call", + { + "{}", + base_name + } + } + })), + build.chain({ + base = "cls.__init", + { + "call", + { + self_name, + "..." + } + } + }), + self_name + } + }) + } + }) + cls = build.chain({ + base = "setmetatable", + { + "call", + { + cls, + cls_mt + } + } + }) + local value = nil + do + local _with_0 = build + value = _with_0.block_exp({ + Run(function(self) + return self:set("super", function(block, chain) + local calling_name = block:get("current_block") + local slice = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = chain + for _index_0 = 3, #_item_0 do + local item = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = item + end + end + return _accum_0 + end)() + slice[1] = { + "call", + { + "self", + unpack(slice[1][2]) + } + } + local act + if ntype(calling_name) ~= "value" then + act = "index" + else + act = "dot" + end + return { + "chain", + parent_cls_name, + { + act, + calling_name + }, + unpack(slice) + } + end) + end), + _with_0.assign_one(parent_cls_name, parent_val == "" and "nil" or parent_val), + _with_0.assign_one(base_name, tbl), + _with_0.assign_one(base_name:chain("__index"), base_name), + build["if"]({ + cond = parent_cls_name, + ["then"] = { + _with_0.chain({ + base = "setmetatable", + { + "call", + { + base_name, + _with_0.chain({ + base = "getmetatable", + { + "call", + { + parent_cls_name + } + }, + { + "dot", + "__index" + } + }) + } + } + }) + } + }), + _with_0.assign_one(cls_name, cls), + _with_0.assign_one(base_name:chain("__class"), cls_name), + cls_name + }) + value = _with_0.group({ + _with_0.assign_one(name), + _with_0.assign({ + names = { + name + }, + values = { + value + } + }) + }) + end + return value + end, chain = function(node) local stub = node[#node] if type(stub) == "table" and stub[1] == "colon_stub" then diff --git a/moonscript/transform.moon b/moonscript/transform.moon index 5ed1256..9c2cbe1 100644 --- a/moonscript/transform.moon +++ b/moonscript/transform.moon @@ -5,7 +5,8 @@ types = require "moonscript.types" util = require "moonscript.util" data = require "moonscript.data" -import ntype, build from types +import ntype, build, smart_node from types +import insert from table export node, NameProxy @@ -18,13 +19,144 @@ class NameProxy @name = scope\free_name @prefix, true @name + chain: (...) => + items = {...} -- todo: fix ... propagation + items = for i in *items + if type(i) == "string" + {"dot", i} + else + i + + build.chain { + base: self + unpack items + } + __tostring: => if @name ("name<%s>")\format @name else ("name")\format @prefix +class Run + new: (@fn) => + self[1] = "run" + + call: (state) => + self.fn state + +constructor_name = "new" + transformers = { + class: (node) -> + _, name, parent_val, tbl = unpack node + + constructor = nil + properties = for entry in *tbl[2] + if entry[1] == constructor_name + constructor = entry[2] + nil + else + entry + + tbl[2] = properties + + parent_cls_name = NameProxy "parent" + base_name = NameProxy "base" + self_name = NameProxy "self" + cls_name = NameProxy "class" + + if not constructor + constructor = build.fndef { + args: {{"..."}} + arrow: "fat" + body: { + build["if"] { + cond: parent_cls_name + then: { + build.chain { base: "super", {"call", {"..."}} } + } + } + } + } + else + smart_node constructor + constructor.arrow = "fat" + + cls = build.table { + {"__init", constructor} + } + + cls_mt = build.table { + {"__index", base_name} + {"__call", build.fndef { + args: {{"cls"}, {"..."}} + body: { + build.assign_one self_name, build.chain { + base: "setmetatable" + {"call", {"{}", base_name}} + } + build.chain { + base: "cls.__init" + {"call", {self_name, "..."}} + } + self_name + } + }} + } + + cls = build.chain { + base: "setmetatable" + {"call", {cls, cls_mt}} + } + + value = nil + with build + value = .block_exp { + Run => + @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]}} + + act = if ntype(calling_name) != "value" then "index" else "dot" + {"chain", parent_cls_name, {act, calling_name}, unpack slice} + + .assign_one parent_cls_name, parent_val == "" and "nil" or parent_val + .assign_one base_name, tbl + .assign_one base_name\chain"__index", base_name + + build["if"] { + cond: parent_cls_name + then: { + .chain { + base: "setmetatable" + {"call", {base_name, .chain { + base: "getmetatable" + {"call", {parent_cls_name}} + {"dot", "__index"} + }}} + } + } + } + + .assign_one cls_name, cls + .assign_one base_name\chain"__class", cls_name + + cls_name + } + + value = .group { + .assign_one name + .assign { + names: {name} + values: {value} + } + } + + value + -- pull out colon chain chain: (node) -> stub = node[#node] diff --git a/moonscript/types.lua b/moonscript/types.lua index 0436263..dad191c 100644 --- a/moonscript/types.lua +++ b/moonscript/types.lua @@ -38,6 +38,16 @@ local node_types = { "values", t } + }, + ["if"] = { + { + "cond", + t + }, + { + "then", + t + } } } local build_table @@ -85,6 +95,28 @@ make_builder = function(name) end build = nil build = setmetatable({ + group = function(body) + return { + "group", + body + } + end, + assign_one = function(name, value) + return build.assign({ + names = { + name + }, + values = { + value + } + }) + end, + table = function(tbl) + return { + "table", + tbl + } + end, block_exp = function(body) local fn = build.fndef({ body = body diff --git a/moonscript/types.moon b/moonscript/types.moon index e817ff0..495d7e9 100644 --- a/moonscript/types.moon +++ b/moonscript/types.moon @@ -26,6 +26,10 @@ node_types = { {"names", t} {"values", t} } + if: { + {"cond", t} + {"then", t} + } } build_table = -> @@ -55,6 +59,15 @@ make_builder = (name) -> build = nil build = setmetatable { + group: (body) -> + {"group", body} + assign_one: (name, value) -> + build.assign { + names: {name} + values: {value} + } + table: (tbl) -> + {"table", tbl} block_exp: (body) -> fn = build.fndef body: body build.chain { base: {"parens", fn}, {"call", {}} } diff --git a/tests/outputs/class.lua b/tests/outputs/class.lua index 163eb61..0d7eeae 100644 --- a/tests/outputs/class.lua +++ b/tests/outputs/class.lua @@ -1,5 +1,6 @@ local Hello -Hello = (function(_parent_0) +Hello = (function() + local _parent_0 = nil local _base_0 = { hello = function(self) return print(self.test, self.world) @@ -19,10 +20,10 @@ Hello = (function(_parent_0) end }, { __index = _base_0, - __call = function(mt, ...) - local self = setmetatable({}, _base_0) - mt.__init(self, ...) - return self + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 end }) _base_0.__class = _class_0 @@ -32,7 +33,8 @@ local x = Hello(1, 2) x:hello() print(x) local Simple -Simple = (function(_parent_0) +Simple = (function() + local _parent_0 = nil local _base_0 = { cool = function(self) return print("cool") @@ -50,17 +52,18 @@ Simple = (function(_parent_0) end }, { __index = _base_0, - __call = function(mt, ...) - local self = setmetatable({}, _base_0) - mt.__init(self, ...) - return self + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 end }) _base_0.__class = _class_0 return _class_0 end)() local Yikes -Yikes = (function(_parent_0) +Yikes = (function() + local _parent_0 = Simple local _base_0 = { } _base_0.__index = _base_0 if _parent_0 then @@ -72,19 +75,20 @@ Yikes = (function(_parent_0) end }, { __index = _base_0, - __call = function(mt, ...) - local self = setmetatable({}, _base_0) - mt.__init(self, ...) - return self + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 end }) _base_0.__class = _class_0 return _class_0 -end)(Simple) +end)() x = Yikes() x:cool() local Hi -Hi = (function(_parent_0) +Hi = (function() + local _parent_0 = nil local _base_0 = { cool = function(self, num) return print("num", num) @@ -100,16 +104,18 @@ Hi = (function(_parent_0) end }, { __index = _base_0, - __call = function(mt, ...) - local self = setmetatable({}, _base_0) - mt.__init(self, ...) - return self + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 end }) _base_0.__class = _class_0 return _class_0 end)() -Simple = (function(_parent_0) +Simple = +Simple = (function() + local _parent_0 = Hi local _base_0 = { cool = function(self) return _parent_0.cool(self, 120302) @@ -125,20 +131,21 @@ Simple = (function(_parent_0) end }, { __index = _base_0, - __call = function(mt, ...) - local self = setmetatable({}, _base_0) - mt.__init(self, ...) - return self + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 end }) _base_0.__class = _class_0 return _class_0 -end)(Hi) +end)() x = Simple() x:cool() print(x.__class == Simple) local Okay -Okay = (function(_parent_0) +Okay = (function() + local _parent_0 = nil local _base_0 = { something = 20323 } @@ -154,10 +161,10 @@ Okay = (function(_parent_0) end }, { __index = _base_0, - __call = function(mt, ...) - local self = setmetatable({}, _base_0) - mt.__init(self, ...) - return self + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 end }) _base_0.__class = _class_0 diff --git a/todo b/todo index b29fb1c..c51ebb5 100644 --- a/todo +++ b/todo @@ -1,5 +1,8 @@ # TODO +- error with stray comma at end of line +- ugly error if attempting to assign to rvalue like #hello + - dump node in mark, see why values are being parsed multiple times if hello else world