local concat
concat = table.concat
local unpack = unpack or table.unpack
local type = type
local moon = {
  is_class = function(value)
    if type(value) == "table" and rawget(value, "__base") ~= nil then
      local mt = getmetatable(value)
      return mt and rawget(mt, "__call") ~= nil
    end
    return false
  end,
  is_instance = function(value)
    if type(value) == "table" then
      local mt = getmetatable(value)
      return mt and rawget(mt, "__index") == mt and rawget(value, "__index") ~= value
    end
    return false
  end
}
local mtype
mtype = function(value)
  local base_type = type(value)
  if base_type == "table" then
    local cls = value.__class
    if cls and rawget(value, "__class") == nil then
      return cls
    end
  end
  return base_type
end
local pos_to_line_col
pos_to_line_col = function(str, pos)
  local line = 1
  local line_start = 1
  while true do
    local nl = str:find("\n", line_start, true)
    if not (nl and nl < pos) then
      break
    end
    line = line + 1
    line_start = nl + 1
  end
  return line, pos - line_start + 1
end
local pos_to_line
pos_to_line = function(str, pos)
  local line = pos_to_line_col(str, pos)
  return line
end
local trim
trim = function(str)
  return str:match("^%s*(.-)%s*$")
end
local get_line
get_line = function(str, line_num)
  for line in str:gmatch("([^\n]*)\n?") do
    if line_num == 1 then
      return line
    end
    line_num = line_num - 1
  end
end
local get_closest_line
get_closest_line = function(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)
  else
    return line, line_num
  end
end
local split
split = function(str, delim)
  if str == "" then
    return { }
  end
  str = str .. delim
  local _accum_0 = { }
  local _len_0 = 1
  for m in str:gmatch("(.-)" .. delim) do
    _accum_0[_len_0] = m
    _len_0 = _len_0 + 1
  end
  return _accum_0
end
local dump
dump = function(what)
  local seen = { }
  local _dump
  _dump = function(what, depth)
    if depth == nil then
      depth = 0
    end
    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
      local lines
      do
        local _accum_0 = { }
        local _len_0 = 1
        for k, v in pairs(what) do
          _accum_0[_len_0] = (" "):rep(depth * 4) .. "[" .. tostring(k) .. "] = " .. _dump(v, depth)
          _len_0 = _len_0 + 1
        end
        lines = _accum_0
      end
      seen[what] = false
      local class_name
      if type(what.__class) == "table" and type(what.__class.__name) == "string" then
        class_name = "<" .. tostring(what.__class.__name) .. ">"
      end
      return (tostring(class_name or "") .. "{\n") .. concat(lines) .. (" "):rep((depth - 1) * 4) .. "}\n"
    else
      return tostring(what) .. "\n"
    end
  end
  return _dump(what)
end
local debug_posmap
debug_posmap = function(posmap, moon_code, lua_code)
  local tuples
  do
    local _accum_0 = { }
    local _len_0 = 1
    for k, v in pairs(posmap) do
      _accum_0[_len_0] = {
        k,
        v
      }
      _len_0 = _len_0 + 1
    end
    tuples = _accum_0
  end
  table.sort(tuples, function(a, b)
    return a[1] < b[1]
  end)
  local lines
  do
    local _accum_0 = { }
    local _len_0 = 1
    for _index_0 = 1, #tuples do
      local pair = tuples[_index_0]
      local lua_line, pos = unpack(pair)
      local moon_line = pos_to_line(moon_code, pos)
      local lua_text = get_line(lua_code, lua_line)
      local moon_text = get_closest_line(moon_code, moon_line)
      local _value_0 = tostring(pos) .. "\t " .. tostring(lua_line) .. ":[ " .. tostring(trim(lua_text)) .. " ] >> " .. tostring(moon_line) .. ":[ " .. tostring(trim(moon_text)) .. " ]"
      _accum_0[_len_0] = _value_0
      _len_0 = _len_0 + 1
    end
    lines = _accum_0
  end
  return concat(lines, "\n")
end
local setfenv = setfenv or function(fn, env)
  local name
  local i = 1
  while true do
    name = debug.getupvalue(fn, i)
    if not name or name == "_ENV" then
      break
    end
    i = i + 1
  end
  if name then
    debug.upvaluejoin(fn, i, (function()
      return env
    end), 1)
  end
  return fn
end
local getfenv = getfenv or function(fn)
  local i = 1
  while true do
    local name, val = debug.getupvalue(fn, i)
    if not (name) then
      break
    end
    if name == "_ENV" then
      return val
    end
    i = i + 1
  end
  return nil
end
local get_options
get_options = function(...)
  local count = select("#", ...)
  local opts = select(count, ...)
  if type(opts) == "table" then
    return opts, unpack({
      ...
    }, nil, count - 1)
  else
    return { }, ...
  end
end
local safe_module
safe_module = function(name, tbl)
  return setmetatable(tbl, {
    __index = function(self, key)
      return error("Attempted to import non-existent `" .. tostring(key) .. "` from " .. tostring(name))
    end
  })
end
return {
  moon = moon,
  mtype = mtype,
  pos_to_line_col = pos_to_line_col,
  pos_to_line = pos_to_line,
  get_closest_line = get_closest_line,
  get_line = get_line,
  trim = trim,
  split = split,
  dump = dump,
  debug_posmap = debug_posmap,
  getfenv = getfenv,
  setfenv = setfenv,
  get_options = get_options,
  unpack = unpack,
  safe_module = safe_module
}
