///|
priv suberror ParseFailure {
  ParseFailure(Diagnostic)
}

///|
priv struct Parser {
  source : Bytes
  mut pos : Int32
  mut nodes : Int32
  limits : ParseLimits
  numeric_ceiling : Int32
}

///|
fn[T] Parser::fail(
  self : Parser,
  code : String,
  message : String,
) -> T raise ParseFailure {
  raise ParseFailure(diagnostic(self.source, self.pos, code, message))
}

///|
fn Parser::peek(self : Parser) -> Int32 {
  if self.pos < self.source.length() {
    self.source[self.pos].to_int()
  } else {
    -1
  }
}

///|
fn Parser::take(self : Parser, c : Int32) -> Bool {
  if self.peek() == c {
    self.pos = self.pos + 1
    true
  } else {
    false
  }
}

///|
fn Parser::need(self : Parser, c : Int32) -> Unit raise ParseFailure {
  if !self.take(c) {
    self.fail("syntax", "expected byte \{c}")
  }
}

///|
fn Parser::node(self : Parser, e : Expr) -> Expr raise ParseFailure {
  self.nodes = self.nodes + 1
  if self.nodes > self.limits.max_nodes {
    self.fail("parse-budget", "AST node budget exhausted")
  }
  e
}

///|
/// Exact RFC c-nl: CRLF, optionally preceded by a printable ASCII comment.
fn Parser::newline(self : Parser) -> Bool raise ParseFailure {
  if self.take(59) {
    while self.peek() != 13 {
      let c = self.peek()
      if c != 9 && (c < 32 || c > 126) {
        self.fail("syntax", "comment requires ASCII and CRLF")
      }
      self.pos = self.pos + 1
    }
  } else if self.peek() != 13 {
    return false
  }
  self.need(13)
  self.need(10)
  true
}

///|
/// c-wsp folds a c-nl only when followed by SP/HTAB.
fn Parser::space(self : Parser) -> Bool raise ParseFailure {
  let start = self.pos
  while true {
    if self.take(32) || self.take(9) {
      continue
    }
    let saved = self.pos
    if self.newline() && (self.take(32) || self.take(9)) {
      continue
    }
    self.pos = saved
    break
  }
  self.pos > start
}

///|
fn Parser::name(self : Parser) -> String raise ParseFailure {
  let out = StringBuilder()
  if !alpha(self.peek()) {
    self.fail("syntax", "expected rule name")
  }
  while alpha(self.peek()) || digit(self.peek()) || self.peek() == 45 {
    out.write_char(ascii_lower(self.peek()).unsafe_to_char())
    self.pos = self.pos + 1
  }
  out.to_string()
}

///|
fn Parser::number(
  self : Parser,
  base : Int32,
  ceiling : Int32,
) -> Int32 raise ParseFailure {
  let start = self.pos
  let mut value = 0
  while true {
    let c = ascii_lower(self.peek())
    let n = if digit(c) {
      c - 48
    } else if c >= 97 && c <= 102 {
      c - 87
    } else {
      -1
    }
    if n < 0 || n >= base {
      break
    }
    if value > (ceiling - n) / base {
      self.fail("number-limit", "numeric value exceeds \{ceiling}")
    }
    value = value * base + n
    self.pos = self.pos + 1
  }
  if self.pos == start {
    self.fail("syntax", "expected base-\{base} digits")
  }
  value
}

///|
fn Parser::literal(
  self : Parser,
  sensitive : Bool,
  start : Int32,
) -> Expr raise ParseFailure {
  self.need(34)
  let bytes : Array[Byte] = []
  while !self.take(34) {
    let c = self.peek()
    if c < 32 || c > 126 {
      self.fail("syntax", "quoted strings require printable ASCII")
    }
    bytes.push(c.to_byte())
    self.pos = self.pos + 1
  }
  self.node(Literal(Bytes::from_array(bytes), sensitive, start))
}

///|
fn Parser::element(self : Parser, depth : Int32) -> Expr raise ParseFailure {
  if depth > self.limits.max_depth {
    self.fail("parse-budget", "grammar nesting depth exhausted")
  }
  let start = self.pos
  if alpha(self.peek()) {
    return self.node(Ref(self.name(), start))
  }
  if self.peek() == 34 {
    return self.literal(false, start)
  }
  if self.take(40) {
    ignore(self.space())
    let e = self.alternation(depth + 1)
    ignore(self.space())
    self.need(41)
    return e
  }
  if self.take(91) {
    ignore(self.space())
    let e = self.alternation(depth + 1)
    ignore(self.space())
    self.need(93)
    return self.node(Repeat(0, Some(1), e, start))
  }
  if self.take(60) {
    let text = StringBuilder()
    while !self.take(62) {
      let c = self.peek()
      if c < 32 || c > 126 {
        self.fail("syntax", "invalid prose value")
      }
      text.write_char(c.unsafe_to_char())
      self.pos = self.pos + 1
    }
    return self.node(Prose(text.to_string(), start))
  }
  if self.take(37) {
    if self.peek() < 0 {
      self.fail("syntax", "missing percent prefix")
    }
    let prefix = ascii_lower(self.peek())
    self.pos = self.pos + 1
    if prefix == 105 || prefix == 115 {
      return self.literal(prefix == 115, start)
    }
    let base = match prefix {
      98 => 2
      100 => 10
      120 => 16
      _ => self.fail("syntax", "unknown percent prefix")
    }
    let lo = self.number(base, self.numeric_ceiling)
    if self.take(45) {
      let hi = self.number(base, self.numeric_ceiling)
      if lo > hi {
        self.fail("range", "descending numeric range")
      }
      return self.node(Range(lo, hi, start))
    }
    let es = [self.node(Range(lo, lo, start))]
    while self.take(46) {
      let n = self.number(base, self.numeric_ceiling)
      es.push(self.node(Range(n, n, start)))
    }
    return if es.length() == 1 { es[0] } else { self.node(Sequence(es, start)) }
  }
  self.fail("syntax", "expected ABNF element")
}

