///|
/// A string literal, decoded.
///
/// A literal newline inside a string is an error, not a continuation: shrubbery
/// has no `\` escape, so a run-away quote is caught at the end of its
/// line rather than swallowing the rest of the file.
fn Scanner::scan_string(
  self : Scanner,
  start : @basic.Pos,
  from : Int,
) -> Token {
  let buf = StringBuilder()
  let mut i = from + 1
  while i < self.src.length() {
    match self.at(i) {
      Some('"') =>
        return self.finish(
          start,
          i + 1,
          Literal(Str(buf.to_string())),
          mode=Continuing,
        )
      Some('\n') | Some('\r') => break
      Some('\\') =>
        match self.decode_escape(i, unicode=true) {
          Some((next, code)) => {
            append_code(buf, code)
            i = next
          }
          None => break
        }
      Some(c) => {
        buf.write_char(c)
        i = self.step(i)
      }
      None => break
    }
  }
  self.finish(start, self.scan_bad_string(from), Fail(ReadError))
}

///|
/// `#"..."`, decoded to bytes.
fn Scanner::scan_byte_string(
  self : Scanner,
  start : @basic.Pos,
  from : Int,
) -> Token {
  let out = []
  let mut i = from + 2
  while i < self.src.length() {
    match self.at(i) {
      Some('"') =>
        return self.finish(
          start,
          i + 1,
          Literal(Bs(Bytes::from_array(out))),
          mode=Continuing,
        )
      Some('\n') | Some('\r') => break
      Some('\\') =>
        match self.decode_escape(i, unicode=false) {
          Some((next, code)) => {
            // The grammar restricts a byte-string element to 0x00..0xFF, so a
            // wider value cannot arrive here.
            out.push((code & 0xFF).to_byte())
            i = next
          }
          None => break
        }
      Some(c) => {
        out.push((c.to_int() & 0xFF).to_byte())
        i = self.step(i)
      }
      None => break
    }
  }
  self.finish(start, self.scan_bad_string(from), Fail(ReadError))
}

///|
/// How far a malformed string reaches: to a closing quote if one turns up on
/// this line, and to the end of the line otherwise.
fn Scanner::scan_bad_string(self : Scanner, from : Int) -> Int {
  let mut i = if self.at(from) is Some('#') { from + 2 } else { from + 1 }
  while i < self.src.length() {
    match self.at(i) {
      Some('\n') | Some('\r') => return i
      Some('"') => return i + 1
      Some('\\') =>
        match self.at(i + 1) {
          Some('\n') | Some('\r') | None => return i + 1
          _ => i = self.step(i + 1)
        }
      _ => i = self.step(i)
    }
  }
  i
}

///|
/// One escape sequence at `i`, returning where it ends and the code point.
///
/// `\u` and `\U` are string-only: a byte string has no code points to name.
/// A `\u` high surrogate followed by a `\u` low surrogate is combined, which is
/// how Racket reads a pair and the only way to write an astral character with
/// four hex digits.
fn Scanner::decode_escape(
  self : Scanner,
  i : Int,
  unicode~ : Bool,
) -> (Int, Int)? {
  let c = match self.at(i + 1) {
    Some(c) => c
    None => return None
  }
  match c {
    '"' => Some((i + 2, 0x22))
    '\\' => Some((i + 2, 0x5C))
    '\'' => Some((i + 2, 0x27))
    'a' => Some((i + 2, 0x07))
    'b' => Some((i + 2, 0x08))
    't' => Some((i + 2, 0x09))
    'n' => Some((i + 2, 0x0A))
    'v' => Some((i + 2, 0x0B))
    'f' => Some((i + 2, 0x0C))
    'r' => Some((i + 2, 0x0D))
    'e' => Some((i + 2, 0x1B))
    'x' => self.hex_escape(i + 2, 1, 2)
    'u' =>
      if !unicode {
        None
      } else {
        match self.hex_escape(i + 2, 1, 4) {
          Some((next, hi)) =>
            // A surrogate pair, written as two escapes, is one character.
            if hi >= 0xD800 && hi <= 0xDBFF && self.has(next, "\\u") {
              match self.hex_escape(next + 2, 1, 4) {
                Some((after, lo)) if lo >= 0xDC00 && lo <= 0xDFFF =>
                  Some((after, 0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00)))
                _ => Some((next, hi))
              }
            } else {
              Some((next, hi))
            }
          None => None
        }
      }
    'U' => if !unicode { None } else { self.hex_escape(i + 2, 1, 6) }
    _ =>
      if is_octal(c) {
        let mut n = 0
        let mut k = 0
        let mut j = i + 1
        while k < 3 {
          match self.at(j) {
            Some(d) if is_octal(d) => {
              n = n * 8 + (d.to_int() - 48)
              j = j + 1
              k = k + 1
            }
            _ => break
          }
        }
        Some((j, n))
      } else {
        None
      }
  }
}

///|
fn Scanner::hex_escape(
  self : Scanner,
  from : Int,
  min : Int,
  max : Int,
) -> (Int, Int)? {
  let mut n = 0
  let mut k = 0
  let mut i = from
  while k < max {
    match self.at(i) {
      Some(d) if is_hex(d) => {
        n = n * 16 + hex_value(d)
        i = i + 1
        k = k + 1
      }
      _ => break
    }
  }
  if k < min {
    None
  } else {
    Some((i, n))
  }
}

///|
/// Append a decoded code point.
///
/// A lone surrogate cannot be a `Char`, so it is written as its raw code unit;
/// the grammar admits one — it "does not constrain to avoid surrogates" — and
/// dropping it would lose text the raw metadata still has to reproduce.
fn append_code(buf : StringBuilder, code : Int) -> Unit {
  if code >= 0xD800 && code <= 0xDFFF {
    buf.write_string(String::from_array([code.unsafe_to_char()]))
  } else {
    buf.write_char(code.unsafe_to_char())
  }
}