///|
/// What a statement's tokens parsed as. `Skipped` is a run the parser
/// reported and skipped (recovery per statement).
pub(all) enum StatementKind {
  Control
  Metadata
  Namespace
  Use
  Shape(@ast.ShapeKind)
  Apply
  Skipped
} derive(Eq, Debug)

///|
/// `Control`, `Shape(Aggregate(Structure))`, ... for tests and tools.
pub impl Show for StatementKind with fn output(self, logger) {
  match self {
    Control => logger.write_string("Control")
    Metadata => logger.write_string("Metadata")
    Namespace => logger.write_string("Namespace")
    Use => logger.write_string("Use")
    Shape(k) => logger.write_string("Shape(\{k})")
    Apply => logger.write_string("Apply")
    Skipped => logger.write_string("Skipped")
  }
}

///|
/// The tokens of one statement, with their trivia. `name` is the control
/// key, metadata key, namespace, used shape ID, shape name or apply
/// target; `None` for a skipped run. `span` runs from the first token's
/// start to the last token's end, trivia excluded; empty at 0 with no token.
pub(all) struct StatementCst {
  kind : StatementKind
  name : String?
  tokens : ArrayView[@scanner.Token]
  span : @span.Span
} derive(Eq, Debug)

///|
/// Group `tokens` as one statement; the span runs from the first token's start to the last token's end.
pub fn StatementCst::new(
  kind~ : StatementKind,
  name~ : String?,
  tokens~ : ArrayView[@scanner.Token],
) -> StatementCst {
  let span = if tokens.length() == 0 {
    @span.Offset::zero().to(@span.Offset::zero())
  } else {
    tokens[0].span.start.to(tokens[tokens.length() - 1].span.end())
  }
  { kind, name, tokens, span, }
}

///|
/// The text of the statement's tokens with their trivia.
pub fn StatementCst::to_source(self : StatementCst) -> String {
  let sb = StringBuilder()
  for t in self.tokens {
    sb.write_string(t.to_source())
  }
  sb.to_string()
}

///|
/// Every token of a file, grouped per statement. `statements` tile
/// `tokens` in order; `trivia` is the stream's trivia for a text with no
/// token.
pub(all) struct IdlCst {
  tokens : ArrayView[@scanner.Token]
  trivia : ArrayView[@scanner.Trivia]
  statements : ArrayView[StatementCst]
} derive(Eq, Debug)

///|
/// The CST of a scanned stream with its statements.
pub fn IdlCst::new(
  stream : @scanner.TokenStream,
  statements : Array[StatementCst],
) -> IdlCst {
  { tokens: stream.tokens, trivia: stream.trivia, statements: statements[:], }
}

///|
/// The scanned text, byte for byte.
pub fn IdlCst::to_source(self : IdlCst) -> String {
  let sb = StringBuilder()
  for t in self.trivia {
    sb.write_string(t.text())
  }
  for token in self.tokens {
    sb.write_string(token.to_source())
  }
  sb.to_string()
}

///|
/// The runs the parser skipped.
pub fn IdlCst::skipped(self : IdlCst) -> Array[StatementCst] {
  self.statements.iter().filter(s => s.kind == Skipped).collect()
}