///|
/// Lexical safety pass before the standard parser. Full grammar validation remains
/// the parser's job. Decode object keys so escaped aliases cannot bypass strict fields.
fn guard_json(text : String) -> Unit raise CodeError {
  let units = text.to_array()
  let objects : Array[Bool] = []
  let key_expected : Array[Bool] = []
  let keys : Array[Map[String, Bool]] = []
  let mut i = 0
  while i < units.length() {
    let c = units[i]
    if c == '"' {
      let start = i
      i += 1
      let mut closed = false
      while i < units.length() {
        if units[i] == '\\' {
          i += 2
        } else if units[i] == '"' {
          i += 1
          closed = true
          break
        } else {
          i += 1
        }
      }
      if !closed {
        raise InvalidFormat(0, "unterminated string")
      }
      if !objects.is_empty() &&
        objects[objects.length() - 1] &&
        key_expected[key_expected.length() - 1] {
        let token = String::from_array(units[start:i].to_owned())
        let value = @json.parse(token) catch {
          _ => raise InvalidFormat(0, "invalid object key")
        }
        let key = match value {
          String(key) => key
          _ => raise InvalidFormat(0, "expected key")
        }
        let names = keys[keys.length() - 1]
        if names.contains(key) {
          raise InvalidFormat(0, "duplicate object key")
        }
        names[key] = true
        key_expected[key_expected.length() - 1] = false
      }
      continue
    }
    if c == '{' || c == '[' {
      if objects.length() >= 32 {
        raise ResourceLimit("JSON nesting depth")
      }
      objects.push(c == '{')
      key_expected.push(c == '{')
      keys.push(Map([]))
    } else if c == '}' || c == ']' {
      if objects.is_empty() {
        raise InvalidFormat(0, "unbalanced container")
      }
      ignore(objects.pop())
      ignore(key_expected.pop())
      ignore(keys.pop())
    } else if c == ',' && !objects.is_empty() && objects[objects.length() - 1] {
      key_expected[key_expected.length() - 1] = true
    }
    i += 1
  }
}