///|
/// Decide, for each `'`, whether it opens or closes.
///
/// `'` is its own opener and closer, so nesting is ambiguous on its face and
/// the decision needs context. The rule: a `'` outside quotes opens; inside
/// quotes it closes, UNLESS a bracket has been opened since the last `'`, in
/// which case it opens a nested quote. That is what makes `'a ('nested') b'`
/// read the way it looks, while two consecutive `'` with nothing between them
/// would otherwise be an opener and its own closer.
///
/// A separate pass rather than lexer state, so the scanner stays context-free
/// and this rule can be read and tested on its own.
pub fn rewrite_quotes(tokens : Array[Token]) -> Array[Token] {
  // Each entry is one level of quoting; the value counts brackets opened at
  // that level since the last `'`.
  let quote_depth : Array[Int] = []
  let out = []
  for t in tokens {
    match t.kind {
      SQuote =>
        if quote_depth.length() == 0 {
          quote_depth.push(0)
          out.push({ ..t, kind: Opener, })
        } else if quote_depth[quote_depth.length() - 1] > 0 {
          // A bracket is open at this level, so this `'` starts a nested quote
          // rather than closing the one we are in.
          quote_depth.push(0)
          out.push({ ..t, kind: Opener, })
        } else {
          let _ = quote_depth.pop()
          out.push({ ..t, kind: Closer, })
        }
      Opener => {
        bump(quote_depth, 1)
        out.push(t)
      }
      Closer => {
        bump(quote_depth, -1)
        out.push(t)
      }
      _ => out.push(t)
    }
  }
  out
}

///|
fn bump(stack : Array[Int], by : Int) -> Unit {
  if stack.length() > 0 {
    stack[stack.length() - 1] = stack[stack.length() - 1] + by
  }
}