///|
/// Repetition combinators: many, some, and separated variants.
///
/// Provides `many`, `some`, `sep_by`, `sep_by1`, and `many_until`.
/// All repetition parsers guard against infinite loops by checking for
/// zero-length progress via `same_cursor`.

///|
/// Prepend an element to the tail of an array.
fn[T] Array::cons(tail : Array[T], head : T) -> Array[T] {
  Array::new(capacity=tail.length() + 1)..push(head)..append(tail)
}

///|
/// Zero or more repetitions of `self`.
///
/// Stops on non-committed failure. Rejects parsers that accept
/// empty input (infinite loop guard).
pub fn[I : Cursor, T, E : Commit + ParseFailure] ParserRaw::many(
  self : ParserRaw[I, T, E],
) -> ParserRaw[I, Array[T], E] {
  ParserRaw::new(input => {
    let values = Array::new()
    for rest = input {
      match self.run(rest) {
        Ok((value, next)) => {
          guard !next.same_cursor(rest) else {
            break Err(
              ParseFailure::message(rest, "many parser accepted empty input"),
            )
          }
          values.push(value)
          continue next
        }
        Err(error) => break Commit::reject_if_committed(error, values, rest)
      }
    }
  })
}

///|
/// One or more repetitions of `self`.
#inline
pub fn[I : Cursor, T, E : Commit + ParseFailure] ParserRaw::some(
  self : ParserRaw[I, T, E],
) -> ParserRaw[I, Array[T], E] {
  self.bind(head => self.many().map(tail => tail.cons(head)))
}

///|
/// Zero or more repetitions of `self` separated by `sep`.
#inline
pub fn[I : Cursor, T, S, E : Commit + CanMerge + ParseFailure] ParserRaw::sep_by(
  self : ParserRaw[I, T, E],
  sep : ParserRaw[I, S, E],
) -> ParserRaw[I, Array[T], E] {
  self.sep_by1(sep).or(pure([]))
}

///|
/// One or more repetitions of `self` separated by `separator`.
pub fn[I : Cursor, T, S, E : Commit + ParseFailure] ParserRaw::sep_by1(
  self : ParserRaw[I, T, E],
  separator : ParserRaw[I, S, E],
) -> ParserRaw[I, Array[T], E] {
  self.bind(head => separator.then(self).many().map(tail => tail.cons(head)))
}

///|
/// Keeps running `self` until `terminator` succeeds.
pub fn[I : Cursor, T, U, E : Commit + ParseFailure] ParserRaw::many_until(
  self : ParserRaw[I, T, E],
  terminator : ParserRaw[I, U, E],
) -> ParserRaw[I, Array[T], E] {
  ParserRaw::new(input => {
    let values = Array::new()
    for rest = input {
      match terminator.run(rest) {
        Ok((_, next)) => break Ok((values, next))
        Err(_) =>
          match self.run(rest) {
            Ok((value, next)) => {
              guard !next.same_cursor(rest) else {
                break Err(
                  ParseFailure::message(
                    rest, "many_until parser accepted empty input",
                  ),
                )
              }
              values.push(value)
              continue next
            }
            Err(error) => break Commit::reject_if_committed(error, values, rest)
          }
      }
    }
  })
}