super for classes, synthesized init calls super

This commit is contained in:
leaf corcoran 2011-06-19 12:07:01 -07:00
parent 885682fe32
commit 27836f1d80
7 changed files with 306 additions and 200 deletions

View File

@ -209,7 +209,7 @@ line_compile = {
return nil
end,
["class"] = function(self, node)
local _, name, parent, tbl = unpack(node)
local _, name, parent_val, tbl = unpack(node)
local constructor = nil
local final_properties = { }
local find_special
@ -226,12 +226,33 @@ 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 = {}
for i, item in ipairs(chain) do
if i > 2 then
table.insert(_moon_0, item)
end
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",
{ },
{ "..." },
"fat",
{ }
{ { "if", parent_loc, { { "chain", "super", { "call", { "..." } } } } } }
}
end
local self_args = { }
@ -266,7 +287,6 @@ line_compile = {
if #self_args > 0 then
insert(body, 1, { "assign", dests, self_args })
end
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))
@ -277,20 +297,21 @@ line_compile = {
"slim",
{ { "raw", ("local self = setmetatable({}, %s)"):format(base_name) }, { "chain", "mt.__init", { "call", { "self", "..." } } }, "self" }
} } } })
local parent_var = def_scope:free_name("parent")
if parent ~= "" then
def_scope:stm({ "if", parent_var, { { "chain", "setmetatable", { "call", { base_name, {
if parent_val ~= "" then
def_scope:stm({ "if", parent_loc, { { "chain", "setmetatable", { "call", { base_name, {
"chain",
"getmetatable",
{ "call", { parent_var } },
{ "call", { parent_loc } },
{ "dot", "__index" }
} } } } } })
end
def_scope:add_line(("return setmetatable(%s, %s)"):format(cls, cls_mt))
if parent ~= "" then
parent = self:value(parent)
if parent_val ~= "" then
parent_val = self:value(parent_val)
end
local def = concat({ ("(function(%s)\n"):format(parent_var), (def_scope:render()), ("\nend)(%s)"):format(parent) })
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 } })
end,
comprehension = function(self, node, action)

View File

@ -161,7 +161,7 @@ line_compile =
nil
["class"]: (node) =>
_, name, parent, tbl = unpack node
_, name, parent_val, tbl = unpack node
constructor = nil
final_properties = {}
@ -175,9 +175,24 @@ line_compile =
find_special unpack entry for entry in *tbl[2]
tbl[2] = final_properties
-- synthesize constructor
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 i, item in ipairs chain when i > 2]
-- 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", {}}
constructor = {"fndef", {"..."}, "fat", {
{"if", parent_loc, {
{"chain", "super", {"call", {"..."}}}
}}
}}
-- organize constructor
-- extract self arguments
@ -192,10 +207,10 @@ line_compile =
constructor[3] = "fat"
body = constructor[4]
-- insert self assigning arguments
dests = [{"self", name} for name in *self_args]
insert body, 1, {"assign", dests, self_args} if #self_args > 0
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)
@ -213,23 +228,24 @@ line_compile =
}}}
}}
parent_var = def_scope:free_name "parent"
if parent != ""
def_scope:stm {"if", parent_var,
if parent_val != ""
def_scope:stm {"if", parent_loc,
{{"chain", "setmetatable", {"call",
{base_name, {"chain", "getmetatable",
{"call", {parent_var}}, {"dot", "__index"}}}}}}}
{"call", {parent_loc}}, {"dot", "__index"}}}}}}}
def_scope:add_line ("return setmetatable(%s, %s)"):format(cls, cls_mt)
parent = @value parent if parent != ""
parent_val = @value parent_val if parent_val != ""
def = concat {
("(function(%s)\n"):format(parent_var)
("(function(%s)\n"):format(parent_loc)
(def_scope:render())
("\nend)(%s)"):format(parent)
("\nend)(%s)"):format(parent_val)
}
@add_line "local", name
@put_name name
@stm {"assign", {name}, {def}}
comprehension: (node, action) =>

View File

