///|
fn parse_double_quoted(scanner : Scanner) -> String raise ParseError {
  scanner.expect('"')
  let lines : Array[String] = []
  let sb = StringBuilder::new()
  while true {
    match scanner.peek() {
      None => raise ParseError::UnexpectedEof(pos=scanner.position())
      Some('"') => {
        ignore(scanner.advance())
        lines.push(sb.to_string())
        let folded = fold_quoted_lines(lines)
        return unescape_double_quoted(folded)
      }
      Some('\\') => {
        ignore(scanner.advance())
        match scanner.peek() {
          Some('\n') => skip_escaped_line_break(scanner)
          Some('\r') => skip_escaped_line_break(scanner)
          Some(c) => {
            sb.write_char('\\')
            sb.write_char(c)
            ignore(scanner.advance())
          }
          None => raise ParseError::UnexpectedEof(pos=scanner.position())
        }
      }
      Some('\n') => {
        ignore(scanner.advance())
        lines.push(sb.to_string())
        sb.reset()
        skip_quoted_indent(scanner)
      }
      Some('\r') => {
        ignore(scanner.advance())
        lines.push(sb.to_string())
        sb.reset()
        skip_quoted_indent(scanner)
      }
      Some(c) => {
        ignore(scanner.advance())
        sb.write_char(c)
      }
    }
  } nobreak {
    sb.to_string()
  }
}

///|
fn parse_single_quoted(scanner : Scanner) -> String raise ParseError {
  scanner.expect('\'')
  let lines : Array[String] = []
  let sb = StringBuilder::new()
  while true {
    match scanner.peek() {
      None => raise ParseError::UnexpectedEof(pos=scanner.position())
      Some('\'') => {
        ignore(scanner.advance())
        if scanner.peek() is Some('\'') {
          ignore(scanner.advance())
          sb.write_char('\'')
        } else {
          lines.push(sb.to_string())
          return fold_quoted_lines(lines)
        }
      }
      Some('\n') => {
        ignore(scanner.advance())
        lines.push(sb.to_string())
        sb.reset()
        skip_quoted_indent(scanner)
      }
      Some('\r') => {
        ignore(scanner.advance())
        lines.push(sb.to_string())
        sb.reset()
        skip_quoted_indent(scanner)
      }
      Some(c) => {
        ignore(scanner.advance())
        sb.write_char(c)
      }
    }
  } nobreak {
    sb.to_string()
  }
}

///|
fn parse_plain_scalar(scanner : Scanner) -> String raise ParseError {
  let lines : Array[String] = []
  let sb = StringBuilder::new()
  let mut prev_space = true
  let mut ended_on_newline = false
  while scanner.peek() is Some(c) &&
        !is_flow_plain_break(scanner, c, prev_space) {
    if c is '\n' || c is '\r' {
      ignore(scanner.advance())
      lines.push(sb.to_string().trim_end().to_string())
      sb.reset()
      ended_on_newline = true
      if !skip_flow_plain_line(scanner, lines) {
        break
      }
      ended_on_newline = false
      prev_space = true
      continue
    }
    ignore(scanner.advance())
    sb.write_char(c)
    prev_space = c.is_whitespace()
  }
  if scanner.peek() is Some('#') && prev_space {
    scanner.skip_comment()
  }
  if lines.length() == 0 {
    let raw = sb.to_string().trim_end().to_string()
    if raw is "" {
      match scanner.peek() {
        None => raise ParseError::UnexpectedEof(pos=scanner.position())
        Some(c) =>
          raise ParseError::UnexpectedChar(pos=scanner.position(), found=c)
      }
    }
    raw
  } else {
    if !ended_on_newline {
      lines.push(sb.to_string().trim_end().to_string())
    }
    let folded = fold_lines(lines)
    if folded is "" {
      match scanner.peek() {
        None => raise ParseError::UnexpectedEof(pos=scanner.position())
        Some(c) =>
          raise ParseError::UnexpectedChar(pos=scanner.position(), found=c)
      }
    }
    folded
  }
}

