mirror of
https://github.com/leafo/moonscript.git
synced 2024-11-22 02:44:23 +00:00
refactor apart transfromers
This commit is contained in:
parent
53ef9fe504
commit
76af9964fe
@ -65,6 +65,9 @@ do
|
||||
end
|
||||
end,
|
||||
lint_check_unused = function(self)
|
||||
do
|
||||
return
|
||||
end
|
||||
if not (self.lint_unused_names and next(self.lint_unused_names)) then
|
||||
return
|
||||
end
|
||||
|
@ -105,6 +105,7 @@ class LinterBlock extends Block
|
||||
@parent\lint_mark_used name
|
||||
|
||||
lint_check_unused: =>
|
||||
do return
|
||||
return unless @lint_unused_names and next @lint_unused_names
|
||||
|
||||
names_by_position = {}
|
||||
|
@ -27,10 +27,10 @@ Num = Space * (Num / function(v)
|
||||
v
|
||||
}
|
||||
end)
|
||||
local Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, flatten_string_chain, wrap_decorator, check_lua_string, self_assign
|
||||
local Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, wrap_decorator, check_lua_string, self_assign
|
||||
do
|
||||
local _obj_0 = require("moonscript.parse.util")
|
||||
Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, flatten_string_chain, wrap_decorator, check_lua_string, self_assign = _obj_0.Indent, _obj_0.Cut, _obj_0.ensure, _obj_0.extract_line, _obj_0.mark, _obj_0.pos, _obj_0.flatten_or_mark, _obj_0.is_assignable, _obj_0.check_assignable, _obj_0.format_assign, _obj_0.format_single_assign, _obj_0.sym, _obj_0.symx, _obj_0.simple_string, _obj_0.wrap_func_arg, _obj_0.join_chain, _obj_0.flatten_string_chain, _obj_0.wrap_decorator, _obj_0.check_lua_string, _obj_0.self_assign
|
||||
Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, wrap_decorator, check_lua_string, self_assign = _obj_0.Indent, _obj_0.Cut, _obj_0.ensure, _obj_0.extract_line, _obj_0.mark, _obj_0.pos, _obj_0.flatten_or_mark, _obj_0.is_assignable, _obj_0.check_assignable, _obj_0.format_assign, _obj_0.format_single_assign, _obj_0.sym, _obj_0.symx, _obj_0.simple_string, _obj_0.wrap_func_arg, _obj_0.join_chain, _obj_0.wrap_decorator, _obj_0.check_lua_string, _obj_0.self_assign
|
||||
end
|
||||
local build_grammar = wrap_env(debug_grammar, function(root)
|
||||
local _indent = Stack(0)
|
||||
|
@ -29,7 +29,7 @@ Num = Space * (Num / (v) -> {"number", v})
|
||||
:Indent, :Cut, :ensure, :extract_line, :mark, :pos, :flatten_or_mark,
|
||||
:is_assignable, :check_assignable, :format_assign, :format_single_assign,
|
||||
:sym, :symx, :simple_string, :wrap_func_arg, :join_chain,
|
||||
:flatten_string_chain, :wrap_decorator, :check_lua_string, :self_assign
|
||||
:wrap_decorator, :check_lua_string, :self_assign
|
||||
} = require "moonscript.parse.util"
|
||||
|
||||
|
||||
|
@ -197,17 +197,6 @@ join_chain = function(callee, args)
|
||||
args
|
||||
}
|
||||
end
|
||||
local flatten_string_chain
|
||||
flatten_string_chain = function(str, chain, args)
|
||||
if not (chain) then
|
||||
return str
|
||||
end
|
||||
return flatten_chain({
|
||||
"chain",
|
||||
str,
|
||||
unpack(chain)
|
||||
}, args)
|
||||
end
|
||||
local wrap_decorator
|
||||
wrap_decorator = function(stm, dec)
|
||||
if not (dec) then
|
||||
@ -254,7 +243,6 @@ return {
|
||||
simple_string = simple_string,
|
||||
wrap_func_arg = wrap_func_arg,
|
||||
join_chain = join_chain,
|
||||
flatten_string_chain = flatten_string_chain,
|
||||
wrap_decorator = wrap_decorator,
|
||||
check_lua_string = check_lua_string,
|
||||
self_assign = self_assign
|
||||
|
@ -139,10 +139,6 @@ join_chain = (callee, args) ->
|
||||
|
||||
{"chain", callee, args}
|
||||
|
||||
flatten_string_chain = (str, chain, args) ->
|
||||
return str unless chain
|
||||
flatten_chain {"chain", str, unpack chain}, args
|
||||
|
||||
-- constructor for decorator node
|
||||
wrap_decorator = (stm, dec) ->
|
||||
return stm unless dec
|
||||
@ -157,5 +153,5 @@ self_assign = (name, pos) ->
|
||||
|
||||
{ :Indent, :Cut, :ensure, :extract_line, :mark, :pos, :flatten_or_mark,
|
||||
:is_assignable, :check_assignable, :format_assign, :format_single_assign,
|
||||
:sym, :symx, :simple_string, :wrap_func_arg, :join_chain,
|
||||
:flatten_string_chain, :wrap_decorator, :check_lua_string, :self_assign }
|
||||
:sym, :symx, :simple_string, :wrap_func_arg, :join_chain, :wrap_decorator,
|
||||
:check_lua_string, :self_assign }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,977 +1,5 @@
|
||||
|
||||
types = require "moonscript.types"
|
||||
util = require "moonscript.util"
|
||||
data = require "moonscript.data"
|
||||
|
||||
import reversed, unpack from util
|
||||
import ntype, mtype, build, smart_node, is_slice, value_is_singular from types
|
||||
import insert from table
|
||||
|
||||
import NameProxy, LocalName from require "moonscript.transform.names"
|
||||
import Run, transform_last_stm, last_stm, chain_is_stub from require "moonscript.transform.statements"
|
||||
|
||||
import Transformer from require "moonscript.transform.transformer"
|
||||
|
||||
destructure = require "moonscript.transform.destructure"
|
||||
NOOP = {"noop"}
|
||||
|
||||
local *
|
||||
|
||||
-- is a body a sindle expression/statement
|
||||
is_singular = (body) ->
|
||||
return false if #body != 1
|
||||
if "group" == ntype body
|
||||
is_singular body[2]
|
||||
else
|
||||
body[1]
|
||||
|
||||
-- this mutates body searching for assigns
|
||||
extract_declarations = (body=@current_stms, start=@current_stm_i + 1, out={}) =>
|
||||
for i=start,#body
|
||||
stm = body[i]
|
||||
continue if stm == nil
|
||||
stm = @transform.statement stm
|
||||
body[i] = stm
|
||||
switch stm[1]
|
||||
when "assign", "declare"
|
||||
for name in *stm[2]
|
||||
if ntype(name) == "ref"
|
||||
insert out, name
|
||||
elseif type(name) == "string"
|
||||
-- TODO: don't use string literal as ref
|
||||
insert out, name
|
||||
when "group"
|
||||
extract_declarations @, stm[2], 1, out
|
||||
out
|
||||
|
||||
expand_elseif_assign = (ifstm) ->
|
||||
for i = 4, #ifstm
|
||||
case = ifstm[i]
|
||||
if ntype(case) == "elseif" and ntype(case[2]) == "assign"
|
||||
split = { unpack ifstm, 1, i - 1 }
|
||||
insert split, {
|
||||
"else", {
|
||||
{"if", case[2], case[3], unpack ifstm, i + 1}
|
||||
}
|
||||
}
|
||||
return split
|
||||
|
||||
ifstm
|
||||
|
||||
constructor_name = "new"
|
||||
|
||||
with_continue_listener = (body) ->
|
||||
continue_name = nil
|
||||
|
||||
{
|
||||
Run =>
|
||||
@listen "continue", ->
|
||||
unless continue_name
|
||||
continue_name = NameProxy"continue"
|
||||
@put_name continue_name
|
||||
continue_name
|
||||
|
||||
build.group body
|
||||
|
||||
Run =>
|
||||
return unless continue_name
|
||||
last = last_stm body
|
||||
enclose_lines = types.terminating[last and ntype(last)]
|
||||
|
||||
@put_name continue_name, nil
|
||||
@splice (lines) ->
|
||||
lines = {"do", {lines}} if enclose_lines
|
||||
|
||||
{
|
||||
{"assign", {continue_name}, {"false"}}
|
||||
{"repeat", "true", {
|
||||
lines
|
||||
{"assign", {continue_name}, {"true"}}
|
||||
}}
|
||||
{"if", {"not", continue_name}, {
|
||||
{"break"}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
construct_comprehension = (inner, clauses) ->
|
||||
current_stms = inner
|
||||
for _, clause in reversed clauses
|
||||
t = clause[1]
|
||||
current_stms = switch t
|
||||
when "for"
|
||||
{_, name, bounds} = clause
|
||||
{"for", name, bounds, current_stms}
|
||||
when "foreach"
|
||||
{_, names, iter} = clause
|
||||
{"foreach", names, {iter}, current_stms}
|
||||
when "when"
|
||||
{_, cond} = clause
|
||||
{"if", cond, current_stms}
|
||||
else
|
||||
error "Unknown comprehension clause: "..t
|
||||
|
||||
current_stms = {current_stms}
|
||||
|
||||
current_stms[1]
|
||||
|
||||
Statement = Transformer {
|
||||
transform: (tuple) =>
|
||||
{_, node, fn} = tuple
|
||||
fn node
|
||||
|
||||
root_stms: (body) =>
|
||||
transform_last_stm body, implicitly_return @
|
||||
|
||||
return: (node) =>
|
||||
ret_val = node[2]
|
||||
ret_val_type = ntype ret_val
|
||||
|
||||
if ret_val_type == "explist" and #ret_val == 2
|
||||
ret_val = ret_val[2]
|
||||
ret_val_type = ntype ret_val
|
||||
|
||||
if types.cascading[ret_val_type]
|
||||
return implicitly_return(@) ret_val
|
||||
|
||||
-- flatten things that create block exp
|
||||
if ret_val_type == "chain" or ret_val_type == "comprehension" or ret_val_type == "tblcomprehension"
|
||||
ret_val = Value\transform_once @, ret_val
|
||||
if ntype(ret_val) == "block_exp"
|
||||
return build.group transform_last_stm ret_val[2], (stm)->
|
||||
{"return", stm}
|
||||
|
||||
node[2] = ret_val
|
||||
node
|
||||
|
||||
declare_glob: (node) =>
|
||||
names = extract_declarations @
|
||||
|
||||
if node[2] == "^"
|
||||
names = for name in *names
|
||||
continue unless name[2]\match "^%u"
|
||||
name
|
||||
|
||||
{"declare", names}
|
||||
|
||||
assign: (node) =>
|
||||
names, values = unpack node, 2
|
||||
|
||||
num_values = #values
|
||||
num_names = #values
|
||||
|
||||
-- special code simplifications for single assigns
|
||||
if num_names == 1 and num_values == 1
|
||||
first_value = values[1]
|
||||
first_name = names[1]
|
||||
first_type = ntype first_value
|
||||
|
||||
-- reduce colon stub chain to block exp
|
||||
if first_type == "chain"
|
||||
first_value = Value\transform_once @, first_value
|
||||
first_type = ntype first_value
|
||||
|
||||
switch ntype first_value
|
||||
when "block_exp"
|
||||
block_body = first_value[2]
|
||||
idx = #block_body
|
||||
block_body[idx] = build.assign_one first_name, block_body[idx]
|
||||
|
||||
return build.group {
|
||||
{"declare", {first_name}}
|
||||
{"do", block_body}
|
||||
}
|
||||
|
||||
when "comprehension", "tblcomprehension", "foreach", "for", "while"
|
||||
return build.assign_one first_name, Value\transform_once @, first_value
|
||||
else
|
||||
values[1] = first_value
|
||||
|
||||
-- bubble cascading assigns
|
||||
transformed = if num_values == 1
|
||||
value = values[1]
|
||||
t = ntype value
|
||||
|
||||
if t == "decorated"
|
||||
value = @transform.statement value
|
||||
t = ntype value
|
||||
|
||||
if types.cascading[t]
|
||||
ret = (stm) ->
|
||||
if types.is_value stm
|
||||
{"assign", names, {stm}}
|
||||
else
|
||||
stm
|
||||
|
||||
build.group {
|
||||
{"declare", names}
|
||||
@transform.statement value, ret, node
|
||||
}
|
||||
|
||||
node = transformed or node
|
||||
|
||||
if destructure.has_destructure names
|
||||
return destructure.split_assign @, node
|
||||
|
||||
node
|
||||
|
||||
continue: (node) =>
|
||||
continue_name = @send "continue"
|
||||
error "continue must be inside of a loop" unless continue_name
|
||||
build.group {
|
||||
build.assign_one continue_name, "true"
|
||||
{"break"}
|
||||
}
|
||||
|
||||
export: (node) =>
|
||||
-- assign values if they are included
|
||||
if #node > 2
|
||||
if node[2] == "class"
|
||||
cls = smart_node node[3]
|
||||
build.group {
|
||||
{"export", {cls.name}}
|
||||
cls
|
||||
}
|
||||
else
|
||||
-- pull out vawlues and assign them after the export
|
||||
build.group {
|
||||
{ "export", node[2] }
|
||||
build.assign {
|
||||
names: node[2]
|
||||
values: node[3]
|
||||
}
|
||||
}
|
||||
else
|
||||
nil
|
||||
|
||||
update: (node) =>
|
||||
_, name, op, exp = unpack node
|
||||
op_final = op\match "^(.+)=$"
|
||||
error "Unknown op: "..op if not op_final
|
||||
exp = {"parens", exp} unless value_is_singular exp
|
||||
build.assign_one name, {"exp", name, op_final, exp}
|
||||
|
||||
import: (node) =>
|
||||
_, names, source = unpack node
|
||||
table_values = for name in *names
|
||||
dest_name = if ntype(name) == "colon"
|
||||
name[2]
|
||||
else
|
||||
name
|
||||
|
||||
{{"key_literal", name}, dest_name}
|
||||
|
||||
dest = { "table", table_values }
|
||||
{ "assign", {dest}, {source}, [-1]: node[-1] }
|
||||
|
||||
comprehension: (node, action) =>
|
||||
_, exp, clauses = unpack node
|
||||
|
||||
action = action or (exp) -> {exp}
|
||||
construct_comprehension action(exp), clauses
|
||||
|
||||
do: (node, ret) =>
|
||||
node[2] = transform_last_stm node[2], ret if ret
|
||||
node
|
||||
|
||||
decorated: (node) =>
|
||||
stm, dec = unpack node, 2
|
||||
|
||||
wrapped = switch dec[1]
|
||||
when "if"
|
||||
cond, fail = unpack dec, 2
|
||||
fail = { "else", { fail } } if fail
|
||||
{ "if", cond, { stm }, fail }
|
||||
when "unless"
|
||||
{ "unless", dec[2], { stm } }
|
||||
when "comprehension"
|
||||
{ "comprehension", stm, dec[2] }
|
||||
else
|
||||
error "Unknown decorator " .. dec[1]
|
||||
|
||||
if ntype(stm) == "assign"
|
||||
wrapped = build.group {
|
||||
build.declare names: [name for name in *stm[2] when ntype(name) == "ref"]
|
||||
wrapped
|
||||
}
|
||||
|
||||
wrapped
|
||||
|
||||
unless: (node) =>
|
||||
{ "if", {"not", {"parens", node[2]}}, unpack node, 3 }
|
||||
|
||||
if: (node, ret) =>
|
||||
-- expand assign in cond
|
||||
if ntype(node[2]) == "assign"
|
||||
_, assign, body = unpack node
|
||||
if destructure.has_destructure assign[2]
|
||||
name = NameProxy "des"
|
||||
|
||||
body = {
|
||||
destructure.build_assign @, assign[2][1], name
|
||||
build.group node[3]
|
||||
}
|
||||
|
||||
return build.do {
|
||||
build.assign_one name, assign[3][1]
|
||||
{"if", name, body, unpack node, 4}
|
||||
}
|
||||
else
|
||||
name = assign[2][1]
|
||||
return build["do"] {
|
||||
assign
|
||||
{"if", name, unpack node, 3}
|
||||
}
|
||||
|
||||
node = expand_elseif_assign node
|
||||
|
||||
-- apply cascading return decorator
|
||||
if ret
|
||||
smart_node node
|
||||
-- mutate all the bodies
|
||||
node['then'] = transform_last_stm node['then'], ret
|
||||
for i = 4, #node
|
||||
case = node[i]
|
||||
body_idx = #node[i]
|
||||
case[body_idx] = transform_last_stm case[body_idx], ret
|
||||
|
||||
node
|
||||
|
||||
with: (node, ret) =>
|
||||
exp, block = unpack node, 2
|
||||
|
||||
copy_scope = true
|
||||
local scope_name, named_assign
|
||||
|
||||
if last = last_stm block
|
||||
ret = false if types.terminating[ntype(last)]
|
||||
|
||||
if ntype(exp) == "assign"
|
||||
names, values = unpack exp, 2
|
||||
first_name = names[1]
|
||||
|
||||
if ntype(first_name) == "ref"
|
||||
scope_name = first_name
|
||||
named_assign = exp
|
||||
exp = values[1]
|
||||
copy_scope = false
|
||||
else
|
||||
scope_name = NameProxy "with"
|
||||
exp = values[1]
|
||||
values[1] = scope_name
|
||||
named_assign = {"assign", names, values}
|
||||
|
||||
elseif @is_local exp
|
||||
scope_name = exp
|
||||
copy_scope = false
|
||||
|
||||
scope_name or= NameProxy "with"
|
||||
|
||||
out = build.do {
|
||||
copy_scope and build.assign_one(scope_name, exp) or NOOP
|
||||
named_assign or NOOP
|
||||
Run => @set "scope_var", scope_name
|
||||
unpack block
|
||||
}
|
||||
|
||||
if ret
|
||||
table.insert out[2], ret scope_name
|
||||
|
||||
out
|
||||
|
||||
foreach: (node, _) =>
|
||||
smart_node node
|
||||
source = unpack node.iter
|
||||
|
||||
destructures = {}
|
||||
node.names = for i, name in ipairs node.names
|
||||
if ntype(name) == "table"
|
||||
with proxy = NameProxy "des"
|
||||
insert destructures, destructure.build_assign @, name, proxy
|
||||
else
|
||||
name
|
||||
|
||||
if next destructures
|
||||
insert destructures, build.group node.body
|
||||
node.body = destructures
|
||||
|
||||
if ntype(source) == "unpack"
|
||||
list = source[2]
|
||||
|
||||
index_name = NameProxy "index"
|
||||
|
||||
list_name = @is_local(list) and list or NameProxy "list"
|
||||
|
||||
slice_var = nil
|
||||
bounds = if is_slice list
|
||||
slice = list[#list]
|
||||
table.remove list
|
||||
table.remove slice, 1
|
||||
|
||||
list_name = list if @is_local list
|
||||
|
||||
slice[2] = if slice[2] and slice[2] != ""
|
||||
max_tmp_name = NameProxy "max"
|
||||
slice_var = build.assign_one max_tmp_name, slice[2]
|
||||
{"exp", max_tmp_name, "<", 0
|
||||
"and", {"length", list_name}, "+", max_tmp_name
|
||||
"or", max_tmp_name }
|
||||
else
|
||||
{"length", list_name}
|
||||
|
||||
slice
|
||||
else
|
||||
{1, {"length", list_name}}
|
||||
|
||||
return build.group {
|
||||
list_name != list and build.assign_one(list_name, list) or NOOP
|
||||
slice_var or NOOP
|
||||
build["for"] {
|
||||
name: index_name
|
||||
bounds: bounds
|
||||
body: {
|
||||
{"assign", node.names, { NameProxy.index list_name, index_name }}
|
||||
build.group node.body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
while: (node) =>
|
||||
smart_node node
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
for: (node) =>
|
||||
smart_node node
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
switch: (node, ret) =>
|
||||
_, exp, conds = unpack node
|
||||
exp_name = NameProxy "exp"
|
||||
|
||||
-- convert switch conds into if statment conds
|
||||
convert_cond = (cond) ->
|
||||
t, case_exps, body = unpack cond
|
||||
out = {}
|
||||
insert out, t == "case" and "elseif" or "else"
|
||||
if t != "else"
|
||||
cond_exp = {}
|
||||
for i, case in ipairs case_exps
|
||||
if i == 1
|
||||
insert cond_exp, "exp"
|
||||
else
|
||||
insert cond_exp, "or"
|
||||
|
||||
case = {"parens", case} unless value_is_singular case
|
||||
insert cond_exp, {"exp", case, "==", exp_name}
|
||||
|
||||
insert out, cond_exp
|
||||
else
|
||||
body = case_exps
|
||||
|
||||
if ret
|
||||
body = transform_last_stm body, ret
|
||||
|
||||
insert out, body
|
||||
|
||||
out
|
||||
|
||||
first = true
|
||||
if_stm = {"if"}
|
||||
for cond in *conds
|
||||
if_cond = convert_cond cond
|
||||
if first
|
||||
first = false
|
||||
insert if_stm, if_cond[2]
|
||||
insert if_stm, if_cond[3]
|
||||
else
|
||||
insert if_stm, if_cond
|
||||
|
||||
build.group {
|
||||
build.assign_one exp_name, exp
|
||||
if_stm
|
||||
}
|
||||
|
||||
class: (node, ret, parent_assign) =>
|
||||
_, name, parent_val, body = unpack node
|
||||
parent_val = nil if parent_val == ""
|
||||
|
||||
-- split apart properties and statements
|
||||
statements = {}
|
||||
properties = {}
|
||||
for item in *body
|
||||
switch item[1]
|
||||
when "stm"
|
||||
insert statements, item[2]
|
||||
when "props"
|
||||
for tuple in *item[2,]
|
||||
if ntype(tuple[1]) == "self"
|
||||
insert statements, build.assign_one unpack tuple
|
||||
else
|
||||
insert properties, tuple
|
||||
|
||||
-- find constructor
|
||||
local constructor
|
||||
properties = for tuple in *properties
|
||||
key = tuple[1]
|
||||
if key[1] == "key_literal" and key[2] == constructor_name
|
||||
constructor = tuple[2]
|
||||
continue
|
||||
else
|
||||
tuple
|
||||
|
||||
parent_cls_name = NameProxy "parent"
|
||||
base_name = NameProxy "base"
|
||||
self_name = NameProxy "self"
|
||||
cls_name = NameProxy "class"
|
||||
|
||||
unless constructor
|
||||
constructor = if parent_val
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
arrow: "fat"
|
||||
body: {
|
||||
build.chain { base: "super", {"call", {"..."}} }
|
||||
}
|
||||
}
|
||||
else
|
||||
build.fndef!
|
||||
|
||||
real_name = name or parent_assign and parent_assign[2][1]
|
||||
real_name = switch ntype real_name
|
||||
when "chain"
|
||||
last = real_name[#real_name]
|
||||
switch ntype last
|
||||
when "dot"
|
||||
{"string", '"', last[2]}
|
||||
when "index"
|
||||
last[2]
|
||||
else
|
||||
"nil"
|
||||
when "nil"
|
||||
"nil"
|
||||
else
|
||||
name_t = type real_name
|
||||
-- TODO: don't use string literal as ref
|
||||
flattened_name = if name_t == "string"
|
||||
real_name
|
||||
elseif name_t == "table" and real_name[1] == "ref"
|
||||
real_name[2]
|
||||
else
|
||||
error "don't know how to extract name from #{name_t}"
|
||||
|
||||
{"string", '"', flattened_name}
|
||||
|
||||
cls = build.table {
|
||||
{"__init", constructor}
|
||||
{"__base", base_name}
|
||||
{"__name", real_name} -- "quote the string"
|
||||
parent_val and {"__parent", parent_cls_name} or nil
|
||||
}
|
||||
|
||||
-- looking up a name in the class object
|
||||
class_index = if parent_val
|
||||
class_lookup = build["if"] {
|
||||
cond: { "exp", {"ref", "val"}, "==", "nil" }
|
||||
then: {
|
||||
build.assign_one LocalName"parent", build.chain {
|
||||
base: "rawget"
|
||||
{
|
||||
"call", {
|
||||
{"ref", "cls"}
|
||||
{"string", '"', "__parent"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build.if {
|
||||
cond: LocalName "parent"
|
||||
then: {
|
||||
build.chain {
|
||||
base: LocalName "parent"
|
||||
{"index", "name"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
insert class_lookup, {"else", {"val"}}
|
||||
|
||||
build.fndef {
|
||||
args: {{"cls"}, {"name"}}
|
||||
body: {
|
||||
build.assign_one LocalName"val", build.chain {
|
||||
base: "rawget", {"call", {base_name, {"ref", "name"}}}
|
||||
}
|
||||
class_lookup
|
||||
}
|
||||
}
|
||||
else
|
||||
base_name
|
||||
|
||||
cls_mt = build.table {
|
||||
{"__index", class_index}
|
||||
{"__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
|
||||
out_body = {
|
||||
Run =>
|
||||
-- make sure we don't assign the class to a local inside the do
|
||||
@put_name name if name
|
||||
|
||||
@set "super", (block, chain) ->
|
||||
relative_parent = {
|
||||
"chain",
|
||||
cls_name
|
||||
{"dot", "__parent"}
|
||||
}
|
||||
|
||||
return relative_parent unless chain
|
||||
|
||||
chain_tail = { unpack chain, 3 }
|
||||
head = chain_tail[1]
|
||||
|
||||
if head == nil
|
||||
return relative_parent
|
||||
|
||||
new_chain = relative_parent
|
||||
|
||||
switch head[1]
|
||||
-- calling super, inject calling name and self into chain
|
||||
when "call"
|
||||
calling_name = block\get"current_block"
|
||||
assert calling_name, "missing calling name"
|
||||
chain_tail[1] = {"call", {"self", unpack head[2]}}
|
||||
|
||||
if ntype(calling_name) == "key_literal"
|
||||
insert new_chain, {"dot", calling_name[2]}
|
||||
else
|
||||
insert new_chain, {"index", calling_name}
|
||||
|
||||
-- colon call on super, replace class with self as first arg
|
||||
when "colon"
|
||||
call = chain_tail[2]
|
||||
-- calling chain tail
|
||||
if call and call[1] == "call"
|
||||
chain_tail[1] = {
|
||||
"dot"
|
||||
head[2]
|
||||
}
|
||||
|
||||
chain_tail[2] = {
|
||||
"call"
|
||||
{
|
||||
"self"
|
||||
unpack call[2]
|
||||
}
|
||||
}
|
||||
|
||||
insert new_chain, item for item in *chain_tail
|
||||
new_chain
|
||||
|
||||
{"declare", { cls_name }}
|
||||
{"declare_glob", "*"}
|
||||
|
||||
parent_val and .assign_one(parent_cls_name, parent_val) or NOOP
|
||||
|
||||
.assign_one base_name, {"table", properties}
|
||||
.assign_one base_name\chain"__index", base_name
|
||||
|
||||
parent_val and .chain({
|
||||
base: "setmetatable"
|
||||
{"call", {
|
||||
base_name,
|
||||
.chain { base: parent_cls_name, {"dot", "__base"}}
|
||||
}}
|
||||
}) or NOOP
|
||||
|
||||
.assign_one cls_name, cls
|
||||
.assign_one base_name\chain"__class", cls_name
|
||||
|
||||
.group if #statements > 0 then {
|
||||
.assign_one LocalName"self", cls_name
|
||||
.group statements
|
||||
}
|
||||
|
||||
-- run the inherited callback
|
||||
parent_val and .if({
|
||||
cond: {"exp", parent_cls_name\chain "__inherited" }
|
||||
then: {
|
||||
parent_cls_name\chain "__inherited", {"call", {
|
||||
parent_cls_name, cls_name
|
||||
}}
|
||||
}
|
||||
}) or NOOP
|
||||
|
||||
.group if name then {
|
||||
.assign_one name, cls_name
|
||||
}
|
||||
|
||||
if ret
|
||||
ret cls_name
|
||||
}
|
||||
|
||||
value = .group {
|
||||
.group if ntype(name) == "value" then {
|
||||
.declare names: {name}
|
||||
}
|
||||
|
||||
.do out_body
|
||||
}
|
||||
|
||||
value
|
||||
{
|
||||
Statement: require "moonscript.transform.statement"
|
||||
Value: require "moonscript.transform.value"
|
||||
}
|
||||
|
||||
class Accumulator
|
||||
body_idx: { for: 4, while: 3, foreach: 4 }
|
||||
|
||||
new: (accum_name) =>
|
||||
@accum_name = NameProxy "accum"
|
||||
@value_name = NameProxy "value"
|
||||
@len_name = NameProxy "len"
|
||||
|
||||
-- wraps node and mutates body
|
||||
convert: (node) =>
|
||||
index = @body_idx[ntype node]
|
||||
node[index] = @mutate_body node[index]
|
||||
@wrap node
|
||||
|
||||
-- wrap the node into a block_exp
|
||||
wrap: (node, group_type="block_exp") =>
|
||||
build[group_type] {
|
||||
build.assign_one @accum_name, build.table!
|
||||
build.assign_one @len_name, 1
|
||||
node
|
||||
group_type == "block_exp" and @accum_name or NOOP
|
||||
}
|
||||
|
||||
-- mutates the body of a loop construct to save last value into accumulator
|
||||
mutate_body: (body) =>
|
||||
-- shortcut to write simpler code if body is a single expression
|
||||
single_stm = is_singular body
|
||||
val = if single_stm and types.is_value single_stm
|
||||
body = {}
|
||||
single_stm
|
||||
else
|
||||
body = transform_last_stm body, (n) ->
|
||||
if types.is_value n
|
||||
build.assign_one @value_name, n
|
||||
else
|
||||
-- just ignore it
|
||||
build.group {
|
||||
{"declare", {@value_name}}
|
||||
n
|
||||
}
|
||||
@value_name
|
||||
|
||||
update = {
|
||||
build.assign_one NameProxy.index(@accum_name, @len_name), val
|
||||
{"update", @len_name, "+=", 1}
|
||||
}
|
||||
|
||||
insert body, build.group update
|
||||
body
|
||||
|
||||
default_accumulator = (node) =>
|
||||
Accumulator!\convert node
|
||||
|
||||
implicitly_return = (scope) ->
|
||||
is_top = true
|
||||
fn = (stm) ->
|
||||
t = ntype stm
|
||||
|
||||
-- expand decorated
|
||||
if t == "decorated"
|
||||
stm = scope.transform.statement stm
|
||||
t = ntype stm
|
||||
|
||||
if types.cascading[t]
|
||||
is_top = false
|
||||
scope.transform.statement stm, fn
|
||||
elseif types.manual_return[t] or not types.is_value stm
|
||||
-- remove blank return statement
|
||||
if is_top and t == "return" and stm[2] == ""
|
||||
NOOP
|
||||
else
|
||||
stm
|
||||
else
|
||||
if t == "comprehension" and not types.comprehension_has_value stm
|
||||
stm
|
||||
else
|
||||
{"return", stm}
|
||||
|
||||
fn
|
||||
|
||||
Value = Transformer {
|
||||
for: default_accumulator
|
||||
while: default_accumulator
|
||||
foreach: default_accumulator
|
||||
|
||||
do: (node) =>
|
||||
build.block_exp node[2]
|
||||
|
||||
decorated: (node) =>
|
||||
@transform.statement node
|
||||
|
||||
class: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
string: (node) =>
|
||||
delim = node[2]
|
||||
|
||||
convert_part = (part) ->
|
||||
if type(part) == "string" or part == nil
|
||||
{"string", delim, part or ""}
|
||||
else
|
||||
build.chain { base: "tostring", {"call", {part[2]}} }
|
||||
|
||||
-- reduced to single item
|
||||
if #node <= 3
|
||||
return if type(node[3]) == "string"
|
||||
node
|
||||
else
|
||||
convert_part node[3]
|
||||
|
||||
e = {"exp", convert_part node[3]}
|
||||
|
||||
for i=4, #node
|
||||
insert e, ".."
|
||||
insert e, convert_part node[i]
|
||||
e
|
||||
|
||||
comprehension: (node) =>
|
||||
a = Accumulator!
|
||||
node = @transform.statement node, (exp) ->
|
||||
a\mutate_body {exp}
|
||||
a\wrap node
|
||||
|
||||
tblcomprehension: (node) =>
|
||||
_, explist, clauses = unpack node
|
||||
key_exp, value_exp = unpack explist
|
||||
|
||||
accum = NameProxy "tbl"
|
||||
|
||||
inner = if value_exp
|
||||
dest = build.chain { base: accum, {"index", key_exp} }
|
||||
{ build.assign_one dest, value_exp }
|
||||
else
|
||||
-- If we only have single expression then
|
||||
-- unpack the result into key and value
|
||||
key_name, val_name = NameProxy"key", NameProxy"val"
|
||||
dest = build.chain { base: accum, {"index", key_name} }
|
||||
{
|
||||
build.assign names: {key_name, val_name}, values: {key_exp}
|
||||
build.assign_one dest, val_name
|
||||
}
|
||||
|
||||
build.block_exp {
|
||||
build.assign_one accum, build.table!
|
||||
construct_comprehension inner, clauses
|
||||
accum
|
||||
}
|
||||
|
||||
fndef: (node) =>
|
||||
smart_node node
|
||||
node.body = transform_last_stm node.body, implicitly_return self
|
||||
node.body = {
|
||||
Run => @listen "varargs", -> -- capture event
|
||||
unpack node.body
|
||||
}
|
||||
|
||||
node
|
||||
|
||||
if: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
unless: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
with: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
switch: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
-- pull out colon chain
|
||||
chain: (node) =>
|
||||
-- escape lua keywords used in dot accessors
|
||||
for i=2,#node
|
||||
part = node[i]
|
||||
if ntype(part) == "dot" and data.lua_keywords[part[2]]
|
||||
node[i] = { "index", {"string", '"', part[2]} }
|
||||
|
||||
if ntype(node[2]) == "string"
|
||||
-- add parens if callee is raw string
|
||||
node[2] = {"parens", node[2] }
|
||||
|
||||
if chain_is_stub node
|
||||
base_name = NameProxy "base"
|
||||
fn_name = NameProxy "fn"
|
||||
colon = table.remove node
|
||||
|
||||
is_super = ntype(node[2]) == "ref" and node[2][2] == "super"
|
||||
build.block_exp {
|
||||
build.assign {
|
||||
names: {base_name}
|
||||
values: {node}
|
||||
}
|
||||
|
||||
build.assign {
|
||||
names: {fn_name}
|
||||
values: {
|
||||
build.chain { base: base_name, {"dot", colon[2]} }
|
||||
}
|
||||
}
|
||||
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
body: {
|
||||
build.chain {
|
||||
base: fn_name, {"call", {is_super and "self" or base_name, "..."}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_exp: (node) =>
|
||||
_, body = unpack node
|
||||
|
||||
fn = nil
|
||||
arg_list = {}
|
||||
|
||||
fn = smart_node build.fndef body: {
|
||||
Run =>
|
||||
@listen "varargs", ->
|
||||
insert arg_list, "..."
|
||||
insert fn.args, {"..."}
|
||||
@unlisten "varargs"
|
||||
|
||||
unpack body
|
||||
}
|
||||
|
||||
build.chain { base: {"parens", fn}, {"call", arg_list} }
|
||||
}
|
||||
|
||||
{ :Statement, :Value, :Run }
|
||||
|
110
moonscript/transform/accumulator.lua
Normal file
110
moonscript/transform/accumulator.lua
Normal file
@ -0,0 +1,110 @@
|
||||
local types = require("moonscript.types")
|
||||
local build, ntype, NOOP
|
||||
build, ntype, NOOP = types.build, types.ntype, types.NOOP
|
||||
local NameProxy
|
||||
NameProxy = require("moonscript.transform.names").NameProxy
|
||||
local insert
|
||||
insert = table.insert
|
||||
local is_singular
|
||||
is_singular = function(body)
|
||||
if #body ~= 1 then
|
||||
return false
|
||||
end
|
||||
if "group" == ntype(body) then
|
||||
return is_singular(body[2])
|
||||
else
|
||||
return body[1]
|
||||
end
|
||||
end
|
||||
local transform_last_stm
|
||||
transform_last_stm = require("moonscript.transform.statements").transform_last_stm
|
||||
local Accumulator
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
body_idx = {
|
||||
["for"] = 4,
|
||||
["while"] = 3,
|
||||
foreach = 4
|
||||
},
|
||||
convert = function(self, node)
|
||||
local index = self.body_idx[ntype(node)]
|
||||
node[index] = self:mutate_body(node[index])
|
||||
return self:wrap(node)
|
||||
end,
|
||||
wrap = function(self, node, group_type)
|
||||
if group_type == nil then
|
||||
group_type = "block_exp"
|
||||
end
|
||||
return build[group_type]({
|
||||
build.assign_one(self.accum_name, build.table()),
|
||||
build.assign_one(self.len_name, 1),
|
||||
node,
|
||||
group_type == "block_exp" and self.accum_name or NOOP
|
||||
})
|
||||
end,
|
||||
mutate_body = function(self, body)
|
||||
local single_stm = is_singular(body)
|
||||
local val
|
||||
if single_stm and types.is_value(single_stm) then
|
||||
body = { }
|
||||
val = single_stm
|
||||
else
|
||||
body = transform_last_stm(body, function(n)
|
||||
if types.is_value(n) then
|
||||
return build.assign_one(self.value_name, n)
|
||||
else
|
||||
return build.group({
|
||||
{
|
||||
"declare",
|
||||
{
|
||||
self.value_name
|
||||
}
|
||||
},
|
||||
n
|
||||
})
|
||||
end
|
||||
end)
|
||||
val = self.value_name
|
||||
end
|
||||
local update = {
|
||||
build.assign_one(NameProxy.index(self.accum_name, self.len_name), val),
|
||||
{
|
||||
"update",
|
||||
self.len_name,
|
||||
"+=",
|
||||
1
|
||||
}
|
||||
}
|
||||
insert(body, build.group(update))
|
||||
return body
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, accum_name)
|
||||
self.accum_name = NameProxy("accum")
|
||||
self.value_name = NameProxy("value")
|
||||
self.len_name = NameProxy("len")
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Accumulator"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Accumulator = _class_0
|
||||
end
|
||||
local default_accumulator
|
||||
default_accumulator = function(self, node)
|
||||
return Accumulator():convert(node)
|
||||
end
|
||||
return {
|
||||
Accumulator = Accumulator,
|
||||
default_accumulator = default_accumulator
|
||||
}
|
71
moonscript/transform/accumulator.moon
Normal file
71
moonscript/transform/accumulator.moon
Normal file
@ -0,0 +1,71 @@
|
||||
types = require "moonscript.types"
|
||||
|
||||
import build, ntype, NOOP from types
|
||||
import NameProxy from require "moonscript.transform.names"
|
||||
|
||||
import insert from table
|
||||
|
||||
-- is a body a single expression/statement
|
||||
is_singular = (body) ->
|
||||
return false if #body != 1
|
||||
if "group" == ntype body
|
||||
is_singular body[2]
|
||||
else
|
||||
body[1]
|
||||
|
||||
import transform_last_stm from require "moonscript.transform.statements"
|
||||
|
||||
class Accumulator
|
||||
body_idx: { for: 4, while: 3, foreach: 4 }
|
||||
|
||||
new: (accum_name) =>
|
||||
@accum_name = NameProxy "accum"
|
||||
@value_name = NameProxy "value"
|
||||
@len_name = NameProxy "len"
|
||||
|
||||
-- wraps node and mutates body
|
||||
convert: (node) =>
|
||||
index = @body_idx[ntype node]
|
||||
node[index] = @mutate_body node[index]
|
||||
@wrap node
|
||||
|
||||
-- wrap the node into a block_exp
|
||||
wrap: (node, group_type="block_exp") =>
|
||||
build[group_type] {
|
||||
build.assign_one @accum_name, build.table!
|
||||
build.assign_one @len_name, 1
|
||||
node
|
||||
group_type == "block_exp" and @accum_name or NOOP
|
||||
}
|
||||
|
||||
-- mutates the body of a loop construct to save last value into accumulator
|
||||
mutate_body: (body) =>
|
||||
-- shortcut to write simpler code if body is a single expression
|
||||
single_stm = is_singular body
|
||||
val = if single_stm and types.is_value single_stm
|
||||
body = {}
|
||||
single_stm
|
||||
else
|
||||
body = transform_last_stm body, (n) ->
|
||||
if types.is_value n
|
||||
build.assign_one @value_name, n
|
||||
else
|
||||
-- just ignore it
|
||||
build.group {
|
||||
{"declare", {@value_name}}
|
||||
n
|
||||
}
|
||||
@value_name
|
||||
|
||||
update = {
|
||||
build.assign_one NameProxy.index(@accum_name, @len_name), val
|
||||
{"update", @len_name, "+=", 1}
|
||||
}
|
||||
|
||||
insert body, build.group update
|
||||
body
|
||||
|
||||
default_accumulator = (node) =>
|
||||
Accumulator!\convert node
|
||||
|
||||
{ :Accumulator, :default_accumulator }
|
1161
moonscript/transform/statement.lua
Normal file
1161
moonscript/transform/statement.lua
Normal file
File diff suppressed because it is too large
Load Diff
720
moonscript/transform/statement.moon
Normal file
720
moonscript/transform/statement.moon
Normal file
@ -0,0 +1,720 @@
|
||||
import Transformer from require "moonscript.transform.transformer"
|
||||
|
||||
import NameProxy, LocalName from require "moonscript.transform.names"
|
||||
|
||||
import Run, transform_last_stm, implicitly_return, construct_comprehension,
|
||||
last_stm from require "moonscript.transform.statements"
|
||||
|
||||
types = require "moonscript.types"
|
||||
|
||||
import build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP
|
||||
from types
|
||||
|
||||
import insert from table
|
||||
|
||||
destructure = require "moonscript.transform.destructure"
|
||||
|
||||
CONSTRUCTOR_NAME = "new"
|
||||
|
||||
with_continue_listener = (body) ->
|
||||
continue_name = nil
|
||||
|
||||
{
|
||||
Run =>
|
||||
@listen "continue", ->
|
||||
unless continue_name
|
||||
continue_name = NameProxy"continue"
|
||||
@put_name continue_name
|
||||
continue_name
|
||||
|
||||
build.group body
|
||||
|
||||
Run =>
|
||||
return unless continue_name
|
||||
last = last_stm body
|
||||
enclose_lines = types.terminating[last and ntype(last)]
|
||||
|
||||
@put_name continue_name, nil
|
||||
@splice (lines) ->
|
||||
lines = {"do", {lines}} if enclose_lines
|
||||
|
||||
{
|
||||
{"assign", {continue_name}, {"false"}}
|
||||
{"repeat", "true", {
|
||||
lines
|
||||
{"assign", {continue_name}, {"true"}}
|
||||
}}
|
||||
{"if", {"not", continue_name}, {
|
||||
{"break"}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- this mutates body searching for assigns
|
||||
extract_declarations = (body=@current_stms, start=@current_stm_i + 1, out={}) =>
|
||||
for i=start,#body
|
||||
stm = body[i]
|
||||
continue if stm == nil
|
||||
stm = @transform.statement stm
|
||||
body[i] = stm
|
||||
switch stm[1]
|
||||
when "assign", "declare"
|
||||
for name in *stm[2]
|
||||
if ntype(name) == "ref"
|
||||
insert out, name
|
||||
elseif type(name) == "string"
|
||||
-- TODO: don't use string literal as ref
|
||||
insert out, name
|
||||
when "group"
|
||||
extract_declarations @, stm[2], 1, out
|
||||
out
|
||||
|
||||
expand_elseif_assign = (ifstm) ->
|
||||
for i = 4, #ifstm
|
||||
case = ifstm[i]
|
||||
if ntype(case) == "elseif" and ntype(case[2]) == "assign"
|
||||
split = { unpack ifstm, 1, i - 1 }
|
||||
insert split, {
|
||||
"else", {
|
||||
{"if", case[2], case[3], unpack ifstm, i + 1}
|
||||
}
|
||||
}
|
||||
return split
|
||||
|
||||
ifstm
|
||||
|
||||
|
||||
Transformer {
|
||||
transform: (tuple) =>
|
||||
{_, node, fn} = tuple
|
||||
fn node
|
||||
|
||||
root_stms: (body) =>
|
||||
transform_last_stm body, implicitly_return @
|
||||
|
||||
return: (node) =>
|
||||
ret_val = node[2]
|
||||
ret_val_type = ntype ret_val
|
||||
|
||||
if ret_val_type == "explist" and #ret_val == 2
|
||||
ret_val = ret_val[2]
|
||||
ret_val_type = ntype ret_val
|
||||
|
||||
if types.cascading[ret_val_type]
|
||||
return implicitly_return(@) ret_val
|
||||
|
||||
-- flatten things that create block exp
|
||||
if ret_val_type == "chain" or ret_val_type == "comprehension" or ret_val_type == "tblcomprehension"
|
||||
-- TODO: clean this up
|
||||
Value = require "moonscript.transform.value"
|
||||
ret_val = Value\transform_once @, ret_val
|
||||
if ntype(ret_val) == "block_exp"
|
||||
return build.group transform_last_stm ret_val[2], (stm)->
|
||||
{"return", stm}
|
||||
|
||||
node[2] = ret_val
|
||||
node
|
||||
|
||||
declare_glob: (node) =>
|
||||
names = extract_declarations @
|
||||
|
||||
if node[2] == "^"
|
||||
names = for name in *names
|
||||
continue unless name[2]\match "^%u"
|
||||
name
|
||||
|
||||
{"declare", names}
|
||||
|
||||
assign: (node) =>
|
||||
names, values = unpack node, 2
|
||||
|
||||
num_values = #values
|
||||
num_names = #values
|
||||
|
||||
-- special code simplifications for single assigns
|
||||
if num_names == 1 and num_values == 1
|
||||
first_value = values[1]
|
||||
first_name = names[1]
|
||||
first_type = ntype first_value
|
||||
|
||||
-- reduce colon stub chain to block exp
|
||||
if first_type == "chain"
|
||||
-- TODO: clean this up
|
||||
Value = require "moonscript.transform.value"
|
||||
first_value = Value\transform_once @, first_value
|
||||
first_type = ntype first_value
|
||||
|
||||
switch ntype first_value
|
||||
when "block_exp"
|
||||
block_body = first_value[2]
|
||||
idx = #block_body
|
||||
block_body[idx] = build.assign_one first_name, block_body[idx]
|
||||
|
||||
return build.group {
|
||||
{"declare", {first_name}}
|
||||
{"do", block_body}
|
||||
}
|
||||
|
||||
when "comprehension", "tblcomprehension", "foreach", "for", "while"
|
||||
-- TODO: clean this up
|
||||
Value = require "moonscript.transform.value"
|
||||
return build.assign_one first_name, Value\transform_once @, first_value
|
||||
else
|
||||
values[1] = first_value
|
||||
|
||||
-- bubble cascading assigns
|
||||
transformed = if num_values == 1
|
||||
value = values[1]
|
||||
t = ntype value
|
||||
|
||||
if t == "decorated"
|
||||
value = @transform.statement value
|
||||
t = ntype value
|
||||
|
||||
if types.cascading[t]
|
||||
ret = (stm) ->
|
||||
if is_value stm
|
||||
{"assign", names, {stm}}
|
||||
else
|
||||
stm
|
||||
|
||||
build.group {
|
||||
{"declare", names}
|
||||
@transform.statement value, ret, node
|
||||
}
|
||||
|
||||
node = transformed or node
|
||||
|
||||
if destructure.has_destructure names
|
||||
return destructure.split_assign @, node
|
||||
|
||||
node
|
||||
|
||||
continue: (node) =>
|
||||
continue_name = @send "continue"
|
||||
error "continue must be inside of a loop" unless continue_name
|
||||
build.group {
|
||||
build.assign_one continue_name, "true"
|
||||
{"break"}
|
||||
}
|
||||
|
||||
export: (node) =>
|
||||
-- assign values if they are included
|
||||
if #node > 2
|
||||
if node[2] == "class"
|
||||
cls = smart_node node[3]
|
||||
build.group {
|
||||
{"export", {cls.name}}
|
||||
cls
|
||||
}
|
||||
else
|
||||
-- pull out vawlues and assign them after the export
|
||||
build.group {
|
||||
{ "export", node[2] }
|
||||
build.assign {
|
||||
names: node[2]
|
||||
values: node[3]
|
||||
}
|
||||
}
|
||||
else
|
||||
nil
|
||||
|
||||
update: (node) =>
|
||||
_, name, op, exp = unpack node
|
||||
op_final = op\match "^(.+)=$"
|
||||
error "Unknown op: "..op if not op_final
|
||||
exp = {"parens", exp} unless value_is_singular exp
|
||||
build.assign_one name, {"exp", name, op_final, exp}
|
||||
|
||||
import: (node) =>
|
||||
_, names, source = unpack node
|
||||
table_values = for name in *names
|
||||
dest_name = if ntype(name) == "colon"
|
||||
name[2]
|
||||
else
|
||||
name
|
||||
|
||||
{{"key_literal", name}, dest_name}
|
||||
|
||||
dest = { "table", table_values }
|
||||
{ "assign", {dest}, {source}, [-1]: node[-1] }
|
||||
|
||||
comprehension: (node, action) =>
|
||||
_, exp, clauses = unpack node
|
||||
|
||||
action = action or (exp) -> {exp}
|
||||
construct_comprehension action(exp), clauses
|
||||
|
||||
do: (node, ret) =>
|
||||
node[2] = transform_last_stm node[2], ret if ret
|
||||
node
|
||||
|
||||
decorated: (node) =>
|
||||
stm, dec = unpack node, 2
|
||||
|
||||
wrapped = switch dec[1]
|
||||
when "if"
|
||||
cond, fail = unpack dec, 2
|
||||
fail = { "else", { fail } } if fail
|
||||
{ "if", cond, { stm }, fail }
|
||||
when "unless"
|
||||
{ "unless", dec[2], { stm } }
|
||||
when "comprehension"
|
||||
{ "comprehension", stm, dec[2] }
|
||||
else
|
||||
error "Unknown decorator " .. dec[1]
|
||||
|
||||
if ntype(stm) == "assign"
|
||||
wrapped = build.group {
|
||||
build.declare names: [name for name in *stm[2] when ntype(name) == "ref"]
|
||||
wrapped
|
||||
}
|
||||
|
||||
wrapped
|
||||
|
||||
unless: (node) =>
|
||||
{ "if", {"not", {"parens", node[2]}}, unpack node, 3 }
|
||||
|
||||
if: (node, ret) =>
|
||||
-- expand assign in cond
|
||||
if ntype(node[2]) == "assign"
|
||||
_, assign, body = unpack node
|
||||
if destructure.has_destructure assign[2]
|
||||
name = NameProxy "des"
|
||||
|
||||
body = {
|
||||
destructure.build_assign @, assign[2][1], name
|
||||
build.group node[3]
|
||||
}
|
||||
|
||||
return build.do {
|
||||
build.assign_one name, assign[3][1]
|
||||
{"if", name, body, unpack node, 4}
|
||||
}
|
||||
else
|
||||
name = assign[2][1]
|
||||
return build["do"] {
|
||||
assign
|
||||
{"if", name, unpack node, 3}
|
||||
}
|
||||
|
||||
node = expand_elseif_assign node
|
||||
|
||||
-- apply cascading return decorator
|
||||
if ret
|
||||
smart_node node
|
||||
-- mutate all the bodies
|
||||
node['then'] = transform_last_stm node['then'], ret
|
||||
for i = 4, #node
|
||||
case = node[i]
|
||||
body_idx = #node[i]
|
||||
case[body_idx] = transform_last_stm case[body_idx], ret
|
||||
|
||||
node
|
||||
|
||||
with: (node, ret) =>
|
||||
exp, block = unpack node, 2
|
||||
|
||||
copy_scope = true
|
||||
local scope_name, named_assign
|
||||
|
||||
if last = last_stm block
|
||||
ret = false if types.terminating[ntype(last)]
|
||||
|
||||
if ntype(exp) == "assign"
|
||||
names, values = unpack exp, 2
|
||||
first_name = names[1]
|
||||
|
||||
if ntype(first_name) == "ref"
|
||||
scope_name = first_name
|
||||
named_assign = exp
|
||||
exp = values[1]
|
||||
copy_scope = false
|
||||
else
|
||||
scope_name = NameProxy "with"
|
||||
exp = values[1]
|
||||
values[1] = scope_name
|
||||
named_assign = {"assign", names, values}
|
||||
|
||||
elseif @is_local exp
|
||||
scope_name = exp
|
||||
copy_scope = false
|
||||
|
||||
scope_name or= NameProxy "with"
|
||||
|
||||
out = build.do {
|
||||
copy_scope and build.assign_one(scope_name, exp) or NOOP
|
||||
named_assign or NOOP
|
||||
Run => @set "scope_var", scope_name
|
||||
unpack block
|
||||
}
|
||||
|
||||
if ret
|
||||
table.insert out[2], ret scope_name
|
||||
|
||||
out
|
||||
|
||||
foreach: (node, _) =>
|
||||
smart_node node
|
||||
source = unpack node.iter
|
||||
|
||||
destructures = {}
|
||||
node.names = for i, name in ipairs node.names
|
||||
if ntype(name) == "table"
|
||||
with proxy = NameProxy "des"
|
||||
insert destructures, destructure.build_assign @, name, proxy
|
||||
else
|
||||
name
|
||||
|
||||
if next destructures
|
||||
insert destructures, build.group node.body
|
||||
node.body = destructures
|
||||
|
||||
if ntype(source) == "unpack"
|
||||
list = source[2]
|
||||
|
||||
index_name = NameProxy "index"
|
||||
|
||||
list_name = @is_local(list) and list or NameProxy "list"
|
||||
|
||||
slice_var = nil
|
||||
bounds = if is_slice list
|
||||
slice = list[#list]
|
||||
table.remove list
|
||||
table.remove slice, 1
|
||||
|
||||
list_name = list if @is_local list
|
||||
|
||||
slice[2] = if slice[2] and slice[2] != ""
|
||||
max_tmp_name = NameProxy "max"
|
||||
slice_var = build.assign_one max_tmp_name, slice[2]
|
||||
{"exp", max_tmp_name, "<", 0
|
||||
"and", {"length", list_name}, "+", max_tmp_name
|
||||
"or", max_tmp_name }
|
||||
else
|
||||
{"length", list_name}
|
||||
|
||||
slice
|
||||
else
|
||||
{1, {"length", list_name}}
|
||||
|
||||
return build.group {
|
||||
list_name != list and build.assign_one(list_name, list) or NOOP
|
||||
slice_var or NOOP
|
||||
build["for"] {
|
||||
name: index_name
|
||||
bounds: bounds
|
||||
body: {
|
||||
{"assign", node.names, { NameProxy.index list_name, index_name }}
|
||||
build.group node.body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
while: (node) =>
|
||||
smart_node node
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
for: (node) =>
|
||||
smart_node node
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
switch: (node, ret) =>
|
||||
_, exp, conds = unpack node
|
||||
exp_name = NameProxy "exp"
|
||||
|
||||
-- convert switch conds into if statment conds
|
||||
convert_cond = (cond) ->
|
||||
t, case_exps, body = unpack cond
|
||||
out = {}
|
||||
insert out, t == "case" and "elseif" or "else"
|
||||
if t != "else"
|
||||
cond_exp = {}
|
||||
for i, case in ipairs case_exps
|
||||
if i == 1
|
||||
insert cond_exp, "exp"
|
||||
else
|
||||
insert cond_exp, "or"
|
||||
|
||||
case = {"parens", case} unless value_is_singular case
|
||||
insert cond_exp, {"exp", case, "==", exp_name}
|
||||
|
||||
insert out, cond_exp
|
||||
else
|
||||
body = case_exps
|
||||
|
||||
if ret
|
||||
body = transform_last_stm body, ret
|
||||
|
||||
insert out, body
|
||||
|
||||
out
|
||||
|
||||
first = true
|
||||
if_stm = {"if"}
|
||||
for cond in *conds
|
||||
if_cond = convert_cond cond
|
||||
if first
|
||||
first = false
|
||||
insert if_stm, if_cond[2]
|
||||
insert if_stm, if_cond[3]
|
||||
else
|
||||
insert if_stm, if_cond
|
||||
|
||||
build.group {
|
||||
build.assign_one exp_name, exp
|
||||
if_stm
|
||||
}
|
||||
|
||||
class: (node, ret, parent_assign) =>
|
||||
_, name, parent_val, body = unpack node
|
||||
parent_val = nil if parent_val == ""
|
||||
|
||||
-- split apart properties and statements
|
||||
statements = {}
|
||||
properties = {}
|
||||
for item in *body
|
||||
switch item[1]
|
||||
when "stm"
|
||||
insert statements, item[2]
|
||||
when "props"
|
||||
for tuple in *item[2,]
|
||||
if ntype(tuple[1]) == "self"
|
||||
insert statements, build.assign_one unpack tuple
|
||||
else
|
||||
insert properties, tuple
|
||||
|
||||
-- find constructor
|
||||
local constructor
|
||||
properties = for tuple in *properties
|
||||
key = tuple[1]
|
||||
if key[1] == "key_literal" and key[2] == CONSTRUCTOR_NAME
|
||||
constructor = tuple[2]
|
||||
continue
|
||||
else
|
||||
tuple
|
||||
|
||||
parent_cls_name = NameProxy "parent"
|
||||
base_name = NameProxy "base"
|
||||
self_name = NameProxy "self"
|
||||
cls_name = NameProxy "class"
|
||||
|
||||
unless constructor
|
||||
constructor = if parent_val
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
arrow: "fat"
|
||||
body: {
|
||||
build.chain { base: "super", {"call", {"..."}} }
|
||||
}
|
||||
}
|
||||
else
|
||||
build.fndef!
|
||||
|
||||
real_name = name or parent_assign and parent_assign[2][1]
|
||||
real_name = switch ntype real_name
|
||||
when "chain"
|
||||
last = real_name[#real_name]
|
||||
switch ntype last
|
||||
when "dot"
|
||||
{"string", '"', last[2]}
|
||||
when "index"
|
||||
last[2]
|
||||
else
|
||||
"nil"
|
||||
when "nil"
|
||||
"nil"
|
||||
else
|
||||
name_t = type real_name
|
||||
-- TODO: don't use string literal as ref
|
||||
flattened_name = if name_t == "string"
|
||||
real_name
|
||||
elseif name_t == "table" and real_name[1] == "ref"
|
||||
real_name[2]
|
||||
else
|
||||
error "don't know how to extract name from #{name_t}"
|
||||
|
||||
{"string", '"', flattened_name}
|
||||
|
||||
cls = build.table {
|
||||
{"__init", constructor}
|
||||
{"__base", base_name}
|
||||
{"__name", real_name} -- "quote the string"
|
||||
parent_val and {"__parent", parent_cls_name} or nil
|
||||
}
|
||||
|
||||
-- looking up a name in the class object
|
||||
class_index = if parent_val
|
||||
class_lookup = build["if"] {
|
||||
cond: { "exp", {"ref", "val"}, "==", "nil" }
|
||||
then: {
|
||||
build.assign_one LocalName"parent", build.chain {
|
||||
base: "rawget"
|
||||
{
|
||||
"call", {
|
||||
{"ref", "cls"}
|
||||
{"string", '"', "__parent"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build.if {
|
||||
cond: LocalName "parent"
|
||||
then: {
|
||||
build.chain {
|
||||
base: LocalName "parent"
|
||||
{"index", "name"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
insert class_lookup, {"else", {"val"}}
|
||||
|
||||
build.fndef {
|
||||
args: {{"cls"}, {"name"}}
|
||||
body: {
|
||||
build.assign_one LocalName"val", build.chain {
|
||||
base: "rawget", {"call", {base_name, {"ref", "name"}}}
|
||||
}
|
||||
class_lookup
|
||||
}
|
||||
}
|
||||
else
|
||||
base_name
|
||||
|
||||
cls_mt = build.table {
|
||||
{"__index", class_index}
|
||||
{"__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
|
||||
out_body = {
|
||||
Run =>
|
||||
-- make sure we don't assign the class to a local inside the do
|
||||
@put_name name if name
|
||||
|
||||
@set "super", (block, chain) ->
|
||||
relative_parent = {
|
||||
"chain",
|
||||
cls_name
|
||||
{"dot", "__parent"}
|
||||
}
|
||||
|
||||
return relative_parent unless chain
|
||||
|
||||
chain_tail = { unpack chain, 3 }
|
||||
head = chain_tail[1]
|
||||
|
||||
if head == nil
|
||||
return relative_parent
|
||||
|
||||
new_chain = relative_parent
|
||||
|
||||
switch head[1]
|
||||
-- calling super, inject calling name and self into chain
|
||||
when "call"
|
||||
calling_name = block\get"current_block"
|
||||
assert calling_name, "missing calling name"
|
||||
chain_tail[1] = {"call", {"self", unpack head[2]}}
|
||||
|
||||
if ntype(calling_name) == "key_literal"
|
||||
insert new_chain, {"dot", calling_name[2]}
|
||||
else
|
||||
insert new_chain, {"index", calling_name}
|
||||
|
||||
-- colon call on super, replace class with self as first arg
|
||||
when "colon"
|
||||
call = chain_tail[2]
|
||||
-- calling chain tail
|
||||
if call and call[1] == "call"
|
||||
chain_tail[1] = {
|
||||
"dot"
|
||||
head[2]
|
||||
}
|
||||
|
||||
chain_tail[2] = {
|
||||
"call"
|
||||
{
|
||||
"self"
|
||||
unpack call[2]
|
||||
}
|
||||
}
|
||||
|
||||
insert new_chain, item for item in *chain_tail
|
||||
new_chain
|
||||
|
||||
{"declare", { cls_name }}
|
||||
{"declare_glob", "*"}
|
||||
|
||||
parent_val and .assign_one(parent_cls_name, parent_val) or NOOP
|
||||
|
||||
.assign_one base_name, {"table", properties}
|
||||
.assign_one base_name\chain"__index", base_name
|
||||
|
||||
parent_val and .chain({
|
||||
base: "setmetatable"
|
||||
{"call", {
|
||||
base_name,
|
||||
.chain { base: parent_cls_name, {"dot", "__base"}}
|
||||
}}
|
||||
}) or NOOP
|
||||
|
||||
.assign_one cls_name, cls
|
||||
.assign_one base_name\chain"__class", cls_name
|
||||
|
||||
.group if #statements > 0 then {
|
||||
.assign_one LocalName"self", cls_name
|
||||
.group statements
|
||||
}
|
||||
|
||||
-- run the inherited callback
|
||||
parent_val and .if({
|
||||
cond: {"exp", parent_cls_name\chain "__inherited" }
|
||||
then: {
|
||||
parent_cls_name\chain "__inherited", {"call", {
|
||||
parent_cls_name, cls_name
|
||||
}}
|
||||
}
|
||||
}) or NOOP
|
||||
|
||||
.group if name then {
|
||||
.assign_one name, cls_name
|
||||
}
|
||||
|
||||
if ret
|
||||
ret cls_name
|
||||
}
|
||||
|
||||
value = .group {
|
||||
.group if ntype(name) == "value" then {
|
||||
.declare names: {name}
|
||||
}
|
||||
|
||||
.do out_body
|
||||
}
|
||||
|
||||
value
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
local ntype, mtype
|
||||
do
|
||||
local _obj_0 = require("moonscript.types")
|
||||
ntype, mtype = _obj_0.ntype, _obj_0.mtype
|
||||
end
|
||||
local types = require("moonscript.types")
|
||||
local ntype, mtype, is_value, NOOP
|
||||
ntype, mtype, is_value, NOOP = types.ntype, types.mtype, types.is_value, types.NOOP
|
||||
local Run
|
||||
do
|
||||
local _class_0
|
||||
@ -74,9 +72,88 @@ chain_is_stub = function(chain)
|
||||
local stub = chain[#chain]
|
||||
return stub and ntype(stub) == "colon"
|
||||
end
|
||||
local implicitly_return
|
||||
implicitly_return = function(scope)
|
||||
local is_top = true
|
||||
local fn
|
||||
fn = function(stm)
|
||||
local t = ntype(stm)
|
||||
if t == "decorated" then
|
||||
stm = scope.transform.statement(stm)
|
||||
t = ntype(stm)
|
||||
end
|
||||
if types.cascading[t] then
|
||||
is_top = false
|
||||
return scope.transform.statement(stm, fn)
|
||||
elseif types.manual_return[t] or not is_value(stm) then
|
||||
if is_top and t == "return" and stm[2] == "" then
|
||||
return NOOP
|
||||
else
|
||||
return stm
|
||||
end
|
||||
else
|
||||
if t == "comprehension" and not types.comprehension_has_value(stm) then
|
||||
return stm
|
||||
else
|
||||
return {
|
||||
"return",
|
||||
stm
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
return fn
|
||||
end
|
||||
local reversed
|
||||
reversed = require("moonscript.util").reversed
|
||||
local construct_comprehension
|
||||
construct_comprehension = function(inner, clauses)
|
||||
local current_stms = inner
|
||||
for _, clause in reversed(clauses) do
|
||||
local t = clause[1]
|
||||
local _exp_0 = t
|
||||
if "for" == _exp_0 then
|
||||
local name, bounds
|
||||
_, name, bounds = clause[1], clause[2], clause[3]
|
||||
current_stms = {
|
||||
"for",
|
||||
name,
|
||||
bounds,
|
||||
current_stms
|
||||
}
|
||||
elseif "foreach" == _exp_0 then
|
||||
local names, iter
|
||||
_, names, iter = clause[1], clause[2], clause[3]
|
||||
current_stms = {
|
||||
"foreach",
|
||||
names,
|
||||
{
|
||||
iter
|
||||
},
|
||||
current_stms
|
||||
}
|
||||
elseif "when" == _exp_0 then
|
||||
local cond
|
||||
_, cond = clause[1], clause[2]
|
||||
current_stms = {
|
||||
"if",
|
||||
cond,
|
||||
current_stms
|
||||
}
|
||||
else
|
||||
current_stms = error("Unknown comprehension clause: " .. t)
|
||||
end
|
||||
current_stms = {
|
||||
current_stms
|
||||
}
|
||||
end
|
||||
return current_stms[1]
|
||||
end
|
||||
return {
|
||||
Run = Run,
|
||||
last_stm = last_stm,
|
||||
transform_last_stm = transform_last_stm,
|
||||
chain_is_stub = chain_is_stub
|
||||
chain_is_stub = chain_is_stub,
|
||||
implicitly_return = implicitly_return,
|
||||
construct_comprehension = construct_comprehension
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
import ntype, mtype from require "moonscript.types"
|
||||
types = require "moonscript.types"
|
||||
import ntype, mtype, is_value, NOOP from types
|
||||
|
||||
-- A Run is a special statement node that lets a function run and mutate the
|
||||
-- state of the compiler
|
||||
@ -44,5 +45,56 @@ chain_is_stub = (chain) ->
|
||||
stub = chain[#chain]
|
||||
stub and ntype(stub) == "colon"
|
||||
|
||||
{:Run, :last_stm, :transform_last_stm, :chain_is_stub}
|
||||
implicitly_return = (scope) ->
|
||||
is_top = true
|
||||
fn = (stm) ->
|
||||
t = ntype stm
|
||||
|
||||
-- expand decorated
|
||||
if t == "decorated"
|
||||
stm = scope.transform.statement stm
|
||||
t = ntype stm
|
||||
|
||||
if types.cascading[t]
|
||||
is_top = false
|
||||
scope.transform.statement stm, fn
|
||||
elseif types.manual_return[t] or not is_value stm
|
||||
-- remove blank return statement
|
||||
if is_top and t == "return" and stm[2] == ""
|
||||
NOOP
|
||||
else
|
||||
stm
|
||||
else
|
||||
if t == "comprehension" and not types.comprehension_has_value stm
|
||||
stm
|
||||
else
|
||||
{"return", stm}
|
||||
|
||||
fn
|
||||
|
||||
-- TODO: reversed unecessary
|
||||
import reversed from require "moonscript.util"
|
||||
construct_comprehension = (inner, clauses) ->
|
||||
current_stms = inner
|
||||
for _, clause in reversed clauses
|
||||
t = clause[1]
|
||||
current_stms = switch t
|
||||
when "for"
|
||||
{_, name, bounds} = clause
|
||||
{"for", name, bounds, current_stms}
|
||||
when "foreach"
|
||||
{_, names, iter} = clause
|
||||
{"foreach", names, {iter}, current_stms}
|
||||
when "when"
|
||||
{_, cond} = clause
|
||||
{"if", cond, current_stms}
|
||||
else
|
||||
error "Unknown comprehension clause: "..t
|
||||
|
||||
current_stms = {current_stms}
|
||||
|
||||
current_stms[1]
|
||||
|
||||
{:Run, :last_stm, :transform_last_stm, :chain_is_stub, :implicitly_return,
|
||||
:construct_comprehension}
|
||||
|
||||
|
74
moonscript/transform/transformer.lua
Normal file
74
moonscript/transform/transformer.lua
Normal file
@ -0,0 +1,74 @@
|
||||
local ntype
|
||||
ntype = require("moonscript.types").ntype
|
||||
local Transformer
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
transform_once = function(self, scope, node, ...)
|
||||
if self.seen_nodes[node] then
|
||||
return node
|
||||
end
|
||||
self.seen_nodes[node] = true
|
||||
local transformer = self.transformers[ntype(node)]
|
||||
if transformer then
|
||||
return transformer(scope, node, ...) or node
|
||||
else
|
||||
return node
|
||||
end
|
||||
end,
|
||||
transform = function(self, scope, node, ...)
|
||||
if self.seen_nodes[node] then
|
||||
return node
|
||||
end
|
||||
self.seen_nodes[node] = true
|
||||
while true do
|
||||
local transformer = self.transformers[ntype(node)]
|
||||
local res
|
||||
if transformer then
|
||||
res = transformer(scope, node, ...) or node
|
||||
else
|
||||
res = node
|
||||
end
|
||||
if res == node then
|
||||
return node
|
||||
end
|
||||
node = res
|
||||
end
|
||||
return node
|
||||
end,
|
||||
bind = function(self, scope)
|
||||
return function(...)
|
||||
return self:transform(scope, ...)
|
||||
end
|
||||
end,
|
||||
__call = function(self, ...)
|
||||
return self:transform(...)
|
||||
end,
|
||||
can_transform = function(self, node)
|
||||
return self.transformers[ntype(node)] ~= nil
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, transformers)
|
||||
self.transformers = transformers
|
||||
self.seen_nodes = setmetatable({ }, {
|
||||
__mode = "k"
|
||||
})
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Transformer"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Transformer = _class_0
|
||||
end
|
||||
return {
|
||||
Transformer = Transformer
|
||||
}
|
42
moonscript/transform/transformer.moon
Normal file
42
moonscript/transform/transformer.moon
Normal file
@ -0,0 +1,42 @@
|
||||
import ntype from require "moonscript.types"
|
||||
|
||||
class Transformer
|
||||
new: (@transformers) =>
|
||||
@seen_nodes = setmetatable {}, __mode: "k"
|
||||
|
||||
transform_once: (scope, node, ...) =>
|
||||
return node if @seen_nodes[node]
|
||||
@seen_nodes[node] = true
|
||||
|
||||
transformer = @transformers[ntype node]
|
||||
if transformer
|
||||
transformer(scope, node, ...) or node
|
||||
else
|
||||
node
|
||||
|
||||
transform: (scope, node, ...) =>
|
||||
return node if @seen_nodes[node]
|
||||
|
||||
@seen_nodes[node] = true
|
||||
while true
|
||||
transformer = @transformers[ntype node]
|
||||
res = if transformer
|
||||
transformer(scope, node, ...) or node
|
||||
else
|
||||
node
|
||||
|
||||
return node if res == node
|
||||
node = res
|
||||
|
||||
node
|
||||
|
||||
bind: (scope) =>
|
||||
(...) -> @transform scope, ...
|
||||
|
||||
__call: (...) => @transform ...
|
||||
|
||||
can_transform: (node) =>
|
||||
@transformers[ntype node] != nil
|
||||
|
||||
|
||||
{ :Transformer }
|
261
moonscript/transform/value.lua
Normal file
261
moonscript/transform/value.lua
Normal file
@ -0,0 +1,261 @@
|
||||
local Transformer
|
||||
Transformer = require("moonscript.transform.transformer").Transformer
|
||||
local build, ntype, smart_node
|
||||
do
|
||||
local _obj_0 = require("moonscript.types")
|
||||
build, ntype, smart_node = _obj_0.build, _obj_0.ntype, _obj_0.smart_node
|
||||
end
|
||||
local NameProxy
|
||||
NameProxy = require("moonscript.transform.names").NameProxy
|
||||
local Accumulator, default_accumulator
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.accumulator")
|
||||
Accumulator, default_accumulator = _obj_0.Accumulator, _obj_0.default_accumulator
|
||||
end
|
||||
local lua_keywords
|
||||
lua_keywords = require("moonscript.data").lua_keywords
|
||||
local Run, transform_last_stm, implicitly_return, chain_is_stub, construct_comprehension
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.statements")
|
||||
Run, transform_last_stm, implicitly_return, chain_is_stub, construct_comprehension = _obj_0.Run, _obj_0.transform_last_stm, _obj_0.implicitly_return, _obj_0.chain_is_stub, _obj_0.construct_comprehension
|
||||
end
|
||||
local insert
|
||||
insert = table.insert
|
||||
return Transformer({
|
||||
["for"] = default_accumulator,
|
||||
["while"] = default_accumulator,
|
||||
foreach = default_accumulator,
|
||||
["do"] = function(self, node)
|
||||
return build.block_exp(node[2])
|
||||
end,
|
||||
decorated = function(self, node)
|
||||
return self.transform.statement(node)
|
||||
end,
|
||||
class = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
string = function(self, node)
|
||||
local delim = node[2]
|
||||
local convert_part
|
||||
convert_part = function(part)
|
||||
if type(part) == "string" or part == nil then
|
||||
return {
|
||||
"string",
|
||||
delim,
|
||||
part or ""
|
||||
}
|
||||
else
|
||||
return build.chain({
|
||||
base = "tostring",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
part[2]
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
if #node <= 3 then
|
||||
if type(node[3]) == "string" then
|
||||
return node
|
||||
else
|
||||
return convert_part(node[3])
|
||||
end
|
||||
end
|
||||
local e = {
|
||||
"exp",
|
||||
convert_part(node[3])
|
||||
}
|
||||
for i = 4, #node do
|
||||
insert(e, "..")
|
||||
insert(e, convert_part(node[i]))
|
||||
end
|
||||
return e
|
||||
end,
|
||||
comprehension = function(self, node)
|
||||
local a = Accumulator()
|
||||
node = self.transform.statement(node, function(exp)
|
||||
return a:mutate_body({
|
||||
exp
|
||||
})
|
||||
end)
|
||||
return a:wrap(node)
|
||||
end,
|
||||
tblcomprehension = function(self, node)
|
||||
local _, explist, clauses = unpack(node)
|
||||
local key_exp, value_exp = unpack(explist)
|
||||
local accum = NameProxy("tbl")
|
||||
local inner
|
||||
if value_exp then
|
||||
local dest = build.chain({
|
||||
base = accum,
|
||||
{
|
||||
"index",
|
||||
key_exp
|
||||
}
|
||||
})
|
||||
inner = {
|
||||
build.assign_one(dest, value_exp)
|
||||
}
|
||||
else
|
||||
local key_name, val_name = NameProxy("key"), NameProxy("val")
|
||||
local dest = build.chain({
|
||||
base = accum,
|
||||
{
|
||||
"index",
|
||||
key_name
|
||||
}
|
||||
})
|
||||
inner = {
|
||||
build.assign({
|
||||
names = {
|
||||
key_name,
|
||||
val_name
|
||||
},
|
||||
values = {
|
||||
key_exp
|
||||
}
|
||||
}),
|
||||
build.assign_one(dest, val_name)
|
||||
}
|
||||
end
|
||||
return build.block_exp({
|
||||
build.assign_one(accum, build.table()),
|
||||
construct_comprehension(inner, clauses),
|
||||
accum
|
||||
})
|
||||
end,
|
||||
fndef = function(self, node)
|
||||
smart_node(node)
|
||||
node.body = transform_last_stm(node.body, implicitly_return(self))
|
||||
node.body = {
|
||||
Run(function(self)
|
||||
return self:listen("varargs", function() end)
|
||||
end),
|
||||
unpack(node.body)
|
||||
}
|
||||
return node
|
||||
end,
|
||||
["if"] = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
unless = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
with = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
switch = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
chain = function(self, node)
|
||||
for i = 2, #node do
|
||||
local part = node[i]
|
||||
if ntype(part) == "dot" and lua_keywords[part[2]] then
|
||||
node[i] = {
|
||||
"index",
|
||||
{
|
||||
"string",
|
||||
'"',
|
||||
part[2]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
if ntype(node[2]) == "string" then
|
||||
node[2] = {
|
||||
"parens",
|
||||
node[2]
|
||||
}
|
||||
end
|
||||
if chain_is_stub(node) then
|
||||
local base_name = NameProxy("base")
|
||||
local fn_name = NameProxy("fn")
|
||||
local colon = table.remove(node)
|
||||
local is_super = ntype(node[2]) == "ref" and node[2][2] == "super"
|
||||
return build.block_exp({
|
||||
build.assign({
|
||||
names = {
|
||||
base_name
|
||||
},
|
||||
values = {
|
||||
node
|
||||
}
|
||||
}),
|
||||
build.assign({
|
||||
names = {
|
||||
fn_name
|
||||
},
|
||||
values = {
|
||||
build.chain({
|
||||
base = base_name,
|
||||
{
|
||||
"dot",
|
||||
colon[2]
|
||||
}
|
||||
})
|
||||
}
|
||||
}),
|
||||
build.fndef({
|
||||
args = {
|
||||
{
|
||||
"..."
|
||||
}
|
||||
},
|
||||
body = {
|
||||
build.chain({
|
||||
base = fn_name,
|
||||
{
|
||||
"call",
|
||||
{
|
||||
is_super and "self" or base_name,
|
||||
"..."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
end
|
||||
end,
|
||||
block_exp = function(self, node)
|
||||
local _, body = unpack(node)
|
||||
local fn = nil
|
||||
local arg_list = { }
|
||||
fn = smart_node(build.fndef({
|
||||
body = {
|
||||
Run(function(self)
|
||||
return self:listen("varargs", function()
|
||||
insert(arg_list, "...")
|
||||
insert(fn.args, {
|
||||
"..."
|
||||
})
|
||||
return self:unlisten("varargs")
|
||||
end)
|
||||
end),
|
||||
unpack(body)
|
||||
}
|
||||
}))
|
||||
return build.chain({
|
||||
base = {
|
||||
"parens",
|
||||
fn
|
||||
},
|
||||
{
|
||||
"call",
|
||||
arg_list
|
||||
}
|
||||
})
|
||||
end
|
||||
})
|
162
moonscript/transform/value.moon
Normal file
162
moonscript/transform/value.moon
Normal file
@ -0,0 +1,162 @@
|
||||
import Transformer from require "moonscript.transform.transformer"
|
||||
import build, ntype, smart_node from require "moonscript.types"
|
||||
|
||||
import NameProxy from require "moonscript.transform.names"
|
||||
import Accumulator, default_accumulator from require "moonscript.transform.accumulator"
|
||||
import lua_keywords from require "moonscript.data"
|
||||
|
||||
import Run, transform_last_stm, implicitly_return, chain_is_stub,
|
||||
construct_comprehension from require "moonscript.transform.statements"
|
||||
|
||||
import insert from table
|
||||
|
||||
Transformer {
|
||||
for: default_accumulator
|
||||
while: default_accumulator
|
||||
foreach: default_accumulator
|
||||
|
||||
do: (node) =>
|
||||
build.block_exp node[2]
|
||||
|
||||
decorated: (node) =>
|
||||
@transform.statement node
|
||||
|
||||
class: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
string: (node) =>
|
||||
delim = node[2]
|
||||
|
||||
convert_part = (part) ->
|
||||
if type(part) == "string" or part == nil
|
||||
{"string", delim, part or ""}
|
||||
else
|
||||
build.chain { base: "tostring", {"call", {part[2]}} }
|
||||
|
||||
-- reduced to single item
|
||||
if #node <= 3
|
||||
return if type(node[3]) == "string"
|
||||
node
|
||||
else
|
||||
convert_part node[3]
|
||||
|
||||
e = {"exp", convert_part node[3]}
|
||||
|
||||
for i=4, #node
|
||||
insert e, ".."
|
||||
insert e, convert_part node[i]
|
||||
e
|
||||
|
||||
comprehension: (node) =>
|
||||
a = Accumulator!
|
||||
node = @transform.statement node, (exp) ->
|
||||
a\mutate_body {exp}
|
||||
a\wrap node
|
||||
|
||||
tblcomprehension: (node) =>
|
||||
_, explist, clauses = unpack node
|
||||
key_exp, value_exp = unpack explist
|
||||
|
||||
accum = NameProxy "tbl"
|
||||
|
||||
inner = if value_exp
|
||||
dest = build.chain { base: accum, {"index", key_exp} }
|
||||
{ build.assign_one dest, value_exp }
|
||||
else
|
||||
-- If we only have single expression then
|
||||
-- unpack the result into key and value
|
||||
key_name, val_name = NameProxy"key", NameProxy"val"
|
||||
dest = build.chain { base: accum, {"index", key_name} }
|
||||
{
|
||||
build.assign names: {key_name, val_name}, values: {key_exp}
|
||||
build.assign_one dest, val_name
|
||||
}
|
||||
|
||||
build.block_exp {
|
||||
build.assign_one accum, build.table!
|
||||
construct_comprehension inner, clauses
|
||||
accum
|
||||
}
|
||||
|
||||
fndef: (node) =>
|
||||
smart_node node
|
||||
node.body = transform_last_stm node.body, implicitly_return self
|
||||
node.body = {
|
||||
Run => @listen "varargs", -> -- capture event
|
||||
unpack node.body
|
||||
}
|
||||
|
||||
node
|
||||
|
||||
if: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
unless: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
with: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
switch: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
-- pull out colon chain
|
||||
chain: (node) =>
|
||||
-- escape lua keywords used in dot accessors
|
||||
for i=2,#node
|
||||
part = node[i]
|
||||
if ntype(part) == "dot" and lua_keywords[part[2]]
|
||||
node[i] = { "index", {"string", '"', part[2]} }
|
||||
|
||||
if ntype(node[2]) == "string"
|
||||
-- add parens if callee is raw string
|
||||
node[2] = {"parens", node[2] }
|
||||
|
||||
if chain_is_stub node
|
||||
base_name = NameProxy "base"
|
||||
fn_name = NameProxy "fn"
|
||||
colon = table.remove node
|
||||
|
||||
is_super = ntype(node[2]) == "ref" and node[2][2] == "super"
|
||||
build.block_exp {
|
||||
build.assign {
|
||||
names: {base_name}
|
||||
values: {node}
|
||||
}
|
||||
|
||||
build.assign {
|
||||
names: {fn_name}
|
||||
values: {
|
||||
build.chain { base: base_name, {"dot", colon[2]} }
|
||||
}
|
||||
}
|
||||
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
body: {
|
||||
build.chain {
|
||||
base: fn_name, {"call", {is_super and "self" or base_name, "..."}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_exp: (node) =>
|
||||
_, body = unpack node
|
||||
|
||||
fn = nil
|
||||
arg_list = {}
|
||||
|
||||
fn = smart_node build.fndef body: {
|
||||
Run =>
|
||||
@listen "varargs", ->
|
||||
insert arg_list, "..."
|
||||
insert fn.args, {"..."}
|
||||
@unlisten "varargs"
|
||||
|
||||
unpack body
|
||||
}
|
||||
|
||||
build.chain { base: {"parens", fn}, {"call", arg_list} }
|
||||
}
|
||||
|
@ -308,6 +308,9 @@ local smart_node
|
||||
smart_node = function(node)
|
||||
return setmetatable(node, smart_node_mt[ntype(node)])
|
||||
end
|
||||
local NOOP = {
|
||||
"noop"
|
||||
}
|
||||
return {
|
||||
ntype = ntype,
|
||||
smart_node = smart_node,
|
||||
@ -320,5 +323,6 @@ return {
|
||||
comprehension_has_value = comprehension_has_value,
|
||||
value_can_be_statement = value_can_be_statement,
|
||||
mtype = mtype,
|
||||
terminating = terminating
|
||||
terminating = terminating,
|
||||
NOOP = NOOP
|
||||
}
|
||||
|
@ -189,9 +189,12 @@ smart_node_mt = setmetatable {}, {
|
||||
smart_node = (node) ->
|
||||
setmetatable node, smart_node_mt[ntype node]
|
||||
|
||||
NOOP = {"noop"}
|
||||
|
||||
{
|
||||
:ntype, :smart_node, :build, :is_value, :is_slice, :manual_return,
|
||||
:cascading, :value_is_singular, :comprehension_has_value,
|
||||
:value_can_be_statement, :mtype, :terminating
|
||||
:NOOP
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user