@ -63,6 +63,10 @@ local value_compile = {
end,
chain = function(self, node)
local callee = node[2]
local sup = self:get("super")
if callee == "super" and sup then
return(self:value(sup(self, node)))
end
local chain_item
chain_item = function(node)
local t, arg = unpack(node)
@ -125,12 +129,16 @@ local value_compile = {
local out
if #tuple == 2 then
local key, value = unpack(tuple)
local key_val = self:value(key)
if type(key) ~= "string" then
key = ("[%s]"):format(self:value(key))
key = ("[%s]"):format(key_val)
else
key = self:value(key)
key = key_val
end
out = ("%s = %s"):format(key, inner:value(value))
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
@ -159,172 +167,176 @@ local value_compile = {
self = function(self, node) return "self." .. self:value(node[2]) end,
self_colon = function(self, node) return "self:" .. self:value(node[2]) end
}
local block_t = { }
local Block
Block = function(parent)
local indent = parent and parent.indent + 1 or 0
local b = setmetatable({ _lines = { }, _names = { }, parent = parent }, block_t)
b:set_indent(indent)
return b
end
local B = {
set_indent = function(self, depth)
self.indent = depth
self.lead = indent_char:rep(self.indent)
end,
declare = function(self, names)
local undeclared = (function()
local _moon_0 = {}
local _item_0 = names
Block = (function(_parent_0)
local _base_0 = {
set = function(self, name, value) self._state[name] = value end,
get = function(self, name) return self._state[name] end,
set_indent = function(self, depth)
self.indent = depth
self.lead = indent_char:rep(self.indent)
end,
declare = function(self, names)
local undeclared = (function()
local _moon_0 = {}
local _item_0 = names
for _index_0=1,#_item_0 do
local name = _item_0[_index_0]
if type(name) == "string" and not self:has_name(name) then
table.insert(_moon_0, name)
end
end
return _moon_0
end)()
local _item_0 = undeclared
for _index_0=1,#_item_0 do
local name = _item_0[_index_0]
if type(name) == "string" and not self:has_name(name) then
table.insert(_moon_0, name)
end
self:put_name(name)
end
return undeclared
end,
put_name = function(self, name) self._names[name] = true end,
has_name = function(self, name) return self._names[name] end,
free_name = function(self, prefix)
prefix = prefix or "moon"
local searching = true
local name, i = nil, 0
while searching do
name = concat({ "", prefix, i }, "_")
i = i + 1
searching = self:has_name(name)
end
return _moon_0
end)()
local _item_0 = undeclared
for _index_0=1,#_item_0 do
local name = _item_0[_index_0]
self:put_name(name)
end
return undeclared
end,
put_name = function(self, name) self._names[name] = true end,
has_name = function(self, name)
if self._names[name] then
return true
elseif self.parent then
return self.parent:has_name(name)
else
return false
end
end,
free_name = function(self, prefix)
prefix = prefix or "moon"
local searching = true
local name, i = nil, 0
while searching do
name = concat({ "", prefix, i }, "_")
i = i + 1
searching = self:has_name(name)
end
self:put_name(name)
return name
end,
add_lines = function(self, lines)
local _item_0 = lines
for _index_0=1,#_item_0 do
local line = _item_0[_index_0]
insert(self._lines, line)
end
return nil
end,
add_line = function(self, ...)
local args = { ... }
local line
if #args == 1 then
line = args[1]
else
line = concat(args, " ")
end
return insert(self._lines, line)
end,
push = function(self) self._names = setmetatable({ }, { __index = self._names }) end,
pop = function(self) self._names = getmetatable(self._names).__index end,
format = function(self, ...) return pretty({ ... }, self.lead) end,
render = function(self)
local out = pretty(self._lines, self.lead)
if self.indent > 0 then
out = indent_char .. out
end
return out
end,
block = function(self, node) return Block(self) end,
is_stm = function(self, node) return line_compile[ntype(node)] ~= nil end,
is_value = function(self, node)
local t = ntype(node)
return value_compile[t] ~= nil or t == "value"
end,
name = function(self, node) return self:value(node) end,
value = function(self, node, ...)
if type(node) ~= "table" then
return(tostring(node))
end
local fn = value_compile[node[1]]
if not fn then
error("Failed to compile value: " .. dump.value(node))
end
return fn(self, node, ...)
end,
values = function(self, values, delim)
delim = delim or ', '
return concat((function()
local _moon_0 = {}
local _item_0 = values
return name
end,
add_lines = function(self, lines)
local _item_0 = lines
for _index_0=1,#_item_0 do
local v = _item_0[_index_0]
table.insert(_moon_0, self:value(v))
local line = _item_0[_index_0]
insert(self._lines, line)
end
return _moon_0
end)(), delim)
end,
stm = function(self, node, ...)
local fn = line_compile[ntype(node)]
if not fn then
if has_value(node) then
return self:stm({ "assign", { "_" }, { node } })
return nil
end,
add_line = function(self, ...)
local args = { ... }
local line
if #args == 1 then
line = args[1]
else
return self:add_line(self:value(node))
line = concat(args, " ")
end
else
local out = fn(self, node, ...)
if out then
return self:add_line(out)
return insert(self._lines, line)
end,
push = function(self) self._names = setmetatable({ }, { __index = self._names }) end,
pop = function(self) self._names = getmetatable(self._names).__index end,
format = function(self, ...) return pretty({ ... }, self.lead) end,
render = function(self)
local out = pretty(self._lines, self.lead)
if self.indent > 0 then
out = indent_char .. out
end
end
end,
ret_stms = function(self, stms, ret)
if not ret then
ret = returner
end
local i = 1
while i < #stms do
self:stm(stms[i])
i = i + 1
end
local last_exp = stms[i]
if last_exp then
if cascading[ntype(last_exp)] then
self:stm(last_exp, ret)
elseif self:is_value(last_exp) then
local line = ret(stms[i])
if self:is_stm(line) then
self:stm(line)
return out
end,
block = function(self, node) return Block(self) end,
is_stm = function(self, node) return line_compile[ntype(node)] ~= nil end,
is_value = function(self, node)
local t = ntype(node)
return value_compile[t] ~= nil or t == "value"
end,
name = function(self, node) return self:value(node) end,
value = function(self, node, ...)
if type(node) ~= "table" then
return(tostring(node))
end
local fn = value_compile[node[1]]
if not fn then
error("Failed to compile value: " .. dump.value(node))
end
return fn(self, node, ...)
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)
end,
stm = function(self, node, ...)
local fn = line_compile[ntype(node)]
if not fn then
if has_value(node) then
return self:stm({ "assign", { "_" }, { node } })
else
error("got a value from implicit return")
return self:add_line(self:value(node))
end
else
self:stm(last_exp)
local out = fn(self, node, ...)
if out then
return self:add_line(out)
end
end
end
return nil
end,
stms = function(self, stms, ret)
if ret then
self:ret_stms(stms, ret)
else
local _item_0 = stms
for _index_0=1,#_item_0 do
local stm = _item_0[_index_0]
self:stm(stm)
end,
ret_stms = function(self, stms, ret)
if not ret then
ret = returner
end
local i = 1
while i < #stms do
self:stm(stms[i])
i = i + 1
end
local last_exp = stms[i]
if last_exp then
if cascading[ntype(last_exp)] then
self:stm(last_exp, ret)
elseif self:is_value(last_exp) then
local line = ret(stms[i])
if self:is_stm(line) then
self:stm(line)
else
error("got a value from implicit return")
end
else
self:stm(last_exp)
end
end
return nil
end,
stms = function(self, stms, ret)
if ret then
self:ret_stms(stms, ret)
else
local _item_0 = stms
for _index_0=1,#_item_0 do
local stm = _item_0[_index_0]
self:stm(stm)
end
end
return nil
end
return nil
end
}
block_t.__index = B
}
_base_0.__index = _base_0
return setmetatable({ __init = function(self, parent)
self.parent = parent
self:set_indent(self.parent and self.parent.indent + 1 or 0)
self._lines = { }
self._names = { }
self._state = { }
if self.parent then
setmetatable(self._state, { __index = self.parent._state })
return setmetatable(self._names, { __index = self.parent._names })
end
end }, { __index = _base_0, __call = function(mt, ...)
local self = setmetatable({}, _base_0)
mt.__init(self, ...)
return self
end })
end)()
local build_compiler
build_compiler = function()
Block(nil)

View File

@ -61,6 +61,10 @@ value_compile =
chain: (node) =>
callee = node[2]
sup = @get "super"
if callee == "super" and sup
return @value sup self, node
chain_item = (node) ->
t, arg = unpack node
if t == "call"
@ -109,11 +113,18 @@ value_compile =
_comp = (i, tuple) ->
out = if #tuple == 2
key, value = unpack tuple
key_val = @value key
key = if type(key) != "string"
("[%s]"):format @value key
("[%s]"):format key_val
else
@value key
("%s = %s"):format key, inner:value value
key_val
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]
@ -141,18 +152,23 @@ value_compile =
self_colon: (node) =>
"self:"..@value node[2]
class Block
new: (@parent) =>
@set_indent @parent and @parent.indent + 1 or 0
@_lines = {}
@_names = {}
@_state = {}
block_t = {}
Block = (parent) ->
indent = parent and parent.indent + 1 or 0
b = setmetatable {
_lines: {}, _names: {}, parent: parent
}, block_t
if @parent
setmetatable @_state, { __index: @parent._state }
setmetatable @_names, { __index: @parent._names }
b:set_indent indent
b
set: (name, value) =>
@_state[name] = value
get: (name) =>
@_state[name]
B =
set_indent: (depth) =>
@indent = depth
@lead = indent_char:rep @indent
@ -166,12 +182,7 @@ B =
@_names[name] = true
has_name: (name) =>
if @_names[name]
true
elseif @parent
@parent:has_name name
else
false
@_names[name]
free_name: (prefix) =>
prefix = prefix or "moon"
@ -278,8 +289,6 @@ B =
@stm stm for stm in *stms
nil
block_t.__index = B
build_compiler = ->
Block(nil)
setmetatable {}, { __index: compiler_index }

