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.statement")
require("moonscript.compile.value") require("moonscript.compile.value")
local transform = require("moonscript.transform") 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 local Set
do do
local _table_0 = require("moonscript.data") local _table_0 = require("moonscript.data")

View File

@ -9,7 +9,7 @@ require "moonscript.compile.value"
transform = require "moonscript.transform" transform = require "moonscript.transform"
import NameProxy, LocalName from transform import NameProxy, LocalName from require "moonscript.transform.names"
import Set from require "moonscript.data" import Set from require "moonscript.data"
import ntype from require "moonscript.types" 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 function is_assignable(node)
local t = ntype(node) local t = ntype(node)
return t == "self" or t == "value" or t == "self_class" or 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 end
local function check_assignable(str, pos, value) local function check_assignable(str, pos, value)

View File

@ -5,6 +5,12 @@ local data = require("moonscript.data")
local reversed = util.reversed 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 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 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 local mtype
do do
local moon_type = util.moon.type local moon_type = util.moon.type
@ -18,136 +24,6 @@ do
end end
end end
local implicitly_return 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 do
local _parent_0 = nil local _parent_0 = nil
local _base_0 = { local _base_0 = {
@ -497,7 +373,11 @@ Statement = Transformer({
}) })
end end
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, end,
continue = function(self, node) continue = function(self, node)
local continue_name = self:send("continue") local continue_name = self:send("continue")

View File

@ -8,6 +8,9 @@ data = require "moonscript.data"
import reversed from util import reversed from util
import ntype, build, smart_node, is_slice, value_is_singular from types import ntype, build, smart_node, is_slice, value_is_singular from types
import insert from table import insert from table
import NameProxy, LocalName from require "moonscript.transform.names"
destructure = require "moonscript.transform.destructure"
mtype = do mtype = do
moon_type = util.moon.type moon_type = util.moon.type
@ -19,48 +22,10 @@ mtype = do
else else
t t
export Statement, Value, NameProxy, LocalName, Run export Statement, Value, Run
local implicitly_return 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 class Run
new: (@fn) => new: (@fn) =>
self[1] = "run" self[1] = "run"
@ -228,7 +193,12 @@ Statement = Transformer {
@transform.statement value, ret, node @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: (node) =>
continue_name = @send "continue" 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