mirror of
https://github.com/leafo/moonscript.git
synced 2024-10-05 05:34:19 +00:00
function stub syntax, and start of tree transformer
This commit is contained in:
parent
5440d0deb9
commit
b7af08f15f
@ -644,6 +644,35 @@ Or...
|
||||
|
||||
me = create_person "Leaf", {dad, mother, sister}
|
||||
|
||||
## Function Stubs
|
||||
|
||||
It is common to pass a function from an object around as a value, for example,
|
||||
passing an instance method into a function as a callback. If the function
|
||||
expects the object it is operating on as the first argument then you must
|
||||
somehow bundle that object with the function so it can be called properly.
|
||||
|
||||
The function stub syntax is a shorthand for creating a new closure function
|
||||
that bundles both the object and function. This new function calls the wrapped
|
||||
function in the correct context of the object.
|
||||
|
||||
Its syntax is the same as calling an instance method with the `\` operator but
|
||||
with no argument list provided.
|
||||
|
||||
my_object = {
|
||||
value: 1000
|
||||
print: => print "the value:", @value
|
||||
}
|
||||
|
||||
run_callback (func) ->
|
||||
print "running callback..."
|
||||
func!
|
||||
|
||||
-- this will not work, the function has to no reference to my_object
|
||||
run_callback my_object.print
|
||||
|
||||
-- function stub syntax lets us bundle the object into a new function
|
||||
run_callback my_object\print
|
||||
|
||||
## The Using Clause; Controlling Destructive Assignment
|
||||
|
||||
While lexical scoping can be a great help in reducing the complexity of the
|
||||
|
@ -4,6 +4,8 @@ local dump = require("moonscript.dump")
|
||||
require("moonscript.compile.format")
|
||||
require("moonscript.compile.line")
|
||||
require("moonscript.compile.value")
|
||||
local transform = require("moonscript.transform")
|
||||
local NameProxy = transform.NameProxy
|
||||
local Set
|
||||
do
|
||||
local _table_0 = require("moonscript.data")
|
||||
@ -114,9 +116,20 @@ Block_ = (function(_parent_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
|
||||
local t = util.moon.type(name)
|
||||
local real_name
|
||||
if t == NameProxy then
|
||||
real_name = name:get_name(self)
|
||||
elseif t == "string" then
|
||||
real_name = name
|
||||
end
|
||||
local _value_0
|
||||
if real_name and not self:has_name(real_name) then
|
||||
_value_0 = real_name
|
||||
end
|
||||
if _value_0 ~= nil then
|
||||
_len_0 = _len_0 + 1
|
||||
_accum_0[_len_0] = name
|
||||
_accum_0[_len_0] = _value_0
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -135,6 +148,9 @@ Block_ = (function(_parent_0)
|
||||
self._name_whitelist = Set(names)
|
||||
end,
|
||||
put_name = function(self, name)
|
||||
if util.moon.type(name) == NameProxy then
|
||||
name = name:get_name(self)
|
||||
end
|
||||
self._names[name] = true
|
||||
end,
|
||||
has_name = function(self, name)
|
||||
@ -318,6 +334,7 @@ Block_ = (function(_parent_0)
|
||||
if not fn then
|
||||
error("Failed to compile value: " .. dump.value(node))
|
||||
end
|
||||
node = transform.node(node)
|
||||
return fn(self, node, ...)
|
||||
end,
|
||||
values = function(self, values, delim)
|
||||
|
@ -7,6 +7,9 @@ require "moonscript.compile.format"
|
||||
require "moonscript.compile.line"
|
||||
require "moonscript.compile.value"
|
||||
|
||||
transform = require "moonscript.transform"
|
||||
|
||||
import NameProxy from transform
|
||||
import Set from require "moonscript.data"
|
||||
import ntype from require "moonscript.types"
|
||||
|
||||
@ -74,7 +77,14 @@ class Block_
|
||||
@_state[name]
|
||||
|
||||
declare: (names) =>
|
||||
undeclared = [name for name in *names when type(name) == "string" and not @has_name name]
|
||||
undeclared = for name in *names
|
||||
t = util.moon.type(name)
|
||||
real_name = if t == NameProxy
|
||||
name\get_name self
|
||||
elseif t == "string"
|
||||
name
|
||||
real_name if real_name and not @has_name real_name
|
||||
|
||||
@put_name name for name in *undeclared
|
||||
undeclared
|
||||
|
||||
@ -82,6 +92,7 @@ class Block_
|
||||
@_name_whitelist = Set names
|
||||
|
||||
put_name: (name) =>
|
||||
name = name\get_name self if util.moon.type(name) == NameProxy
|
||||
@_names[name] = true
|
||||
|
||||
has_name: (name) =>
|
||||
@ -218,6 +229,7 @@ class Block_
|
||||
|
||||
fn = value_compile[action]
|
||||
error "Failed to compile value: "..dump.value node if not fn
|
||||
node = transform.node node
|
||||
fn self, node, ...
|
||||
|
||||
values: (values, delim) =>
|
||||
|
@ -368,6 +368,9 @@ value_compile = {
|
||||
minus = function(self, node)
|
||||
return self:line("-", self:value(node[2]))
|
||||
end,
|
||||
temp_name = function(self, node)
|
||||
return node:get_name(self)
|
||||
end,
|
||||
number = function(self, node)
|
||||
return node[2]
|
||||
end,
|
||||
|
@ -197,6 +197,9 @@ value_compile =
|
||||
minus: (node) =>
|
||||
@line "-", @value node[2]
|
||||
|
||||
temp_name: (node) =>
|
||||
node\get_name self
|
||||
|
||||
number: (node) =>
|
||||
node[2]
|
||||
|
||||
|
102
moonscript/transform.lua
Normal file
102
moonscript/transform.lua
Normal file
@ -0,0 +1,102 @@
|
||||
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
|
||||
NameProxy = (function(_parent_0)
|
||||
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,
|
||||
__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, getmetatable(_parent_0).__index)
|
||||
end
|
||||
local _class_0 = setmetatable({
|
||||
__init = function(self, prefix)
|
||||
self.prefix = prefix
|
||||
self[1] = "temp_name"
|
||||
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 transformers = {
|
||||
chain = function(node)
|
||||
local stub = node[#node]
|
||||
if type(stub) == "table" and stub[1] == "colon_stub" then
|
||||
table.remove(node, #node)
|
||||
local base_name = NameProxy("base")
|
||||
local fn_name = NameProxy("fn")
|
||||
return build.block_exp({
|
||||
build.assign({
|
||||
names = {
|
||||
base_name
|
||||
},
|
||||
values = {
|
||||
node
|
||||
}
|
||||
}),
|
||||
build.assign({
|
||||
names = {
|
||||
fn_name
|
||||
},
|
||||
values = {
|
||||
build.chain({
|
||||
base = base_name,
|
||||
{
|
||||
"dot",
|
||||
stub[2]
|
||||
}
|
||||
})
|
||||
}
|
||||
}),
|
||||
build.fndef({
|
||||
args = {
|
||||
{
|
||||
"..."
|
||||
}
|
||||
},
|
||||
body = {
|
||||
build.chain({
|
||||
base = fn_name,
|
||||
{
|
||||
"call",
|
||||
{
|
||||
base_name,
|
||||
"..."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
end
|
||||
end
|
||||
}
|
||||
node = function(n)
|
||||
local transformer = transformers[ntype(n)]
|
||||
if transformer then
|
||||
return transformer(n) or n
|
||||
else
|
||||
return n
|
||||
end
|
||||
end
|
68
moonscript/transform.moon
Normal file
68
moonscript/transform.moon
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
module "moonscript.transform", package.seeall
|
||||
|
||||
types = require "moonscript.types"
|
||||
util = require "moonscript.util"
|
||||
data = require "moonscript.data"
|
||||
|
||||
import ntype, build from types
|
||||
|
||||
export node, NameProxy
|
||||
|
||||
class NameProxy
|
||||
new: (@prefix) =>
|
||||
self[1] = "temp_name"
|
||||
|
||||
get_name: (scope) =>
|
||||
if not @name
|
||||
@name = scope\free_name @prefix, true
|
||||
@name
|
||||
|
||||
__tostring: =>
|
||||
if @name
|
||||
("name<%s>")\format @name
|
||||
else
|
||||
("name<prefix(%s)>")\format @prefix
|
||||
|
||||
transformers = {
|
||||
-- pull out colon chain
|
||||
chain: (node) ->
|
||||
stub = node[#node]
|
||||
if type(stub) == "table" and stub[1] == "colon_stub"
|
||||
table.remove node, #node
|
||||
|
||||
base_name = NameProxy "base"
|
||||
fn_name = NameProxy "fn"
|
||||
|
||||
build.block_exp {
|
||||
build.assign {
|
||||
names: {base_name}
|
||||
values: {node}
|
||||
}
|
||||
|
||||
build.assign {
|
||||
names: {fn_name}
|
||||
values: {
|
||||
build.chain { base: base_name, {"dot", stub[2]} }
|
||||
}
|
||||
}
|
||||
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
body: {
|
||||
build.chain {
|
||||
base: fn_name, {"call", {base_name, "..."}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = (n) ->
|
||||
transformer = transformers[ntype n]
|
||||
if transformer
|
||||
transformer(n) or n
|
||||
else
|
||||
n
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
module("moonscript.types", package.seeall)
|
||||
local util = require("moonscript.util")
|
||||
local data = require("moonscript.data")
|
||||
local insert = table.insert
|
||||
ntype = function(node)
|
||||
if type(node) ~= "table" then
|
||||
return "value"
|
||||
@ -27,6 +28,16 @@ local node_types = {
|
||||
"body",
|
||||
t
|
||||
}
|
||||
},
|
||||
assign = {
|
||||
{
|
||||
"names",
|
||||
t
|
||||
},
|
||||
{
|
||||
"values",
|
||||
t
|
||||
}
|
||||
}
|
||||
}
|
||||
local build_table
|
||||
@ -57,11 +68,10 @@ make_builder = function(name)
|
||||
name
|
||||
}
|
||||
for i, arg in ipairs(spec) do
|
||||
local default_value
|
||||
name, default_value = unpack(arg)
|
||||
local key, default_value = unpack(arg)
|
||||
local val
|
||||
if props[name] then
|
||||
val = props[name]
|
||||
if props[key] then
|
||||
val = props[key]
|
||||
else
|
||||
val = default_value
|
||||
end
|
||||
@ -73,7 +83,39 @@ make_builder = function(name)
|
||||
return node
|
||||
end
|
||||
end
|
||||
build = setmetatable({ }, {
|
||||
build = nil
|
||||
build = setmetatable({
|
||||
block_exp = function(body)
|
||||
local fn = build.fndef({
|
||||
body = body
|
||||
})
|
||||
return build.chain({
|
||||
base = {
|
||||
"parens",
|
||||
fn
|
||||
},
|
||||
{
|
||||
"call",
|
||||
{ }
|
||||
}
|
||||
})
|
||||
end,
|
||||
chain = function(parts)
|
||||
local base = parts.base or error("expecting base property for chain")
|
||||
local node = {
|
||||
"chain",
|
||||
base
|
||||
}
|
||||
do
|
||||
local _item_0 = parts
|
||||
for _index_0 = 1, #_item_0 do
|
||||
local part = _item_0[_index_0]
|
||||
insert(node, part)
|
||||
end
|
||||
end
|
||||
return node
|
||||
end
|
||||
}, {
|
||||
__index = function(self, name)
|
||||
self[name] = make_builder(name)
|
||||
return rawget(self, name)
|
||||
|
@ -3,6 +3,8 @@ util = require "moonscript.util"
|
||||
data = require "moonscript.data"
|
||||
|
||||
export ntype, smart_node, build
|
||||
import insert from table
|
||||
|
||||
|
||||
-- type of node as string
|
||||
ntype = (node) ->
|
||||
@ -20,6 +22,10 @@ node_types = {
|
||||
{"arrow", "slim"}
|
||||
{"body", t}
|
||||
}
|
||||
assign: {
|
||||
{"names", t}
|
||||
{"values", t}
|
||||
}
|
||||
}
|
||||
|
||||
build_table = ->
|
||||
@ -41,13 +47,24 @@ make_builder = (name) ->
|
||||
(props={}) ->
|
||||
node = { name }
|
||||
for i, arg in ipairs spec
|
||||
name, default_value = unpack arg
|
||||
val = if props[name] then props[name] else default_value
|
||||
key, default_value = unpack arg
|
||||
val = if props[key] then props[key] else default_value
|
||||
val = {} if val == t
|
||||
node[i + 1] = val
|
||||
node
|
||||
|
||||
build = setmetatable {}, {
|
||||
build = nil
|
||||
build = setmetatable {
|
||||
block_exp: (body) ->
|
||||
fn = build.fndef body: body
|
||||
build.chain { base: {"parens", fn}, {"call", {}} }
|
||||
chain: (parts) ->
|
||||
base = parts.base or error"expecting base property for chain"
|
||||
node = {"chain", base}
|
||||
for part in *parts
|
||||
insert node, part
|
||||
node
|
||||
}, {
|
||||
__index: (name) =>
|
||||
self[name] = make_builder name
|
||||
rawget self, name
|
||||
|
13
tests/inputs/stub.moon
Normal file
13
tests/inputs/stub.moon
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
x = {
|
||||
val: 100
|
||||
hello: =>
|
||||
print @val
|
||||
}
|
||||
|
||||
fn = x\val
|
||||
print fn!
|
||||
print x\val!
|
||||
|
||||
|
15
tests/outputs/stub.lua
Normal file
15
tests/outputs/stub.lua
Normal file
@ -0,0 +1,15 @@
|
||||
local x = {
|
||||
val = 100,
|
||||
hello = function(self)
|
||||
return print(self.val)
|
||||
end
|
||||
}
|
||||
local fn = (function()
|
||||
local _base_0 = x
|
||||
local _fn_0 = _base_0.val
|
||||
return function(...)
|
||||
return _fn_0(_base_0, ...)
|
||||
end
|
||||
end)()
|
||||
print(fn())
|
||||
print(x:val())
|
5
todo
5
todo
@ -1,18 +1,17 @@
|
||||
# TODO
|
||||
|
||||
- dump node in mark, see why values are being parsed multiple times
|
||||
|
||||
if hello else world
|
||||
-> if hello then hello else world
|
||||
|
||||
- varargs that get put in a nested generated function aren't valid anymore:
|
||||
|
||||
- allow a return without an argument
|
||||
|
||||
- class expressions, x = class extends Hello do new: => print "hello"
|
||||
|
||||
* multiline comments
|
||||
* table slices (almost)
|
||||
* add continue keyword (ouch)
|
||||
* vim syntax file
|
||||
|
||||
* combine for and if line decorators
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user