add destructuring assignment

This commit is contained in:
leaf corcoran 2012-11-27 23:46:32 -08:00
parent 3adaed672e
commit 89f4a83f26
11 changed files with 681 additions and 174 deletions

View File

@ -5,7 +5,11 @@ require("moonscript.compile.format")
require("moonscript.compile.statement")
require("moonscript.compile.value")
local transform = require("moonscript.transform")
local NameProxy, LocalName = transform.NameProxy, transform.LocalName
local NameProxy, LocalName
do
local _table_0 = require("moonscript.transform.names")
NameProxy, LocalName = _table_0.NameProxy, _table_0.LocalName
end
local Set
do
local _table_0 = require("moonscript.data")

View File

@ -9,7 +9,7 @@ require "moonscript.compile.value"
transform = require "moonscript.transform"
import NameProxy, LocalName from transform
import NameProxy, LocalName from require "moonscript.transform.names"
import Set from require "moonscript.data"
import ntype from require "moonscript.types"

View File

@ -170,7 +170,8 @@ local _chain_assignable = { index = true, dot = true, slice = true }
local function is_assignable(node)
local t = ntype(node)
return t == "self" or t == "value" or t == "self_class" or
t == "chain" and _chain_assignable[ntype(node[#node])]
t == "chain" and _chain_assignable[ntype(node[#node])] or
t == "table"
end
local function check_assignable(str, pos, value)

View File

@ -5,6 +5,12 @@ local data = require("moonscript.data")
local reversed = util.reversed
local ntype, build, smart_node, is_slice, value_is_singular = types.ntype, types.build, types.smart_node, types.is_slice, types.value_is_singular
local insert = table.insert
local NameProxy, LocalName
do
local _table_0 = require("moonscript.transform.names")
NameProxy, LocalName = _table_0.NameProxy, _table_0.LocalName
end
local destructure = require("moonscript.transform.destructure")
local mtype
do
local moon_type = util.moon.type
@ -18,136 +24,6 @@ do
end
end
local implicitly_return
do
local _parent_0 = nil
local _base_0 = {
get_name = function(self)
return self.name
end
}
_base_0.__index = _base_0
if _parent_0 then
setmetatable(_base_0, _parent_0.__base)
end
local _class_0 = setmetatable({
__init = function(self, name)
self.name = name
self[1] = "temp_name"
end,
__base = _base_0,
__name = "LocalName",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil and _parent_0 then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0 and _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
LocalName = _class_0
end
do
local _parent_0 = nil
local _base_0 = {
get_name = function(self, scope)
if not self.name then
self.name = scope:free_name(self.prefix, true)
end
return self.name
end,
chain = function(self, ...)
local items = {
...
}
items = (function()
local _accum_0 = { }
local _len_0 = 0
local _list_0 = items
for _index_0 = 1, #_list_0 do
local i = _list_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
return _accum_0
end)()
return build.chain({
base = self,
unpack(items)
})
end,
index = function(self, key)
return build.chain({
base = self,
{
"index",
key
}
})
end,
__tostring = function(self)
if self.name then
return ("name<%s>"):format(self.name)
else
return ("name<prefix(%s)>"):format(self.prefix)
end
end
}
_base_0.__index = _base_0
if _parent_0 then
setmetatable(_base_0, _parent_0.__base)
end
local _class_0 = setmetatable({
__init = function(self, prefix)
self.prefix = prefix
self[1] = "temp_name"
end,
__base = _base_0,
__name = "NameProxy",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil and _parent_0 then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0 and _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
NameProxy = _class_0
end
do
local _parent_0 = nil
local _base_0 = {
@ -497,7 +373,11 @@ Statement = Transformer({
})
end
end
return transformed or node
node = transformed or node
if destructure.has_destructure(names) then
return destructure.split_assign(node)
end
return node
end,
continue = function(self, node)
local continue_name = self:send("continue")

View File

@ -8,6 +8,9 @@ data = require "moonscript.data"
import reversed from util
import ntype, build, smart_node, is_slice, value_is_singular from types
import insert from table
import NameProxy, LocalName from require "moonscript.transform.names"
destructure = require "moonscript.transform.destructure"
mtype = do
moon_type = util.moon.type
@ -19,48 +22,10 @@ mtype = do
else
t
export Statement, Value, NameProxy, LocalName, Run
export Statement, Value, Run
local implicitly_return
-- always declares as local
class LocalName
new: (@name) => self[1] = "temp_name"
get_name: => @name
class NameProxy
new: (@prefix) =>
self[1] = "temp_name"
get_name: (scope) =>
if not @name
@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
}
index: (key) =>
build.chain {
base: self, {"index", key}
}
__tostring: =>
if @name
("name<%s>")\format @name
else
("name<prefix(%s)>")\format @prefix
class Run
new: (@fn) =>
self[1] = "run"
@ -228,7 +193,12 @@ Statement = Transformer {
@transform.statement value, ret, node
}
transformed or node
node = transformed or node
if destructure.has_destructure names
return destructure.split_assign node
node
continue: (node) =>
continue_name = @send "continue"

View File

@ -0,0 +1,227 @@
local ntype, build
do
local _table_0 = require("moonscript.types")
ntype, build = _table_0.ntype, _table_0.build
end
local NameProxy
do
local _table_0 = require("moonscript.transform.names")
NameProxy = _table_0.NameProxy
end
local insert = table.insert
local user_error
do
local _table_0 = require("moonscript.compile")
user_error = _table_0.user_error
end
local join
join = function(...)
do
local _with_0 = { }
local out = _with_0
local i = 1
local _list_0 = {
...
}
for _index_0 = 1, #_list_0 do
local tbl = _list_0[_index_0]
local _list_1 = tbl
for _index_1 = 1, #_list_1 do
local v = _list_1[_index_1]
out[i] = v
i = i + 1
end
end
return _with_0
end
end
local has_destructure
has_destructure = function(names)
local _list_0 = names
for _index_0 = 1, #_list_0 do
local n = _list_0[_index_0]
if ntype(n) == "table" then
return true
end
end
return false
end
local build_assign
build_assign = function(extracted_names, receiver)
local obj = NameProxy("obj")
local names = { }
local values = { }
local _list_0 = extracted_names
for _index_0 = 1, #_list_0 do
local tuple = _list_0[_index_0]
insert(names, tuple[1])
insert(values, obj:chain(unpack(tuple[2])))
end
return build.group({
{
"declare",
names
},
build["do"]({
build.assign_one(obj, receiver),
{
"assign",
names,
values
}
})
})
end
local extract_assign_names
extract_assign_names = function(name, accum, prefix)
if accum == nil then
accum = { }
end
if prefix == nil then
prefix = { }
end
local i = 1
local _list_0 = name[2]
for _index_0 = 1, #_list_0 do
local tuple = _list_0[_index_0]
local value, suffix
if #tuple == 1 then
local s = {
"index",
{
"number",
i
}
}
i = i + 1
value, suffix = tuple[1], s
else
local key = tuple[1]
local s
if ntype(key) == "key_literal" then
s = {
"dot",
key[2]
}
else
s = {
"index",
key
}
end
value, suffix = tuple[2], s
end
suffix = join(prefix, {
suffix
})
local t = ntype(value)
if t == "value" or t == "chain" or t == "self" then
insert(accum, {
value,
suffix
})
elseif t == "table" then
extract_assign_names(value, accum, suffix)
else
user_error("Can't destructure value of type: " .. tostring(ntype(value)))
end
end
return accum
end
local split_assign
split_assign = function(assign)
local names, values = unpack(assign, 2)
local g = { }
local total_names = #names
local total_values = #values
local start = 1
for i, n in ipairs(names) do
if ntype(n) == "table" then
if i > start then
local stop = i - 1
insert(g, {
"assign",
(function()
local _accum_0 = { }
local _len_0 = 0
for i = start, stop do
local _value_0 = names[i]
if _value_0 ~= nil then
_len_0 = _len_0 + 1
_accum_0[_len_0] = _value_0
end
end
return _accum_0
end)(),
(function()
local _accum_0 = { }
local _len_0 = 0
for i = start, stop do
local _value_0 = values[i]
if _value_0 ~= nil then
_len_0 = _len_0 + 1
_accum_0[_len_0] = _value_0
end
end
return _accum_0
end)()
})
end
local extracted = extract_assign_names(n)
insert(g, build_assign(extracted, values[i]))
start = i + 1
end
end
if total_names >= start or total_values >= start then
local name_slice
if total_names < start then
name_slice = {
"_"
}
else
name_slice = (function()
local _accum_0 = { }
local _len_0 = 0
for i = start, total_names do
local _value_0 = names[i]
if _value_0 ~= nil then
_len_0 = _len_0 + 1
_accum_0[_len_0] = _value_0
end
end
return _accum_0
end)()
end
local value_slice
if total_values < start then
value_slice = {
"nil"
}
else
value_slice = (function()
local _accum_0 = { }
local _len_0 = 0
for i = start, total_values do
local _value_0 = values[i]
if _value_0 ~= nil then
_len_0 = _len_0 + 1
_accum_0[_len_0] = _value_0
end
end
return _accum_0
end)()
end
insert(g, {
"assign",
name_slice,
value_slice
})
end
return build.group(g)
end
return {
has_destructure = has_destructure,
split_assign = split_assign,
extract_assign_names = extract_assign_names,
build_assign = build_assign
}

View File

@ -0,0 +1,109 @@
import ntype, build from require "moonscript.types"
import NameProxy from require "moonscript.transform.names"
import insert from table
import user_error from require "moonscript.compile"
join = (...) ->
with out = {}
i = 1
for tbl in *{...}
for v in *tbl
out[i] = v
i += 1
has_destructure = (names) ->
for n in *names
return true if ntype(n) == "table"
false
build_assign = (extracted_names, receiver) ->
obj = NameProxy "obj"
names = {}
values = {}
for tuple in *extracted_names
insert names, tuple[1]
insert values, obj\chain unpack tuple[2]
build.group {
{"declare", names}
build.do {
build.assign_one obj, receiver
{"assign", names, values}
}
}
extract_assign_names = (name, accum={}, prefix={}) ->
i = 1
for tuple in *name[2]
value, suffix = if #tuple == 1
s = {"index", {"number", i}}
i += 1
tuple[1], s
else
key = tuple[1]
s = if ntype(key) == "key_literal"
{"dot", key[2]}
else
{"index", key}
tuple[2], s
suffix = join prefix, {suffix}
t = ntype value
if t == "value" or t == "chain" or t == "self"
insert accum, {value, suffix}
elseif t == "table"
extract_assign_names value, accum, suffix
else
user_error "Can't destructure value of type: #{ntype value}"
accum
-- applies to destructuring to a assign node
split_assign = (assign) ->
names, values = unpack assign, 2
g = {}
total_names = #names
total_values = #values
-- We have to break apart the assign into groups of regular
-- assigns, and then the destructuring assignments
start = 1
for i, n in ipairs names
if ntype(n) == "table"
if i > start
stop = i - 1
insert g, {
"assign"
for i=start,stop
names[i]
for i=start,stop
values[i]
}
extracted = extract_assign_names n
insert g, build_assign extracted, values[i]
start = i + 1
if total_names >= start or total_values >= start
name_slice = if total_names < start
{"_"}
else
for i=start,total_names do names[i]
value_slice = if total_values < start
{"nil"}
else
for i=start,total_values do values[i]
insert g, {"assign", name_slice, value_slice}
build.group g
{ :has_destructure, :split_assign, :extract_assign_names, :build_assign }

View File

@ -0,0 +1,140 @@
local build
do
local _table_0 = require("moonscript.types")
build = _table_0.build
end
local LocalName
do
local _parent_0 = nil
local _base_0 = {
get_name = function(self)
return self.name
end
}
_base_0.__index = _base_0
if _parent_0 then
setmetatable(_base_0, _parent_0.__base)
end
local _class_0 = setmetatable({
__init = function(self, name)
self.name = name
self[1] = "temp_name"
end,
__base = _base_0,
__name = "LocalName",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil and _parent_0 then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0 and _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
LocalName = _class_0
end
local NameProxy
do
local _parent_0 = nil
local _base_0 = {
get_name = function(self, scope)
if not self.name then
self.name = scope:free_name(self.prefix, true)
end
return self.name
end,
chain = function(self, ...)
local items = (function(...)
local _accum_0 = { }
local _len_0 = 0
local _list_0 = {
...
}
for _index_0 = 1, #_list_0 do
local i = _list_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
return _accum_0
end)(...)
return build.chain({
base = self,
unpack(items)
})
end,
index = function(self, key)
return build.chain({
base = self,
{
"index",
key
}
})
end,
__tostring = function(self)
if self.name then
return ("name<%s>"):format(self.name)
else
return ("name<prefix(%s)>"):format(self.prefix)
end
end
}
_base_0.__index = _base_0
if _parent_0 then
setmetatable(_base_0, _parent_0.__base)
end
local _class_0 = setmetatable({
__init = function(self, prefix)
self.prefix = prefix
self[1] = "temp_name"
end,
__base = _base_0,
__name = "NameProxy",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil and _parent_0 then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0 and _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
NameProxy = _class_0
end
return {
NameProxy = NameProxy,
LocalName = LocalName
}

View File

@ -0,0 +1,43 @@
import build from require "moonscript.types"
-- always declares as local
class LocalName
new: (@name) => self[1] = "temp_name"
get_name: => @name
-- creates a unique name when used
class NameProxy
new: (@prefix) =>
self[1] = "temp_name"
get_name: (scope) =>
if not @name
@name = scope\free_name @prefix, true
@name
chain: (...) =>
items = for i in *{...}
if type(i) == "string"
{"dot", i}
else
i
build.chain {
base: self
unpack items
}
index: (key) =>
build.chain {
base: self, {"index", key}
}
__tostring: =>
if @name
("name<%s>")\format @name
else
("name<prefix(%s)>")\format @prefix
{ :NameProxy, :LocalName }

View File

@ -0,0 +1,45 @@
{a, b} = hello
{{a}, b, {c}} = hello
{ :hello, :world } = value
{ yes: no, thing } = world
{:a,:b,:c,:d} = yeah
{a} = one, two
{b}, c = one
{d}, e = one, two
x, {y} = one, two
xx, yy = 1, 2
{yy, xx} = {xx, yy}
{a, :b, c, :d, e, :f, g} = tbl
---
futurists =
sculptor: "Umberto Boccioni"
painter: "Vladimir Burliuk"
poet:
name: "F.T. Marinetti"
address: {
"Via Roma 42R"
"Bellagio, Italy 22021"
}
{poet: {:name, address: {street, city}}} = futurists
print name, street, city
--
{ @world } = x
{ a.b, c.y, func!.z } = x
{ world: @world } = x

View File

@ -0,0 +1,88 @@
local a, b
do
local _obj_0 = hello
a, b = _obj_0[1], _obj_0[2]
end
local c
do
local _obj_0 = hello
a, b, c = _obj_0[1][1], _obj_0[2], _obj_0[3][1]
end
local hello, world
do
local _obj_0 = value
hello, world = _obj_0.hello, _obj_0.world
end
local no, thing
do
local _obj_0 = world
no, thing = _obj_0.yes, _obj_0[1]
end
local d
do
local _obj_0 = yeah
a, b, c, d = _obj_0.a, _obj_0.b, _obj_0.c, _obj_0.d
end
do
local _obj_0 = one
a = _obj_0[1]
end
local _ = two
do
local _obj_0 = one
b = _obj_0[1]
end
c = nil
do
local _obj_0 = one
d = _obj_0[1]
end
local e = two
local x = one
local y
do
local _obj_0 = two
y = _obj_0[1]
end
local xx, yy = 1, 2
do
local _obj_0 = {
xx,
yy
}
yy, xx = _obj_0[1], _obj_0[2]
end
local f, g
do
local _obj_0 = tbl
a, b, c, d, e, f, g = _obj_0[1], _obj_0.b, _obj_0[2], _obj_0.d, _obj_0[3], _obj_0.f, _obj_0[4]
end
local futurists = {
sculptor = "Umberto Boccioni",
painter = "Vladimir Burliuk",
poet = {
name = "F.T. Marinetti",
address = {
"Via Roma 42R",
"Bellagio, Italy 22021"
}
}
}
local name, street, city
do
local _obj_0 = futurists
name, street, city = _obj_0.poet.name, _obj_0.poet.address[1], _obj_0.poet.address[2]
end
print(name, street, city)
do
local _obj_0 = x
self.world = _obj_0[1]
end
do
local _obj_0 = x
a.b, c.y, func().z = _obj_0[1], _obj_0[2], _obj_0[3]
end
do
local _obj_0 = x
self.world = _obj_0.world
end