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
}
}