///|
fn fold_quoted_lines(lines : Array[String]) -> String {
  if lines.length() == 0 {
    return ""
  }
  let mut all_empty = true
  for line in lines {
    if !(line is "") {
      all_empty = false
      break
    }
  }
  if all_empty {
    if lines.length() == 2 {
      return " "
    }
    let mut count = lines.length() - 2
    if count < 0 {
      count = 0
    }
    let sb = StringBuilder::new()
    for _ in 0.. 0 {
    let mut has_content = false
    for i in 0.. String raise ParseError {
  let scanner = Scanner::new(text)
  let sb = StringBuilder::new()
  while scanner.peek() is Some(c) {
    if c is '\\' {
      ignore(scanner.advance())
      match scanner.peek() {
        Some('\n') => skip_escaped_line_break(scanner)
        Some('\r') => skip_escaped_line_break(scanner)
        _ => sb.write_char(parse_escape(scanner))
      }
      continue
    }
    ignore(scanner.advance())
    sb.write_char(c)
  }
  sb.to_string()
}

///|
fn trim_end_whitespace(text : String) -> String {
  let chars = text.to_array()
  let mut end = chars.length()
  while end > 0 {
    let c = chars[end - 1]
    if c is ' ' || c is '\t' {
      if end > 1 && chars[end - 2] is '\\' {
        break
      }
      end = end - 1
      continue
    }
    break
  }
  if end == chars.length() {
    return text
  }
  let sb = StringBuilder::new()
  for i in 0.. Bool {
  let chars = text.to_array()
  if chars.length() == 0 {
    return false
  }
  let last = chars[chars.length() - 1]
  last is ' '
}

///|
fn is_flow_plain_break(scanner : Scanner, c : Char, prev_space : Bool) -> Bool {
  if c is '\t' {
    return true
  }
  if c is ',' || c is ']' || c is '}' || c is '[' || c is '{' {
    return true
  }
  if c is '#' && prev_space {
    return true
  }
  if c is ':' {
    match scanner.peek_next() {
      None => true
      Some(next) =>
        next.is_whitespace() || next is ',' || next is ']' || next is '}'
    }
  } else {
    false
  }
}

///|
fn skip_flow_plain_line(scanner : Scanner, lines : Array[String]) -> Bool {
  while true {
    while scanner.peek() is Some(c) && (c is ' ' || c is '\t') {
      ignore(scanner.advance())
    }
    match scanner.peek() {
      None => return false
      Some('\n') => {
        ignore(scanner.advance())
        lines.push("")
        continue
      }
      Some('\r') => {
        ignore(scanner.advance())
        lines.push("")
        continue
      }
      Some('#') => {
        scanner.skip_comment()
        match scanner.peek() {
          Some('\n') => {
            ignore(scanner.advance())
            return false
          }
          Some('\r') => {
            ignore(scanner.advance())
            return false
          }
          None => return false
          Some(_) => return false
        }
      }
      Some(c) =>
        if is_flow_plain_break(scanner, c, true) {
          return false
        } else {
          return true
        }
    }
  }
  false
}

///|
fn parse_escape(scanner : Scanner) -> Char raise ParseError {
  match scanner.advance() {
    None => raise ParseError::UnexpectedEof(pos=scanner.position())
    Some('0') => '\u{0000}'
    Some('a') => '\u{0007}'
    Some('b') => '\u{0008}'
    Some('t') => '\t'
    Some('\t') => '\t'
    Some('n') => '\n'
    Some('v') => '\u{000b}'
    Some('f') => '\u{000c}'
    Some('r') => '\r'
    Some('e') => '\u{001b}'
    Some('N') => '\u{0085}'
    Some('_') => '\u{00a0}'
    Some('L') => '\u{2028}'
    Some('P') => '\u{2029}'
    Some(' ') => ' '
    Some('"') => '"'
    Some('\\') => '\\'
    Some('/') => '/'
    Some('x') => {
      let code = read_hex(scanner, 2)
      char_from_code(code, scanner)
    }
    Some('u') => {
      let code = read_hex(scanner, 4)
      char_from_code(code, scanner)
    }
    Some('U') => {
      let code = read_hex(scanner, 8)
      char_from_code(code, scanner)
    }
    Some(other) =>
      raise ParseError::InvalidEscape(
        pos=scanner.position(),
        value=other.to_string(),
      )
  }
}

///|
fn skip_escaped_line_break(scanner : Scanner) -> Unit {
  if scanner.peek() is Some('\r') {
    ignore(scanner.advance())
    if scanner.peek() is Some('\n') {
      ignore(scanner.advance())
    }
  } else if scanner.peek() is Some('\n') {
    ignore(scanner.advance())
  }
  while scanner.peek() is Some(c) && (c is ' ' || c is '\t') {
    ignore(scanner.advance())
  }
}

///|
fn skip_quoted_indent(scanner : Scanner) -> Unit {
  while scanner.peek() is Some(c) && (c is ' ' || c is '\t') {
    ignore(scanner.advance())
  }
}

///|
fn read_hex(scanner : Scanner, digits : Int32) -> Int32 raise ParseError {
  let mut value = 0
  for _ in 0.. raise ParseError::UnexpectedEof(pos=scanner.position())
      Some(c) =>
        match hex_value(c) {
          None =>
            raise ParseError::InvalidEscape(
              pos=scanner.position(),
              value=c.to_string(),
            )
          Some(v) => value = value * 16 + v
        }
    }
  }
  value
}

///|
fn hex_value(c : Char) -> Int32? {
  if c.is_ascii_digit() {
    Some(c.to_int() - '0'.to_int())
  } else if c.is_ascii_lowercase() {
    Some(c.to_int() - 'a'.to_int() + 10)
  } else if c.is_ascii_uppercase() {
    Some(c.to_int() - 'A'.to_int() + 10)
  } else {
    None
  }
}

///|
fn char_from_code(code : Int32, scanner : Scanner) -> Char raise ParseError {
  match code.to_char() {
    Some(c) => c
    None =>
      raise ParseError::InvalidEscape(
        pos=scanner.position(),
        value=code.to_string(),
      )
  }
}

///|
fn parse_scalar(scanner : Scanner) -> Yaml raise ParseError {
  match scanner.peek() {
    Some('"') => Yaml::String(parse_double_quoted(scanner))
    Some('\'') => Yaml::String(parse_single_quoted(scanner))
    _ => parse_scalar_value(parse_plain_scalar(scanner))
  }
}

///|
fn parse_scalar_value(raw : String) -> Yaml {
  if raw is "" {
    return Yaml::Null
  }
  let lowered = raw.to_lower()
  if lowered is "null" || raw is "~" {
    Yaml::Null
  } else if lowered is "true" {
    Yaml::Bool(true)
  } else if lowered is "false" {
    Yaml::Bool(false)
  } else {
    match parse_number(raw) {
      Some(value) => value
      None => Yaml::String(raw)
    }
  }
}

///|
fn parse_number(raw : String) -> Yaml? {
  let normalized = raw.replace_all(old="_", new="")
  let lowered = normalized.to_lower()
  match parse_special_float(lowered) {
    Some(value) => return Some(Yaml::Float(value))
    None => ()
  }
  let mut has_digit = false
  for c in normalized {
    if c.is_ascii_digit() {
      has_digit = true
    }
  }
  if !has_digit {
    return None
  }
  let (sign, body) = strip_sign(normalized)
  if body.has_prefix("0x") || body.has_prefix("0X") {
    return parse_int_base(body.view(start_offset=2).to_string(), 16, sign)
  }
  if body.has_prefix("0o") || body.has_prefix("0O") {
    return parse_int_base(body.view(start_offset=2).to_string(), 8, sign)
  }
  if body.has_prefix("0b") || body.has_prefix("0B") {
    return parse_int_base(body.view(start_offset=2).to_string(), 2, sign)
  }
  let has_float = lowered.contains(".") ||
    lowered.contains("e") ||
    lowered.contains("E")
  if has_float {
    try @string.parse_double(lowered[:]) catch {
      _ => None
    } noraise {
      value => Some(Yaml::Float(value))
    }
  } else {
    try @string.parse_int(lowered[:]) catch {
      _ => None
    } noraise {
      value => Some(Yaml::Int(value))
    }
  }
}

///|
fn strip_sign(value : String) -> (Int32, String) {
  if value.has_prefix("-") {
    (-1, value.view(start_offset=1).to_string())
  } else if value.has_prefix("+") {
    (1, value.view(start_offset=1).to_string())
  } else {
    (1, value)
  }
}

///|
fn parse_int_base(digits : String, base : Int32, sign : Int32) -> Yaml? {
  try @string.parse_int(digits[:], base~) catch {
    _ => None
  } noraise {
    value => Some(Yaml::Int(value * sign))
  }
}

///|
fn parse_special_float(lowered : String) -> Double? {
  if lowered is ".inf" || lowered is "+.inf" || lowered is "-.inf" {
    let sign = if lowered.has_prefix("-") { -1.0 } else { 1.0 }
    try @string.parse_double("inf") catch {
      _ => None
    } noraise {
      value => Some(value * sign)
    }
  } else if lowered is ".nan" || lowered is "+.nan" || lowered is "-.nan" {
    try @string.parse_double("nan") catch {
      _ => None
    } noraise {
      value => Some(value)
    }
  } else {
    None
  }
}