///|
/// An unresolved name of the query form. `prefix` is a query namespace
/// prefix; `None` means no namespace.
pub struct QName {
  prefix : String?
  local_name : String
} derive(Eq, Debug)

///|
pub extend QName with Eq::{equal, not_equal}

///|
pub extend QName with @debug.Debug::{to_repr}

///|
pub fn QName::new(local_name : String, prefix? : String) -> QName {
  { prefix, local_name }
}

///|
/// What a node pattern selects: an exact kind, a category or any node.
pub(all) enum Selector {
  Kind(QName)
  Category(String)
  Wildcard
} derive(Eq, Debug)

///|
pub extend Selector with Eq::{equal, not_equal}

///|
pub extend Selector with @debug.Debug::{to_repr}

///|
/// A structural pattern. `Node` has a selector, ordered parts and capture
/// names. `Alternation` tries its branches in order; its captures bind the
/// node that the selected branch matched.
pub(all) enum Pattern {
  Node(Selector, ReadOnlyArray[Part], ReadOnlyArray[String])
  Alternation(ReadOnlyArray[Pattern], ReadOnlyArray[String])
} derive(Eq, Debug)

///|
pub extend Pattern with Eq::{equal, not_equal}

///|
pub extend Pattern with @debug.Debug::{to_repr}

///|
/// A part of a node pattern. `Child` matches a direct child, `Field` a
/// direct child in a named field, and `Absent` requires that the field has no
/// child.
pub(all) enum Part {
  Child(Pattern)
  Field(QName, Pattern)
  Absent(QName)
} derive(Eq, Debug)

///|
pub extend Part with Eq::{equal, not_equal}

///|
pub extend Part with @debug.Debug::{to_repr}

///|
/// A native predicate. `name` is the predicate name without `#`, for example
/// `attr-eq?`. `operands` are the strings after the capture.
pub struct Predicate {
  name : String
  capture : String
  operands : ReadOnlyArray[String]
} derive(Eq, Debug)

///|
pub extend Predicate with Eq::{equal, not_equal}

///|
pub extend Predicate with @debug.Debug::{to_repr}

///|
/// One source pattern with its conjunctive predicates.
pub struct Rule {
  pattern : Pattern
  predicates : ReadOnlyArray[Predicate]
} derive(Eq, Debug)

///|
pub extend Rule with Eq::{equal, not_equal}

///|
pub extend Rule with @debug.Debug::{to_repr}

///|
/// The typed form of a query. `parse` gives the same form for query text,
/// with source spans for diagnostics; forms built in code have no spans.
pub struct QueryForm {
  rules : ReadOnlyArray[Rule]
  priv spans : ReadOnlyArray[QuerySpan]
}

///|
/// Forms are equal when their rules are equal; source spans do not count.
pub impl Eq for QueryForm with equal(a, b) {
  a.rules == b.rules
}

///|
pub extend QueryForm with Eq::{equal, not_equal}

///|
pub impl @debug.Debug for QueryForm with to_repr(self) {
  @debug.Repr::Repr(self.rules.iter().to_array())
}

///|
pub extend QueryForm with @debug.Debug::{to_repr}

///|
pub fn QueryForm::new(rules : ArrayView[Rule]) -> QueryForm {
  { rules: ReadOnlyArray::from_array(rules.to_owned()), spans: [] }
}

///|
pub fn Rule::new(
  pattern : Pattern,
  predicates? : ArrayView[Predicate] = [],
) -> Rule {
  { pattern, predicates: ReadOnlyArray::from_array(predicates.to_owned()) }
}

///|
/// A node pattern.
pub fn Pattern::node(
  selector : Selector,
  parts? : ArrayView[Part] = [],
  captures? : ArrayView[String] = [],
) -> Pattern {
  Node(
    selector,
    ReadOnlyArray::from_array(parts.to_owned()),
    ReadOnlyArray::from_array(captures.to_owned()),
  )
}

///|
pub fn Pattern::alternation(
  branches : ArrayView[Pattern],
  captures? : ArrayView[String] = [],
) -> Pattern {
  Alternation(
    ReadOnlyArray::from_array(branches.to_owned()),
    ReadOnlyArray::from_array(captures.to_owned()),
  )
}

///|
pub fn Predicate::new(
  name : String,
  capture : String,
  operands? : ArrayView[String] = [],
) -> Predicate {
  { name, capture, operands: ReadOnlyArray::from_array(operands.to_owned()) }
}

///|
pub fn Predicate::has_attr(capture : String, attribute : String) -> Predicate {
  Predicate::new("has-attr?", capture, operands=[attribute])
}

///|
pub fn Predicate::missing_attr(
  capture : String,
  attribute : String,
) -> Predicate {
  Predicate::new("missing-attr?", capture, operands=[attribute])
}

///|
pub fn Predicate::attr_eq(
  capture : String,
  attribute : String,
  value : String,
) -> Predicate {
  Predicate::new("attr-eq?", capture, operands=[attribute, value])
}

///|
pub fn Predicate::attr_not_eq(
  capture : String,
  attribute : String,
  value : String,
) -> Predicate {
  Predicate::new("attr-not-eq?", capture, operands=[attribute, value])
}

///|
pub fn Predicate::attr_in(
  capture : String,
  attribute : String,
  values : ArrayView[String],
) -> Predicate {
  let operands = [attribute]
  operands.append(values.to_owned())
  Predicate::new("attr-in?", capture, operands~)
}

///|
pub fn Predicate::text_eq(capture : String, value : String) -> Predicate {
  Predicate::new("text-eq?", capture, operands=[value])
}