///|
/// A `plain-identifier`: an alphabetic, `_` or emoji start, then any number of
/// those plus numerics.
///
/// Emoji are matched as SEQUENCES, longest first. `👨‍👩‍👦` is one identifier
/// character; stopping at the first `👨` would leave zero-width joiners loose,
/// and those are not identifier characters at all.
fn Scanner::scan_plain_identifier(self : Scanner, from : Int) -> Int? {
  let e = self.emoji_or(from, c => @unicode.is_alphabetic(c) || c == '_')
  if e == from {
    return None
  }
  let mut i = e
  while i < self.src.length() {
    let n = self.emoji_or(i, c => {
      @unicode.is_alphabetic(c) || @unicode.is_numeric(c) || c == '_'
    })
    if n == i {
      break
    }
    i = n
  }
  Some(i)
}

///|
/// One identifier character at `from`: an emoji sequence if one starts here,
/// otherwise a single character satisfying `pred`. Returns `from` if neither.
fn Scanner::emoji_or(self : Scanner, from : Int, pred : (Char) -> Bool) -> Int {
  let n = @unicode.emoji_sequence_at(self.src, from)
  if n > 0 {
    return from + n
  }
  match self.at(from) {
    Some(c) => if pred(c) { self.step(from) } else { from }
    None => from
  }
}

///|
/// An identifier, with the optional `#%` prefix for internal names.
fn Scanner::scan_identifier(self : Scanner, from : Int) -> Int? {
  if self.has(from, "#%") {
    match self.scan_plain_identifier(from + 2) {
      Some(e) => Some(e)
      None => None
    }
  } else {
    self.scan_plain_identifier(from)
  }
}

///|
/// The longest operator starting at `from`, or `None`.
///
/// Four rules, all of which exist to keep operators out of the way of something
/// else:
///
///   * `//` and `/*` may not appear inside one, or an operator would swallow a
///     comment.
///   * A multi-character operator may not end in `:`, or `x+:` would be
///     ambiguous with `x+` before a block. All-colon operators are the
///     exception, so `::` still works.
///   * A multi-character operator may not end in `/` — but a `/` NOT followed
///     by `/` or `*` is absorbed afterwards, so `+/` is an operator when what
///     comes next could not have started a comment.
///   * A bare `~`, `|` or `:` is not an operator; each is its own token.
fn Scanner::scan_operator(self : Scanner, from : Int) -> Int? {
  // `#` plus one of `' , : ; |` is a two-character operator of its own; `#` is
  // not an operator character, so nothing longer can be built from it.
  match self.at(from) {
    Some('#') =>
      match self.at(from + 1) {
        Some(c) if is_escopchar(c) =>
          return Some(self.absorb_slash(self.step(from + 1)))
        _ => return None
      }
    _ => ()
  }
  let mut end = from
  let mut all_colons = true
  while end < self.src.length() {
    let c = match self.at(end) {
      Some(c) => c
      None => break
    }
    if !is_opchar(c) {
      break
    }
    // Stop before a comment opener rather than trimming back to it later: the
    // run may not CONTAIN `//` or `/*` at all.
    if c == '/' {
      match self.at(end + 1) {
        Some('/') | Some('*') => break
        _ => ()
      }
    }
    if c != ':' {
      all_colons = false
    }
    end = self.step(end)
  }
  if end == from {
    return None
  }
  if all_colons {
    // `:` alone is the block operator; `::` and longer are operators.
    return if end - from == 1 { None } else { Some(end) }
  }
  // Trim a trailing `:` or `/`, repeatedly: `+::` is `+` then `::`.
  let mut e = end
  while e - from > 1 {
    match self.at(e - 1) {
      Some(':') | Some('/') => e = e - 1
      _ => break
    }
  }
  if e - from == 1 {
    let c = self.at(from).unwrap()
    if c == '~' || c == '|' || c == ':' {
      return None
    }
  }
  Some(self.absorb_slash(e))
}

///|
/// Take a following `/` into the operator when it cannot start a comment.
///
/// This is why `+/ 2` divides and `+// 2` is `+` and a comment.
fn Scanner::absorb_slash(self : Scanner, end : Int) -> Int {
  match self.at(end) {
    Some('/') =>
      match self.at(end + 1) {
        Some('/') | Some('*') => end
        _ => end + 1
      }
    _ => end
  }
}

///|
/// Whether a multi-character operator starts at `from`.
///
/// The lookahead behind the trailing-dot rule: `1.` is the number one when
/// nothing follows, and `1` then `.+` when `.+` is an operator.
fn Scanner::multi_char_operator_at(self : Scanner, from : Int) -> Bool {
  match self.scan_operator(from) {
    Some(e) => self.src.char_length(start_offset=from, end_offset=e) > 1
    None => false
  }
}