///|
#callsite(autofill(loc))
pub fn[A] error(msg : String, loc~ : SourceLoc) -> A raise ControlFlow {
  raise ControlFlow::Error(msg + " " + loc.to_string())
}

///|
fn fromCharCode(hex : StringView, base : Int) -> Char {
  (try! @strconv.parse_int(hex, base~)).to_char().unwrap_or('?')
}

///|
pub fn manualUnescape(input : StringView) -> StringView {
  try {
    lexmatch input {
      (tail, "\\n", rest) => "\{tail}\n\{manualUnescape(rest)}"
      (tail, "\\t", rest) => "\{tail}\t\{manualUnescape(rest)}"
      (tail, "\\r", rest) => "\{tail}\r\{manualUnescape(rest)}"
      // Unicode escape
      (tail, "\\u[0-9a-fA-F]{4}" as raw, rest) =>
        "\{tail}\{fromCharCode(raw[2:],16)}\{manualUnescape(rest)}"
      (tail, "\\x[0-9a-fA-F]{2}" as raw, rest) =>
        "\{tail}\{fromCharCode(raw[2:],16)}\{manualUnescape(rest)}"
      // Unicode escape with braces
      (tail, "\\u[{][0-9a-fA-F]+[}]" as raw, rest) =>
        "\{tail}\{fromCharCode(raw[3:-1],16)}\{manualUnescape(rest)}"
      // Octal escape
      (tail, "\\o[0-3][0-7]{2}" as raw, rest) =>
        "\{tail}\{fromCharCode(raw[2:],8)}\{manualUnescape(rest)}"
      (tail, "\\\\", rest) => "\{tail}\\\\\{manualUnescape(rest)}"
      _ => input
    }
  } catch {
    _ => input
  }
}

///|
test "manualUnescape" {
  inspect(manualUnescape("\\nhello"), content="\nhello")
  inspect(
    manualUnescape("hello\\nworld"),
    content=(
      #|hello
      #|world
    ),
  )
  inspect(manualUnescape("\\u0041"), content="A")
  inspect(manualUnescape("\\x41"), content="A")
  inspect(manualUnescape("\\o061"), content="1")
}