--[[
This module implements a parser for Lua 5.3 with LPeg,
and generates an Abstract Syntax Tree that is similar to the one generated by Metalua.
For more information about Metalua, please, visit:
https://github.com/fab13n/metalua-parser

block: { stat* }

stat:
    `Do{ stat* }
  | `Set{ {lhs+} {expr+} }                    -- lhs1, lhs2... = e1, e2...
  | `While{ expr block }                      -- while e do b end
  | `Repeat{ block expr }                     -- repeat b until e
  | `If{ (expr block)+ block? }               -- if e1 then b1 [elseif e2 then b2] ... [else bn] end
  | `Fornum{ ident expr expr expr? block }    -- for ident = e, e[, e] do b end
  | `Forin{ {ident+} {expr+} block }          -- for i1, i2... in e1, e2... do b end
  | `Local{ {ident+} {expr+}? }               -- local i1, i2... = e1, e2...
  | `Localrec{ ident expr }                   -- only used for 'local function'
  | `Goto{ <string> }                         -- goto str
  | `Label{ <string> }                        -- ::str::
  | `Return{ <expr*> }                        -- return e1, e2...
  | `Break                                    -- break
  | apply

expr:
    `Nil
  | `Dots
  | `Boolean{ <boolean> }
  | `Number{ <number> }
  | `String{ <string> }
  | `Function{ { `Id{ <string> }* `Dots? } block }
  | `Table{ ( `Pair{ expr expr } | expr )* }
  | `Op{ opid expr expr? }
  | `Paren{ expr }       -- significant to cut multiple values returns
  | apply
  | lhs

apply:
    `Call{ expr expr* }
  | `Invoke{ expr `String{ <string> } expr* }

lhs: `Id{ <string> } | `Index{ expr expr }

opid:  -- includes additional operators from Lua 5.3 and all relational operators
    'add'  | 'sub' | 'mul'  | 'div'
  | 'idiv' | 'mod' | 'pow'  | 'concat'
  | 'band' | 'bor' | 'bxor' | 'shl' | 'shr'
  | 'eq'   | 'ne'  | 'lt'   | 'gt'  | 'le'   | 'ge'
  | 'and'  | 'or'  | 'unm'  | 'len' | 'bnot' | 'not'
]]

local lpeg = require "lpeglabel"

lpeg.locale(lpeg)

local P, S, V = lpeg.P, lpeg.S, lpeg.V
local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc
local Cf, Cg, Cmt, Cp, Cs, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct
local Lc, T = lpeg.Lc, lpeg.T

local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum
local xdigit = lpeg.xdigit
local space = lpeg.space

local commentCollector

-- error message auxiliary functions

local labels = {
  { "ErrExtra", "unexpected character(s), expected EOF" },
  { "ErrInvalidStat", "unexpected token, invalid start of statement" },

  { "ErrEndIf", "expected 'end' to close the if statement" },
  { "ErrExprIf", "expected a condition after 'if'" },
  { "ErrThenIf", "expected 'then' after the condition" },
  { "ErrExprEIf", "expected a condition after 'elseif'" },
  { "ErrThenEIf", "expected 'then' after the condition" },

  { "ErrEndDo", "expected 'end' to close the do block" },
  { "ErrExprWhile", "expected a condition after 'while'" },
  { "ErrDoWhile", "expected 'do' after the condition" },
  { "ErrEndWhile", "expected 'end' to close the while loop" },
  { "ErrUntilRep", "expected 'until' at the end of the repeat loop" },
  { "ErrExprRep", "expected a conditions after 'until'" },

  { "ErrForRange", "expected a numeric or generic range after 'for'" },
  { "ErrEndFor", "expected 'end' to close the for loop" },
  { "ErrExprFor1", "expected a starting expression for the numeric range" },
  { "ErrCommaFor", "expected ',' to split the start and end of the range" },
  { "ErrExprFor2", "expected an ending expression for the numeric range" },
  { "ErrExprFor3", "expected a step expression for the numeric range after ','" },
  { "ErrInFor", "expected '=' or 'in' after the variable(s)" },
  { "ErrEListFor", "expected one or more expressions after 'in'" },
  { "ErrDoFor", "expected 'do' after the range of the for loop" },

  { "ErrDefLocal", "expected a function definition or assignment after local" },
  { "ErrNameLFunc", "expected a function name after 'function'" },
  { "ErrEListLAssign", "expected one or more expressions after '='" },
  { "ErrEListAssign", "expected one or more expressions after '='" },

  { "ErrFuncName", "expected a function name after 'function'" },
  { "ErrNameFunc1", "expected a function name after '.'" },
  { "ErrNameFunc2", "expected a method name after ':'" },
  { "ErrOParenPList", "expected '(' for the parameter list" },
  { "ErrCParenPList", "expected ')' to close the parameter list" },
  { "ErrEndFunc", "expected 'end' to close the function body" },
  { "ErrParList", "expected a variable name or '...' after ','" },

  { "ErrLabel", "expected a label name after '::'" },
  { "ErrCloseLabel", "expected '::' after the label" },
  { "ErrGoto", "expected a label after 'goto'" },
  { "ErrRetList", "expected an expression after ',' in the return statement" },

  { "ErrVarList", "expected a variable name after ','" },
  { "ErrExprList", "expected an expression after ','" },

  { "ErrOrExpr", "expected an expression after 'or'" },
  { "ErrAndExpr", "expected an expression after 'and'" },
  { "ErrRelExpr", "expected an expression after the relational operator" },
  { "ErrBOrExpr", "expected an expression after '|'" },
  { "ErrBXorExpr", "expected an expression after '~'" },
  { "ErrBAndExpr", "expected an expression after '&'" },
  { "ErrShiftExpr", "expected an expression after the bit shift" },
  { "ErrConcatExpr", "expected an expression after '..'" },
  { "ErrAddExpr", "expected an expression after the additive operator" },
  { "ErrMulExpr", "expected an expression after the multiplicative operator" },
  { "ErrUnaryExpr", "expected an expression after the unary operator" },
  { "ErrPowExpr", "expected an expression after '^'" },

  { "ErrExprParen", "expected an expression after '('" },
  { "ErrCParenExpr", "expected ')' to close the expression" },
  { "ErrNameIndex", "expected a field name after '.'" },
  { "ErrExprIndex", "expected an expression after '['" },
  { "ErrCBracketIndex", "expected ']' to close the indexing expression" },
  { "ErrNameMeth", "expected a method name after ':'" },
  { "ErrMethArgs", "expected some arguments for the method call (or '()')" },

  { "ErrArgList", "expected an expression after ',' in the argument list" },
  { "ErrCParenArgs", "expected ')' to close the argument list" },

  { "ErrCBraceTable", "expected '}' to close the table constructor" },
  { "ErrEqField", "expected '=' after the table key" },
  { "ErrExprField", "expected an expression after '='" },
  { "ErrExprFKey", "expected an expression after '[' for the table key" },
  { "ErrCBracketFKey", "expected ']' to close the table key" },

  { "ErrDigitHex", "expected one or more hexadecimal digits after '0x'" },
  { "ErrDigitDeci", "expected one or more digits after the decimal point" },
  { "ErrDigitExpo", "expected one or more digits for the exponent" },

  { "ErrQuote", "unclosed string" },
  { "ErrHexEsc", "expected exactly two hexadecimal digits after '\\x'" },
  { "ErrOBraceUEsc", "expected '{' after '\\u'" },
  { "ErrDigitUEsc", "expected one or more hexadecimal digits for the UTF-8 code point" },
  { "ErrCBraceUEsc", "expected '}' after the code point" },
  { "ErrEscSeq", "invalid escape sequence" },
  { "ErrCloseLStr", "unclosed long string" },
}

local function throw(label)
  label = "Err" .. label
  for i, labelinfo in ipairs(labels) do
    if labelinfo[1] == label then
      return T(i)
    end
  end

  error("Label not found: " .. label)
end

local function expect (patt, label)
  return patt + throw(label)
end


-- regular combinators and auxiliary functions

local function kw (str)
  return P(str) * -V"IdRest"
end

local function dec(n)
  return n - 1
end

local function tagC (tag, patt)
  return Ct(Cg(Cp(), "pos") * Cg(Cc(tag), "tag") * patt * Cg(Cp() / dec, "end_pos"))
end

local function unaryOp (op, e)
  return { tag = "Op", pos = e.pos, end_pos = e.end_pos, [1] = op, [2] = e }
end

local function binaryOp (e1, op, e2)
  if not op then
    return e1
  else
    return { tag = "Op", pos = e1.pos, end_pos = e2.end_pos, [1] = op, [2] = e1, [3] = e2 }
  end
end

local function sepBy (patt, sep, label)
  if label then
    return patt * Cg(V"Skip" * sep * V"Skip" * expect(patt, label))^0
  else
    return patt * Cg(V"Skip" * sep * V"Skip" * patt)^0
  end
end

local function chainOp (patt, sep, label)
  return Cf(sepBy(patt, sep, label), binaryOp)
end

local function tagDo (block)
  block.tag = "Do"
  return block
end

local function fixFuncStat (func)
  if func[1].is_method then table.insert(func[2][1], 1, { tag = "Id", [1] = "self" }) end
  func[1] = {func[1]}
  func[2] = {func[2]}
  return func
end

local function addDots (params, dots)
  if dots then table.insert(params, dots) end
  return params
end

local function insertIndex (t, index)
  return { tag = "Index", pos = t.pos, end_pos = index.end_pos, [1] = t, [2] = index }
end

local function markMethod(t, method)
  if method then
    return { tag = "Index", pos = t.pos, end_pos = method.end_pos, is_method = true, [1] = t, [2] = method }
  end
  return t
end

local function makeIndexOrCall (t1, t2)
  if not t2 then return t1 end
  if t2.tag == "Call" or t2.tag == "Invoke" then
    local t = { tag = t2.tag, pos = t1.pos, end_pos = t2.end_pos, [1] = t1 }
    for k, v in ipairs(t2) do
      table.insert(t, v)
    end
    return t
  end
  return { tag = "Index", pos = t1.pos, end_pos = t2.end_pos, [1] = t1, [2] = t2[1] }
end

-- grammar
local G = { V"Lua",
  Lua      = V"Shebang"^-1 * V"Skip" * V"Block" * expect(P(-1), "Extra");
  Shebang  = P"#!" * (P(1) - P"\n")^0;

  Block       = tagC("Block", V"Stat"^0 * V"RetStat"^-1);
  Stat        = V"IfStat" + V"DoStat" + V"WhileStat" + V"RepeatStat" + V"ForStat"
              + V"LocalStat" + V"FuncStat" + V"BreakStat" + V"LabelStat" + V"GoToStat"
              + V"FuncCall" * V"Skip" + V"Assignment" + P(";") * V"Skip" + -V"BlockEnd" * throw("InvalidStat");
  BlockEnd    = P"return" + "end" + "elseif" + "else" + "until" + -1;

  IfStat      = tagC("If", V"IfPart" * V"ElseIfPart"^0 * V"ElsePart"^-1 * expect(kw("end") * V"Skip", "EndIf"));
  IfPart      = kw("if") * V"Skip" * expect(V"Expr", "ExprIf") * V"Skip" * expect(kw("then") * V"Skip", "ThenIf") * V"Block";
  ElseIfPart  = kw("elseif") * V"Skip" * expect(V"Expr", "ExprEIf") * V"Skip" * expect(kw("then") * V"Skip", "ThenEIf") * V"Block";
  ElsePart    = kw("else") * V"Skip" * V"Block";

  DoStat      = kw("do") * V"Skip" * V"Block" * expect(kw("end") * V"Skip", "EndDo") / tagDo;
  WhileStat   = tagC("While", kw("while") * V"Skip" * expect(V"Expr", "ExprWhile") * V"Skip" * V"WhileBody");
  WhileBody   = expect(kw("do") * V"Skip", "DoWhile") * V"Block" * expect(kw("end") * V"Skip", "EndWhile");
  RepeatStat  = tagC("Repeat", kw("repeat") * V"Skip" * V"Block" * expect(kw("until") * V"Skip", "UntilRep") * expect(V"Expr", "ExprRep")) * V"Skip" ;

  ForStat   = kw("for") * V"Skip" * expect(V"ForNum" + V"ForIn", "ForRange") * expect(kw("end") * V"Skip", "EndFor");
  ForNum    = tagC("Fornum", V"Id" * V"Skip" * P("=") * V"Skip" * V"NumRange" * V"ForBody");
  NumRange  = expect(V"Expr", "ExprFor1")  * V"Skip" * expect(P(","), "CommaFor") * V"Skip" * expect(V"Expr", "ExprFor2") * V"Skip" 
            * (P(",") * V"Skip" * expect(V"Expr", "ExprFor3"))^-1 * V"Skip";
  ForIn     = tagC("Forin", V"NameList" * expect(kw("in") * V"Skip", "InFor") * expect(V"ExprList", "EListFor") * V"Skip" * V"ForBody");
  ForBody   = expect(kw("do") * V"Skip", "DoFor") * V"Block";

--  LocalStat    = kw("local") * V"Skip" * expect(V"LocalFunc" + V"LocalAssign", "DefLocal");
--  LocalFunc    = tagC("Localrec", kw("function") * V"Skip" * expect(V"Id", "NameLFunc") * V"Skip" * V"FuncBody") / fixFuncStat * V"Skip";
--  LocalAssign  = tagC("Local", V"NameList" * (P("=") * V"Skip" * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) * V"Skip";

  LocalStat    = V"LocalFunc" + V"LocalAssign" + kw("local") * V"Skip" * throw ("DefLocal");
  LocalFunc    = tagC("Localrec", kw("local") * V"Skip" * kw("function") * V"Skip" * expect(V"Id", "NameLFunc") * V"Skip" * V"FuncBody") / fixFuncStat * V"Skip";
  LocalAssign  = tagC("Local",    kw("local") * V"Skip" * V"NameList" * (P("=") * V"Skip" * expect(V"ExprList", "EListLAssign") + Ct(Cc()))) * V"Skip";

  Assignment   = tagC("Set", V"VarList" * P("=") * V"Skip" * expect(V"ExprList", "EListAssign")) * V"Skip";

  FuncStat    = tagC("Set", kw("function") * V"Skip" * expect(V"FuncName", "FuncName") * V"FuncBody") / fixFuncStat * V"Skip";
  FuncName    = Cf(V"Id" * (V"Skip" * P(".") * V"Skip" * expect(V"StrId", "NameFunc1") * V"Skip")^0, insertIndex) * V"Skip"
              * (P(":") * V"Skip" * expect(V"StrId", "NameFunc2") * V"Skip")^-1 / markMethod;
  FuncBody    = tagC("Function", V"FuncParams" * V"Block" * expect(kw("end"), "EndFunc"));
  FuncParams  = expect(P("(") * V"Skip", "OParenPList") * V"ParList" * expect(P(")") * V"Skip", "CParenPList");
  ParList     = V"NameList" * (P(",") * V"Skip" * expect(tagC("Dots", P("...") * V"Skip"), "ParList"))^-1 / addDots
              + Ct(tagC("Dots", P("...") * V"Skip"))
              + Ct(Cc()); -- Cc({}) generates a bug since the {} would be shared across parses

  LabelStat  = tagC("Label", P("::") * V"Skip" * expect(V"Name", "Label") * V"Skip" * expect(P("::") * V"Skip", "CloseLabel"));
  GoToStat   = tagC("Goto", kw("goto") * V"Skip" * expect(V"Name", "Goto") * V"Skip");
  BreakStat  = tagC("Break", kw("break") * V"Skip");
  RetStat    = tagC("Return", kw("return") * (V"Skip" * sepBy(V"Expr", P(","), "RetList"))^-1 * (V"Skip" * P(";"))^-1) * V"Skip";

  NameList  = tagC("NameList", sepBy(V"Id", P(","))) * V"Skip";
  VarList   = tagC("VarList", sepBy(V"VarExpr", P(","), "VarList")) * V"Skip";
  ExprList  = tagC("ExpList", sepBy(V"Expr", P(","), "ExprList"));

  Expr        = V"OrExpr";
  OrExpr      = chainOp(V"AndExpr", V"OrOp", "OrExpr");
  AndExpr     = chainOp(V"RelExpr", V"AndOp", "AndExpr");
  RelExpr     = chainOp(V"BOrExpr", V"RelOp", "RelExpr");
  BOrExpr     = chainOp(V"BXorExpr", V"BOrOp", "BOrExpr");
  BXorExpr    = chainOp(V"BAndExpr", V"BXorOp", "BXorExpr");
  BAndExpr    = chainOp(V"ShiftExpr", V"BAndOp", "BAndExpr");
  ShiftExpr   = chainOp(V"ConcatExpr", V"ShiftOp", "ShiftExpr");
  ConcatExpr  = V"AddExpr" * (V"Skip" * V"ConcatOp" * V"Skip" * expect(V"ConcatExpr", "ConcatExpr"))^-1 / binaryOp;
  AddExpr     = chainOp(V"MulExpr", V"AddOp", "AddExpr");
  MulExpr     = chainOp(V"UnaryExpr", V"MulOp", "MulExpr");
  UnaryExpr   = V"UnaryOp" * V"Skip" * expect(V"UnaryExpr", "UnaryExpr") / unaryOp
              + V"PowExpr";
  PowExpr     = V"SimpleExpr" * (V"Skip" * V"PowOp" * V"Skip" * expect(V"UnaryExpr", "PowExpr"))^-1 / binaryOp;

  SimpleExpr = tagC("Number", V"Number")
             + tagC("String", V"String")
             + tagC("Nil", kw("nil"))
             + tagC("Boolean", kw("false") * Cc(false))
             + tagC("Boolean", kw("true")  * Cc(true))
             + tagC("Dots", P("..."))
             + V"FuncDef"
             + V"Table"
             + V"SuffixedExpr";

  FuncCall  = Cmt(V"SuffixedExpr", function(s, i, exp) return exp.tag == "Call" or exp.tag == "Invoke", exp end);
  VarExpr   = Cmt(V"SuffixedExpr", function(s, i, exp) return exp.tag == "Id" or exp.tag == "Index", exp end);

  SuffixedExpr  = Cf(V"PrimaryExpr" * (V"Skip" * (V"Index" + V"Call"))^0, makeIndexOrCall);
  PrimaryExpr   = V"Id" * V"Skip" + tagC("Paren", P("(") * V"Skip" * expect(V"Expr", "ExprParen") * V"Skip" * expect(P(")"), "CParenExpr"));
  Index         = tagC("DotIndex", P"." * V"Skip" * -P"." * expect(V"StrId", "NameIndex"))
                + tagC("ArrayIndex", P("[") * -S"=[" * V"Skip" * expect(V"Expr", "ExprIndex") * V"Skip" * expect(P("]"), "CBracketIndex"));
  Call          = tagC("Invoke", Cg(P(":") * -P":" * V"Skip" * expect(V"StrId", "NameMeth") * V"Skip" * expect(V"FuncArgs", "MethArgs")))
                + tagC("Call", V"FuncArgs");

  FuncDef   = kw("function") * V"Skip" * V"FuncBody";
  FuncArgs  = P("(") * V"Skip" * sepBy(V"Expr", P(","), "ArgList")^-1 * V"Skip" * expect(P(")"), "CParenArgs")
            + V"Table"
            + tagC("String", V"String");

  Table      = tagC("Table", P("{") * V"Skip" * V"FieldList"^-1 * expect(P("}"), "CBraceTable"));
  FieldList  = sepBy(V"Field", V"FieldSep") * (V"Skip" * V"FieldSep")^-1 * V"Skip";
  Field      = tagC("Pair", V"FieldKey" * expect(P("=") * V"Skip", "EqField") * expect(V"Expr", "ExprField"))
             + V"Expr";
  FieldKey   = P("[" * -P(S"=[")) * V"Skip" * expect(V"Expr", "ExprFKey") * V"Skip" * expect(P("]") * V"Skip", "CBracketFKey")
             + V"StrId" * V"Skip" * #("=" * -P"=");
  FieldSep   = P(",") * V"Skip" + P(";") * V"Skip";

  Id     = tagC("Id", V"Name");
  StrId  = tagC("String", V"Name");

  -- lexer
  Skip     = ((Cp() * space^0 * Cp() * C(V"Comment") * Cp() * space^0 * Cp()) /   function(p1a, p1b, c, p2a, p2b) 
                                                                        commentCollector.add(p1a, p1b, p2a-1, p2b-1, c)
                                                                    end
              + V"Space")^0;
              
  Space    = space^1;
  Comment  = P"--" * V"LongStr" / function () return end
           + P"--" * (P(1) - P"\n")^0;

  Name      = -V"Reserved" * C(V"Ident");
  Reserved  = V"Keywords" * -V"IdRest";
  Keywords  = P"and" + "break" + "do" + "elseif" + "else" + "end"
            + "false" + "for" + "function" + "goto" + "if" + "in"
            + "local" + "nil" + "not" + "or" + "repeat" + "return"
            + "then" + "true" + "until" + "while";
  Ident     = V"IdStart" * V"IdRest"^0;
  IdStart   = alpha + P"_";
  IdRest    = alnum + P"_";

  Number   = (V"Hex" + V"Float" + V"Int") / tonumber;
  Hex      = (P"0x" + "0X") * expect(xdigit^1, "DigitHex");
  Float    = V"Decimal" * V"Expo"^-1
           + V"Int" * V"Expo";
  Decimal  = digit^1 * "." * digit^0
           + P"." * -P"." * expect(digit^1, "DigitDeci");
  Expo     = S"eE" * S"+-"^-1 * expect(digit^1, "DigitExpo");
  Int      = digit^1;

  String    = (V"ShortStr" + V"LongStr");
  ShortStr  = P'"' * Cs((V"EscSeq" + (P(1)-S'"\n'))^0) * expect(P'"', "Quote")
            + P"'" * Cs((V"EscSeq" + (P(1)-S"'\n"))^0) * expect(P"'", "Quote");

  EscSeq = P"\\" / ""  -- remove backslash
         * ( P"a" / "\a"
           + P"b" / "\b"
           + P"f" / "\f"
           + P"n" / "\n"
           + P"r" / "\r"
           + P"t" / "\t"
           + P"v" / "\v"
 
           + P"\n" / "\n"
           + P"\r" / "\n"
 
           + P"\\" / "\\"
           + P"\"" / "\""
           + P"\'" / "\'"

           + P"z" * space^0  / ""

           + digit * digit^-2 / tonumber / string.char
           + P"x" * expect(C(xdigit * xdigit), "HexEsc") * Cc(16) / tonumber / string.char
           + P"u" * expect("{", "OBraceUEsc")
                  * expect(C(xdigit^1), "DigitUEsc") * Cc(16)
                  * expect("}", "CBraceUEsc")
                  / tonumber 
                  / (utf8 and utf8.char or string.char)  -- true max is \u{10FFFF}
                                                         -- utf8.char needs Lua 5.3
                                                         -- string.char works only until \u{FF}

           + throw("EscSeq")
           );

  LongStr  = V"Open" * C((P(1) - V"CloseEq")^0) * expect(V"Close", "CloseLStr") / function (s, eqs) return s end;
  Open     = "[" * Cg(V"Equals", "openEq") * "[" * P"\n"^-1;
  Close    = "]" * C(V"Equals") * "]";
  Equals   = P"="^0;
  CloseEq  = Cmt(V"Close" * Cb("openEq"), function (s, i, closeEq, openEq) return #openEq == #closeEq end);

  OrOp      = kw("or")   / "or";
  AndOp     = kw("and")  / "and";
  RelOp     = P("~=")  / "ne"
            + P("==")  / "eq"
            + P("<=")  / "le"
            + P(">=")  / "ge"
            + P("<")   / "lt"
            + P(">")   / "gt";
  BOrOp     = P("|")   / "bor";
  BXorOp    = P("~" * -P"=") / "bxor";
  BAndOp    = P("&")   / "band";
  ShiftOp   = P("<<")  / "shl"
            + P(">>")  / "shr";
  ConcatOp  = P("..")  / "concat";
  AddOp     = P("+")   / "add"
            + P("-")   / "sub";
  MulOp     = P("*")   / "mul"
            + P("//")  / "idiv"
            + P("/")   / "div"
            + P("%")   / "mod";
  UnaryOp   = kw("not")  / "not"
            + P("-")   / "unm"
            + P("#")   / "len"
            + P("~")   / "bnot";
  PowOp     = P("^")   / "pow";
}

local parser = { detailed_errors = false }

local validator = require("mkdoc.lua-parser.validator")
local validate = validator.validate
local syntaxerror = validator.syntaxerror

function parser.parse (subject, filename, comments)
  commentCollector = comments
  local errorinfo = { subject = subject, filename = filename }
  lpeg.setmaxstack(1000)
  local ast, label, errorpos = lpeg.match(G, subject, nil, errorinfo)
  if not ast then
    if parser.detailed_errors then
      local re = require "relabel"
      local line, col = re.calcline(subject, errorpos)
      return ast, {
        line = line;
        column = col;
        id = labels[label][1];
        message = labels[label][2];
        position = errorpos;
      }
    else
      local errmsg = labels[label][2]
      return ast, syntaxerror(errorinfo, errorpos, errmsg)
    end
  end
  commentCollector = nil
  return validate(ast, errorinfo)
end

return parser
