mirror of
https://github.com/leafo/moonscript.git
synced 2024-10-05 05:34:19 +00:00
split compiler across multiple files
This commit is contained in:
parent
f133f263b4
commit
c6aa90bcbc
70
moonscript/compile/format.lua
Normal file
70
moonscript/compile/format.lua
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
module("moonscript.compile", package.seeall)
|
||||||
|
local util = require("moonscript.util")
|
||||||
|
local data = require("moonscript.data")
|
||||||
|
local itwos = util.itwos
|
||||||
|
local Set, ntype = data.Set, data.ntype
|
||||||
|
local concat, insert = table.concat, table.insert
|
||||||
|
indent_char = " "
|
||||||
|
pretty = function(lines, indent)
|
||||||
|
indent = indent or ""
|
||||||
|
local render
|
||||||
|
render = function(line)
|
||||||
|
if type(line) == "table" then
|
||||||
|
return indent_char .. pretty(line, indent .. indent_char)
|
||||||
|
else
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
lines = (function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = lines
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local line = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, render(line))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)()
|
||||||
|
local fix
|
||||||
|
fix = function(i, left, k, right)
|
||||||
|
if left:sub(-1) == ")" and right:sub(1, 1) == "(" then
|
||||||
|
lines[i] = lines[i] .. ";"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i, l, k, r in itwos(lines) do
|
||||||
|
fix(i, l, k, r)
|
||||||
|
end
|
||||||
|
return concat(lines, "\n" .. indent)
|
||||||
|
end
|
||||||
|
returner = function(exp)
|
||||||
|
if ntype(exp) == "chain" and exp[2] == "return" then
|
||||||
|
local items = { "explist" }
|
||||||
|
local _item_0 = exp[3][2]
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local v = _item_0[_index_0]
|
||||||
|
insert(items, v)
|
||||||
|
end
|
||||||
|
return { "return", items }
|
||||||
|
else
|
||||||
|
return { "return", exp }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
moonlib = { bind = function(tbl, name) return concat({
|
||||||
|
"moon.bind(",
|
||||||
|
tbl,
|
||||||
|
".",
|
||||||
|
name,
|
||||||
|
", ",
|
||||||
|
tbl,
|
||||||
|
")"
|
||||||
|
}) end }
|
||||||
|
cascading = Set({ "if" })
|
||||||
|
non_atomic = Set({ "update" })
|
||||||
|
has_value = function(node)
|
||||||
|
if ntype(node) == "chain" then
|
||||||
|
local ctype = ntype(node[#node])
|
||||||
|
return ctype ~= "call" and ctype ~= "colon"
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
is_non_atomic = function(node) return non_atomic[ntype(node)] end
|
58
moonscript/compile/format.moon
Normal file
58
moonscript/compile/format.moon
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
module "moonscript.compile", package.seeall
|
||||||
|
|
||||||
|
util = require "moonscript.util"
|
||||||
|
data = require "moonscript.data"
|
||||||
|
|
||||||
|
import itwos from util
|
||||||
|
import Set, ntype from data
|
||||||
|
import concat, insert from table
|
||||||
|
|
||||||
|
export indent_char, pretty, returner, moonlib, cascading, non_atomic, has_value, is_non_atomic
|
||||||
|
|
||||||
|
indent_char = " "
|
||||||
|
pretty = (lines, indent) ->
|
||||||
|
indent = indent or ""
|
||||||
|
render = (line) ->
|
||||||
|
if type(line) == "table"
|
||||||
|
indent_char..pretty(line, indent..indent_char)
|
||||||
|
else
|
||||||
|
line
|
||||||
|
|
||||||
|
lines = [render line for line in *lines]
|
||||||
|
|
||||||
|
-- add semicolons for ambiguities
|
||||||
|
fix = (i, left, k, right) ->
|
||||||
|
if left:sub(-1) == ")" and right:sub(1,1) == "("
|
||||||
|
lines[i] = lines[i]..";"
|
||||||
|
fix(i,l, k,r) for i,l,k,r in itwos lines
|
||||||
|
|
||||||
|
concat lines, "\n"..indent
|
||||||
|
|
||||||
|
returner = (exp) ->
|
||||||
|
if ntype(exp) == "chain" and exp[2] == "return"
|
||||||
|
-- extract the return
|
||||||
|
items = {"explist"}
|
||||||
|
insert items, v for v in *exp[3][2]
|
||||||
|
{"return", items}
|
||||||
|
else
|
||||||
|
{"return", exp}
|
||||||
|
|
||||||
|
moonlib =
|
||||||
|
bind: (tbl, name) ->
|
||||||
|
concat {"moon.bind(", tbl, ".", name, ", ", tbl, ")"}
|
||||||
|
|
||||||
|
cascading = Set{ "if" }
|
||||||
|
|
||||||
|
-- an action that can't be completed in a single line
|
||||||
|
non_atomic = Set{ "update" }
|
||||||
|
|
||||||
|
-- does this always return a value
|
||||||
|
has_value = (node) ->
|
||||||
|
if ntype(node) == "chain"
|
||||||
|
ctype = ntype(node[#node])
|
||||||
|
ctype != "call" and ctype != "colon"
|
||||||
|
else
|
||||||
|
true
|
||||||
|
|
||||||
|
is_non_atomic = (node) ->
|
||||||
|
non_atomic[ntype(node)]
|
320
moonscript/compile/line.lua
Normal file
320
moonscript/compile/line.lua
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
module("moonscript.compile", package.seeall)
|
||||||
|
local util = require("moonscript.util")
|
||||||
|
local data = require("moonscript.data")
|
||||||
|
local dump = require("moonscript.dump")
|
||||||
|
require("moonscript.compile.format")
|
||||||
|
local reversed = util.reversed
|
||||||
|
local ntype = data.ntype
|
||||||
|
local concat, insert = table.concat, table.insert
|
||||||
|
line_compile = {
|
||||||
|
assign = function(self, node)
|
||||||
|
local _, names, values = unpack(node)
|
||||||
|
local undeclared = self:declare(names)
|
||||||
|
local declare = "local " .. (concat(undeclared, ", "))
|
||||||
|
if self:is_stm(values) then
|
||||||
|
if #undeclared > 0 then
|
||||||
|
self:add_line(declare)
|
||||||
|
end
|
||||||
|
if cascading[ntype(values)] then
|
||||||
|
local decorate
|
||||||
|
decorate = function(value) return { "assign", names, { value } } end
|
||||||
|
return self:stm(values, decorate)
|
||||||
|
else
|
||||||
|
return self:add_line(concat((function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local n = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, self:value(n))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)(), ", ") .. " = " .. self:value(values))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local has_fndef = false
|
||||||
|
local i = 1
|
||||||
|
while i <= #values do
|
||||||
|
if ntype(values[i]) == "fndef" then
|
||||||
|
has_fndef = true
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
values = concat((function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = values
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local v = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, self:value(v))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)(), ", ")
|
||||||
|
if #undeclared == #names and not has_fndef then
|
||||||
|
return self:add_line(declare .. ' = ' .. values)
|
||||||
|
else
|
||||||
|
if #undeclared > 0 then
|
||||||
|
self:add_line(declare)
|
||||||
|
end
|
||||||
|
return self:add_line(concat((function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local n = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, self:value(n))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)(), ", ") .. " = " .. values)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
update = function(self, node)
|
||||||
|
local _, name, op, exp = unpack(node)
|
||||||
|
local op_final = op:match("(.)=")
|
||||||
|
if not op_final then
|
||||||
|
_ = error("unknown op: ") .. op
|
||||||
|
end
|
||||||
|
return self:stm({ "assign", { name }, { {
|
||||||
|
"exp",
|
||||||
|
name,
|
||||||
|
op_final,
|
||||||
|
exp
|
||||||
|
} } })
|
||||||
|
end,
|
||||||
|
["return"] = function(self, node) return self:add_line("return", self:value(node[2])) end,
|
||||||
|
["break"] = function(self, node) return self:add_line("break") end,
|
||||||
|
["import"] = function(self, node)
|
||||||
|
local _, names, source = unpack(node)
|
||||||
|
local to_bind = { }
|
||||||
|
local get_name
|
||||||
|
get_name = function(name)
|
||||||
|
if ntype(name) == ":" then
|
||||||
|
local tmp = self:name(name[2])
|
||||||
|
to_bind[tmp] = true
|
||||||
|
return tmp
|
||||||
|
else
|
||||||
|
return self:name(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local final_names = (function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local n = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, get_name(n))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)()
|
||||||
|
local _item_0 = final_names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local name = _item_0[_index_0]
|
||||||
|
self:put_name(name)
|
||||||
|
end
|
||||||
|
local get_value
|
||||||
|
get_value = function(name)
|
||||||
|
if to_bind[name] then
|
||||||
|
return moonlib.bind(source, name)
|
||||||
|
else
|
||||||
|
return source .. "." .. name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if type(source) == "string" then
|
||||||
|
local values = (function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = final_names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local name = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, get_value(name))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)()
|
||||||
|
self:add_line("local", (concat(final_names, ", ")), "=", (concat(values, ", ")))
|
||||||
|
return(nil)
|
||||||
|
end
|
||||||
|
self:add_line("local", concat(final_names, ", "))
|
||||||
|
self:add_line("do")
|
||||||
|
local inner = self:block()
|
||||||
|
local tmp_name = inner:free_name("table")
|
||||||
|
inner:add_line("local", tmp_name, "=", self:value(source))
|
||||||
|
source = tmp_name
|
||||||
|
local _item_0 = final_names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local name = _item_0[_index_0]
|
||||||
|
inner:add_line(name .. " = " .. get_value(name))
|
||||||
|
end
|
||||||
|
self:add_line(inner:render())
|
||||||
|
return self:add_line("end")
|
||||||
|
end,
|
||||||
|
["if"] = function(self, node, ret)
|
||||||
|
local cond, block = node[2], node[3]
|
||||||
|
local add_clause
|
||||||
|
add_clause = function(clause)
|
||||||
|
local type = clause[1]
|
||||||
|
if type == "else" then
|
||||||
|
self:add_line("else")
|
||||||
|
block = clause[2]
|
||||||
|
else
|
||||||
|
self:add_line("elseif", (self:value(clause[2])), "then")
|
||||||
|
block = clause[3]
|
||||||
|
end
|
||||||
|
local b = self:block()
|
||||||
|
b:stms(block, ret)
|
||||||
|
return self:add_line(b:render())
|
||||||
|
end
|
||||||
|
self:add_line("if", (self:value(cond)), "then")
|
||||||
|
local b = self:block()
|
||||||
|
b:stms(block, ret)
|
||||||
|
self:add_line(b:render())
|
||||||
|
for i, cond in ipairs(node) do
|
||||||
|
if i > 3 then
|
||||||
|
add_clause(cond)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self:add_line("end")
|
||||||
|
end,
|
||||||
|
["while"] = function(self, node)
|
||||||
|
local _, cond, block = unpack(node)
|
||||||
|
local inner = self:block()
|
||||||
|
if is_non_atomic(cond) then
|
||||||
|
self:add_line("while", "true", "do")
|
||||||
|
inner:stm({ "if", { "not", cond }, { { "break" } } })
|
||||||
|
else
|
||||||
|
self:add_line("while", self:value(cond), "do")
|
||||||
|
end
|
||||||
|
inner:stms(block)
|
||||||
|
self:add_line(inner:render())
|
||||||
|
return self:add_line("end")
|
||||||
|
end,
|
||||||
|
["for"] = function(self, node)
|
||||||
|
local _, name, bounds, block = unpack(node)
|
||||||
|
bounds = self:value({ "explist", unpack(bounds) })
|
||||||
|
self:add_line("for", self:name(name), "=", bounds, "do")
|
||||||
|
local inner = self:block()
|
||||||
|
inner:stms(block)
|
||||||
|
self:add_line(inner:render())
|
||||||
|
return self:add_line("end")
|
||||||
|
end,
|
||||||
|
["export"] = function(self, node)
|
||||||
|
local _, names = unpack(node)
|
||||||
|
local _item_0 = names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local name = _item_0[_index_0]
|
||||||
|
if type(name) == "string" then
|
||||||
|
self:put_name(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end,
|
||||||
|
["class"] = function(self, node)
|
||||||
|
local _, name, table = unpack(node)
|
||||||
|
local mt_name = "_" .. name .. "_mt"
|
||||||
|
self:add_line("local", concat(self:declare({ name, mt_name }), ", "))
|
||||||
|
local constructor = nil
|
||||||
|
local meta_methods = { }
|
||||||
|
local final_properties = { }
|
||||||
|
local overloaded_index = value
|
||||||
|
local find_special
|
||||||
|
find_special = function(name, value)
|
||||||
|
if name == "constructor" then
|
||||||
|
constructor = value
|
||||||
|
elseif name:match("^__%a") then
|
||||||
|
insert(meta_methods, { name, value })
|
||||||
|
if name == "__index" then
|
||||||
|
overloaded_index = value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return insert(final_properties, { name, value })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local _item_0 = table[2]
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local entry = _item_0[_index_0]
|
||||||
|
find_special(unpack(entry))
|
||||||
|
end
|
||||||
|
if not overloaded_index then
|
||||||
|
insert(meta_methods, { "__index", { "table", final_properties } })
|
||||||
|
end
|
||||||
|
print(util.dump(constructor))
|
||||||
|
print(util.dump(meta_methods))
|
||||||
|
print(util.dump(final_properties))
|
||||||
|
self:stm({ "assign", { mt_name }, { { "table", meta_methods } } })
|
||||||
|
if not constructor then
|
||||||
|
constructor = {
|
||||||
|
"fndef",
|
||||||
|
{ },
|
||||||
|
"slim",
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
local self_args = { }
|
||||||
|
local get_initializers
|
||||||
|
get_initializers = function(arg)
|
||||||
|
if ntype(arg) == "self" then
|
||||||
|
arg = arg[2]
|
||||||
|
insert(self_args, arg)
|
||||||
|
end
|
||||||
|
return arg
|
||||||
|
end
|
||||||
|
constructor[2] = (function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = constructor[2]
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local arg = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, get_initializers(arg))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)()
|
||||||
|
print(util.dump(constructor))
|
||||||
|
return self:stm({ "assign", { name }, { constructor } })
|
||||||
|
end,
|
||||||
|
comprehension = function(self, node, action)
|
||||||
|
local _, exp, clauses = unpack(node)
|
||||||
|
if not action then
|
||||||
|
action = self:block()
|
||||||
|
action:stm(exp)
|
||||||
|
end
|
||||||
|
local depth = #clauses
|
||||||
|
action:set_indent(self.indent + depth)
|
||||||
|
local render_clause
|
||||||
|
render_clause = function(self, clause)
|
||||||
|
local t = clause[1]
|
||||||
|
action = self:block()
|
||||||
|
action:set_indent(-1 + self.indent)
|
||||||
|
if "for" == t then
|
||||||
|
local names, iter
|
||||||
|
_, names, iter = unpack(clause)
|
||||||
|
local name_list = concat((function()
|
||||||
|
local _moon_0 = {}
|
||||||
|
local _item_0 = names
|
||||||
|
for _index_0=1,#_item_0 do
|
||||||
|
local name = _item_0[_index_0]
|
||||||
|
table.insert(_moon_0, self:name(name))
|
||||||
|
end
|
||||||
|
return _moon_0
|
||||||
|
end)(), ", ")
|
||||||
|
if ntype(iter) == "unpack" then
|
||||||
|
iter = iter[2]
|
||||||
|
local items_tmp = self:free_name("item")
|
||||||
|
local index_tmp = self:free_name("index")
|
||||||
|
insert(self._lines, 1, ("local %s = %s[%s]"):format(name_list, items_tmp, index_tmp))
|
||||||
|
return action:add_lines({
|
||||||
|
("local %s = %s"):format(items_tmp, self:value(iter)),
|
||||||
|
("for %s=1,#%s do"):format(index_tmp, items_tmp),
|
||||||
|
self:render(true),
|
||||||
|
"end"
|
||||||
|
})
|
||||||
|
else
|
||||||
|
return action:add_lines({ ("for %s in %s do"):format(name_list, self:value(iter)), self:render(true), "end" })
|
||||||
|
end
|
||||||
|
elseif "when" == t then
|
||||||
|
local cond
|
||||||
|
_, cond = unpack(clause)
|
||||||
|
return action:add_lines({ ("if %s then"):format(self:value(cond)), self:render(true), "end" })
|
||||||
|
else
|
||||||
|
return error("Unknown comprehension clause: " .. t)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i, clause in reversed(clauses) do
|
||||||
|
render_clause(action, clause)
|
||||||
|
end
|
||||||
|
return self:add_lines(action._lines)
|
||||||
|
end
|
||||||
|
}
|
258
moonscript/compile/line.moon
Normal file
258
moonscript/compile/line.moon
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
module "moonscript.compile", package.seeall
|
||||||
|
|
||||||
|
util = require "moonscript.util"
|
||||||
|
data = require "moonscript.data"
|
||||||
|
dump = require "moonscript.dump"
|
||||||
|
|
||||||
|
require "moonscript.compile.format"
|
||||||
|
|
||||||
|
import reversed from util
|
||||||
|
import ntype from data
|
||||||
|
import concat, insert from table
|
||||||
|
|
||||||
|
export line_compile
|
||||||
|
|
||||||
|
line_compile =
|
||||||
|
assign: (node) =>
|
||||||
|
_, names, values = unpack node
|
||||||
|
|
||||||
|
undeclared = @declare names
|
||||||
|
declare = "local "..(concat undeclared, ", ")
|
||||||
|
|
||||||
|
if @is_stm values
|
||||||
|
@add_line declare if #undeclared > 0
|
||||||
|
if cascading[ntype(values)]
|
||||||
|
decorate = (value) ->
|
||||||
|
{"assign", names, {value}}
|
||||||
|
|
||||||
|
@stm values, decorate
|
||||||
|
else
|
||||||
|
@add_line concat([@value n for n in *names], ", ").." = "..@value values
|
||||||
|
else
|
||||||
|
has_fndef = false
|
||||||
|
i = 1
|
||||||
|
while i <= #values
|
||||||
|
if ntype(values[i]) == "fndef"
|
||||||
|
has_fndef = true
|
||||||
|
i = i +1
|
||||||
|
|
||||||
|
-- need new compiler
|
||||||
|
-- (if ntype(v) == "fndef" then has_fndef = true) for v in *values
|
||||||
|
|
||||||
|
values = concat [@value v for v in *values], ", "
|
||||||
|
|
||||||
|
if #undeclared == #names and not has_fndef
|
||||||
|
@add_line declare..' = '..values
|
||||||
|
else
|
||||||
|
@add_line declare if #undeclared > 0
|
||||||
|
@add_line concat([@value n for n in *names], ", ").." = "..values
|
||||||
|
|
||||||
|
update: (node) =>
|
||||||
|
_, name, op, exp = unpack node
|
||||||
|
op_final = op:match"(.)="
|
||||||
|
error"unknown op: "..op if not op_final
|
||||||
|
@stm {"assign", {name}, {{"exp", name, op_final, exp}}}
|
||||||
|
|
||||||
|
["return"]: (node) =>
|
||||||
|
@add_line "return", @value node[2]
|
||||||
|
|
||||||
|
["break"]: (node) =>
|
||||||
|
@add_line "break"
|
||||||
|
|
||||||
|
["import"]: (node) =>
|
||||||
|
_, names, source = unpack node
|
||||||
|
|
||||||
|
to_bind = {}
|
||||||
|
get_name = (name) ->
|
||||||
|
if ntype(name) == ":"
|
||||||
|
tmp = @name name[2]
|
||||||
|
to_bind[tmp] = true
|
||||||
|
tmp
|
||||||
|
else
|
||||||
|
@name name
|
||||||
|
|
||||||
|
final_names = [get_name n for n in *names]
|
||||||
|
@put_name name for name in *final_names
|
||||||
|
|
||||||
|
get_value = (name) ->
|
||||||
|
if to_bind[name]
|
||||||
|
moonlib.bind source, name
|
||||||
|
else
|
||||||
|
source.."."..name
|
||||||
|
|
||||||
|
-- from constant expression, put it on one line
|
||||||
|
if type(source) == "string"
|
||||||
|
values = [get_value name for name in *final_names]
|
||||||
|
@add_line "local", (concat final_names, ", "), "=", (concat values, ", ")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
@add_line "local", concat(final_names, ", ")
|
||||||
|
@add_line "do"
|
||||||
|
|
||||||
|
inner = @block()
|
||||||
|
tmp_name = inner:free_name "table"
|
||||||
|
inner:add_line "local", tmp_name , "=", @value source
|
||||||
|
|
||||||
|
source = tmp_name
|
||||||
|
inner:add_line name.." = "..get_value name for name in *final_names
|
||||||
|
|
||||||
|
@add_line inner:render()
|
||||||
|
|
||||||
|
@add_line "end"
|
||||||
|
|
||||||
|
["if"]: (node, ret) =>
|
||||||
|
cond, block = node[2], node[3]
|
||||||
|
|
||||||
|
add_clause = (clause) ->
|
||||||
|
type = clause[1]
|
||||||
|
block = if type == "else"
|
||||||
|
@add_line "else"
|
||||||
|
clause[2]
|
||||||
|
else
|
||||||
|
@add_line "elseif", (@value clause[2]), "then"
|
||||||
|
clause[3]
|
||||||
|
|
||||||
|
b = @block()
|
||||||
|
b:stms block, ret
|
||||||
|
@add_line b:render()
|
||||||
|
|
||||||
|
@add_line "if", (@value cond), "then"
|
||||||
|
|
||||||
|
b = @block()
|
||||||
|
b:stms block, ret
|
||||||
|
@add_line b:render()
|
||||||
|
|
||||||
|
add_clause cond for i, cond in ipairs node when i > 3
|
||||||
|
|
||||||
|
@add_line "end"
|
||||||
|
|
||||||
|
["while"]: (node) =>
|
||||||
|
_, cond, block = unpack node
|
||||||
|
|
||||||
|
inner = @block()
|
||||||
|
if is_non_atomic cond
|
||||||
|
@add_line "while", "true", "do"
|
||||||
|
inner:stm {"if", {"not", cond}, {{"break"}}}
|
||||||
|
else
|
||||||
|
@add_line "while", @value(cond), "do"
|
||||||
|
|
||||||
|
inner:stms block
|
||||||
|
|
||||||
|
@add_line inner:render()
|
||||||
|
@add_line "end"
|
||||||
|
|
||||||
|
["for"]: (node) =>
|
||||||
|
_, name, bounds, block = unpack node
|
||||||
|
bounds = @value {"explist", unpack bounds}
|
||||||
|
@add_line "for", @name(name), "=", bounds, "do"
|
||||||
|
inner = @block()
|
||||||
|
inner:stms block
|
||||||
|
@add_line inner:render()
|
||||||
|
@add_line "end"
|
||||||
|
|
||||||
|
["export"]: (node) =>
|
||||||
|
_, names = unpack node
|
||||||
|
@put_name name for name in *names when type(name) == "string"
|
||||||
|
nil
|
||||||
|
|
||||||
|
["class"]: (node) =>
|
||||||
|
_, name, table = unpack node
|
||||||
|
mt_name = "_"..name.."_mt"
|
||||||
|
@add_line "local", concat @declare({ name, mt_name }), ", "
|
||||||
|
|
||||||
|
constructor = nil
|
||||||
|
meta_methods = {}
|
||||||
|
final_properties = {}
|
||||||
|
|
||||||
|
overloaded_index = value
|
||||||
|
|
||||||
|
find_special = (name, value) ->
|
||||||
|
if name == "constructor"
|
||||||
|
constructor = value
|
||||||
|
elseif name:match("^__%a")
|
||||||
|
insert meta_methods, {name, value}
|
||||||
|
overloaded_index = value if name == "__index"
|
||||||
|
else
|
||||||
|
insert final_properties, {name, value}
|
||||||
|
|
||||||
|
find_special unpack entry for entry in *table[2]
|
||||||
|
|
||||||
|
if not overloaded_index
|
||||||
|
insert meta_methods, {"__index", {"table", final_properties}}
|
||||||
|
|
||||||
|
print util.dump constructor
|
||||||
|
print util.dump meta_methods
|
||||||
|
print util.dump final_properties
|
||||||
|
|
||||||
|
@stm {"assign", {mt_name}, {{"table", meta_methods}}}
|
||||||
|
|
||||||
|
-- synthesize constructor
|
||||||
|
if not constructor
|
||||||
|
constructor = {"fndef", {}, "slim", {}}
|
||||||
|
|
||||||
|
-- extract self arguments
|
||||||
|
self_args = {}
|
||||||
|
get_initializers = (arg) ->
|
||||||
|
if ntype(arg) == "self"
|
||||||
|
arg = arg[2]
|
||||||
|
insert self_args, arg
|
||||||
|
arg
|
||||||
|
|
||||||
|
constructor[2] = [get_initializers arg for arg in *constructor[2]]
|
||||||
|
|
||||||
|
print util.dump constructor
|
||||||
|
@stm {"assign", {name}, {constructor}}
|
||||||
|
|
||||||
|
comprehension: (node, action) =>
|
||||||
|
_, exp, clauses = unpack node
|
||||||
|
|
||||||
|
if not action
|
||||||
|
action = @block()
|
||||||
|
action:stm exp
|
||||||
|
|
||||||
|
depth = #clauses
|
||||||
|
action:set_indent @indent + depth
|
||||||
|
|
||||||
|
render_clause = (clause) =>
|
||||||
|
t = clause[1]
|
||||||
|
action = @block()
|
||||||
|
action:set_indent -1 + @indent
|
||||||
|
|
||||||
|
if "for" == t
|
||||||
|
_, names, iter = unpack clause
|
||||||
|
name_list = concat [@name name for name in *names], ", "
|
||||||
|
|
||||||
|
if ntype(iter) == "unpack"
|
||||||
|
iter = iter[2]
|
||||||
|
items_tmp = @free_name "item"
|
||||||
|
index_tmp = @free_name "index"
|
||||||
|
|
||||||
|
insert self._lines, 1, ("local %s = %s[%s]"):format name_list, items_tmp, index_tmp
|
||||||
|
|
||||||
|
action:add_lines {
|
||||||
|
("local %s = %s"):format items_tmp, @value iter
|
||||||
|
("for %s=1,#%s do"):format index_tmp, items_tmp
|
||||||
|
@render true
|
||||||
|
"end"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
action:add_lines {
|
||||||
|
("for %s in %s do"):format(name_list, @value iter)
|
||||||
|
@render true
|
||||||
|
"end"
|
||||||
|
}
|
||||||
|
elseif "when" == t
|
||||||
|
_, cond = unpack clause
|
||||||
|
action:add_lines {
|
||||||
|
("if %s then"):format @value cond
|
||||||
|
@render true
|
||||||
|
"end"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error "Unknown comprehension clause: "..t
|
||||||
|
|
||||||
|
render_clause action, clause for i, clause in reversed clauses
|
||||||
|
|
||||||
|
@add_lines action._lines -- do this better?
|
||||||
|
|
||||||
|
|
@ -2,378 +2,11 @@ module("moonscript.compile", package.seeall)
|
|||||||
local util = require("moonscript.util")
|
local util = require("moonscript.util")
|
||||||
local data = require("moonscript.data")
|
local data = require("moonscript.data")
|
||||||
local dump = require("moonscript.dump")
|
local dump = require("moonscript.dump")
|
||||||
|
require("moonscript.compile.format")
|
||||||
|
require("moonscript.compile.line")
|
||||||
local map, bind, itwos, every, reversed = util.map, util.bind, util.itwos, util.every, util.reversed
|
local map, bind, itwos, every, reversed = util.map, util.bind, util.itwos, util.every, util.reversed
|
||||||
local Stack, Set, ntype = data.Stack, data.Set, data.ntype
|
local Stack, Set, ntype = data.Stack, data.Set, data.ntype
|
||||||
local concat, insert = table.concat, table.insert
|
local concat, insert = table.concat, table.insert
|
||||||
local indent_char = " "
|
|
||||||
local pretty
|
|
||||||
pretty = function(lines, indent)
|
|
||||||
indent = indent or ""
|
|
||||||
local render
|
|
||||||
render = function(line)
|
|
||||||
if type(line) == "table" then
|
|
||||||
return indent_char .. pretty(line, indent .. indent_char)
|
|
||||||
else
|
|
||||||
return line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
lines = (function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = lines
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local line = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, render(line))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)()
|
|
||||||
local fix
|
|
||||||
fix = function(i, left, k, right)
|
|
||||||
if left:sub(-1) == ")" and right:sub(1, 1) == "(" then
|
|
||||||
lines[i] = lines[i] .. ";"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for i, l, k, r in itwos(lines) do
|
|
||||||
fix(i, l, k, r)
|
|
||||||
end
|
|
||||||
return concat(lines, "\n" .. indent)
|
|
||||||
end
|
|
||||||
local returner
|
|
||||||
returner = function(exp)
|
|
||||||
if ntype(exp) == "chain" and exp[2] == "return" then
|
|
||||||
local items = { "explist" }
|
|
||||||
local _item_0 = exp[3][2]
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local v = _item_0[_index_0]
|
|
||||||
insert(items, v)
|
|
||||||
end
|
|
||||||
return { "return", items }
|
|
||||||
else
|
|
||||||
return { "return", exp }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local moonlib = { bind = function(tbl, name) return concat({
|
|
||||||
"moon.bind(",
|
|
||||||
tbl,
|
|
||||||
".",
|
|
||||||
name,
|
|
||||||
", ",
|
|
||||||
tbl,
|
|
||||||
")"
|
|
||||||
}) end }
|
|
||||||
local cascading = Set({ "if" })
|
|
||||||
local non_atomic = Set({ "update" })
|
|
||||||
local has_value
|
|
||||||
has_value = function(node)
|
|
||||||
if ntype(node) == "chain" then
|
|
||||||
local ctype = ntype(node[#node])
|
|
||||||
return ctype ~= "call" and ctype ~= "colon"
|
|
||||||
else
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local is_non_atomic
|
|
||||||
is_non_atomic = function(node) return non_atomic[ntype(node)] end
|
|
||||||
local line_compile = {
|
|
||||||
assign = function(self, node)
|
|
||||||
local _, names, values = unpack(node)
|
|
||||||
local undeclared = self:declare(names)
|
|
||||||
local declare = "local " .. (concat(undeclared, ", "))
|
|
||||||
if self:is_stm(values) then
|
|
||||||
if #undeclared > 0 then
|
|
||||||
self:add_line(declare)
|
|
||||||
end
|
|
||||||
if cascading[ntype(values)] then
|
|
||||||
local decorate
|
|
||||||
decorate = function(value) return { "assign", names, { value } } end
|
|
||||||
return self:stm(values, decorate)
|
|
||||||
else
|
|
||||||
return self:add_line(concat((function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local n = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, self:value(n))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)(), ", ") .. " = " .. self:value(values))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local has_fndef = false
|
|
||||||
local i = 1
|
|
||||||
while i <= #values do
|
|
||||||
if ntype(values[i]) == "fndef" then
|
|
||||||
has_fndef = true
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
values = concat((function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = values
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local v = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, self:value(v))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)(), ", ")
|
|
||||||
if #undeclared == #names and not has_fndef then
|
|
||||||
return self:add_line(declare .. ' = ' .. values)
|
|
||||||
else
|
|
||||||
if #undeclared > 0 then
|
|
||||||
self:add_line(declare)
|
|
||||||
end
|
|
||||||
return self:add_line(concat((function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local n = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, self:value(n))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)(), ", ") .. " = " .. values)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
update = function(self, node)
|
|
||||||
local _, name, op, exp = unpack(node)
|
|
||||||
local op_final = op:match("(.)=")
|
|
||||||
if not op_final then
|
|
||||||
_ = error("unknown op: ") .. op
|
|
||||||
end
|
|
||||||
return self:stm({ "assign", { name }, { {
|
|
||||||
"exp",
|
|
||||||
name,
|
|
||||||
op_final,
|
|
||||||
exp
|
|
||||||
} } })
|
|
||||||
end,
|
|
||||||
["return"] = function(self, node) return self:add_line("return", self:value(node[2])) end,
|
|
||||||
["break"] = function(self, node) return self:add_line("break") end,
|
|
||||||
["import"] = function(self, node)
|
|
||||||
local _, names, source = unpack(node)
|
|
||||||
local to_bind = { }
|
|
||||||
local get_name
|
|
||||||
get_name = function(name)
|
|
||||||
if ntype(name) == ":" then
|
|
||||||
local tmp = self:name(name[2])
|
|
||||||
to_bind[tmp] = true
|
|
||||||
return tmp
|
|
||||||
else
|
|
||||||
return self:name(name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local final_names = (function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local n = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, get_name(n))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)()
|
|
||||||
local _item_0 = final_names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local name = _item_0[_index_0]
|
|
||||||
self:put_name(name)
|
|
||||||
end
|
|
||||||
local get_value
|
|
||||||
get_value = function(name)
|
|
||||||
if to_bind[name] then
|
|
||||||
return moonlib.bind(source, name)
|
|
||||||
else
|
|
||||||
return source .. "." .. name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if type(source) == "string" then
|
|
||||||
local values = (function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = final_names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local name = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, get_value(name))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)()
|
|
||||||
self:add_line("local", (concat(final_names, ", ")), "=", (concat(values, ", ")))
|
|
||||||
return(nil)
|
|
||||||
end
|
|
||||||
self:add_line("local", concat(final_names, ", "))
|
|
||||||
self:add_line("do")
|
|
||||||
local inner = self:block()
|
|
||||||
local tmp_name = inner:free_name("table")
|
|
||||||
inner:add_line("local", tmp_name, "=", self:value(source))
|
|
||||||
source = tmp_name
|
|
||||||
local _item_0 = final_names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local name = _item_0[_index_0]
|
|
||||||
inner:add_line(name .. " = " .. get_value(name))
|
|
||||||
end
|
|
||||||
self:add_line(inner:render())
|
|
||||||
return self:add_line("end")
|
|
||||||
end,
|
|
||||||
["if"] = function(self, node, ret)
|
|
||||||
local cond, block = node[2], node[3]
|
|
||||||
local add_clause
|
|
||||||
add_clause = function(clause)
|
|
||||||
local type = clause[1]
|
|
||||||
if type == "else" then
|
|
||||||
self:add_line("else")
|
|
||||||
block = clause[2]
|
|
||||||
else
|
|
||||||
self:add_line("elseif", (self:value(clause[2])), "then")
|
|
||||||
block = clause[3]
|
|
||||||
end
|
|
||||||
local b = self:block()
|
|
||||||
b:stms(block, ret)
|
|
||||||
return self:add_line(b:render())
|
|
||||||
end
|
|
||||||
self:add_line("if", (self:value(cond)), "then")
|
|
||||||
local b = self:block()
|
|
||||||
b:stms(block, ret)
|
|
||||||
self:add_line(b:render())
|
|
||||||
for i, cond in ipairs(node) do
|
|
||||||
if i > 3 then
|
|
||||||
add_clause(cond)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self:add_line("end")
|
|
||||||
end,
|
|
||||||
["while"] = function(self, node)
|
|
||||||
local _, cond, block = unpack(node)
|
|
||||||
local inner = self:block()
|
|
||||||
if is_non_atomic(cond) then
|
|
||||||
self:add_line("while", "true", "do")
|
|
||||||
inner:stm({ "if", { "not", cond }, { { "break" } } })
|
|
||||||
else
|
|
||||||
self:add_line("while", self:value(cond), "do")
|
|
||||||
end
|
|
||||||
inner:stms(block)
|
|
||||||
self:add_line(inner:render())
|
|
||||||
return self:add_line("end")
|
|
||||||
end,
|
|
||||||
["for"] = function(self, node)
|
|
||||||
local _, name, bounds, block = unpack(node)
|
|
||||||
bounds = self:value({ "explist", unpack(bounds) })
|
|
||||||
self:add_line("for", self:name(name), "=", bounds, "do")
|
|
||||||
local inner = self:block()
|
|
||||||
inner:stms(block)
|
|
||||||
self:add_line(inner:render())
|
|
||||||
return self:add_line("end")
|
|
||||||
end,
|
|
||||||
["class"] = function(self, node)
|
|
||||||
local _, name, table = unpack(node)
|
|
||||||
local mt_name = "_" .. name .. "_mt"
|
|
||||||
self:add_line("local", concat(self:declare({ name, mt_name }), ", "))
|
|
||||||
local constructor = nil
|
|
||||||
local meta_methods = { }
|
|
||||||
local final_properties = { }
|
|
||||||
local overloaded_index = value
|
|
||||||
local find_special
|
|
||||||
find_special = function(name, value)
|
|
||||||
if name == "constructor" then
|
|
||||||
constructor = value
|
|
||||||
elseif name:match("^__%a") then
|
|
||||||
insert(meta_methods, { name, value })
|
|
||||||
if name == "__index" then
|
|
||||||
overloaded_index = value
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return insert(final_properties, { name, value })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local _item_0 = table[2]
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local entry = _item_0[_index_0]
|
|
||||||
find_special(unpack(entry))
|
|
||||||
end
|
|
||||||
if not overloaded_index then
|
|
||||||
insert(meta_methods, { "__index", { "table", final_properties } })
|
|
||||||
end
|
|
||||||
print(util.dump(constructor))
|
|
||||||
print(util.dump(meta_methods))
|
|
||||||
print(util.dump(final_properties))
|
|
||||||
self:stm({ "assign", { mt_name }, { { "table", meta_methods } } })
|
|
||||||
if not constructor then
|
|
||||||
constructor = {
|
|
||||||
"fndef",
|
|
||||||
{ },
|
|
||||||
"slim",
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
local self_args = { }
|
|
||||||
local get_initializers
|
|
||||||
get_initializers = function(arg)
|
|
||||||
if ntype(arg) == "self" then
|
|
||||||
arg = arg[2]
|
|
||||||
insert(self_args, arg)
|
|
||||||
end
|
|
||||||
return arg
|
|
||||||
end
|
|
||||||
constructor[2] = (function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = constructor[2]
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local arg = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, get_initializers(arg))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)()
|
|
||||||
print(util.dump(constructor))
|
|
||||||
return self:stm({ "assign", { name }, { constructor } })
|
|
||||||
end,
|
|
||||||
comprehension = function(self, node, action)
|
|
||||||
local _, exp, clauses = unpack(node)
|
|
||||||
if not action then
|
|
||||||
action = self:block()
|
|
||||||
action:stm(exp)
|
|
||||||
end
|
|
||||||
local depth = #clauses
|
|
||||||
action:set_indent(self.indent + depth)
|
|
||||||
local render_clause
|
|
||||||
render_clause = function(self, clause)
|
|
||||||
local t = clause[1]
|
|
||||||
action = self:block()
|
|
||||||
action:set_indent(-1 + self.indent)
|
|
||||||
if "for" == t then
|
|
||||||
local names, iter
|
|
||||||
_, names, iter = unpack(clause)
|
|
||||||
local name_list = concat((function()
|
|
||||||
local _moon_0 = {}
|
|
||||||
local _item_0 = names
|
|
||||||
for _index_0=1,#_item_0 do
|
|
||||||
local name = _item_0[_index_0]
|
|
||||||
table.insert(_moon_0, self:name(name))
|
|
||||||
end
|
|
||||||
return _moon_0
|
|
||||||
end)(), ", ")
|
|
||||||
if ntype(iter) == "unpack" then
|
|
||||||
iter = iter[2]
|
|
||||||
local items_tmp = self:free_name("item")
|
|
||||||
local index_tmp = self:free_name("index")
|
|
||||||
insert(self._lines, 1, ("local %s = %s[%s]"):format(name_list, items_tmp, index_tmp))
|
|
||||||
return action:add_lines({
|
|
||||||
("local %s = %s"):format(items_tmp, self:value(iter)),
|
|
||||||
("for %s=1,#%s do"):format(index_tmp, items_tmp),
|
|
||||||
self:render(true),
|
|
||||||
"end"
|
|
||||||
})
|
|
||||||
else
|
|
||||||
return action:add_lines({ ("for %s in %s do"):format(name_list, self:value(iter)), self:render(true), "end" })
|
|
||||||
end
|
|
||||||
elseif "when" == t then
|
|
||||||
local cond
|
|
||||||
_, cond = unpack(clause)
|
|
||||||
return action:add_lines({ ("if %s then"):format(self:value(cond)), self:render(true), "end" })
|
|
||||||
else
|
|
||||||
return error("Unknown comprehension clause: " .. t)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for i, clause in reversed(clauses) do
|
|
||||||
render_clause(action, clause)
|
|
||||||
end
|
|
||||||
return self:add_lines(action._lines)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
local value_compile = {
|
local value_compile = {
|
||||||
exp = function(self, node)
|
exp = function(self, node)
|
||||||
local _comp
|
local _comp
|
||||||
@ -697,7 +330,7 @@ build_compiler = function()
|
|||||||
Block(nil)
|
Block(nil)
|
||||||
return setmetatable({ }, { __index = compiler_index })
|
return setmetatable({ }, { __index = compiler_index })
|
||||||
end
|
end
|
||||||
_M.tree = function(tree)
|
tree = function(tree)
|
||||||
local scope = Block()
|
local scope = Block()
|
||||||
local _item_0 = tree
|
local _item_0 = tree
|
||||||
for _index_0=1,#_item_0 do
|
for _index_0=1,#_item_0 do
|
||||||
|
@ -1,299 +1,17 @@
|
|||||||
|
|
||||||
module "moonscript.compile", package.seeall
|
module "moonscript.compile", package.seeall
|
||||||
|
|
||||||
util = require "moonscript.util"
|
util = require "moonscript.util"
|
||||||
data = require "moonscript.data"
|
data = require "moonscript.data"
|
||||||
dump = require "moonscript.dump"
|
dump = require "moonscript.dump"
|
||||||
|
|
||||||
|
require "moonscript.compile.format"
|
||||||
|
require "moonscript.compile.line"
|
||||||
|
|
||||||
import map, bind, itwos, every, reversed from util
|
import map, bind, itwos, every, reversed from util
|
||||||
import Stack, Set, ntype from data
|
import Stack, Set, ntype from data
|
||||||
import concat, insert from table
|
import concat, insert from table
|
||||||
|
|
||||||
indent_char = " "
|
export tree
|
||||||
pretty = (lines, indent) ->
|
|
||||||
indent = indent or ""
|
|
||||||
render = (line) ->
|
|
||||||
if type(line) == "table"
|
|
||||||
indent_char..pretty(line, indent..indent_char)
|
|
||||||
else
|
|
||||||
line
|
|
||||||
|
|
||||||
lines = [render line for line in *lines]
|
|
||||||
|
|
||||||
-- add semicolons for ambiguities
|
|
||||||
fix = (i, left, k, right) ->
|
|
||||||
if left:sub(-1) == ")" and right:sub(1,1) == "("
|
|
||||||
lines[i] = lines[i]..";"
|
|
||||||
fix(i,l, k,r) for i,l,k,r in itwos lines
|
|
||||||
|
|
||||||
concat lines, "\n"..indent
|
|
||||||
|
|
||||||
returner = (exp) ->
|
|
||||||
if ntype(exp) == "chain" and exp[2] == "return"
|
|
||||||
-- extract the return
|
|
||||||
items = {"explist"}
|
|
||||||
insert items, v for v in *exp[3][2]
|
|
||||||
{"return", items}
|
|
||||||
else
|
|
||||||
{"return", exp}
|
|
||||||
|
|
||||||
moonlib =
|
|
||||||
bind: (tbl, name) ->
|
|
||||||
concat {"moon.bind(", tbl, ".", name, ", ", tbl, ")"}
|
|
||||||
|
|
||||||
cascading = Set{ "if" }
|
|
||||||
|
|
||||||
-- an action that can't be completed in a single line
|
|
||||||
non_atomic = Set{ "update" }
|
|
||||||
|
|
||||||
-- does this always return a value
|
|
||||||
has_value = (node) ->
|
|
||||||
if ntype(node) == "chain"
|
|
||||||
ctype = ntype(node[#node])
|
|
||||||
ctype != "call" and ctype != "colon"
|
|
||||||
else
|
|
||||||
true
|
|
||||||
|
|
||||||
is_non_atomic = (node) ->
|
|
||||||
non_atomic[ntype(node)]
|
|
||||||
|
|
||||||
line_compile =
|
|
||||||
assign: (node) =>
|
|
||||||
_, names, values = unpack node
|
|
||||||
|
|
||||||
undeclared = @declare names
|
|
||||||
declare = "local "..(concat undeclared, ", ")
|
|
||||||
|
|
||||||
if @is_stm values
|
|
||||||
@add_line declare if #undeclared > 0
|
|
||||||
if cascading[ntype(values)]
|
|
||||||
decorate = (value) ->
|
|
||||||
{"assign", names, {value}}
|
|
||||||
|
|
||||||
@stm values, decorate
|
|
||||||
else
|
|
||||||
@add_line concat([@value n for n in *names], ", ").." = "..@value values
|
|
||||||
else
|
|
||||||
has_fndef = false
|
|
||||||
i = 1
|
|
||||||
while i <= #values
|
|
||||||
if ntype(values[i]) == "fndef"
|
|
||||||
has_fndef = true
|
|
||||||
i = i +1
|
|
||||||
|
|
||||||
-- need new compiler
|
|
||||||
-- (if ntype(v) == "fndef" then has_fndef = true) for v in *values
|
|
||||||
|
|
||||||
values = concat [@value v for v in *values], ", "
|
|
||||||
|
|
||||||
if #undeclared == #names and not has_fndef
|
|
||||||
@add_line declare..' = '..values
|
|
||||||
else
|
|
||||||
@add_line declare if #undeclared > 0
|
|
||||||
@add_line concat([@value n for n in *names], ", ").." = "..values
|
|
||||||
|
|
||||||
update: (node) =>
|
|
||||||
_, name, op, exp = unpack node
|
|
||||||
op_final = op:match"(.)="
|
|
||||||
error"unknown op: "..op if not op_final
|
|
||||||
@stm {"assign", {name}, {{"exp", name, op_final, exp}}}
|
|
||||||
|
|
||||||
["return"]: (node) =>
|
|
||||||
@add_line "return", @value node[2]
|
|
||||||
|
|
||||||
["break"]: (node) =>
|
|
||||||
@add_line "break"
|
|
||||||
|
|
||||||
["import"]: (node) =>
|
|
||||||
_, names, source = unpack node
|
|
||||||
|
|
||||||
to_bind = {}
|
|
||||||
get_name = (name) ->
|
|
||||||
if ntype(name) == ":"
|
|
||||||
tmp = @name name[2]
|
|
||||||
to_bind[tmp] = true
|
|
||||||
tmp
|
|
||||||
else
|
|
||||||
@name name
|
|
||||||
|
|
||||||
final_names = [get_name n for n in *names]
|
|
||||||
@put_name name for name in *final_names
|
|
||||||
|
|
||||||
get_value = (name) ->
|
|
||||||
if to_bind[name]
|
|
||||||
moonlib.bind source, name
|
|
||||||
else
|
|
||||||
source.."."..name
|
|
||||||
|
|
||||||
-- from constant expression, put it on one line
|
|
||||||
if type(source) == "string"
|
|
||||||
values = [get_value name for name in *final_names]
|
|
||||||
@add_line "local", (concat final_names, ", "), "=", (concat values, ", ")
|
|
||||||
return nil
|
|
||||||
|
|
||||||
@add_line "local", concat(final_names, ", ")
|
|
||||||
@add_line "do"
|
|
||||||
|
|
||||||
inner = @block()
|
|
||||||
tmp_name = inner:free_name "table"
|
|
||||||
inner:add_line "local", tmp_name , "=", @value source
|
|
||||||
|
|
||||||
source = tmp_name
|
|
||||||
inner:add_line name.." = "..get_value name for name in *final_names
|
|
||||||
|
|
||||||
@add_line inner:render()
|
|
||||||
|
|
||||||
@add_line "end"
|
|
||||||
|
|
||||||
["if"]: (node, ret) =>
|
|
||||||
cond, block = node[2], node[3]
|
|
||||||
|
|
||||||
add_clause = (clause) ->
|
|
||||||
type = clause[1]
|
|
||||||
block = if type == "else"
|
|
||||||
@add_line "else"
|
|
||||||
clause[2]
|
|
||||||
else
|
|
||||||
@add_line "elseif", (@value clause[2]), "then"
|
|
||||||
clause[3]
|
|
||||||
|
|
||||||
b = @block()
|
|
||||||
b:stms block, ret
|
|
||||||
@add_line b:render()
|
|
||||||
|
|
||||||
@add_line "if", (@value cond), "then"
|
|
||||||
|
|
||||||
b = @block()
|
|
||||||
b:stms block, ret
|
|
||||||
@add_line b:render()
|
|
||||||
|
|
||||||
add_clause cond for i, cond in ipairs node when i > 3
|
|
||||||
|
|
||||||
@add_line "end"
|
|
||||||
|
|
||||||
["while"]: (node) =>
|
|
||||||
_, cond, block = unpack node
|
|
||||||
|
|
||||||
inner = @block()
|
|
||||||
if is_non_atomic cond
|
|
||||||
@add_line "while", "true", "do"
|
|
||||||
inner:stm {"if", {"not", cond}, {{"break"}}}
|
|
||||||
else
|
|
||||||
@add_line "while", @value(cond), "do"
|
|
||||||
|
|
||||||
inner:stms block
|
|
||||||
|
|
||||||
@add_line inner:render()
|
|
||||||
@add_line "end"
|
|
||||||
|
|
||||||
["for"]: (node) =>
|
|
||||||
_, name, bounds, block = unpack node
|
|
||||||
bounds = @value {"explist", unpack bounds}
|
|
||||||
@add_line "for", @name(name), "=", bounds, "do"
|
|
||||||
inner = @block()
|
|
||||||
inner:stms block
|
|
||||||
@add_line inner:render()
|
|
||||||
@add_line "end"
|
|
||||||
|
|
||||||
["class"]: (node) =>
|
|
||||||
_, name, table = unpack node
|
|
||||||
mt_name = "_"..name.."_mt"
|
|
||||||
@add_line "local", concat @declare({ name, mt_name }), ", "
|
|
||||||
|
|
||||||
constructor = nil
|
|
||||||
meta_methods = {}
|
|
||||||
final_properties = {}
|
|
||||||
|
|
||||||
overloaded_index = value
|
|
||||||
|
|
||||||
find_special = (name, value) ->
|
|
||||||
if name == "constructor"
|
|
||||||
constructor = value
|
|
||||||
elseif name:match("^__%a")
|
|
||||||
insert meta_methods, {name, value}
|
|
||||||
overloaded_index = value if name == "__index"
|
|
||||||
else
|
|
||||||
insert final_properties, {name, value}
|
|
||||||
|
|
||||||
find_special unpack entry for entry in *table[2]
|
|
||||||
|
|
||||||
if not overloaded_index
|
|
||||||
insert meta_methods, {"__index", {"table", final_properties}}
|
|
||||||
|
|
||||||
print util.dump constructor
|
|
||||||
print util.dump meta_methods
|
|
||||||
print util.dump final_properties
|
|
||||||
|
|
||||||
@stm {"assign", {mt_name}, {{"table", meta_methods}}}
|
|
||||||
|
|
||||||
-- synthesize constructor
|
|
||||||
if not constructor
|
|
||||||
constructor = {"fndef", {}, "slim", {}}
|
|
||||||
|
|
||||||
-- extract self arguments
|
|
||||||
self_args = {}
|
|
||||||
get_initializers = (arg) ->
|
|
||||||
if ntype(arg) == "self"
|
|
||||||
arg = arg[2]
|
|
||||||
insert self_args, arg
|
|
||||||
arg
|
|
||||||
|
|
||||||
constructor[2] = [get_initializers arg for arg in *constructor[2]]
|
|
||||||
|
|
||||||
print util.dump constructor
|
|
||||||
@stm {"assign", {name}, {constructor}}
|
|
||||||
|
|
||||||
comprehension: (node, action) =>
|
|
||||||
_, exp, clauses = unpack node
|
|
||||||
|
|
||||||
if not action
|
|
||||||
action = @block()
|
|
||||||
action:stm exp
|
|
||||||
|
|
||||||
depth = #clauses
|
|
||||||
action:set_indent @indent + depth
|
|
||||||
|
|
||||||
render_clause = (clause) =>
|
|
||||||
t = clause[1]
|
|
||||||
action = @block()
|
|
||||||
action:set_indent -1 + @indent
|
|
||||||
|
|
||||||
if "for" == t
|
|
||||||
_, names, iter = unpack clause
|
|
||||||
name_list = concat [@name name for name in *names], ", "
|
|
||||||
|
|
||||||
if ntype(iter) == "unpack"
|
|
||||||
iter = iter[2]
|
|
||||||
items_tmp = @free_name "item"
|
|
||||||
index_tmp = @free_name "index"
|
|
||||||
|
|
||||||
insert self._lines, 1, ("local %s = %s[%s]"):format name_list, items_tmp, index_tmp
|
|
||||||
|
|
||||||
action:add_lines {
|
|
||||||
("local %s = %s"):format items_tmp, @value iter
|
|
||||||
("for %s=1,#%s do"):format index_tmp, items_tmp
|
|
||||||
@render true
|
|
||||||
"end"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
action:add_lines {
|
|
||||||
("for %s in %s do"):format(name_list, @value iter)
|
|
||||||
@render true
|
|
||||||
"end"
|
|
||||||
}
|
|
||||||
elseif "when" == t
|
|
||||||
_, cond = unpack clause
|
|
||||||
action:add_lines {
|
|
||||||
("if %s then"):format @value cond
|
|
||||||
@render true
|
|
||||||
"end"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error "Unknown comprehension clause: "..t
|
|
||||||
|
|
||||||
render_clause action, clause for i, clause in reversed clauses
|
|
||||||
|
|
||||||
@add_lines action._lines -- do this better?
|
|
||||||
|
|
||||||
value_compile =
|
value_compile =
|
||||||
exp: (node) =>
|
exp: (node) =>
|
||||||
@ -566,7 +284,7 @@ build_compiler = ->
|
|||||||
Block(nil)
|
Block(nil)
|
||||||
setmetatable {}, { __index: compiler_index }
|
setmetatable {}, { __index: compiler_index }
|
||||||
|
|
||||||
_M.tree = (tree) ->
|
tree = (tree) ->
|
||||||
scope = Block()
|
scope = Block()
|
||||||
scope:stm line for line in *tree
|
scope:stm line for line in *tree
|
||||||
scope:render()
|
scope:render()
|
||||||
|
@ -227,7 +227,7 @@ local build_grammar = wrap(function()
|
|||||||
Block = Ct(Line * (Break^1 * Line)^0),
|
Block = Ct(Line * (Break^1 * Line)^0),
|
||||||
Line = Cmt(Indent, check_indent) * Statement + _Space * Comment,
|
Line = Cmt(Indent, check_indent) * Statement + _Space * Comment,
|
||||||
|
|
||||||
Statement = (Import + While + For + BreakLoop + Ct(ExpList) / flatten_or_mark"explist" * Space) * (
|
Statement = (Import + While + For + Export + BreakLoop + Ct(ExpList) / flatten_or_mark"explist" * Space) * (
|
||||||
-- statement decorators
|
-- statement decorators
|
||||||
key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" +
|
key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" +
|
||||||
CompInner / mark"comprehension"
|
CompInner / mark"comprehension"
|
||||||
@ -340,6 +340,10 @@ local build_grammar = wrap(function()
|
|||||||
|
|
||||||
TableBlock = Break * #Cmt(Indent, advance_indent) * TableBlockInner * OutBlock / mark"table",
|
TableBlock = Break * #Cmt(Indent, advance_indent) * TableBlockInner * OutBlock / mark"table",
|
||||||
|
|
||||||
|
ClassDecl = key"class" * Name * TableBlock / mark"class",
|
||||||
|
|
||||||
|
Export = key"export" * Ct(NameList) / mark"export",
|
||||||
|
|
||||||
KeyValue = Ct((Name + sym"[" * Exp * sym"]") * symx":" * (Exp + TableBlock)),
|
KeyValue = Ct((Name + sym"[" * Exp * sym"]") * symx":" * (Exp + TableBlock)),
|
||||||
KeyValueList = KeyValue * (sym"," * KeyValue)^0,
|
KeyValueList = KeyValue * (sym"," * KeyValue)^0,
|
||||||
KeyValueLine = Cmt(Indent, check_indent) * KeyValueList * sym","^-1,
|
KeyValueLine = Cmt(Indent, check_indent) * KeyValueList * sym","^-1,
|
||||||
@ -371,7 +375,20 @@ local build_grammar = wrap(function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local tree = self._g:match(str, ...)
|
|
||||||
|
local tree
|
||||||
|
local args = {...}
|
||||||
|
local pass, err = pcall(function()
|
||||||
|
tree = self._g:match(str, unpack(args))
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not pass then
|
||||||
|
local line_no = pos_to_line(last_pos)
|
||||||
|
print("stopped at", line_no)
|
||||||
|
error(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if not tree then
|
if not tree then
|
||||||
local line_no = pos_to_line(last_pos)
|
local line_no = pos_to_line(last_pos)
|
||||||
local line_str = get_line(line_no)
|
local line_str = get_line(line_no)
|
||||||
|
Loading…
Reference in New Issue
Block a user