View File

@ -133,7 +133,6 @@ local build_grammar = wrap(function()
return Space * C(word)
end
local function sym(chars)
return Space * chars
end

View File

@ -20,3 +20,20 @@ class Yikes extends Simple
x = Yikes()
x:cool()
class Hi
new: (arg) =>
print "init arg", arg
cool: (num) =>
print "num", num
class Simple extends Hi
new: => super "man"
cool: => super 120302
x = Simple()
x:cool()

View File

@ -1,4 +1,5 @@
local Hello = (function(_parent_0)
local Hello
Hello = (function(_parent_0)
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)
@ -13,16 +14,22 @@ end)()
local x = Hello(1, 2)
x:hello()
print(x)
local Simple = (function(_parent_0)
local Simple
Simple = (function(_parent_0)
local _base_0 = { cool = function(self) return print("cool") end }
_base_0.__index = _base_0
return setmetatable({ __init = function(self) end }, { __index = _base_0, __call = function(mt, ...)
return setmetatable({ __init = function(self, ...)
if _parent_0 then
return _parent_0.__init(self, ...)
end
end }, { __index = _base_0, __call = function(mt, ...)
local self = setmetatable({}, _base_0)
mt.__init(self, ...)
return self
end })
end)()
local Yikes = (function(_parent_0)
local Yikes
Yikes = (function(_parent_0)
local _base_0 = { }
_base_0.__index = _base_0
if _parent_0 then
@ -35,4 +42,29 @@ local Yikes = (function(_parent_0)
end })
end)(Simple)
x = Yikes()
x:cool()
local Hi
Hi = (function(_parent_0)
local _base_0 = { cool = function(self, num) return print("num", num) end }
_base_0.__index = _base_0
return setmetatable({ __init = function(self, arg) return print("init arg", arg) end }, { __index = _base_0, __call = function(mt, ...)
local self = setmetatable({}, _base_0)
mt.__init(self, ...)
return self
end })
end)()
local Simple
Simple = (function(_parent_0)
local _base_0 = { cool = function(self) return _parent_0.cool(self, 120302) end }
_base_0.__index = _base_0
if _parent_0 then
setmetatable(_base_0, getmetatable(_parent_0).__index)
end
return setmetatable({ __init = function(self) return _parent_0.__init(self, "man") end }, { __index = _base_0, __call = function(mt, ...)
local self = setmetatable({}, _base_0)
mt.__init(self, ...)
return self
end })
end)(Hi)
x = Simple()
x:cool()