///|
/// The root of a parsed grammar. Code-generation passes fill in the constant
/// tables and `code`.
pub(all) struct Grammar {
  mut initializer : Initializer?
  mut rules : Array[Rule]
  /// Comments keyed by start offset, in source order; `None` unless the
  /// `extractComments` option was set.
  mut comments : Array[(Int, Comment)]?
  location : Location
  mut literals : Array[String]?
  mut classes : Array[@bytecode.ClassConst]?
  mut expectations : Array[@runtime.Expectation]?
  mut functions : Array[@bytecode.FunctionConst]?
  /// Generated source code (set by the code generation pass).
  mut code : String?
} derive(Debug)

///|
pub fn Grammar::new(
  initializer : Initializer?,
  rules : Array[Rule],
  comments : Array[(Int, Comment)]?,
  location : Location,
) -> Grammar {
  {
    initializer,
    rules,
    comments,
    location,
    literals: None,
    classes: None,
    expectations: None,
    functions: None,
    code: None,
  }
}

///|
pub fn Grammar::find_rule(self : Grammar, name : String) -> Rule? {
  for rule in self.rules {
    if rule.name == name {
      return Some(rule)
    }
  }
  None
}

///|
/// Index of the rule named `name`, or -1.
pub fn Grammar::index_of_rule(self : Grammar, name : String) -> Int {
  for i, rule in self.rules {
    if rule.name == name {
      return i
    }
  }
  -1
}

///|
/// Whether `node` always consumes input when it succeeds (`Grammar.js`).
pub fn Grammar::always_consumes_on_success(self : Grammar, node : Node) -> Bool {
  match node.kind {
    Choice(alternatives) =>
      alternatives.iter().all(a => self.always_consumes_on_success(a))
    Sequence(elements) =>
      elements.iter().any(e => self.always_consumes_on_success(e))
    RuleRef(name~) =>
      match self.find_rule(name) {
        Some(rule) => self.always_consumes_on_success(rule.expression)
        // PEG.js would throw on the missing rule; undefined-rule reporting
        // runs first, so this is unreachable with the standard passes.
        None => abort("Rule \"\{name}\" is not defined")
      }
    Literal(value~, ..) => value != ""
    Named(e, ..)
    | Action(e, ..)
    | Labeled(e, ..)
    | Text(e)
    | OneOrMore(e)
    | Group(e) => self.always_consumes_on_success(e)
    SimpleAnd(_)
    | SimpleNot(_)
    | Optional(_)
    | ZeroOrMore(_)
    | SemanticAnd(..)
    | SemanticNot(..) => false
    Class(..) | Any => true
  }
}