func _consume()

in Sources/Prototypes/PEG/PEGInterpreter.swift [61:160]


func _consume<C: Collection>(
  _ c: C,
  from idx: C.Index,
  _ pattern: PEG<C.Element>.Pattern,
  _ environment: PEG<C.Element>.Environment,
  _ captures: inout Array<Range<C.Index>>
) -> C.Index? {
  typealias Pattern = PEG<C.Element>.Pattern

  var idx = idx

  func consume(_ p: Pattern, from idx: C.Index) -> C.Index? {
    _consume(c, from: idx, p, environment, &captures)
  }

  func consumeIf(_ p: (C.Element) -> Bool) -> C.Index? {
    guard idx < c.endIndex, p(c[idx]) else { return nil }
    return c.index(after: idx)
  }

  func consumePrefix<Prefix: Sequence>(
    _ p: Prefix
  ) -> C.Index? where Prefix.Element == C.Element {
    var idx = idx
    for e in p {
      guard idx < c.endIndex, e == c[idx] else { return nil }
      c.formIndex(after: &idx)
    }
    return idx
  }

  func consumeMany(_ p: Pattern, atLeast: Int) -> C.Index? {
    var counter = 0
    var idx = idx
    while let nextIdx = consume(p, from: idx) {
      counter += 1
      idx = nextIdx
    }
    guard counter >= atLeast else { return nil }
    return idx
  }

  func peek(_ p: Pattern) -> Bool {
    nil != consume(p, from: idx)
  }

  switch pattern {
  // Terminals
  case .success: return idx
  case .failure: return nil

  case .end: return idx == c.endIndex ? idx : nil

  // Consume a single unit of input
  case .any: return consumeIf { _ in true }
  case .element(let e): return consumeIf { e == $0 }
  case .charactetSet(let p): return consumeIf(p)

  // Consume many inputs
  case .literal(let lit): return consumePrefix(lit)

  case .repeat(let e, atLeast: let atLeast):
    return consumeMany(e, atLeast: atLeast)

  case .repeatRange(_, atLeast: _, atMost: _):
    fatalError()

  // Assertions (does not consume)
  case .and(let p):
    return peek(p) ? idx : nil
  case .not(let p):
    return peek(p) ? nil : idx

  // Combinations of patterns
  case .orderedChoice(let p1, let p2):
    return consume(p1, from: idx) ?? consume(p2, from: idx)

  case .concat(let ps):
    for p in ps {
      guard let resume = consume(p, from: idx) else {
        return nil
      }
      idx = resume
    }
    return idx

  case .difference(let p1, let p2):
    guard !peek(p2) else { return nil }
    return consume(p1, from: idx)

  case .variable(let name):
    return consume(environment[name]!, from: idx)

  case .capture(let p):
    // TODO: Are captured pre-order or post-order?
    guard let endIdx = consume(p, from: idx) else { return nil }
    captures.append(idx ..< endIdx)
    return endIdx
  }
}