///|
/// Parses a token with predicate
///
/// The predicate should return `Some(value)` if the tokens fulfills
/// or `None` otherwise
pub fn[Token, Value] pvalue(
  predicate : (Token) -> Value?,
) -> Parser[Token, Value] {
  Parser(fn(seq) {
    let (hd, rest) = match seq.uncons() {
      Some(s) => s
      None => return None
    }
    let value = match predicate(hd) {
      Some(s) => s
      None => return None
    }
    Some((value, rest))
  })
}

///|
/// Parser that always fail
pub fn[Token, A] pfail() -> Parser[Token, A] {
  Parser(fn(_tokens) { None })
}

///|
/// Parser that always succeed with given value
pub fn[Token, A] pconst(a : A) -> Parser[Token, A] {
  Parser(fn(tokens) { Some((a, tokens)) })
}

// String specific parsers

///|
pub fn pchar_such_that(predicate : (Char) -> Bool) -> Parser[Char, Char] {
  pvalue(char => if predicate(char) { Some(char) } else { None })
}

///|
pub fn pchar(char : Char) -> Parser[Char, Char] {
  pchar_such_that(fn(c) { c == char })
}

///|
/// parser for integer
///
/// [ "-" ] ( %x30 / (%x31-39) *(%x30-39)
pub let pint : Parser[Char, Int] = pdigits.map(fn(tuple) {
  let sign = tuple.0
  let digits = tuple.1
  let i = digits.1.fold(fn(a, b) { 10 * a + b }, init=digits.0)
  match sign {
    Some(_) => -i
    None => i
  }
})

///|
/// parser for integer 64 bits
///
/// [ "-" ] ( %x30 / (%x31-39) *(%x30-39)
pub let pint64 : Parser[Char, Int64] = pdigits.map(fn(tuple) {
  let sign = tuple.0
  let digits = tuple.1
  let i = digits.1.fold(
    fn(a, b) { 10L * a + b.to_int64() },
    init=digits.0.to_int64(),
  )
  match sign {
    Some(_) => -i
    None => i
  }
})

///|
/// parser that tries to match the given string
pub fn pstring(string : String) -> Parser[Char, String] {
  Parser(fn(seq) {
    let iter = for iter = seq, input = Seq::from_string(string) {
      if !input.is_empty() {
        let (target, rest_target) = match input.uncons() {
          None => return None
          Some(s) => s
        }
        let (char, rest) = match iter.uncons() {
          None => return None
          Some(s) => s
        }
        if target != char {
          return None
        }
        continue rest, rest_target
      }
      break iter
    }
    Some((string, iter))
  })
}

///|
/// parse digit
///
/// %x30-39
pub let pdigit : Parser[Char, Int] = one_of([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

///|
let pdigits : Parser[Char, (Char?, (Int, Array[Int]))] = pchar('-')
  .optional()
  .and_then(
    pchar('0')
    .map(fn(_ch) { (0, []) })
    .or_else(
      pvalue(x => {
        match x {
          '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' as char =>
            Some(char.to_int() - 0x30)
          _ => None
        }
      }).and_then(pdigit.repeat()),
    ),
  )