From 5b1a886bcda3cfef1ccab9227463668f52cacbf2 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Thu, 18 Aug 2011 12:32:02 +0000 Subject: [PATCH] Added experimental support for MoonScript (a CoffeeScript like language for lua) git-svn-id: http://svn.net-core.org/repos/t-engine4@4181 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/engines/default/engine/Module.lua | 6 + game/engines/default/engine/class.lua | 2 + game/thirdparty/moonscript.lua | 1 + game/thirdparty/moonscript/compile.lua | 505 ++++++++++++ game/thirdparty/moonscript/compile/format.lua | 85 ++ game/thirdparty/moonscript/compile/line.lua | 744 ++++++++++++++++++ game/thirdparty/moonscript/compile/types.lua | 44 ++ game/thirdparty/moonscript/compile/value.lua | 359 +++++++++ game/thirdparty/moonscript/data.lua | 52 ++ game/thirdparty/moonscript/dump.lua | 29 + game/thirdparty/moonscript/errors.lua | 65 ++ game/thirdparty/moonscript/init.lua | 73 ++ game/thirdparty/moonscript/parse.lua | 455 +++++++++++ game/thirdparty/moonscript/util.lua | 144 ++++ game/thirdparty/moonscript/version.lua | 7 + 15 files changed, 2571 insertions(+) create mode 100644 game/thirdparty/moonscript.lua create mode 100644 game/thirdparty/moonscript/compile.lua create mode 100644 game/thirdparty/moonscript/compile/format.lua create mode 100644 game/thirdparty/moonscript/compile/line.lua create mode 100644 game/thirdparty/moonscript/compile/types.lua create mode 100644 game/thirdparty/moonscript/compile/value.lua create mode 100644 game/thirdparty/moonscript/data.lua create mode 100644 game/thirdparty/moonscript/dump.lua create mode 100644 game/thirdparty/moonscript/errors.lua create mode 100644 game/thirdparty/moonscript/init.lua create mode 100644 game/thirdparty/moonscript/parse.lua create mode 100644 game/thirdparty/moonscript/util.lua create mode 100644 game/thirdparty/moonscript/version.lua diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua index 13cfd7bfa4..bb33da8254 100644 --- a/game/engines/default/engine/Module.lua +++ b/game/engines/default/engine/Module.lua @@ -134,6 +134,12 @@ function _M:loadDefinition(dir, team, incompatible) fs.mount(src, "/", false) end + + -- Load moonscript support + if mod.moonscript then + require "moonscript" + require "moonscript.errors" + end elseif mode == "init" then local m = require(mod.starter) m[1].__session_time_played_start = os.time() diff --git a/game/engines/default/engine/class.lua b/game/engines/default/engine/class.lua index 1e92b9a6a3..9623079c6d 100644 --- a/game/engines/default/engine/class.lua +++ b/game/engines/default/engine/class.lua @@ -286,3 +286,5 @@ function _M:cloneReloaded() -- Computed delayed loads for i = 1, #delay_load do delay_load[i]:loaded() end end + +return _M diff --git a/game/thirdparty/moonscript.lua b/game/thirdparty/moonscript.lua new file mode 100644 index 0000000000..e3a64e5a82 --- /dev/null +++ b/game/thirdparty/moonscript.lua @@ -0,0 +1 @@ +require "moonscript.init" diff --git a/game/thirdparty/moonscript/compile.lua b/game/thirdparty/moonscript/compile.lua new file mode 100644 index 0000000000..ee0e8b1bef --- /dev/null +++ b/game/thirdparty/moonscript/compile.lua @@ -0,0 +1,505 @@ +module("moonscript.compile", package.seeall) +local util = require("moonscript.util") +local data = require("moonscript.data") +local dump = require("moonscript.dump") +require("moonscript.compile.format") +require("moonscript.compile.line") +require("moonscript.compile.value") +local ntype, Set = data.ntype, data.Set +local concat, insert = table.concat, table.insert +local pos_to_line, get_closest_line, trim = util.pos_to_line, util.get_closest_line, util.trim +local bubble_names = { + "has_varargs" +} +local Line +Line = (function(_parent_0) + local _base_0 = { + _append_single = function(self, item) + if util.moon.type(item) == Line then + do + local _item_0 = item + for _index_0 = 1, #_item_0 do + local value = _item_0[_index_0] + self:_append_single(value) + end + end + else + insert(self, item) + end + return nil + end, + append_list = function(self, items, delim) + for i = 1, #items do + self:_append_single(items[i]) + if i < #items then + insert(self, delim) + end + end + end, + append = function(self, ...) + do + local _item_0 = { + ... + } + for _index_0 = 1, #_item_0 do + local item = _item_0[_index_0] + self:_append_single(item) + end + end + return nil + end, + render = function(self) + local buff = { } + for i = 1, #self do + local c = self[i] + insert(buff, (function() + if util.moon.type(c) == Block then + return c:render() + else + return c + end + end)()) + end + return concat(buff) + end + } + _base_0.__index = _base_0 + if _parent_0 then + setmetatable(_base_0, getmetatable(_parent_0).__index) + end + local _class_0 = setmetatable({ + __init = function(self, ...) + if _parent_0 then + return _parent_0.__init(self, ...) + end + end + }, { + __index = _base_0, + __call = function(mt, ...) + local self = setmetatable({}, _base_0) + mt.__init(self, ...) + return self + end + }) + _base_0.__class = _class_0 + return _class_0 +end)() +local Block_ +Block_ = (function(_parent_0) + local _base_0 = { + header = "do", + footer = "end", + line_table = function(self) + return self._posmap + end, + set = function(self, name, value) + self._state[name] = value + end, + get = function(self, name) + return self._state[name] + end, + declare = function(self, names) + local undeclared = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + if type(name) == "string" and not self:has_name(name) then + _len_0 = _len_0 + 1 + _accum_0[_len_0] = name + end + end + end + return _accum_0 + end)() + do + local _item_0 = undeclared + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + self:put_name(name) + end + end + return undeclared + end, + whitelist_names = function(self, names) + self._name_whitelist = Set(names) + end, + put_name = function(self, name) + self._names[name] = true + end, + has_name = function(self, name) + 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) + end + else + return yes + end + end, + shadow_name = function(self, name) + self._names[name] = 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) + 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, + mark_pos = function(self, node) + if node[-1] then + self.last_pos = node[-1] + if not self._posmap[self.current_line] then + self._posmap[self.current_line] = self.last_pos + end + end + end, + add_line_text = function(self, text) + return insert(self._lines, text) + end, + append_line_table = function(self, sub_table, offset) + offset = offset + self.current_line + for line, source in pairs(sub_table) do + local line = line + offset + if not self._posmap[line] then + self._posmap[line] = source + end + end + end, + add_line_tables = function(self, line) + do + local _item_0 = line + for _index_0 = 1, #_item_0 do + local chunk = _item_0[_index_0] + if util.moon.type(chunk) == Block then + local current = chunk + while current do + if util.moon.type(current.header) == Line then + self:add_line_tables(current.header) + end + self:append_line_table(current:line_table(), 0) + self.current_line = self.current_line + current.current_line + current = current.next + end + end + end + end + end, + add = function(self, line) + local t = util.moon.type(line) + if t == "string" then + self:add_line_text(line) + elseif t == Block then + do + local _item_0 = bubble_names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + if line[name] then + self[name] = line.name + end + end + end + self:add(self:line(line)) + elseif t == Line then + self:add_line_tables(line) + self:add_line_text(line:render()) + self.current_line = self.current_line + 1 + else + error("Adding unknown item") + end + return nil + end, + _insert_breaks = function(self) + for i = 1, #self._lines - 1 do + local left, right = self._lines[i], self._lines[i + 1] + if left:sub(-1) == ")" and right:sub(1, 1) == "(" then + self._lines[i] = self._lines[i] .. ";" + end + end + end, + render = function(self) + local flatten + flatten = function(line) + if type(line) == "string" then + return line + else + return line:render() + end + end + local header = flatten(self.header) + if #self._lines == 0 then + local footer = flatten(self.footer) + return concat({ + header, + footer + }, " ") + end + local indent = indent_char:rep(self.indent) + if not self.delim then + self:_insert_breaks() + end + local body = indent .. concat(self._lines, (self.delim or "") .. "\n" .. indent) + return concat({ + header, + body, + indent_char:rep(self.indent - 1) .. (function() + if self.next then + return self.next:render() + else + return flatten(self.footer) + end + end)() + }, "\n") + 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 line_compile[ntype(node)] ~= nil + end, + is_value = function(self, node) + local t = ntype(node) + return value_compile[t] ~= nil or t == "value" + end, + name = function(self, node) + return self:value(node) + end, + value = function(self, node, ...) + local action + if type(node) ~= "table" then + action = "raw_value" + else + self:mark_pos(node) + action = node[1] + end + local fn = value_compile[action] + if not fn then + error("Failed to compile value: " .. dump.value(node)) + end + return fn(self, node, ...) + 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 = 0 + do + local _item_0 = values + for _index_0 = 1, #_item_0 do + local v = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:value(v) + end + end + return _accum_0 + end)(), delim) + return _with_0 + end + end, + stm = function(self, node, ...) + local fn = line_compile[ntype(node)] + if not fn then + if has_value(node) then + return self:stm({ + "assign", + { + "_" + }, + { + node + } + }) + else + return self:add(self:value(node)) + end + else + self:mark_pos(node) + local out = fn(self, node, ...) + if out then + return self:add(out) + end + end + end, + ret_stms = function(self, stms, ret) + if not ret then + ret = default_return + end + local i = 1 + while i < #stms do + self:stm(stms[i]) + i = i + 1 + end + local last_exp = stms[i] + if last_exp then + if cascading[ntype(last_exp)] then + self:stm(last_exp, ret) + elseif self:is_value(last_exp) then + local line = ret(stms[i]) + if self:is_stm(line) then + self:stm(line) + else + error("got a value from implicit return") + end + else + self:stm(last_exp) + end + end + return nil + end, + stms = function(self, stms, ret) + if ret then + self:ret_stms(stms, ret) + else + do + local _item_0 = stms + for _index_0 = 1, #_item_0 do + local stm = _item_0[_index_0] + self:stm(stm) + end + end + end + return nil + end + } + _base_0.__index = _base_0 + if _parent_0 then + setmetatable(_base_0, getmetatable(_parent_0).__index) + end + local _class_0 = setmetatable({ + __init = function(self, parent, header, footer) + self.parent, self.header, self.footer = parent, header, footer + self.current_line = 1 + self._lines = { } + self._posmap = { } + self._names = { } + self._state = { } + if self.parent then + self.indent = self.parent.indent + 1 + return setmetatable(self._state, { + __index = self.parent._state + }) + else + self.indent = 0 + end + end + }, { + __index = _base_0, + __call = function(mt, ...) + local self = setmetatable({}, _base_0) + mt.__init(self, ...) + return self + end + }) + _base_0.__class = _class_0 + return _class_0 +end)() +local RootBlock +RootBlock = (function(_parent_0) + local _base_0 = { + render = function(self) + self:_insert_breaks() + return concat(self._lines, "\n") + end + } + _base_0.__index = _base_0 + if _parent_0 then + setmetatable(_base_0, getmetatable(_parent_0).__index) + end + local _class_0 = setmetatable({ + __init = function(self, ...) + if _parent_0 then + return _parent_0.__init(self, ...) + end + end + }, { + __index = _base_0, + __call = function(mt, ...) + local self = setmetatable({}, _base_0) + mt.__init(self, ...) + return self + end + }) + _base_0.__class = _class_0 + return _class_0 +end)(Block_) +Block = Block_ +format_error = function(msg, pos, file_str) + 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 "" + return concat({ + "Compile error: " .. msg, + (" [%d] >> %s"):format(line, trim(line_str)) + }, "\n") +end +tree = function(tree) + local scope = RootBlock() + local runner = coroutine.create(function() + do + local _item_0 = tree + for _index_0 = 1, #_item_0 do + local line = _item_0[_index_0] + scope:stm(line) + end + end + return scope:render() + end) + local success, result = coroutine.resume(runner) + if not success then + local error_msg + if type(result) == "table" then + local error_type = result[1] + if error_type == "user-error" then + error_msg = result[2] + else + error_msg = error("Unknown error thrown", util.dump(error_msg)) + end + else + error_msg = concat({ + result, + debug.traceback(runner) + }, "\n") + end + return nil, error_msg, scope.last_pos + else + local tbl = scope:line_table() + return result, tbl + end +end diff --git a/game/thirdparty/moonscript/compile/format.lua b/game/thirdparty/moonscript/compile/format.lua new file mode 100644 index 0000000000..c27d0dda19 --- /dev/null +++ b/game/thirdparty/moonscript/compile/format.lua @@ -0,0 +1,85 @@ +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 = " " +user_error = function(...) + return error({ + "user-error", + ... + }) +end +local manual_return = Set({ + "foreach", + "for", + "while" +}) +default_return = function(exp) + local t = ntype(exp) + if t == "chain" and exp[2] == "return" then + local items = { + "explist" + } + do + local _item_0 = exp[3][2] + for _index_0 = 1, #_item_0 do + local v = _item_0[_index_0] + insert(items, v) + end + end + return { + "return", + items + } + elseif manual_return[t] then + return exp + else + return { + "return", + exp + } + end +end +moonlib = { + bind = function(tbl, name) + return concat({ + "moon.bind(", + tbl, + ".", + name, + ", ", + tbl, + ")" + }) + end +} +cascading = Set({ + "if", + "with" +}) +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 +is_slice = function(node) + return ntype(node) == "chain" and ntype(node[#node]) == "slice" +end +count_lines = function(str) + local count = 1 + for _ in str:gmatch("\n") do + count = count + 1 + end + return count +end diff --git a/game/thirdparty/moonscript/compile/line.lua b/game/thirdparty/moonscript/compile/line.lua new file mode 100644 index 0000000000..77ecc1abe0 --- /dev/null +++ b/game/thirdparty/moonscript/compile/line.lua @@ -0,0 +1,744 @@ +module("moonscript.compile", package.seeall) +local util = require("moonscript.util") +local data = require("moonscript.data") +local dump = require("moonscript.dump") +require("moonscript.compile.format") +require("moonscript.compile.types") +local reversed = util.reversed +local ntype = data.ntype +local concat, insert = table.concat, table.insert +local constructor_name = "new" +line_compile = { + raw = function(self, node) + local _, text = unpack(node) + return self:add(text) + end, + declare = function(self, node) + local _, names = unpack(node) + 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 = 0 + do + local _item_0 = names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:name(name) + end + end + return _accum_0 + end)(), ", ") + return _with_0 + end + end + end, + assign = function(self, node) + local _, names, values = unpack(node) + if #values == 1 and cascading[ntype(values[1])] then + return self:stm({ + "assign", + names, + values[1] + }) + end + local undeclared = self:declare(names) + local declare = "local " .. concat(undeclared, ", ") + if self:is_stm(values) then + if #undeclared > 0 then + self:add(declare) + end + if cascading[ntype(values)] then + local decorate + decorate = function(value) + return { + "assign", + names, + { + value + } + } + end + return self:stm(values, decorate) + else + return error("Assigning unsupported statement") + 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 + 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 = 0 + do + local _item_0 = names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:value(name) + end + end + return _accum_0 + end)(), ", ") + end + _with_0:append(" = ") + _with_0:append_list((function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = values + for _index_0 = 1, #_item_0 do + local v = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:value(v) + end + end + return _accum_0 + end)(), ", ") + return _with_0 + 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:line("return ", self:value(node[2])) + end, + ["break"] = function(self, node) + return "break" + end, + import = function(self, node) + local _, names, source = unpack(node) + local final_names, to_bind = { }, { } + do + local _item_0 = names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + local final + if ntype(name) == ":" then + local tmp = self:name(name[2]) + to_bind[tmp] = true + final = tmp + else + final = self:name(name) + end + self:put_name(final) + insert(final_names, final) + end + 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 _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = final_names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = get_value(name) + end + end + return _accum_0 + end)() + local line + do + local _with_0 = self:line("local ", concat(final_names, ", "), " = ") + _with_0:append_list(values, ", ") + line = _with_0 + end + return line + end + self:add(self:line("local ", concat(final_names, ", "))) + do + local _with_0 = self:block("do") + source = _with_0:init_free_var("table", source) + do + local _item_0 = final_names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _with_0:stm({ + "assign", + { + name + }, + { + get_value(name) + } + }) + end + end + return _with_0 + end + end, + ["if"] = function(self, node, ret) + 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, ret) + 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], ret) + current.next = next + current = next + end + do + local _item_0 = node + for _index_0 = 4, #_item_0 do + local cond = _item_0[_index_0] + add_clause(cond) + end + end + return root + end, + ["while"] = function(self, node) + local _, cond, block = unpack(node) + local out + if is_non_atomic(cond) then + do + local _with_0 = self:block("while true do") + _with_0:stm({ + "if", + { + "not", + cond + }, + { + { + "break" + } + } + }) + out = _with_0 + end + else + out = self:block(self:line("while ", self:value(cond), " do")) + end + out:stms(block) + return out + 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:stms(block) + return _with_0 + end + end, + foreach = function(self, node) + local _, names, exp, block = unpack(node) + if ntype(exp) == "unpack" then + local iter = exp[2] + local loop + do + local _with_0 = self:block() + local items_tmp = _with_0:free_name("item", true) + local bounds + if is_slice(iter) then + local slice = iter[#iter] + table.remove(iter) + table.remove(slice, 1) + if slice[2] and slice[2] ~= "" then + local max_tmp = _with_0:init_free_var("max", slice[2]) + slice[2] = { + "exp", + max_tmp, + "<", + 0, + "and", + { + "length", + items_tmp + }, + "+", + max_tmp, + "or", + max_tmp + } + else + slice[2] = { + "length", + items_tmp + } + end + bounds = slice + else + bounds = { + 1, + { + "length", + items_tmp + } + } + end + local index_tmp = _with_0:free_name("index") + _with_0:stm({ + "assign", + { + items_tmp + }, + { + iter + } + }) + block = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = block + for _index_0 = 1, #_item_0 do + local s = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = s + end + end + return _accum_0 + end)() + do + local _item_0 = names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _with_0:shadow_name(name) + end + end + insert(block, 1, { + "assign", + names, + { + { + "chain", + items_tmp, + { + "index", + index_tmp + } + } + } + }) + _with_0:stm({ + "for", + index_tmp, + bounds, + block + }) + loop = _with_0 + end + return loop + end + local loop + do + local _with_0 = self:line() + _with_0:append("for ") + _with_0:append_list((function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = names + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:name(name) + end + end + return _accum_0 + end)(), ", ") + _with_0:append(" in ", self:value(exp), " do") + loop = _with_0 + end + do + local _with_0 = self:block(loop) + _with_0:stms(block) + return _with_0 + end + end, + export = function(self, node) + local _, names = unpack(node) + self:declare(names) + return nil + end, + class = function(self, node) + local _, name, parent_val, tbl = unpack(node) + local constructor = nil + local final_properties = { } + do + local _item_0 = tbl[2] + for _index_0 = 1, #_item_0 do + local entry = _item_0[_index_0] + if entry[1] == constructor_name then + constructor = entry[2] + else + insert(final_properties, entry) + end + end + end + tbl[2] = final_properties + local parent_loc = self:free_name("parent", true) + if not constructor then + constructor = { + "fndef", + { + "..." + }, + { }, + "fat", + { + { + "if", + parent_loc, + { + { + "chain", + "super", + { + "call", + { + "..." + } + } + } + } + } + } + } + end + smart_node(constructor) + 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.args = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = constructor.args + for _index_0 = 1, #_item_0 do + local arg = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = get_initializers(arg) + end + end + return _accum_0 + end)() + constructor.arrow = "fat" + local dests = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = self_args + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = { + "self", + name + } + end + end + return _accum_0 + end)() + if #self_args > 0 then + insert(constructor.body, 1, { + "assign", + dests, + self_args + }) + end + local def_scope + do + local _with_0 = self:block() + if parent_val ~= "" then + parent_val = self:value(parent_val) + end + _with_0:put_name(parent_loc) + _with_0.header = self:line("(function(", parent_loc, ")") + _with_0.footer = self:line("end)(", parent_val, ")") + _with_0:set("super", function(block, chain) + local calling_name = block:get("current_block") + local slice = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = chain + for _index_0 = 3, #_item_0 do + local item = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = item + end + end + return _accum_0 + end)() + slice[1] = { + "call", + { + "self", + unpack(slice[1][2]) + } + } + local act + if ntype(calling_name) ~= "value" then + act = "index" + else + act = "dot" + end + return { + "chain", + parent_loc, + { + act, + calling_name + }, + unpack(slice) + } + end) + local base_name = _with_0:init_free_var("base", tbl) + _with_0:stm({ + "assign", + { + { + "chain", + base_name, + { + "dot", + "__index" + } + } + }, + { + base_name + } + }) + _with_0:stm({ + "if", + parent_loc, + { + { + "chain", + "setmetatable", + { + "call", + { + base_name, + { + "chain", + "getmetatable", + { + "call", + { + parent_loc + } + }, + { + "dot", + "__index" + } + } + } + } + } + } + }) + local cls = { + "table", + { + { + "__init", + constructor + } + } + } + local cls_mt = { + "table", + { + { + "__index", + base_name + }, + { + "__call", + { + "fndef", + { + "mt", + "..." + }, + { }, + "slim", + { + { + "raw", + ("local self = setmetatable({}, %s)"):format(base_name) + }, + { + "chain", + "mt.__init", + { + "call", + { + "self", + "..." + } + } + }, + "self" + } + } + } + } + } + local cls_name = _with_0:init_free_var("class", { + "chain", + "setmetatable", + { + "call", + { + cls, + cls_mt + } + } + }) + _with_0:stm({ + "assign", + { + { + "chain", + base_name, + { + "dot", + "__class" + } + } + }, + { + cls_name + } + }) + _with_0:stm({ + "return", + cls_name + }) + def_scope = _with_0 + end + self:stm({ + "declare", + { + name + } + }) + return self:line(name, " = ", def_scope) + end, + comprehension = function(self, node, action) + local _, exp, clauses = unpack(node) + if not action then + action = function(exp) + return { + exp + } + end + end + local current_stms = action(exp) + for _, clause in reversed(clauses) do + local t = clause[1] + if t == "for" then + local names, iter + _, names, iter = unpack(clause) + current_stms = { + "foreach", + names, + iter, + current_stms + } + elseif t == "when" then + local cond + _, cond = unpack(clause) + current_stms = { + "if", + cond, + current_stms + } + else + current_stms = error("Unknown comprehension clause: " .. t) + end + current_stms = { + current_stms + } + end + return self:stms(current_stms) + end, + with = function(self, node, ret) + local _, exp, block = unpack(node) + do + local _with_0 = self:block() + local var = _with_0:init_free_var("with", exp) + self:set("scope_var", var) + _with_0:stms(block) + if ret then + _with_0:stm(ret(var)) + end + return _with_0 + end + end +} diff --git a/game/thirdparty/moonscript/compile/types.lua b/game/thirdparty/moonscript/compile/types.lua new file mode 100644 index 0000000000..b9fb9f1fa0 --- /dev/null +++ b/game/thirdparty/moonscript/compile/types.lua @@ -0,0 +1,44 @@ +module("moonscript.compile", package.seeall) +local util = require("moonscript.util") +local data = require("moonscript.data") +local ntype = data.ntype +local key_table = { + fndef = { + "args", + "whitelist", + "arrow", + "body" + } +} +local build_table +build_table = function() + for key, value in pairs(key_table) do + local index = { } + for i, name in ipairs(value) do + index[name] = i + 1 + end + key_table[key] = index + end +end +build_table() +smart_node = function(node) + local index = key_table[ntype(node)] + if not index then + return node + end + return setmetatable(node, { + __index = function(node, key) + if index[key] then + return rawget(node, index[key]) + elseif type(key) == "string" then + return error("unknown key: `" .. key .. "` on node type: `" .. ntype(node) .. "`") + end + end, + __newindex = function(node, key, value) + if index[key] then + key = index[key] + end + return rawset(node, key, value) + end + }) +end diff --git a/game/thirdparty/moonscript/compile/value.lua b/game/thirdparty/moonscript/compile/value.lua new file mode 100644 index 0000000000..2303513767 --- /dev/null +++ b/game/thirdparty/moonscript/compile/value.lua @@ -0,0 +1,359 @@ +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 ntype = data.ntype +local concat, insert = table.concat, table.insert +local table_append +table_append = function(name, len, value) + return { + { + "update", + len, + "+=", + 1 + }, + { + "assign", + { + { + "chain", + name, + { + "index", + len + } + } + }, + { + value + } + } + } +end +local create_accumulate_wrapper +create_accumulate_wrapper = function(block_pos) + return function(self, node) + do + local _with_0 = self:block("(function()", "end)()") + local accum_name = _with_0:init_free_var("accum", { + "table" + }) + local count_name = _with_0:init_free_var("len", 0) + local value_name = _with_0:free_name("value", true) + local inner = node[block_pos] + inner[#inner] = { + "assign", + { + value_name + }, + { + inner[#inner] + } + } + insert(inner, { + "if", + { + "exp", + value_name, + "~=", + "nil" + }, + table_append(accum_name, count_name, value_name) + }) + _with_0:stm(node) + _with_0:stm({ + "return", + accum_name + }) + return _with_0 + end + end +end +value_compile = { + 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 = 0 + for i, v in ipairs(node) do + if i > 1 then + _len_0 = _len_0 + 1 + _accum_0[_len_0] = _comp(i, v) + end + end + return _accum_0 + end)(), " ") + return _with_0 + end + end, + update = function(self, node) + local _, name = unpack(node) + self:stm(node) + return self:name(name) + end, + explist = function(self, node) + do + local _with_0 = self:line() + _with_0:append_list((function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = node + for _index_0 = 2, #_item_0 do + local v = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = self:value(v) + end + 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, delim_end = unpack(node) + return delim .. inner .. (delim_end or delim) + end, + with = function(self, node) + do + local _with_0 = self:block("(function()", "end)()") + _with_0:stm(node, default_return) + return _with_0 + end + end, + ["if"] = function(self, node) + do + local _with_0 = self:block("(function()", "end)()") + _with_0:stm(node, default_return) + return _with_0 + end + end, + comprehension = function(self, node) + local _, exp, iter = unpack(node) + do + local _with_0 = self:block() + local tmp_name = _with_0:init_free_var("accum", { + "table" + }) + local len_name = _with_0:init_free_var("len", 0) + local action + action = function(value) + return table_append(tmp_name, len_name, value) + end + _with_0:stm(node, action) + _with_0:stm({ + "return", + tmp_name + }) + if _with_0.has_varargs then + _with_0.header, _with_0.footer = "(function(...)", "end)(...)" + else + _with_0.header, _with_0.footer = "(function()", "end)()" + end + return _with_0 + end + end, + ["for"] = create_accumulate_wrapper(4), + foreach = create_accumulate_wrapper(4), + ["while"] = create_accumulate_wrapper(3), + chain = function(self, node) + local callee = node[2] + 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 + local sup = self:get("super") + if callee == "super" and sup then + return self:value(sup(self, node)) + 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 ".", 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: " .. t) + end + end + local actions + do + local _with_0 = self:line() + do + local _item_0 = node + for _index_0 = 3, #_item_0 do + local action = _item_0[_index_0] + _with_0:append(chain_item(action)) + end + end + actions = _with_0 + end + if ntype(callee) == "self" and node[3] and ntype(node[3]) == "call" then + callee[1] = "self_colon" + end + local callee_value = self:name(callee) + if ntype(callee) == "exp" then + callee_value = self:line("(", callee_value, ")") + end + return self:line(callee_value, actions) + end, + fndef = function(self, node) + local _, args, whitelist, arrow, block = unpack(node) + local default_args = { } + local format_names + format_names = function(arg) + if type(arg) == "string" then + return arg + else + insert(default_args, arg) + return arg[1] + end + end + args = (function() + local _accum_0 = { } + local _len_0 = 0 + do + local _item_0 = args + for _index_0 = 1, #_item_0 do + local arg = _item_0[_index_0] + _len_0 = _len_0 + 1 + _accum_0[_len_0] = format_names(arg) + end + end + return _accum_0 + end)() + if arrow == "fat" then + insert(args, 1, "self") + end + do + local _with_0 = self:block("function(" .. concat(args, ", ") .. ")") + if #whitelist > 0 then + _with_0:whitelist_names(whitelist) + end + do + local _item_0 = args + for _index_0 = 1, #_item_0 do + local name = _item_0[_index_0] + _with_0:put_name(name) + end + end + do + local _item_0 = default_args + for _index_0 = 1, #_item_0 do + local default = _item_0[_index_0] + local name, value = unpack(default) + _with_0:stm({ + 'if', + { + 'exp', + name, + '==', + 'nil' + }, + { + { + 'assign', + { + name + }, + { + value + } + } + } + }) + end + end + _with_0:ret_stms(block) + return _with_0 + end + end, + table = function(self, node) + local _, items = unpack(node) + do + local _with_0 = self:block("{", "}") + _with_0.delim = "," + local format_line + format_line = function(tuple) + if #tuple == 2 then + local key, value = unpack(tuple) + if type(key) == "string" and data.lua_keywords[key] then + key = { + "string", + '"', + key + } + end + local assign + if type(key) ~= "string" then + assign = self:line("[", _with_0:value(key), "]") + else + assign = 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 + do + local _item_0 = items + for _index_0 = 1, #_item_0 do + local line = _item_0[_index_0] + _with_0:add(format_line(line)) + end + end + end + return _with_0 + end + end, + minus = function(self, node) + return self:line("-", self:value(node[2])) + 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:value(node[2]) + end, + self_colon = function(self, node) + return "self:" .. self:value(node[2]) + end, + raw_value = function(self, value) + if value == "..." then + self.has_varargs = true + end + return tostring(value) + end +} diff --git a/game/thirdparty/moonscript/data.lua b/game/thirdparty/moonscript/data.lua new file mode 100644 index 0000000000..1eb670f9d0 --- /dev/null +++ b/game/thirdparty/moonscript/data.lua @@ -0,0 +1,52 @@ + +module("moonscript.data", package.seeall) + +local stack_t = {} +local _stack_mt = { __index = stack_t, __tostring = function(self) + return "<Stack {"..table.concat(self, ", ").."}>" +end} + +function stack_t:pop() + return table.remove(self) +end + +function stack_t:push(value) + table.insert(self, value) + return value +end + +function stack_t:top() + return self[#self] +end + +function Stack(...) + local self = setmetatable({}, _stack_mt) + + for _, v in ipairs{...} do + self:push(v) + end + + return self +end + +function Set(items) + local self = {} + for _,item in ipairs(items) do + self[item] = true + end + return self +end + +-- find out the type of a node +function ntype(node) + if type(node) ~= "table" then return "value" end + return node[1] +end + +lua_keywords = Set{ + 'and', 'break', 'do', 'else', 'elseif', + 'end', 'false', 'for', 'function', 'if', + 'in', 'local', 'nil', 'not', 'or', + 'repeat', 'return', 'then', 'true', + 'until', 'while' +} diff --git a/game/thirdparty/moonscript/dump.lua b/game/thirdparty/moonscript/dump.lua new file mode 100644 index 0000000000..c00bc32410 --- /dev/null +++ b/game/thirdparty/moonscript/dump.lua @@ -0,0 +1,29 @@ + +module("moonscript.dump", package.seeall) + +local function flat_value(op, depth) + depth = depth or 1 + + if type(op) == "string" then return '"'..op..'"' end + if type(op) ~= "table" then return tostring(op) end + local items = {} + for _, item in ipairs(op) do + table.insert(items, flat_value(item, depth+1)) + end + + local pos = op[-1] + + return "{"..(pos and "["..pos.."] " or "")..table.concat(items, ", ").."}" +end + +function value(op) + return flat_value(op) +end + +function tree(block, depth) + depth = depth or 0 + for _, op in ipairs(block) do + print(flat_value(op)) + end +end + diff --git a/game/thirdparty/moonscript/errors.lua b/game/thirdparty/moonscript/errors.lua new file mode 100644 index 0000000000..6c62770e04 --- /dev/null +++ b/game/thirdparty/moonscript/errors.lua @@ -0,0 +1,65 @@ +module("moonscript.errors", package.seeall) +local moon = require("moonscript") +local util = require("moonscript.util") +require("lpeg") +local concat, insert = table.concat, table.insert +local split, pos_to_line = util.split, util.pos_to_line +local lookup_line +lookup_line = function(fname, pos, cache) + if not cache[fname] then + do + local _with_0 = fs.open(fname, "r") + cache[fname] = _with_0:read() + _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 +rewrite_traceback = function(text, err) + local line_tables = moon.line_tables + local 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), + ": ", + msg + }) + else + return trace + end + end + err = rewrite_single(err) + local match = g:match(text) + 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 diff --git a/game/thirdparty/moonscript/init.lua b/game/thirdparty/moonscript/init.lua new file mode 100644 index 0000000000..bf680934ac --- /dev/null +++ b/game/thirdparty/moonscript/init.lua @@ -0,0 +1,73 @@ +module("moonscript", package.seeall) +require("moonscript.parse") +require("moonscript.compile") +require("moonscript.util") +local concat, insert = table.concat, table.insert +local split, dump = util.split, util.dump +dirsep = "/" +line_tables = { } +local create_moonpath +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 +moon_chunk = function(file, file_path) + local text = file:read() + if not text then + error("Could not read file") + end + local tree, err = parse.string(text) + if not tree then + error("Parse error: " .. err) + end + local code, ltable, pos = compile.tree(tree) + if not code then + error(compile.format_error(ltable, pos, text)) + end + line_tables[file_path] = ltable + local runner + runner = function() + do + local _with_0 = code + code = nil + return _with_0 + end + end + return load(runner, file_path) +end +moon_loader = function(name) + local name_path = name:gsub("%.", dirsep) + local file, file_path = nil, nil + do + local _item_0 = split(package.moonpath, ";") + for _index_0 = 1, #_item_0 do + local path = _item_0[_index_0] + file_path = path:gsub("?", name_path) + file = fs.open(file_path, "r") + if file then + break + end + end + end + if file then + return moon_chunk(file, file_path) + else + return nil, "Could not find moon file" + end +end +if not package.moonpath then + package.moonpath = create_moonpath(package.path) +end +local init_loader +init_loader = function() + return insert(package.loaders, 2, moon_loader) +end +if not _G.moon_no_loader then + init_loader() +end diff --git a/game/thirdparty/moonscript/parse.lua b/game/thirdparty/moonscript/parse.lua new file mode 100644 index 0000000000..346fbf0e63 --- /dev/null +++ b/game/thirdparty/moonscript/parse.lua @@ -0,0 +1,455 @@ +module("moonscript.parse", package.seeall) + +local util = require"moonscript.util" + +require"lpeg" + +local dump = require"moonscript.dump" +local data = require"moonscript.data" + +local ntype = data.ntype +local trim = util.trim + +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(3000) + +local White = S" \t\n"^0 +local _Space = S" \t"^0 +local Break = S"\n" +local Stop = Break + -1 +local Indent = C(S"\t "^0) / count_indent + +local Comment = P"--" * (1 - S"\n")^0 * #Stop +local Space = _Space * Comment^-1 +local SomeSpace = S" \t"^1 * Comment^-1 + +local _Name = C(R("az", "AZ", "__") * R("az", "AZ", "09", "__")^0) +local Name = Space * _Name + +local Num = P"0x" * R("09", "af", "AF")^1 + + R"09"^1 * (P"." * R"09"^1)^-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 + +local function wrap(fn) + local env = getfenv(fn) + + 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 = V(name) + rawset(self, name, v) + return v + end + error("unknown variable referenced: "..name) + end + })) +end + +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 + +local build_grammar = wrap(function() + local err_msg = "Failed to parse:\n [%d] >> %s (%d)" + + local _indent = Stack(0) -- current indent + + 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) + if indent > _indent:top() then + _indent:push(indent) + return true + end + end + + local function pop_indent(str, pos) + if not _indent:pop() then error("unexpected outdent") end + return true + end + + local keywords = {} + local function key(word) + keywords[word] = true + return Space * word + end + + local function op(word) + if word:match("^%w*$") then + keywords[word] = true + end + return Space * C(word) + end + + local function sym(chars) + return Space * chars + end + + local function symx(chars) + return chars + end + + 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 wrap_func_arg(value) + return {"call", {value}} + end + + -- makes sure the last item in a chain is an index + local _assignable = { index = true, dot = true, slice = true } + local function check_assignable(str, pos, value) + if ntype(value) == "chain" and _assignable[ntype(value[#value])] + or type(value) == "string" + then + return true, value + end + return false + end + + local SimpleName = Name -- for table key + + -- 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 Name = sym"@" * Name / mark"self" + Name + Space * "..." / trim + + local function simple_string(delim, x) + return C(symx(delim)) * C((P('\\'..delim) + (1 - S('\n'..delim)))^0) * sym(delim) / mark"string" + 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 wrap_decorator(stm, dec) + if not dec then return stm end + + local arg = {stm, dec} + + if dec[1] == "if" then + local _, cond, fail = unpack(dec) + if fail then fail = {"else", {fail}} end + stm = {"if", cond, {stm}, fail} + elseif dec[1] == "comprehension" then + local _, clauses = unpack(dec) + stm = {"comprehension", stm, clauses} + end + + return stm + end + + local function wrap_default_arg(name, default) + if not default then return name end + return {name, default} + end + + local function check_lua_string(str, pos, right, left) + return #left == #right + end + + local g = lpeg.P{ + File, + File = Shebang^-1 * (Block + Ct""), + Block = Ct(Line * (Break^1 * Line)^0), + Line = Cmt(Indent, check_indent) * Statement + Space * #Break, + + Statement = (Import + While + With + For + ForEach + Return + + ClassDecl + Export + BreakLoop + Ct(ExpList) / flatten_or_mark"explist" * Space) * (( + -- statement decorators + key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" + + CompInner / mark"comprehension" + ) * Space)^-1 / wrap_decorator, + + EmptyLine = Space * Break, + Body = Space^-1 * Break * EmptyLine^0 * InBlock + Ct(Statement), + + InBlock = #Cmt(Indent, advance_indent) * Block * OutBlock, + OutBlock = Cmt("", pop_indent), + + Import = key"import" * Ct(ImportNameList) * key"from" * Exp / mark"import", + ImportName = (sym"\\" * Ct(Cc":" * Name) + Name), + ImportNameList = ImportName * (sym"," * ImportName)^0, + + NameList = Name * (sym"," * Name)^0, + + BreakLoop = Ct(key"break"/trim), + + Return = key"return" * (ExpListLow/ mark"explist") / mark"return", + + With = key"with" * Exp * key"do"^-1 * Body / mark"with", + + If = key"if" * Exp * key"then"^-1 * Body * + ((Break * Cmt(Indent, check_indent))^-1 * EmptyLine^0 * key"elseif" * Exp * key"then"^-1 * Body / mark"elseif")^0 * + ((Break * Cmt(Indent, check_indent))^-1 * EmptyLine^0 * key"else" * Body / mark"else")^-1 / mark"if", + + While = key"while" * Exp * key"do"^-1 * Body / mark"while", + + For = key"for" * (Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1)) * + key"do"^-1 * Body / mark"for", + + ForEach = key"for" * Ct(NameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) * key"do"^-1 * Body / mark"foreach", + + Comprehension = sym"[" * Exp * CompInner * sym"]" / mark"comprehension", + + CompInner = Ct(CompFor * CompClause^0), + CompFor = key"for" * Ct(NameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) / mark"for", + CompClause = CompFor + key"when" * Exp / mark"when", + + Assign = Ct(AssignableList) * sym"=" * (With + If + Ct(TableBlock + ExpListLow)) / mark"assign", + Update = Assignable * ((sym"+=" + sym"-=" + sym"*=" + sym"/=" + sym"%=")/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, + AssignableList = Assignable * (sym"," * Assignable)^0, + + 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 + + With + + ForEach + For + While + + sym"-" * -SomeSpace * Exp / mark"minus" + + sym"#" * Exp / mark"length" + + sym"not" * Exp / mark"not" + + TableLit + + Comprehension + + ColonChain * Ct(ExpList^0) / flatten_func + -- have precedence over open table + Assign + Update + FunLit + String + + Num, + + ChainValue = + ((Chain + DotChain + Callable) * Ct(ExpList^0)) / flatten_func, + + Value = pos( + SimpleValue + + Ct(KeyValueList) / mark"table" + + ChainValue), + + SliceValue = SimpleValue + ChainValue, + + String = Space * DoubleString + Space * SingleString + LuaString, + SingleString = simple_string("'"), + DoubleString = simple_string('"'), + + LuaString = Cg(LuaStringOpen, "string_open") * Cb"string_open" * P"\n"^-1 * + C((1 - Cmt(C(LuaStringClose) * Cb"string_open", check_lua_string))^0) * + C(LuaStringClose) / mark"string", + + LuaStringOpen = sym"[" * P"="^0 * "[" / trim, + LuaStringClose = "]" * P"="^0 * "]", + + Callable = Name + Parens / mark"parens", + Parens = sym"(" * Exp * sym")", + + FnArgs = symx"(" * Ct(ExpList^-1) * sym")" + sym"!" * -P"=" * Ct"", + + -- chain that starts with colon expression (for precedence over table literal) + ColonChain = + Callable * (ColonCall * (ChainItem)^0 + ColonSuffix) / mark"chain", + + -- a list of funcalls and indexs on a callable + Chain = Callable * (ChainItem^1 * ColonSuffix^-1 + ColonSuffix) / mark"chain", + + -- shorthand dot call for use in with statement + DotChain = + (sym"." * Cc(-1) * (_Name / mark"dot") * ChainItem^0) / mark"chain" + + (sym"\\" * Cc(-1) * ( + (_Name * Invoke / mark"colon") * ChainItem^0 + + (_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"{" * White * + Ct((TableValue * ((sym"," + Break) * White * TableValue)^0)^-1) * sym","^-1 * + White * sym"}" / mark"table", + + TableBlockInner = Ct(KeyValueLine * (Break^1 * KeyValueLine)^0), + + TableBlock = Break * #Cmt(Indent, advance_indent) * TableBlockInner * OutBlock / mark"table", + + ClassDecl = key"class" * Name * (key"extends" * Exp + C"")^-1 * TableBlock / mark"class", + Export = key"export" * Ct(NameList) / mark"export", + + KeyValue = Ct((SimpleName + sym"[" * Exp * sym"]") * symx":" * (Exp + TableBlock)), + KeyValueList = KeyValue * (sym"," * KeyValue)^0, + KeyValueLine = Cmt(Indent, check_indent) * KeyValueList * sym","^-1, + + FnArgsDef = sym"(" * Ct(FnArgDefList^-1) * + (key"using" * Ct(NameList + Space * "nil") + Ct"") * + sym")" + Ct"" * Ct"", + + FnArgDefList = FnArgDef * (sym"," * FnArgDef)^0, + FnArgDef = Name * (sym"=" * Exp)^-1 / wrap_default_arg, + + FunLit = FnArgsDef * + (sym"->" * Cc"slim" + sym"=>" * Cc"fat") * + (Body + Ct"") / mark"fndef", + + NameList = Name * (sym"," * Name)^0, + ExpList = Exp * (sym"," * Exp)^0, + ExpListLow = Exp * ((sym"," + sym";") * Exp)^0, + } + + return { + _g = White * g * White * -1, + match = function(self, str, ...) + + local pos_to_line = function(pos) + return util.pos_to_line(str, pos) + end + + local get_line = function(num) + return util.get_line(str, num) + end + + 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 + local line_no = pos_to_line(last_pos) + local line_str = get_line(line_no) or "" + + return nil, err_msg:format(line_no, trim(line_str), _indent:top()) + end + return tree + end + } + +end) + +-- parse a string +-- returns tree, or nil and error message +function string(str) + local g = build_grammar() + return g:match(str) +end + diff --git a/game/thirdparty/moonscript/util.lua b/game/thirdparty/moonscript/util.lua new file mode 100644 index 0000000000..15d916676b --- /dev/null +++ b/game/thirdparty/moonscript/util.lua @@ -0,0 +1,144 @@ + +module("moonscript.util", package.seeall) + +moon = { + is_object = function(value) + return type(value) == "table" and value.__class + end, + type = function(value) + base_type = type(value) + if base_type == "table" then + cls = value.__class + if cls then return cls end + end + return base_type + end +} + +function pos_to_line(str, pos) + local line = 1 + for _ in str:sub(1, pos):gmatch("\n") do + line = line + 1 + end + return line +end + +function get_closest_line(str, line_num) + local line = get_line(str, line_num) + if (not line or trim(line) == "") and line_num > 1 then + return get_closest_line(str, line_num - 1) + end + + return line, line_num +end + +function get_line(str, line_num) + for line in str:gmatch("(.-)[\n$]") do + if line_num == 1 then + return line + end + line_num = line_num - 1 + end +end + +-- shallow copy +function clone(tbl) + local out = {} + for k,v in pairs(tbl) do + out[k] = v + end + return out +end + +function map(tbl, fn) + local out = {} + for i,v in ipairs(tbl) do + out[i] = fn(v) + end + return out +end + +function every(tbl, fn) + for i=1,#tbl do + local pass + if fn then + pass = fn(tbl[i]) + else + pass = tbl[i] + end + + if not pass then return false end + end + return true +end + +function bind(obj, name) + return function(...) + return obj[name](obj, ...) + end +end + +function itwos(seq) + n = 2 + return coroutine.wrap(function() + for i = 1, #seq-n+1 do + coroutine.yield(i, seq[i], i+1, seq[i+1]) + end + end) +end + +function reversed(seq) + return coroutine.wrap(function() + for i=#seq,1,-1 do + coroutine.yield(i, seq[i]) + end + end) +end + +function dump(what) + local seen = {} + local function _dump(what, depth) + depth = depth or 0 + local t = type(what) + + if t == "string" then + return '"'..what..'"\n' + elseif t == "table" then + if seen[what] then + return "recursion("..tostring(what)..")...\n" + end + seen[what] = true + + depth = depth + 1 + out = "{\n" + for k,v in pairs(what) do + out = out..(" "):rep(depth*4).."["..tostring(k).."] = ".._dump(v, depth) + end + + seen[what] = false + + return out .. (" "):rep((depth-1)*4) .. "}\n" + else + return tostring(what).."\n" + end + end + + return _dump(what) +end + +function split(str, delim) + if str == "" then return {} end + str = str..delim + local out = {} + for m in str:gmatch("(.-)"..delim) do + table.insert(out, m) + end + return out +end + + +function trim(str) + return str:match("^%s*(.-)%s*$") +end + + diff --git a/game/thirdparty/moonscript/version.lua b/game/thirdparty/moonscript/version.lua new file mode 100644 index 0000000000..3da289521f --- /dev/null +++ b/game/thirdparty/moonscript/version.lua @@ -0,0 +1,7 @@ + +module("moonscript.version", package.seeall) + +version = "0.2.0-dev" +function print_version() + print("MoonScript version "..version) +end -- GitLab