///|
/// Decode one token. Unknown escapes are diagnosed, never silently discarded.
fn decode_value(
  source : String,
  line : Line,
  start : Int,
  end : Int,
  dialect : Dialect,
  diagnostics : Array[Diagnostic],
) -> (String, Span) {
  if start == end {
    return ("", line_span(line, start, end))
  }
  if source[start] == '"' || source[start] == '\'' {
    let quote = source[start]
    let out = StringBuilder()
    let mut i = start + 1
    let mut chunk = i
    let mut closed = false
    while i < end {
      if source[i] == quote {
        out.write_string(text(source, chunk, i))
        i += 1
        closed = true
        break
      } else if source[i] == '\\' && quote == '"' {
        out.write_string(text(source, chunk, i))
        if i + 1 >= end {
          break
        }
        match source[i + 1] {
          'n' => out.write_string("\n")
          'r' => out.write_string("\r")
          't' => out.write_string("\t")
          '\\' => out.write_string("\\")
          '"' => out.write_string("\"")
          _ => {
            diagnostics.push(
              diagnostic(
                "INI008",
                "unsupported quoted escape",
                line_span(line, i, i + 2),
              ),
            )
            out.write_string(text(source, i, i + 2))
          }
        }
        i += 2
        chunk = i
      } else {
        i += 1
      }
    }
    if !closed {
      diagnostics.push(
        diagnostic(
          "INI006",
          "unclosed quoted value",
          line_span(line, start, end),
        ),
      )
      return (text(source, start, end), line_span(line, start, end))
    }
    let (tail, _) = trim_bounds(source, i, end)
    if tail < end &&
      !(dialect.inline_comments && tail > i && is_comment(source[tail], dialect)) {
      diagnostics.push(
        diagnostic(
          "INI007",
          "unexpected text after quoted value",
          line_span(line, tail, end),
        ),
      )
    }
    (out.to_string(), line_span(line, start, i))
  } else {
    let mut stop = end
    if dialect.inline_comments {
      let mut i = start
      while i < end {
        if is_comment(source[i], dialect) &&
          (i == start || horizontal(source[i - 1])) {
          stop = i
          break
        }
        i += 1
      }
    }
    let (_, b) = trim_bounds(source, start, stop)
    (text(source, start, b), line_span(line, start, b))
  }
}