///|
fn Parser::repetition(self : Parser, depth : Int32) -> Expr raise ParseFailure {
  let start = self.pos
  let exact = if digit(self.peek()) {
    Some(self.number(10, 1000000))
  } else {
    None
  }
  if self.take(42) {
    let hi = if digit(self.peek()) {
      Some(self.number(10, 1000000))
    } else {
      None
    }
    let lo = exact.unwrap_or(0)
    if hi is Some(n) && n < lo {
      self.fail("repeat", "minimum exceeds maximum")
    }
    let e = self.element(depth)
    return self.node(Repeat(lo, hi, e, start))
  }
  let e = self.element(depth)
  match exact {
    Some(n) => self.node(Repeat(n, Some(n), e, start))
    None => e
  }
}

///|
fn Parser::concatenation(
  self : Parser,
  depth : Int32,
) -> Expr raise ParseFailure {
  let start = self.pos
  let es = [self.repetition(depth)]
  while true {
    let saved = self.pos
    if !self.space() {
      break
    }
    let c = self.peek()
    if c == 47 || c == 41 || c == 93 || c == 13 || c == 59 || c == -1 {
      self.pos = saved
      break
    }
    es.push(self.repetition(depth))
  }
  if es.length() == 1 {
    es[0]
  } else {
    self.node(Sequence(es, start))
  }
}

///|
fn Parser::alternation(self : Parser, depth : Int32) -> Expr raise ParseFailure {
  let start = self.pos
  let es = [self.concatenation(depth)]
  while true {
    let saved = self.pos
    ignore(self.space())
    if !self.take(47) {
      self.pos = saved
      break
    }
    ignore(self.space())
    es.push(self.concatenation(depth))
  }
  if es.length() == 1 {
    es[0]
  } else {
    self.node(Choice(es, start))
  }
}

///|
/// Strict grammar input: CRLF required, including the final rule terminator.
fn parse_domain(
  source : Bytes,
  limits : ParseLimits,
  numeric_ceiling : Int32,
) -> Result[Grammar, Diagnostic] {
  if limits.max_bytes < 0 ||
    limits.max_bytes > 1048576 ||
    limits.max_rules < 1 ||
    limits.max_rules > 512 ||
    limits.max_nodes < 1 ||
    limits.max_nodes > 8192 ||
    limits.max_depth < 1 ||
    limits.max_depth > 64 {
    return Err(
      diagnostic(
        source, 0, "invalid-limits", "parse limits exceed implementation ceilings",
      ),
    )
  }
  if source.length() > limits.max_bytes {
    return Err(
      diagnostic(source, 0, "parse-budget", "grammar byte budget exhausted"),
    )
  }
  let p = { source, pos: 0, nodes: 0, limits, numeric_ceiling, }
  try {
    let rules : Array[Rule] = []
    while p.pos < source.length() {
      let saved = p.pos
      ignore(p.space())
      if p.newline() {
        continue
      }
      p.pos = saved
      let offset = p.pos
      let name = p.name()
      ignore(p.space())
      p.need(61)
      let incremental = p.take(47)
      ignore(p.space())
      let expression = p.alternation(0)
      ignore(p.space())
      if !p.newline() {
        p.fail("syntax", "rule requires CRLF terminator")
      }
      rules.push({ name, expression, incremental, offset, })
      if rules.length() > limits.max_rules {
        p.fail("parse-budget", "rule budget exhausted")
      }
    }
    if source.is_empty() {
      p.fail("syntax", "empty rulelist")
    }
    Ok({ rules, })
  } catch {
    ParseFailure(d) => Err(d)
  }
}

///|
/// Legacy byte-domain grammar parser, with numeric values limited to 255.
pub fn parse(
  source : Bytes,
  limits~ : ParseLimits = default_parse_limits(),
) -> Result[Grammar, Diagnostic] {
  parse_domain(source, limits, 255)
}

///|
/// Encoding-independent numeric AST, restricted to nonnegative Int32 values.
/// RFC 5234 does not prescribe an external character encoding. This parser
/// deliberately does not imply Unicode or byte recognition. Source offsets
/// remain byte offsets into the ASCII grammar source.
pub fn parse_numeric(
  source : Bytes,
  limits~ : ParseLimits = default_parse_limits(),
) -> Result[Grammar, Diagnostic] {
  parse_domain(source, limits, 2147483647)
}