///|
fn validate_structure(body : Array[String]) -> Unit raise ForthError {
  let scopes : Array[String] = []
  let mut pc = 0
  while pc < body.length() {
    let word = body[pc]
    pc += 1
    if word == "[" {
      let (_, next) = bracket_chunk(body, pc)
      pc = next
      continue
    }
    if word == "]" {
      raise Invalid("unexpected closing bracket")
    }
    if word == "[']" {
      if pc >= body.length() {
        raise Invalid("missing execution token name")
      }
      pc += 1
      continue
    }
    match word {
      "if" | "begin" | "do" | "?do" => {
        if scopes.length() >= 64 {
          raise Invalid("control nesting limit")
        }
        scopes.push(if word == "?do" { "do" } else { word })
      }
      "else" => {
        if scopes.pop() != Some("if") {
          raise Invalid("ELSE without matching IF")
        }
        scopes.push("else")
      }
      "then" => {
        let open = scopes.pop()
        if open != Some("if") && open != Some("else") {
          raise Invalid("THEN without matching IF")
        }
      }
      "while" => {
        if scopes.pop() != Some("begin") {
          raise Invalid("WHILE without matching BEGIN")
        }
        scopes.push("while")
      }
      "repeat" =>
        if scopes.pop() != Some("while") {
          raise Invalid("REPEAT without matching WHILE")
        }
      "until" | "again" =>
        if scopes.pop() != Some("begin") {
          raise Invalid("UNTIL/AGAIN without matching BEGIN")
        }
      "loop" | "+loop" =>
        if scopes.pop() != Some("do") {
          raise Invalid("LOOP without matching DO")
        }
      "does>" =>
        if !scopes.is_empty() {
          raise Invalid("DOES> inside control structure")
        }
      _ => ()
    }
  }
  if !scopes.is_empty() {
    raise Invalid("unclosed control structure in definition")
  }
}