Aegisub/automation/include/moonscript.lua

5292 lines
134 KiB
Lua

package.preload['moonscript.base'] = function()
local compile = require("moonscript.compile")
local parse = require("moonscript.parse")
local concat, insert, remove
do
local _obj_0 = table
concat, insert, remove = _obj_0.concat, _obj_0.insert, _obj_0.remove
end
local split, dump, get_options, unpack
do
local _obj_0 = require("moonscript.util")
split, dump, get_options, unpack = _obj_0.split, _obj_0.dump, _obj_0.get_options, _obj_0.unpack
end
local lua = {
loadstring = loadstring,
load = load
}
local dirsep, line_tables, create_moonpath, to_lua, moon_loader, loadstring, loadfile, dofile, insert_loader, remove_loader
dirsep = "/"
line_tables = require("moonscript.line_tables")
create_moonpath = function(package_path)
local paths = split(package_path, ";")
for i, path in ipairs(paths) do
local p = path:match("^(.-)%.lua$")
if p then
paths[i] = p .. ".moon"
end
end
return concat(paths, ";")
end
to_lua = function(text, options)
if options == nil then
options = { }
end
if "string" ~= type(text) then
local t = type(text)
return nil, "expecting string (got " .. t .. ")", 2
end
local tree, err = parse.string(text)
if not tree then
return nil, err
end
local code, ltable, pos = compile.tree(tree, options)
if not code then
return nil, compile.format_error(ltable, pos, text), 2
end
return code, ltable
end
moon_loader = function(name)
local name_path = name:gsub("%.", dirsep)
local file, file_path
local _list_0 = split(package.moonpath, ";")
for _index_0 = 1, #_list_0 do
local path = _list_0[_index_0]
file_path = path:gsub("?", name_path)
file = io.open(file_path)
if file then
break
end
end
if file then
local text = file:read("*a")
file:close()
local res, err = loadstring(text, file_path)
if not res then
error(file_path .. ": " .. err)
end
return res
end
return nil, "Could not find moon file"
end
loadstring = function(...)
local options, str, chunk_name, mode, env = get_options(...)
chunk_name = chunk_name or "=(moonscript.loadstring)"
local code, ltable_or_err = to_lua(str, options)
if not (code) then
return nil, ltable_or_err
end
if chunk_name then
line_tables[chunk_name] = ltable_or_err
end
return (lua.loadstring or lua.load)(code, chunk_name, unpack({
mode,
env
}))
end
loadfile = function(fname, ...)
local file, err = io.open(fname)
if not (file) then
return nil, err
end
local text = assert(file:read("*a"))
file:close()
return loadstring(text, fname, ...)
end
dofile = function(...)
local f = assert(loadfile(...))
return f()
end
insert_loader = function(pos)
if pos == nil then
pos = 2
end
if not package.moonpath then
package.moonpath = create_moonpath(package.path)
end
local loaders = package.loaders or package.searchers
for _index_0 = 1, #loaders do
local loader = loaders[_index_0]
if loader == moon_loader then
return false
end
end
insert(loaders, pos, moon_loader)
return true
end
remove_loader = function()
local loaders = package.loaders or package.searchers
for i, loader in ipairs(loaders) do
if loader == moon_loader then
remove(loaders, i)
return true
end
end
return false
end
return {
_NAME = "moonscript",
insert_loader = insert_loader,
remove_loader = remove_loader,
to_lua = to_lua,
moon_chunk = moon_chunk,
moon_loader = moon_loader,
dirsep = dirsep,
dofile = dofile,
loadfile = loadfile,
loadstring = loadstring
}
end
package.preload['moonscript.cmd.coverage'] = function()
local log
log = function(str)
if str == nil then
str = ""
end
return io.stderr:write(str .. "\n")
end
local create_counter
create_counter = function()
return setmetatable({ }, {
__index = function(self, name)
do
local tbl = setmetatable({ }, {
__index = function(self)
return 0
end
})
self[name] = tbl
return tbl
end
end
})
end
local position_to_lines
position_to_lines = function(file_content, positions)
local lines = { }
local current_pos = 0
local line_no = 1
for char in file_content:gmatch(".") do
do
local count = rawget(positions, current_pos)
if count then
lines[line_no] = count
end
end
if char == "\n" then
line_no = line_no + 1
end
current_pos = current_pos + 1
end
return lines
end
local format_file
format_file = function(fname, positions)
local file = assert(io.open(fname))
local content = file:read("*a")
file:close()
local lines = position_to_lines(content, positions)
log("------| @" .. tostring(fname))
local line_no = 1
for line in (content .. "\n"):gmatch("(.-)\n") do
local foramtted_no = ("% 5d"):format(line_no)
local sym = lines[line_no] and "*" or " "
log(tostring(sym) .. tostring(foramtted_no) .. "| " .. tostring(line))
line_no = line_no + 1
end
return log()
end
local CodeCoverage
do
local _base_0 = {
reset = function(self)
self.line_counts = create_counter()
end,
start = function(self)
return debug.sethook((function()
local _base_1 = self
local _fn_0 = _base_1.process_line
return function(...)
return _fn_0(_base_1, ...)
end
end)(), "l")
end,
stop = function(self)
return debug.sethook()
end,
print_results = function(self)
return self:format_results()
end,
process_line = function(self, _, line_no)
local debug_data = debug.getinfo(2, "S")
local source = debug_data.source
self.line_counts[source][line_no] = self.line_counts[source][line_no] + 1
end,
format_results = function(self)
local line_table = require("moonscript.line_tables")
local positions = create_counter()
for file, lines in pairs(self.line_counts) do
local _continue_0 = false
repeat
local file_table = line_table[file]
if not (file_table) then
_continue_0 = true
break
end
for line, count in pairs(lines) do
local _continue_1 = false
repeat
local position = file_table[line]
if not (position) then
_continue_1 = true
break
end
positions[file][position] = positions[file][position] + count
_continue_1 = true
until true
if not _continue_1 then
break
end
end
_continue_0 = true
until true
if not _continue_0 then
break
end
end
for file, ps in pairs(positions) do
format_file(file, ps)
end
end
}
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function(self)
return self:reset()
end,
__base = _base_0,
__name = "CodeCoverage"
}, {
__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
CodeCoverage = _class_0
end
return {
CodeCoverage = CodeCoverage
}
end
package.preload['moonscript.cmd.lint'] = function()
local insert
do
local _obj_0 = table
insert = _obj_0.insert
end
local Set
do
local _obj_0 = require("moonscript.data")
Set = _obj_0.Set
end
local Block
do
local _obj_0 = require("moonscript.compile")
Block = _obj_0.Block
end
local default_whitelist = Set({
'_G',
'_VERSION',
'assert',
'bit32',
'collectgarbage',
'coroutine',
'debug',
'dofile',
'error',
'getfenv',
'getmetatable',
'io',
'ipairs',
'load',
'loadfile',
'loadstring',
'math',
'module',
'next',
'os',
'package',
'pairs',
'pcall',
'print',
'rawequal',
'rawget',
'rawlen',
'rawset',
'require',
'select',
'setfenv',
'setmetatable',
'string',
'table',
'tonumber',
'tostring',
'type',
'unpack',
'xpcall',
"nil",
"true",
"false"
})
local LinterBlock
do
local _parent_0 = Block
local _base_0 = {
block = function(self, ...)
do
local _with_0 = _parent_0.block(self, ...)
_with_0.block = self.block
_with_0.value_compilers = self.value_compilers
return _with_0
end
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
local _class_0 = setmetatable({
__init = function(self, whitelist_globals, ...)
if whitelist_globals == nil then
whitelist_globals = default_whitelist
end
_parent_0.__init(self, ...)
self.lint_errors = { }
local vc = self.value_compilers
self.value_compilers = setmetatable({
ref = function(block, val)
local name = val[2]
if not (block:has_name(name) or whitelist_globals[name] or name:match("%.")) then
insert(self.lint_errors, {
"accessing global " .. tostring(name),
val[-1]
})
end
return vc.ref(block, val)
end
}, {
__index = vc
})
end,
__base = _base_0,
__name = "LinterBlock",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
LinterBlock = _class_0
end
local format_lint
format_lint = function(errors, code, header)
if not (next(errors)) then
return
end
local pos_to_line, get_line
do
local _obj_0 = require("moonscript.util")
pos_to_line, get_line = _obj_0.pos_to_line, _obj_0.get_line
end
local formatted
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #errors do
local _des_0 = errors[_index_0]
local msg, pos
msg, pos = _des_0[1], _des_0[2]
if pos then
local line = pos_to_line(code, pos)
msg = "line " .. tostring(line) .. ": " .. tostring(msg)
local line_text = "> " .. get_line(code, line)
local sep_len = math.max(#msg, #line_text)
_accum_0[_len_0] = table.concat({
msg,
("="):rep(sep_len),
line_text
}, "\n")
else
_accum_0[_len_0] = msg
end
_len_0 = _len_0 + 1
end
formatted = _accum_0
end
if header then
table.insert(formatted, 1, header)
end
return table.concat(formatted, "\n\n")
end
local whitelist_for_file
do
local lint_config
whitelist_for_file = function(fname)
if not (lint_config) then
lint_config = { }
pcall(function()
lint_config = require("lint_config")
end)
end
if not (lint_config.whitelist_globals) then
return default_whitelist
end
local final_list = { }
for pattern, list in pairs(lint_config.whitelist_globals) do
if fname:match(pattern) then
for _index_0 = 1, #list do
local item = list[_index_0]
insert(final_list, item)
end
end
end
return setmetatable(Set(final_list), {
__index = default_whitelist
})
end
end
local lint_code
lint_code = function(code, name, whitelist_globals)
if name == nil then
name = "string input"
end
local parse = require("moonscript.parse")
local tree, err = parse.string(code)
if not (tree) then
return nil, err
end
local scope = LinterBlock(whitelist_globals)
scope:stms(tree)
return format_lint(scope.lint_errors, code, name)
end
local lint_file
lint_file = function(fname)
local f, err = io.open(fname)
if not (f) then
return nil, err
end
return lint_code(f:read("*a"), fname, whitelist_for_file(fname))
end
return {
lint_code = lint_code,
lint_file = lint_file
}
end
package.preload['moonscript.compile.statement'] = function()
local util = require("moonscript.util")
local data = require("moonscript.data")
local reversed, unpack
reversed, unpack = util.reversed, util.unpack
local ntype
do
local _obj_0 = require("moonscript.types")
ntype = _obj_0.ntype
end
local concat, insert
do
local _obj_0 = table
concat, insert = _obj_0.concat, _obj_0.insert
end
return {
raw = function(self, node)
return self:add(node[2])
end,
lines = function(self, node)
local _list_0 = node[2]
for _index_0 = 1, #_list_0 do
local line = _list_0[_index_0]
self:add(line)
end
end,
declare = function(self, node)
local names = node[2]
local undeclared = self:declare(names)
if #undeclared > 0 then
do
local _with_0 = self:line("local ")
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #undeclared do
local name = undeclared[_index_0]
_accum_0[_len_0] = self:name(name)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")
return _with_0
end
end
end,
declare_with_shadows = function(self, node)
local names = node[2]
self:declare(names)
do
local _with_0 = self:line("local ")
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #names do
local name = names[_index_0]
_accum_0[_len_0] = self:name(name)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")
return _with_0
end
end,
assign = function(self, node)
local _, names, values = unpack(node)
local undeclared = self:declare(names)
local declare = "local " .. concat(undeclared, ", ")
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
do
local _with_0 = self:line()
if #undeclared == #names and not has_fndef then
_with_0:append(declare)
else
if #undeclared > 0 then
self:add(declare)
end
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #names do
local name = names[_index_0]
_accum_0[_len_0] = self:value(name)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")
end
_with_0:append(" = ")
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #values do
local v = values[_index_0]
_accum_0[_len_0] = self:value(v)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")
return _with_0
end
end,
["return"] = function(self, node)
return self:line("return ", (function()
if node[2] ~= "" then
return self:value(node[2])
end
end)())
end,
["break"] = function(self, node)
return "break"
end,
["if"] = function(self, node)
local cond, block = node[2], node[3]
local root
do
local _with_0 = self:block(self:line("if ", self:value(cond), " then"))
_with_0:stms(block)
root = _with_0
end
local current = root
local add_clause
add_clause = function(clause)
local type = clause[1]
local i = 2
local next
if type == "else" then
next = self:block("else")
else
i = i + 1
next = self:block(self:line("elseif ", self:value(clause[2]), " then"))
end
next:stms(clause[i])
current.next = next
current = next
end
for _index_0 = 4, #node do
cond = node[_index_0]
add_clause(cond)
end
return root
end,
["repeat"] = function(self, node)
local cond, block = unpack(node, 2)
do
local _with_0 = self:block("repeat", self:line("until ", self:value(cond)))
_with_0:stms(block)
return _with_0
end
end,
["while"] = function(self, node)
local _, cond, block = unpack(node)
do
local _with_0 = self:block(self:line("while ", self:value(cond), " do"))
_with_0:stms(block)
return _with_0
end
end,
["for"] = function(self, node)
local _, name, bounds, block = unpack(node)
local loop = self:line("for ", self:name(name), " = ", self:value({
"explist",
unpack(bounds)
}), " do")
do
local _with_0 = self:block(loop)
_with_0:declare({
name
})
_with_0:stms(block)
return _with_0
end
end,
foreach = function(self, node)
local _, names, exps, block = unpack(node)
local loop
do
local _with_0 = self:line()
_with_0:append("for ")
loop = _with_0
end
do
local _with_0 = self:block(loop)
loop:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #names do
local name = names[_index_0]
_accum_0[_len_0] = _with_0:name(name, false)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")
loop:append(" in ")
loop:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #exps do
local exp = exps[_index_0]
_accum_0[_len_0] = self:value(exp)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ",")
loop:append(" do")
_with_0:declare(names)
_with_0:stms(block)
return _with_0
end
end,
export = function(self, node)
local _, names = unpack(node)
if type(names) == "string" then
if names == "*" then
self.export_all = true
elseif names == "^" then
self.export_proper = true
end
else
self:declare(names)
end
return nil
end,
run = function(self, code)
code:call(self)
return nil
end,
group = function(self, node)
return self:stms(node[2])
end,
["do"] = function(self, node)
do
local _with_0 = self:block()
_with_0:stms(node[2])
return _with_0
end
end,
noop = function(self) end
}
end
package.preload['moonscript.compile.value'] = function()
local util = require("moonscript.util")
local data = require("moonscript.data")
local ntype
do
local _obj_0 = require("moonscript.types")
ntype = _obj_0.ntype
end
local user_error
do
local _obj_0 = require("moonscript.errors")
user_error = _obj_0.user_error
end
local concat, insert
do
local _obj_0 = table
concat, insert = _obj_0.concat, _obj_0.insert
end
local unpack
unpack = util.unpack
local table_delim = ","
local string_chars = {
["\r"] = "\\r",
["\n"] = "\\n"
}
return {
exp = function(self, node)
local _comp
_comp = function(i, value)
if i % 2 == 1 and value == "!=" then
value = "~="
end
return self:value(value)
end
do
local _with_0 = self:line()
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for i, v in ipairs(node) do
if i > 1 then
_accum_0[_len_0] = _comp(i, v)
_len_0 = _len_0 + 1
end
end
return _accum_0
end)(), " ")
return _with_0
end
end,
explist = function(self, node)
do
local _with_0 = self:line()
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 2, #node do
local v = node[_index_0]
_accum_0[_len_0] = self:value(v)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ")
return _with_0
end
end,
parens = function(self, node)
return self:line("(", self:value(node[2]), ")")
end,
string = function(self, node)
local _, delim, inner = unpack(node)
local end_delim = delim:gsub("%[", "]")
if delim == "'" or delim == '"' then
inner = inner:gsub("[\r\n]", string_chars)
end
return delim .. inner .. end_delim
end,
chain = function(self, node)
local callee = node[2]
local callee_type = ntype(callee)
if callee == -1 then
callee = self:get("scope_var")
if not callee then
user_error("Short-dot syntax must be called within a with block")
end
end
if callee_type == "ref" and callee[2] == "super" or callee == "super" then
do
local sup = self:get("super")
if sup then
return self:value(sup(self, node))
end
end
end
local chain_item
chain_item = function(node)
local t, arg = unpack(node)
if t == "call" then
return "(", self:values(arg), ")"
elseif t == "index" then
return "[", self:value(arg), "]"
elseif t == "dot" then
return ".", tostring(arg)
elseif t == "colon" then
return ":", arg, chain_item(node[3])
elseif t == "colon_stub" then
return user_error("Uncalled colon stub")
else
return error("Unknown chain action: " .. tostring(t))
end
end
if (callee_type == "self" or callee_type == "self_class") and node[3] and ntype(node[3]) == "call" then
callee[1] = callee_type .. "_colon"
end
local callee_value = self:value(callee)
if ntype(callee) == "exp" then
callee_value = self:line("(", callee_value, ")")
end
local actions
do
local _with_0 = self:line()
for _index_0 = 3, #node do
local action = node[_index_0]
_with_0:append(chain_item(action))
end
actions = _with_0
end
return self:line(callee_value, actions)
end,
fndef = function(self, node)
local _, args, whitelist, arrow, block = unpack(node)
local default_args = { }
local self_args = { }
local arg_names
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #args do
local arg = args[_index_0]
local name, default_value = unpack(arg)
if type(name) == "string" then
name = name
else
if name[1] == "self" or name[1] == "self_class" then
insert(self_args, name)
end
name = name[2]
end
if default_value then
insert(default_args, arg)
end
local _value_0 = name
_accum_0[_len_0] = _value_0
_len_0 = _len_0 + 1
end
arg_names = _accum_0
end
if arrow == "fat" then
insert(arg_names, 1, "self")
end
do
local _with_0 = self:block()
if #whitelist > 0 then
_with_0:whitelist_names(whitelist)
end
for _index_0 = 1, #arg_names do
local name = arg_names[_index_0]
_with_0:put_name(name)
end
for _index_0 = 1, #default_args do
local default = default_args[_index_0]
local name, value = unpack(default)
if type(name) == "table" then
name = name[2]
end
_with_0:stm({
'if',
{
'exp',
{
"ref",
name
},
'==',
'nil'
},
{
{
'assign',
{
name
},
{
value
}
}
}
})
end
local self_arg_values
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #self_args do
local arg = self_args[_index_0]
_accum_0[_len_0] = arg[2]
_len_0 = _len_0 + 1
end
self_arg_values = _accum_0
end
if #self_args > 0 then
_with_0:stm({
"assign",
self_args,
self_arg_values
})
end
_with_0:stms(block)
if #args > #arg_names then
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #args do
local arg = args[_index_0]
_accum_0[_len_0] = arg[1]
_len_0 = _len_0 + 1
end
arg_names = _accum_0
end
end
_with_0.header = "function(" .. concat(arg_names, ", ") .. ")"
return _with_0
end
end,
table = function(self, node)
local _, items = unpack(node)
do
local _with_0 = self:block("{", "}")
local format_line
format_line = function(tuple)
if #tuple == 2 then
local key, value = unpack(tuple)
if ntype(key) == "key_literal" and data.lua_keywords[key[2]] then
key = {
"string",
'"',
key[2]
}
end
local assign
if ntype(key) == "key_literal" then
assign = key[2]
else
assign = self:line("[", _with_0:value(key), "]")
end
_with_0:set("current_block", key)
local out = self:line(assign, " = ", _with_0:value(value))
_with_0:set("current_block", nil)
return out
else
return self:line(_with_0:value(tuple[1]))
end
end
if items then
local count = #items
for i, tuple in ipairs(items) do
local line = format_line(tuple)
if not (count == i) then
line:append(table_delim)
end
_with_0:add(line)
end
end
return _with_0
end
end,
minus = function(self, node)
return self:line("-", self:value(node[2]))
end,
temp_name = function(self, node, ...)
return node:get_name(self, ...)
end,
number = function(self, node)
return node[2]
end,
length = function(self, node)
return self:line("#", self:value(node[2]))
end,
["not"] = function(self, node)
return self:line("not ", self:value(node[2]))
end,
self = function(self, node)
return "self." .. self:name(node[2])
end,
self_class = function(self, node)
return "self.__class." .. self:name(node[2])
end,
self_colon = function(self, node)
return "self:" .. self:name(node[2])
end,
self_class_colon = function(self, node)
return "self.__class:" .. self:name(node[2])
end,
ref = function(self, value)
do
local sup = value[2] == "super" and self:get("super")
if sup then
return self:value(sup(self))
end
end
return tostring(value[2])
end,
raw_value = function(self, value)
if value == "..." then
self:send("varargs")
end
return tostring(value)
end
}
end
package.preload['moonscript.compile'] = function()
local util = require("moonscript.util")
local dump = require("moonscript.dump")
local transform = require("moonscript.transform")
local NameProxy, LocalName
do
local _obj_0 = require("moonscript.transform.names")
NameProxy, LocalName = _obj_0.NameProxy, _obj_0.LocalName
end
local Set
do
local _obj_0 = require("moonscript.data")
Set = _obj_0.Set
end
local ntype, has_value
do
local _obj_0 = require("moonscript.types")
ntype, has_value = _obj_0.ntype, _obj_0.has_value
end
local statement_compilers = require("moonscript.compile.statement")
local value_compilers = require("moonscript.compile.value")
local concat, insert
do
local _obj_0 = table
concat, insert = _obj_0.concat, _obj_0.insert
end
local pos_to_line, get_closest_line, trim, unpack
pos_to_line, get_closest_line, trim, unpack = util.pos_to_line, util.get_closest_line, util.trim, util.unpack
local mtype = util.moon.type
local indent_char = " "
local Line, DelayedLine, Lines, Block, RootBlock
do
local _base_0 = {
mark_pos = function(self, pos, line)
if line == nil then
line = #self
end
if not (self.posmap[line]) then
self.posmap[line] = pos
end
end,
add = function(self, item)
local _exp_0 = mtype(item)
if Line == _exp_0 then
item:render(self)
elseif Block == _exp_0 then
item:render(self)
else
self[#self + 1] = item
end
return self
end,
flatten_posmap = function(self, line_no, out)
if line_no == nil then
line_no = 0
end
if out == nil then
out = { }
end
local posmap = self.posmap
for i, l in ipairs(self) do
local _exp_0 = mtype(l)
if "string" == _exp_0 or DelayedLine == _exp_0 then
line_no = line_no + 1
out[line_no] = posmap[i]
for _ in l:gmatch("\n") do
line_no = line_no + 1
end
out[line_no] = posmap[i]
elseif Lines == _exp_0 then
local _
_, line_no = l:flatten_posmap(line_no, out)
else
error("Unknown item in Lines: " .. tostring(l))
end
end
return out, line_no
end,
flatten = function(self, indent, buffer)
if indent == nil then
indent = nil
end
if buffer == nil then
buffer = { }
end
for i = 1, #self do
local l = self[i]
local t = mtype(l)
if t == DelayedLine then
l = l:render()
t = "string"
end
local _exp_0 = t
if "string" == _exp_0 then
if indent then
insert(buffer, indent)
end
insert(buffer, l)
if "string" == type(self[i + 1]) then
local lc = l:sub(-1)
if (lc == ")" or lc == "]") and self[i + 1]:sub(1, 1) == "(" then
insert(buffer, ";")
end
end
insert(buffer, "\n")
local last = l
elseif Lines == _exp_0 then
l:flatten(indent and indent .. indent_char or indent_char, buffer)
else
error("Unknown item in Lines: " .. tostring(l))
end
end
return buffer
end,
__tostring = function(self)
local strip
strip = function(t)
if "table" == type(t) then
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #t do
local v = t[_index_0]
_accum_0[_len_0] = strip(v)
_len_0 = _len_0 + 1
end
return _accum_0
else
return t
end
end
return "Lines<" .. tostring(util.dump(strip(self)):sub(1, -2)) .. ">"
end
}
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function(self)
self.posmap = { }
end,
__base = _base_0,
__name = "Lines"
}, {
__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
Lines = _class_0
end
do
local _base_0 = {
pos = nil,
append_list = function(self, items, delim)
for i = 1, #items do
self:append(items[i])
if i < #items then
insert(self, delim)
end
end
return nil
end,
append = function(self, first, ...)
if Line == mtype(first) then
if not (self.pos) then
self.pos = first.pos
end
for _index_0 = 1, #first do
local value = first[_index_0]
self:append(value)
end
else
insert(self, first)
end
if ... then
return self:append(...)
end
end,
render = function(self, buffer)
local current = { }
local add_current
add_current = function()
buffer:add(concat(current))
return buffer:mark_pos(self.pos)
end
for _index_0 = 1, #self do
local chunk = self[_index_0]
local _exp_0 = mtype(chunk)
if Block == _exp_0 then
local _list_0 = chunk:render(Lines())
for _index_1 = 1, #_list_0 do
local block_chunk = _list_0[_index_1]
if "string" == type(block_chunk) then
insert(current, block_chunk)
else
add_current()
buffer:add(block_chunk)
current = { }
end
end
else
insert(current, chunk)
end
end
if current[1] then
add_current()
end
return buffer
end,
__tostring = function(self)
return "Line<" .. tostring(util.dump(self):sub(1, -2)) .. ">"
end
}
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function() end,
__base = _base_0,
__name = "Line"
}, {
__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
Line = _class_0
end
do
local _base_0 = {
prepare = function() end,
render = function(self)
self:prepare()
return concat(self)
end
}
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function(self, fn)
self.prepare = fn
end,
__base = _base_0,
__name = "DelayedLine"
}, {
__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
DelayedLine = _class_0
end
do
local _base_0 = {
header = "do",
footer = "end",
export_all = false,
export_proper = false,
value_compilers = value_compilers,
__tostring = function(self)
local h
if "string" == type(self.header) then
h = self.header
else
h = unpack(self.header:render({ }))
end
return "Block<" .. tostring(h) .. "> <- " .. tostring(self.parent)
end,
set = function(self, name, value)
self._state[name] = value
end,
get = function(self, name)
return self._state[name]
end,
get_current = function(self, name)
return rawget(self._state, name)
end,
listen = function(self, name, fn)
self._listeners[name] = fn
end,
unlisten = function(self, name)
self._listeners[name] = nil
end,
send = function(self, name, ...)
do
local fn = self._listeners[name]
if fn then
return fn(self, ...)
end
end
end,
declare = function(self, names)
local undeclared
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #names do
local _continue_0 = false
repeat
local name = names[_index_0]
local is_local = false
local real_name
local _exp_0 = mtype(name)
if LocalName == _exp_0 then
is_local = true
real_name = name:get_name(self)
elseif NameProxy == _exp_0 then
real_name = name:get_name(self)
elseif "table" == _exp_0 then
real_name = name[1] == "ref" and name[2]
elseif "string" == _exp_0 then
real_name = name
end
if not (is_local or real_name and not self:has_name(real_name, true)) then
_continue_0 = true
break
end
self:put_name(real_name)
if self:name_exported(real_name) then
_continue_0 = true
break
end
local _value_0 = real_name
_accum_0[_len_0] = _value_0
_len_0 = _len_0 + 1
_continue_0 = true
until true
if not _continue_0 then
break
end
end
undeclared = _accum_0
end
return undeclared
end,
whitelist_names = function(self, names)
self._name_whitelist = Set(names)
end,
name_exported = function(self, name)
if self.export_all then
return true
end
if self.export_proper and name:match("^%u") then
return true
end
end,
put_name = function(self, name, ...)
local value = ...
if select("#", ...) == 0 then
value = true
end
if NameProxy == mtype(name) then
name = name:get_name(self)
end
self._names[name] = value
end,
has_name = function(self, name, skip_exports)
if not skip_exports and self:name_exported(name) then
return true
end
local yes = self._names[name]
if yes == nil and self.parent then
if not self._name_whitelist or self._name_whitelist[name] then
return self.parent:has_name(name, true)
end
else
return yes
end
end,
is_local = function(self, node)
local t = mtype(node)
if t == "string" then
return self:has_name(node, false)
end
if t == NameProxy or t == LocalName then
return true
end
if t == "table" then
if node[1] == "ref" or (node[1] == "chain" and #node == 2) then
return self:is_local(node[2])
end
end
return false
end,
free_name = function(self, prefix, dont_put)
prefix = prefix or "moon"
local searching = true
local name, i = nil, 0
while searching do
name = concat({
"",
prefix,
i
}, "_")
i = i + 1
searching = self:has_name(name, true)
end
if not dont_put then
self:put_name(name)
end
return name
end,
init_free_var = function(self, prefix, value)
local name = self:free_name(prefix, true)
self:stm({
"assign",
{
name
},
{
value
}
})
return name
end,
add = function(self, item)
self._lines:add(item)
return item
end,
render = function(self, buffer)
buffer:add(self.header)
buffer:mark_pos(self.pos)
if self.next then
buffer:add(self._lines)
self.next:render(buffer)
else
if #self._lines == 0 and "string" == type(buffer[#buffer]) then
buffer[#buffer] = buffer[#buffer] .. (" " .. (unpack(Lines():add(self.footer))))
else
buffer:add(self._lines)
buffer:add(self.footer)
buffer:mark_pos(self.pos)
end
end
return buffer
end,
block = function(self, header, footer)
return Block(self, header, footer)
end,
line = function(self, ...)
do
local _with_0 = Line()
_with_0:append(...)
return _with_0
end
end,
is_stm = function(self, node)
return statement_compilers[ntype(node)] ~= nil
end,
is_value = function(self, node)
local t = ntype(node)
return self.value_compilers[t] ~= nil or t == "value"
end,
name = function(self, node, ...)
if type(node) == "string" then
return node
else
return self:value(node, ...)
end
end,
value = function(self, node, ...)
node = self.transform.value(node)
local action
if type(node) ~= "table" then
action = "raw_value"
else
action = node[1]
end
local fn = self.value_compilers[action]
if not (fn) then
error({
"compile-error",
"Failed to find value compiler for: " .. dump.value(node),
node[-1]
})
end
local out = fn(self, node, ...)
if type(node) == "table" and node[-1] then
if type(out) == "string" then
do
local _with_0 = Line()
_with_0:append(out)
out = _with_0
end
end
out.pos = node[-1]
end
return out
end,
values = function(self, values, delim)
delim = delim or ', '
do
local _with_0 = Line()
_with_0:append_list((function()
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #values do
local v = values[_index_0]
_accum_0[_len_0] = self:value(v)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), delim)
return _with_0
end
end,
stm = function(self, node, ...)
if not node then
return
end
node = self.transform.statement(node)
local result
do
local fn = statement_compilers[ntype(node)]
if fn then
result = fn(self, node, ...)
else
if has_value(node) then
result = self:stm({
"assign",
{
"_"
},
{
node
}
})
else
result = self:value(node)
end
end
end
if result then
if type(node) == "table" and type(result) == "table" and node[-1] then
result.pos = node[-1]
end
self:add(result)
end
return nil
end,
stms = function(self, stms, ret)
if ret then
error("deprecated stms call, use transformer")
end
local current_stms, current_stm_i
current_stms, current_stm_i = self.current_stms, self.current_stm_i
self.current_stms = stms
for i = 1, #stms do
self.current_stm_i = i
self:stm(stms[i])
end
self.current_stms = current_stms
self.current_stm_i = current_stm_i
return nil
end,
splice = function(self, fn)
local lines = {
"lines",
self._lines
}
self._lines = Lines()
return self:stms(fn(lines))
end
}
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function(self, parent, header, footer)
self.parent, self.header, self.footer = parent, header, footer
self._lines = Lines()
self._names = { }
self._state = { }
self._listeners = { }
do
self.transform = {
value = transform.Value:bind(self),
statement = transform.Statement:bind(self)
}
end
if self.parent then
self.root = self.parent.root
self.indent = self.parent.indent + 1
setmetatable(self._state, {
__index = self.parent._state
})
return setmetatable(self._listeners, {
__index = self.parent._listeners
})
else
self.indent = 0
end
end,
__base = _base_0,
__name = "Block"
}, {
__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
Block = _class_0
end
do
local _parent_0 = Block
local _base_0 = {
__tostring = function(self)
return "RootBlock<>"
end,
root_stms = function(self, stms)
if not (self.options.implicitly_return_root == false) then
stms = transform.Statement.transformers.root_stms(self, stms)
end
return self:stms(stms)
end,
render = function(self)
local buffer = self._lines:flatten()
if buffer[#buffer] == "\n" then
buffer[#buffer] = nil
end
return table.concat(buffer)
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
local _class_0 = setmetatable({
__init = function(self, options)
self.options = options
self.root = self
return _parent_0.__init(self)
end,
__base = _base_0,
__name = "RootBlock",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
return _parent_0[name]
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
RootBlock = _class_0
end
local format_error
format_error = function(msg, pos, file_str)
local line_message
if pos then
local line = pos_to_line(file_str, pos)
local line_str
line_str, line = get_closest_line(file_str, line)
line_str = line_str or ""
line_message = (" [%d] >> %s"):format(line, trim(line_str))
end
return concat({
"Compile error: " .. msg,
line_message
}, "\n")
end
local value
value = function(value)
local out = nil
do
local _with_0 = RootBlock()
_with_0:add(_with_0:value(value))
out = _with_0:render()
end
return out
end
local tree
tree = function(tree, options)
if options == nil then
options = { }
end
assert(tree, "missing tree")
local scope = (options.scope or RootBlock)(options)
local runner = coroutine.create(function()
return scope:root_stms(tree)
end)
local success, err = coroutine.resume(runner)
if not (success) then
local error_msg, error_pos
if type(err) == "table" then
local error_type = err[1]
local _exp_0 = err[1]
if "user-error" == _exp_0 or "compile-error" == _exp_0 then
error_msg, error_pos = unpack(err, 2)
else
error_msg, error_pos = error("Unknown error thrown", util.dump(error_msg))
end
else
error_msg, error_pos = concat({
err,
debug.traceback(runner)
}, "\n")
end
return nil, error_msg, error_pos or scope.last_pos
end
local lua_code = scope:render()
local posmap = scope._lines:flatten_posmap()
return lua_code, posmap
end
do
local data = require("moonscript.data")
for name, cls in pairs({
Line = Line,
Lines = Lines,
DelayedLine = DelayedLine
}) do
data[name] = cls
end
end
return {
tree = tree,
value = value,
format_error = format_error,
Block = Block,
RootBlock = RootBlock
}
end
package.preload['moonscript.data'] = function()
local concat, remove, insert
do
local _obj_0 = table
concat, remove, insert = _obj_0.concat, _obj_0.remove, _obj_0.insert
end
local Set
Set = function(items)
local _tbl_0 = { }
for _index_0 = 1, #items do
local k = items[_index_0]
_tbl_0[k] = true
end
return _tbl_0
end
local Stack
do
local _base_0 = {
__tostring = function(self)
return "<Stack {" .. concat(self, ", ") .. "}>"
end,
pop = function(self)
return remove(self)
end,
push = function(self, value, ...)
insert(self, value)
if ... then
return self:push(...)
else
return value
end
end,
top = function(self)
return self[#self]
end
}
_base_0.__index = _base_0
local _class_0 = setmetatable({
__init = function(self, ...)
self:push(...)
return nil
end,
__base = _base_0,
__name = "Stack"
}, {
__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
Stack = _class_0
end
local lua_keywords = Set({
'and',
'break',
'do',
'else',
'elseif',
'end',
'false',
'for',
'function',
'if',
'in',
'local',
'nil',
'not',
'or',
'repeat',
'return',
'then',
'true',
'until',
'while'
})
return {
Set = Set,
Stack = Stack,
lua_keywords = lua_keywords
}
end
package.preload['moonscript.dump'] = function()
local flat_value
flat_value = function(op, depth)
if depth == nil then
depth = 1
end
if type(op) == "string" then
return '"' .. op .. '"'
end
if type(op) ~= "table" then
return tostring(op)
end
local items
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #op do
local item = op[_index_0]
_accum_0[_len_0] = flat_value(item, depth + 1)
_len_0 = _len_0 + 1
end
items = _accum_0
end
local pos = op[-1]
return "{" .. (pos and "[" .. pos .. "] " or "") .. table.concat(items, ", ") .. "}"
end
local value
value = function(op)
return flat_value(op)
end
local tree
tree = function(block)
local _list_0 = block
for _index_0 = 1, #_list_0 do
value = _list_0[_index_0]
print(flat_value(value))
end
end
return {
value = value,
tree = tree
}
end
package.preload['moonscript.errors'] = function()
local util = require("moonscript.util")
local lpeg = require("lpeg")
local concat, insert
do
local _obj_0 = table
concat, insert = _obj_0.concat, _obj_0.insert
end
local split, pos_to_line
split, pos_to_line = util.split, util.pos_to_line
local user_error
user_error = function(...)
return error({
"user-error",
...
})
end
local lookup_line
lookup_line = function(fname, pos, cache)
if not cache[fname] then
do
local _with_0 = io.open(fname)
cache[fname] = _with_0:read("*a")
_with_0:close()
end
end
return pos_to_line(cache[fname], pos)
end
local reverse_line_number
reverse_line_number = function(fname, line_table, line_num, cache)
for i = line_num, 0, -1 do
if line_table[i] then
return lookup_line(fname, line_table[i], cache)
end
end
return "unknown"
end
local truncate_traceback
truncate_traceback = function(traceback, chunk_func)
if chunk_func == nil then
chunk_func = "moonscript_chunk"
end
traceback = split(traceback, "\n")
local stop = #traceback
while stop > 1 do
if traceback[stop]:match(chunk_func) then
break
end
stop = stop - 1
end
do
local _accum_0 = { }
local _len_0 = 1
local _max_0 = stop
for _index_0 = 1, _max_0 < 0 and #traceback + _max_0 or _max_0 do
local t = traceback[_index_0]
_accum_0[_len_0] = t
_len_0 = _len_0 + 1
end
traceback = _accum_0
end
local rep = "function '" .. chunk_func .. "'"
traceback[#traceback] = traceback[#traceback]:gsub(rep, "main chunk")
return concat(traceback, "\n")
end
local rewrite_traceback
rewrite_traceback = function(text, err)
local line_tables = require("moonscript.line_tables")
local V, S, Ct, C
V, S, Ct, C = lpeg.V, lpeg.S, lpeg.Ct, lpeg.C
local header_text = "stack traceback:"
local Header, Line = V("Header"), V("Line")
local Break = lpeg.S("\n")
local g = lpeg.P({
Header,
Header = header_text * Break * Ct(Line ^ 1),
Line = "\t" * C((1 - Break) ^ 0) * (Break + -1)
})
local cache = { }
local rewrite_single
rewrite_single = function(trace)
local fname, line, msg = trace:match('^%[string "(.-)"]:(%d+): (.*)$')
local tbl = line_tables[fname]
if fname and tbl then
return concat({
fname,
":",
reverse_line_number(fname, tbl, line, cache),
": ",
"(",
line,
") ",
msg
})
else
return trace
end
end
err = rewrite_single(err)
local match = g:match(text)
if not (match) then
return nil
end
for i, trace in ipairs(match) do
match[i] = rewrite_single(trace)
end
return concat({
"moon: " .. err,
header_text,
"\t" .. concat(match, "\n\t")
}, "\n")
end
return {
rewrite_traceback = rewrite_traceback,
truncate_traceback = truncate_traceback,
user_error = user_error,
reverse_line_number = reverse_line_number
}
end
package.preload['moonscript'] = function()
do
local _with_0 = require("moonscript.base")
_with_0.insert_loader()
return _with_0
end
end
package.preload['moonscript.line_tables'] = function()
return { }
end
package.preload['moonscript.parse'] = function()
local util = require"moonscript.util"
local lpeg = require"lpeg"
local debug_grammar = false
local data = require"moonscript.data"
local types = require"moonscript.types"
local ntype = types.ntype
local dump = util.dump
local trim = util.trim
local getfenv = util.getfenv
local setfenv = util.setfenv
local unpack = util.unpack
local Stack = data.Stack
local function count_indent(str)
local sum = 0
for v in str:gmatch("[\t ]") do
if v == ' ' then sum = sum + 1 end
if v == '\t' then sum = sum + 4 end
end
return sum
end
local R, S, V, P = lpeg.R, lpeg.S, lpeg.V, lpeg.P
local C, Ct, Cmt, Cg, Cb, Cc = lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cb, lpeg.Cc
lpeg.setmaxstack(10000)
local White = S" \t\r\n"^0
local _Space = S" \t"^0
local Break = P"\r"^-1 * P"\n"
local Stop = Break + -1
local Indent = C(S"\t "^0) / count_indent
local Comment = P"--" * (1 - S"\r\n")^0 * #Stop
local Space = _Space * Comment^-1
local SomeSpace = S" \t"^1 * Comment^-1
local SpaceBreak = Space * Break
local EmptyLine = SpaceBreak
local AlphaNum = R("az", "AZ", "09", "__")
local _Name = C(R("az", "AZ", "__") * AlphaNum^0)
local Name = Space * _Name
local Num = P"0x" * R("09", "af", "AF")^1 * (S"uU"^-1 * S"lL"^2)^-1 +
R"09"^1 * (S"uU"^-1 * S"lL"^2) +
(
R"09"^1 * (P"." * R"09"^1)^-1 +
P"." * R"09"^1
) * (S"eE" * P"-"^-1 * R"09"^1)^-1
Num = Space * (Num / function(value) return {"number", value} end)
local FactorOp = Space * C(S"+-")
local TermOp = Space * C(S"*/%^")
local Shebang = P"#!" * P(1 - Stop)^0
-- can't have P(false) because it causes preceding patterns not to run
local Cut = P(function() return false end)
local function ensure(patt, finally)
return patt * finally + finally * Cut
end
-- auto declare Proper variables with lpeg.V
local function wrap_env(fn)
local env = getfenv(fn)
local wrap_name = V
if debug_grammar then
local indent = 0
local indent_char = " "
local function iprint(...)
local args = {...}
for i=1,#args do
args[i] = tostring(args[i])
end
io.stdout:write(indent_char:rep(indent) .. table.concat(args, ", ") .. "\n")
end
wrap_name = function(name)
local v = V(name)
v = Cmt("", function()
iprint("* " .. name)
indent = indent + 1
return true
end) * Cmt(v, function(str, pos, ...)
iprint(name, true)
indent = indent - 1
return true, ...
end) + Cmt("", function()
iprint(name, false)
indent = indent - 1
return false
end)
return v
end
end
return setfenv(fn, setmetatable({}, {
__index = function(self, name)
local value = env[name]
if value ~= nil then return value end
if name:match"^[A-Z][A-Za-z0-9]*$" then
local v = wrap_name(name)
rawset(self, name, v)
return v
end
error("unknown variable referenced: "..name)
end
}))
end
local function extract_line(str, start_pos)
str = str:sub(start_pos)
m = str:match"^(.-)\n"
if m then return m end
return str:match"^.-$"
end
local function mark(name)
return function(...)
return {name, ...}
end
end
local function insert_pos(pos, value)
if type(value) == "table" then
value[-1] = pos
end
return value
end
local function pos(patt)
return (lpeg.Cp() * patt) / insert_pos
end
local function got(what)
return Cmt("", function(str, pos, ...)
local cap = {...}
print("++ got "..what, "["..extract_line(str, pos).."]")
return true
end)
end
local function flatten(tbl)
if #tbl == 1 then
return tbl[1]
end
return tbl
end
local function flatten_or_mark(name)
return function(tbl)
if #tbl == 1 then return tbl[1] end
table.insert(tbl, 1, name)
return tbl
end
end
-- makes sure the last item in a chain is an index
local _chain_assignable = { index = true, dot = true, slice = true }
local function is_assignable(node)
if node == "..." then
return false
end
local t = ntype(node)
return t == "ref" or t == "self" or t == "value" or t == "self_class" or
t == "chain" and _chain_assignable[ntype(node[#node])] or
t == "table"
end
local function check_assignable(str, pos, value)
if is_assignable(value) then
return true, value
end
return false
end
local flatten_explist = flatten_or_mark"explist"
local function format_assign(lhs_exps, assign)
if not assign then
return flatten_explist(lhs_exps)
end
for _, assign_exp in ipairs(lhs_exps) do
if not is_assignable(assign_exp) then
error {assign_exp, "left hand expression is not assignable"}
end
end
local t = ntype(assign)
if t == "assign" then
return {"assign", lhs_exps, unpack(assign, 2)}
elseif t == "update" then
return {"update", lhs_exps[1], unpack(assign, 2)}
end
error "unknown assign expression"
end
-- the if statement only takes a single lhs, so we wrap in table to git to
-- "assign" tuple format
local function format_single_assign(lhs, assign)
if assign then
return format_assign({lhs}, assign)
end
return lhs
end
local function sym(chars)
return Space * chars
end
local function symx(chars)
return chars
end
local function simple_string(delim, allow_interpolation)
local inner = P('\\'..delim) + "\\\\" + (1 - P(delim))
if allow_interpolation then
inter = symx"#{" * V"Exp" * sym"}"
inner = (C((inner - inter)^1) + inter / mark"interpolate")^0
else
inner = C(inner^0)
end
return C(symx(delim)) *
inner * sym(delim) / mark"string"
end
local function wrap_func_arg(value)
return {"call", {value}}
end
-- DOCME
local function flatten_func(callee, args)
if #args == 0 then return callee end
args = {"call", args}
if ntype(callee) == "chain" then
-- check for colon stub that needs arguments
if ntype(callee[#callee]) == "colon_stub" then
local stub = callee[#callee]
stub[1] = "colon"
table.insert(stub, args)
else
table.insert(callee, args)
end
return callee
end
return {"chain", callee, args}
end
local function flatten_string_chain(str, chain, args)
if not chain then return str end
return flatten_func({"chain", str, unpack(chain)}, args)
end
-- transforms a statement that has a line decorator
local function wrap_decorator(stm, dec)
if not dec then return stm end
return { "decorated", stm, dec }
end
-- wrap if statement if there is a conditional decorator
local function wrap_if(stm, cond)
if cond then
local pass, fail = unpack(cond)
if fail then fail = {"else", {fail}} end
return {"if", cond[2], {stm}, fail}
end
return stm
end
local function check_lua_string(str, pos, right, left)
return #left == #right
end
-- :name in table literal
local function self_assign(name)
return {{"key_literal", name}, name}
end
local err_msg = "Failed to parse:%s\n [%d] >> %s"
local build_grammar = wrap_env(function()
local _indent = Stack(0) -- current indent
local _do_stack = Stack(0)
local last_pos = 0 -- used to know where to report error
local function check_indent(str, pos, indent)
last_pos = pos
return _indent:top() == indent
end
local function advance_indent(str, pos, indent)
local top = _indent:top()
if top ~= -1 and indent > _indent:top() then
_indent:push(indent)
return true
end
end
local function push_indent(str, pos, indent)
_indent:push(indent)
return true
end
local function pop_indent(str, pos)
if not _indent:pop() then error("unexpected outdent") end
return true
end
local function check_do(str, pos, do_node)
local top = _do_stack:top()
if top == nil or top then
return true, do_node
end
return false
end
local function disable_do(str_pos)
_do_stack:push(false)
return true
end
local function enable_do(str_pos)
_do_stack:push(true)
return true
end
local function pop_do(str, pos)
if nil == _do_stack:pop() then error("unexpected do pop") end
return true
end
local DisableDo = Cmt("", disable_do)
local EnableDo = Cmt("", enable_do)
local PopDo = Cmt("", pop_do)
local keywords = {}
local function key(chars)
keywords[chars] = true
return Space * chars * -AlphaNum
end
local function op(word)
local patt = Space * C(word)
if word:match("^%w*$") then
keywords[word] = true
patt = patt * -AlphaNum
end
return patt
end
-- make sure name is not a keyword
local Name = Cmt(Name, function(str, pos, name)
if keywords[name] then return false end
return true
end) / trim
local SelfName = Space * "@" * (
"@" * (_Name / mark"self_class" + Cc"self.__class") +
_Name / mark"self" + Cc"self")
local KeyName = SelfName + Space * _Name / mark"key_literal"
local VarArg = Space * P"..." / trim
local g = lpeg.P{
File,
File = Shebang^-1 * (Block + Ct""),
Block = Ct(Line * (Break^1 * Line)^0),
CheckIndent = Cmt(Indent, check_indent), -- validates line is in correct indent
Line = (CheckIndent * Statement + Space * #Stop),
Statement = pos(
Import + While + With + For + ForEach + Switch + Return +
Local + Export + BreakLoop +
Ct(ExpList) * (Update + Assign)^-1 / format_assign
) * Space * ((
-- statement decorators
key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" +
key"unless" * Exp / mark"unless" +
CompInner / mark"comprehension"
) * Space)^-1 / wrap_decorator,
Body = Space^-1 * Break * EmptyLine^0 * InBlock + Ct(Statement), -- either a statement, or an indented block
Advance = #Cmt(Indent, advance_indent), -- Advances the indent, gives back whitespace for CheckIndent
PushIndent = Cmt(Indent, push_indent),
PreventIndent = Cmt(Cc(-1), push_indent),
PopIndent = Cmt("", pop_indent),
InBlock = Advance * Block * PopIndent,
Local = key"local" * ((op"*" + op"^") / mark"declare_glob" + Ct(NameList) / mark"declare_with_shadows"),
Import = key"import" * Ct(ImportNameList) * SpaceBreak^0 * key"from" * Exp / mark"import",
ImportName = (sym"\\" * Ct(Cc"colon_stub" * Name) + Name),
ImportNameList = SpaceBreak^0 * ImportName * ((SpaceBreak^1 + sym"," * SpaceBreak^0) * ImportName)^0,
BreakLoop = Ct(key"break"/trim) + Ct(key"continue"/trim),
Return = key"return" * (ExpListLow/mark"explist" + C"") / mark"return",
WithExp = Ct(ExpList) * Assign^-1 / format_assign,
With = key"with" * DisableDo * ensure(WithExp, PopDo) * key"do"^-1 * Body / mark"with",
Switch = key"switch" * DisableDo * ensure(Exp, PopDo) * key"do"^-1 * Space^-1 * Break * SwitchBlock / mark"switch",
SwitchBlock = EmptyLine^0 * Advance * Ct(SwitchCase * (Break^1 * SwitchCase)^0 * (Break^1 * SwitchElse)^-1) * PopIndent,
SwitchCase = key"when" * Ct(ExpList) * key"then"^-1 * Body / mark"case",
SwitchElse = key"else" * Body / mark"else",
IfCond = Exp * Assign^-1 / format_single_assign,
If = key"if" * IfCond * key"then"^-1 * Body *
((Break * CheckIndent)^-1 * EmptyLine^0 * key"elseif" * pos(IfCond) * key"then"^-1 * Body / mark"elseif")^0 *
((Break * CheckIndent)^-1 * EmptyLine^0 * key"else" * Body / mark"else")^-1 / mark"if",
Unless = key"unless" * IfCond * key"then"^-1 * Body *
((Break * CheckIndent)^-1 * EmptyLine^0 * key"else" * Body / mark"else")^-1 / mark"unless",
While = key"while" * DisableDo * ensure(Exp, PopDo) * key"do"^-1 * Body / mark"while",
For = key"for" * DisableDo * ensure(Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1), PopDo) *
key"do"^-1 * Body / mark"for",
ForEach = key"for" * Ct(AssignableNameList) * key"in" * DisableDo * ensure(Ct(sym"*" * Exp / mark"unpack" + ExpList), PopDo) * key"do"^-1 * Body / mark"foreach",
Do = key"do" * Body / mark"do",
Comprehension = sym"[" * Exp * CompInner * sym"]" / mark"comprehension",
TblComprehension = sym"{" * Ct(Exp * (sym"," * Exp)^-1) * CompInner * sym"}" / mark"tblcomprehension",
CompInner = Ct((CompForEach + CompFor) * CompClause^0),
CompForEach = key"for" * Ct(NameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) / mark"foreach",
CompFor = key "for" * Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1) / mark"for",
CompClause = CompFor + CompForEach + key"when" * Exp / mark"when",
Assign = sym"=" * (Ct(With + If + Switch) + Ct(TableBlock + ExpListLow)) / mark"assign",
Update = ((sym"..=" + sym"+=" + sym"-=" + sym"*=" + sym"/=" + sym"%=" + sym"or=" + sym"and=") / trim) * Exp / mark"update",
-- we can ignore precedence for now
OtherOps = op"or" + op"and" + op"<=" + op">=" + op"~=" + op"!=" + op"==" + op".." + op"<" + op">",
Assignable = Cmt(DotChain + Chain, check_assignable) + Name + SelfName,
Exp = Ct(Value * ((OtherOps + FactorOp + TermOp) * Value)^0) / flatten_or_mark"exp",
-- Exp = Ct(Factor * (OtherOps * Factor)^0) / flatten_or_mark"exp",
-- Factor = Ct(Term * (FactorOp * Term)^0) / flatten_or_mark"exp",
-- Term = Ct(Value * (TermOp * Value)^0) / flatten_or_mark"exp",
SimpleValue =
If + Unless +
Switch +
With +
ClassDecl +
ForEach + For + While +
Cmt(Do, check_do) +
sym"-" * -SomeSpace * Exp / mark"minus" +
sym"#" * Exp / mark"length" +
key"not" * Exp / mark"not" +
TblComprehension +
TableLit +
Comprehension +
FunLit +
Num,
ChainValue = -- a function call or an object access
StringChain +
((Chain + DotChain + Callable) * Ct(InvokeArgs^-1)) / flatten_func,
Value = pos(
SimpleValue +
Ct(KeyValueList) / mark"table" +
ChainValue),
SliceValue = SimpleValue + ChainValue,
StringChain = String *
(Ct((ColonCall + ColonSuffix) * ChainTail^-1) * Ct(InvokeArgs^-1))^-1 / flatten_string_chain,
String = Space * DoubleString + Space * SingleString + LuaString,
SingleString = simple_string("'"),
DoubleString = simple_string('"', true),
LuaString = Cg(LuaStringOpen, "string_open") * Cb"string_open" * Break^-1 *
C((1 - Cmt(C(LuaStringClose) * Cb"string_open", check_lua_string))^0) *
LuaStringClose / mark"string",
LuaStringOpen = sym"[" * P"="^0 * "[" / trim,
LuaStringClose = "]" * P"="^0 * "]",
Callable = pos(Name / mark"ref") + SelfName + VarArg + Parens / mark"parens",
Parens = sym"(" * Exp * sym")",
FnArgs = symx"(" * Ct(ExpList^-1) * sym")" + sym"!" * -P"=" * Ct"",
ChainTail = ChainItem^1 * ColonSuffix^-1 + ColonSuffix,
-- a list of funcalls and indexes on a callable
Chain = Callable * ChainTail / mark"chain",
-- shorthand dot call for use in with statement
DotChain =
(sym"." * Cc(-1) * (_Name / mark"dot") * ChainTail^-1) / mark"chain" +
(sym"\\" * Cc(-1) * (
(_Name * Invoke / mark"colon") * ChainTail^-1 +
(_Name / mark"colon_stub")
)) / mark"chain",
ChainItem =
Invoke +
Slice +
symx"[" * Exp/mark"index" * sym"]" +
symx"." * _Name/mark"dot" +
ColonCall,
Slice = symx"[" * (SliceValue + Cc(1)) * sym"," * (SliceValue + Cc"") *
(sym"," * SliceValue)^-1 *sym"]" / mark"slice",
ColonCall = symx"\\" * (_Name * Invoke) / mark"colon",
ColonSuffix = symx"\\" * _Name / mark"colon_stub",
Invoke = FnArgs/mark"call" +
SingleString / wrap_func_arg +
DoubleString / wrap_func_arg,
TableValue = KeyValue + Ct(Exp),
TableLit = sym"{" * Ct(
TableValueList^-1 * sym","^-1 *
(SpaceBreak * TableLitLine * (sym","^-1 * SpaceBreak * TableLitLine)^0 * sym","^-1)^-1
) * White * sym"}" / mark"table",
TableValueList = TableValue * (sym"," * TableValue)^0,
TableLitLine = PushIndent * ((TableValueList * PopIndent) + (PopIndent * Cut)) + Space,
-- the unbounded table
TableBlockInner = Ct(KeyValueLine * (SpaceBreak^1 * KeyValueLine)^0),
TableBlock = SpaceBreak^1 * Advance * ensure(TableBlockInner, PopIndent) / mark"table",
ClassDecl = key"class" * -P":" * (Assignable + Cc(nil)) * (key"extends" * PreventIndent * ensure(Exp, PopIndent) + C"")^-1 * (ClassBlock + Ct("")) / mark"class",
ClassBlock = SpaceBreak^1 * Advance *
Ct(ClassLine * (SpaceBreak^1 * ClassLine)^0) * PopIndent,
ClassLine = CheckIndent * ((
KeyValueList / mark"props" +
Statement / mark"stm" +
Exp / mark"stm"
) * sym","^-1),
Export = key"export" * (
Cc"class" * ClassDecl +
op"*" + op"^" +
Ct(NameList) * (sym"=" * Ct(ExpListLow))^-1) / mark"export",
KeyValue = (sym":" * -SomeSpace * Name) / self_assign + Ct((KeyName + sym"[" * Exp * sym"]" + DoubleString + SingleString) * symx":" * (Exp + TableBlock)),
KeyValueList = KeyValue * (sym"," * KeyValue)^0,
KeyValueLine = CheckIndent * KeyValueList * sym","^-1,
FnArgsDef = sym"(" * Ct(FnArgDefList^-1) *
(key"using" * Ct(NameList + Space * "nil") + Ct"") *
sym")" + Ct"" * Ct"",
FnArgDefList = FnArgDef * (sym"," * FnArgDef)^0 * (sym"," * Ct(VarArg))^0 + Ct(VarArg),
FnArgDef = Ct((Name + SelfName) * (sym"=" * Exp)^-1),
FunLit = FnArgsDef *
(sym"->" * Cc"slim" + sym"=>" * Cc"fat") *
(Body + Ct"") / mark"fndef",
NameList = Name * (sym"," * Name)^0,
NameOrDestructure = Name + TableLit,
AssignableNameList = NameOrDestructure * (sym"," * NameOrDestructure)^0,
ExpList = Exp * (sym"," * Exp)^0,
ExpListLow = Exp * ((sym"," + sym";") * Exp)^0,
InvokeArgs = -P"-" * (ExpList *<