///|
/// Parse UTF-8 text. Include directives are explicitly unsupported.
pub fn parse(
  input : String,
  limits~ : Limits = Limits::default(),
) -> Value raise ConfigError {
  // UTF-16 length is a cheap lower bound before UTF-8 allocation.
  if input.length() > limits.max_input && limits.max_input >= 0 {
    raise failure("input_limit", "input exceeds byte limit")
  }
  parse_bytes(@utf8.encode(input), limits~)
}

///|
/// Parse libconfig bytes; returned root is always a Group.
pub fn parse_bytes(
  input : Bytes,
  limits~ : Limits = Limits::default(),
) -> Value raise ConfigError {
  let reader = Reader::new(input, limits)
  Group(reader.settings(0, -1))
}

///|
fn Reader::settings(
  self : Reader,
  depth : Int32,
  closing : Int32,
  path~ : String = "",
) -> Array[(String, Value)] raise ConfigError {
  let items : Array[(String, Value)] = []
  let names : Map[String, Bool] = Map([])
  self.skip()
  while self.peek() != closing {
    if self.peek() == 64 {
      raise self.error(
        "unsupported_include", "include directives require an external preprocessor",
      )
    }
    if self.peek() == -1 {
      raise self.error("unexpected_eof", "unclosed group")
    }
    let setting = self.position()
    let name = self.name()
    let child = if self.trace { member_path(path, name) } else { "" }
    self.current_path = child
    if names.contains(name) {
      raise self.error("duplicate_name", "duplicate setting: " + name)
    }
    names[name] = true
    self.skip()
    if self.peek() != 61 && self.peek() != 58 {
      raise self.error("unexpected_token", "expected = or :")
    }
    self.advance()
    let value = self.value(depth, path=child)
    if self.trace {
      match self.locations.get(child) {
        Some(location) =>
          self.locations[child] = { setting, value: location.value, }
        None => ()
      }
    }
    items.push((name, value))
    self.skip()
    if self.peek() == 59 || self.peek() == 44 {
      self.advance()
      self.skip()
    }
  }
  if closing != -1 {
    self.advance()
  }
  items
}

///|
fn Reader::value(
  self : Reader,
  depth : Int32,
  path~ : String = "",
) -> Value raise ConfigError {
  self.skip()
  let position = self.position()
  self.current_path = path
  let value = self.read_value(depth, path)
  self.current_path = path
  if self.trace {
    self.locations[path] = { setting: position, value: position, }
  }
  value
}

///|
fn Reader::read_value(
  self : Reader,
  depth : Int32,
  path : String,
) -> Value raise ConfigError {
  self.skip()
  if self.nodes >= self.limits.max_nodes {
    raise self.error("node_limit", "value count exceeds limit")
  }
  self.nodes += 1
  let c = self.peek()
  if c == 64 {
    raise self.error(
      "unsupported_include", "include directives are not supported",
    )
  }
  if c == 34 {
    return Text(self.text())
  }
  if c == 123 || c == 91 || c == 40 {
    if depth >= self.limits.max_depth {
      raise self.error("depth_limit", "container nesting exceeds limit")
    }
    self.advance()
    if c == 123 {
      return Group(self.settings(depth + 1, 125, path~))
    }
    let close = if c == 91 { 93 } else { 41 }
    let values : Array[Value] = []
    self.skip()
    while self.peek() != close {
      if c == 91 &&
        (self.peek() == 123 || self.peek() == 91 || self.peek() == 40) {
        raise self.error("array_type", "array elements must be scalars")
      }
      let child = if self.trace {
        path + "[" + values.length().to_string() + "]"
      } else {
        ""
      }
      let v = self.value(depth + 1, path=child)
      if c == 91 && values.length() > 0 && values[0].kind() != v.kind() {
        raise self.error(
          "array_type", "array elements must have the same scalar type",
        )
      }
      values.push(v)
      self.skip()
      if self.peek() == close {
        break
      }
      self.expect(44)
      self.skip()
    }
    self.expect(close)
    if c == 91 {
      ScalarArray(values)
    } else {
      List(values)
    }
  } else {
    self.atom()
  }
}