// String parsing utilities — quoted strings, escapes, identifiers

///|
pub fn quoted_string(input : ParseInput) -> (ParseInput, String)? {
  match tag("\"", input) {
    Some((r1, _)) => quoted_content(r1, "")
    None =>
      match tag("'", input) {
        Some((r1, _)) => single_content(r1, "")
        None => None
      }
  }
}

///|
fn quoted_content(input : ParseInput, acc : String) -> (ParseInput, String)? {
  if input.is_eof() {
    return None
  }
  let ch = input.current()
  if ch == "\"" {
    Some((input.advance(), acc))
  } else if ch == "\\" {
    parse_dq_escape(input.advance(), acc)
  } else {
    quoted_content(input.advance(), acc + ch)
  }
}

///|
fn single_content(input : ParseInput, acc : String) -> (ParseInput, String)? {
  if input.is_eof() {
    return None
  }
  let ch = input.current()
  if ch == "'" {
    Some((input.advance(), acc))
  } else if ch == "\\" {
    parse_sq_escape(input.advance(), acc)
  } else {
    single_content(input.advance(), acc + ch)
  }
}

///|
fn parse_dq_escape(input : ParseInput, acc : String) -> (ParseInput, String)? {
  if input.is_eof() {
    return None
  }
  let ch = input.current()
  let rest = input.advance()
  let escaped = match ch {
    "n" => "\n"
    "t" => "\t"
    "r" => "\r"
    "\\" => "\\"
    "\"" => "\""
    "/" => "/"
    _ => ch
  }
  quoted_content(rest, acc + escaped)
}

///|
fn parse_sq_escape(input : ParseInput, acc : String) -> (ParseInput, String)? {
  if input.is_eof() {
    return None
  }
  let ch = input.current()
  let rest = input.advance()
  let escaped = match ch {
    "n" => "\n"
    "t" => "\t"
    "r" => "\r"
    "\\" => "\\"
    "'" => "'"
    _ => ch
  }
  single_content(rest, acc + escaped)
}

///|
pub fn triple_quoted_string(input : ParseInput) -> (ParseInput, String)? {
  match tag("\"\"\"", input) {
    Some((r1, _)) => triple_content(r1, "")
    None => None
  }
}

///|
fn triple_content(input : ParseInput, acc : String) -> (ParseInput, String)? {
  if input.is_eof() {
    return None
  }
  if input.source.substring(start=input.pos, end=input.pos + 3) == "\"\"\"" {
    Some((input.advance_by(3), acc))
  } else {
    let ch = input.current()
    triple_content(input.advance(), acc + ch)
  }
}

///|
pub fn identifier(input : ParseInput) -> (ParseInput, String)? {
  match alpha(input) {
    Some((r1, first)) => {
      let (rest, rest_str) = take_while(
        fn(ch : String) -> Bool { is_alpha(ch) || is_digit(ch) || ch == "_" },
        r1,
      )
      Some((rest, first + rest_str))
    }
    None =>
      match tag("_", input) {
        Some((r1, u)) => {
          let (rest, rest_str) = take_while(
            fn(ch : String) -> Bool {
              is_alpha(ch) || is_digit(ch) || ch == "_"
            },
            r1,
          )
          Some((rest, u + rest_str))
        }
        None => None
      }
  }
}

///|
pub fn keyword(kw : String, input : ParseInput) -> (ParseInput, String)? {
  match tag(kw, input) {
    Some((r1, val)) => {
      if r1.is_eof() {
        return Some((r1, val))
      }
      let ch = r1.current()
      if is_alpha(ch) || is_digit(ch) || ch == "_" {
        None
      } else {
        Some((r1, val))
      }
    }
    None => None
  }
}