local debug_grammar = false
local lpeg = require("lpeg")
lpeg.setmaxstack(10000)
local err_msg = "Failed to parse:%s\n [%d] >>    %s"
local Stack
Stack = require("moonscript.data").Stack
local trim, pos_to_line, get_line
do
  local _obj_0 = require("moonscript.util")
  trim, pos_to_line, get_line = _obj_0.trim, _obj_0.pos_to_line, _obj_0.get_line
end
local unpack
unpack = require("moonscript.util").unpack
local wrap_env
wrap_env = require("moonscript.parse.env").wrap_env
local R, S, V, P, C, Ct, Cmt, Cg, Cb, Cc
R, S, V, P, C, Ct, Cmt, Cg, Cb, Cc = lpeg.R, lpeg.S, lpeg.V, lpeg.P, lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cb, lpeg.Cc
local White, Break, Stop, Comment, Space, SomeSpace, SpaceBreak, EmptyLine, AlphaNum, Num, Shebang, _Name
do
  local _obj_0 = require("moonscript.parse.literals")
  White, Break, Stop, Comment, Space, SomeSpace, SpaceBreak, EmptyLine, AlphaNum, Num, Shebang, _Name = _obj_0.White, _obj_0.Break, _obj_0.Stop, _obj_0.Comment, _obj_0.Space, _obj_0.SomeSpace, _obj_0.SpaceBreak, _obj_0.EmptyLine, _obj_0.AlphaNum, _obj_0.Num, _obj_0.Shebang, _obj_0.Name
end
local SpaceName = Space * _Name
Num = Space * (Num / function(v)
  return {
    "number",
    v
  }
end)
local Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, flatten_func, flatten_string_chain, wrap_decorator, check_lua_string, self_assign
do
  local _obj_0 = require("moonscript.parse.util")
  Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, flatten_func, flatten_string_chain, wrap_decorator, check_lua_string, self_assign = _obj_0.Indent, _obj_0.Cut, _obj_0.ensure, _obj_0.extract_line, _obj_0.mark, _obj_0.pos, _obj_0.flatten_or_mark, _obj_0.is_assignable, _obj_0.check_assignable, _obj_0.format_assign, _obj_0.format_single_assign, _obj_0.sym, _obj_0.symx, _obj_0.simple_string, _obj_0.wrap_func_arg, _obj_0.flatten_func, _obj_0.flatten_string_chain, _obj_0.wrap_decorator, _obj_0.check_lua_string, _obj_0.self_assign
end
local build_grammar = wrap_env(debug_grammar, function()
  local _indent = Stack(0)
  local _do_stack = Stack(0)
  local last_pos = 0
  local check_indent
  check_indent = function(str, pos, indent)
    last_pos = pos
    return _indent:top() == indent
  end
  local advance_indent
  advance_indent = function(str, pos, indent)
    local top = _indent:top()
    if top ~= -1 and indent > top then
      _indent:push(indent)
      return true
    end
  end
  local push_indent
  push_indent = function(str, pos, indent)
    _indent:push(indent)
    return true
  end
  local pop_indent
  pop_indent = function()
    assert(_indent:pop(), "unexpected outdent")
    return true
  end
  local check_do
  check_do = function(str, pos, do_node)
    local top = _do_stack:top()
    if top == nil or top then
      return true, do_node
    end
    return false
  end
  local disable_do
  disable_do = function()
    _do_stack:push(false)
    return true
  end
  local pop_do
  pop_do = function()
    assert(_do_stack:pop() ~= nil, "unexpected do pop")
    return true
  end
  local DisableDo = Cmt("", disable_do)
  local PopDo = Cmt("", pop_do)
  local keywords = { }
  local key
  key = function(chars)
    keywords[chars] = true
    return Space * chars * -AlphaNum
  end
  local op
  op = function(chars)
    local patt = Space * C(chars)
    if chars:match("^%w*$") then
      keywords[chars] = true
      patt = patt * -AlphaNum
    end
    return patt
  end
  local Name = Cmt(SpaceName, 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 = P({
    File,
    File = Shebang ^ -1 * (Block + Ct("")),
    Block = Ct(Line * (Break ^ 1 * Line) ^ 0),
    CheckIndent = Cmt(Indent, check_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 * ((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),
    Advance = #Cmt(Indent, advance_indent),
    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"),
    CharOperators = Space * C(S("+-*/%^><")),
    WordOperators = op("or") + op("and") + op("<=") + op(">=") + op("~=") + op("!=") + op("==") + op(".."),
    BinaryOperator = (WordOperators + CharOperators) * SpaceBreak ^ 0,
    Assignable = Cmt(DotChain + Chain, check_assignable) + Name + SelfName,
    Exp = Ct(Value * (BinaryOperator * 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 = 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("(") * SpaceBreak ^ 0 * Exp * SpaceBreak ^ 0 * sym(")"),
    FnArgs = symx("(") * SpaceBreak ^ 0 * Ct(ExpList ^ -1) * SpaceBreak ^ 0 * sym(")") + sym("!") * -P("=") * Ct(""),
    ChainTail = ChainItem ^ 1 * ColonSuffix ^ -1 + ColonSuffix,
    Chain = Callable * ChainTail / mark("chain"),
    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,
    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 * lpeg.Cp()) / self_assign + Ct((KeyName + sym("[") * Exp * sym("]") + DoubleString + SingleString) * symx(":") * (Exp + TableBlock + SpaceBreak ^ 1 * Exp)),
    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 * (sym(",") * (TableBlock + SpaceBreak * Advance * ArgBlock * TableBlock ^ -1) + TableBlock) ^ -1 + TableBlock),
    ArgBlock = ArgLine * (sym(",") * SpaceBreak * ArgLine) ^ 0 * PopIndent,
    ArgLine = CheckIndent * ExpList
  })
  local file_grammar = White * g * White * -1
  return {
    match = function(self, str)
      local tree
      local _, err = xpcall((function()
        tree = file_grammar:match(str)
      end), function(err)
        return debug.traceback(err, 2)
      end)
      if type(err) == "string" then
        return nil, err
      end
      if not (tree) then
        local msg
        local err_pos = last_pos
        if err then
          local node
          node, msg = unpack(err)
          if msg then
            msg = " " .. msg
          end
          err_pos = node[-1]
        end
        local line_no = pos_to_line(str, err_pos)
        local line_str = get_line(str, line_no) or ""
        return nil, err_msg:format(msg or "", line_no, trim(line_str))
      end
      return tree
    end
  }
end)
return {
  extract_line = extract_line,
  string = function(str)
    return build_grammar():match(str)
  end
}
