in Sources/_StringProcessing/Legacy/HareVM.swift [72:217]
func execute(
input: String, in range: Range<String.Index>, mode: MatchMode
) -> MatchResult? {
let (start, end) = range.destructure
assert(code.last!.isAccept)
var bunny = Leveret(code.startIndex, start, input: input)
var stack = BunnyStack()
// TODO: Which bunny to return? Longest, left most, or what?
func yieldBunny() -> MatchResult? {
if mode == .wholeString, bunny.sp != end {
return nil
}
return MatchResult(
start ..< bunny.sp, bunny.core.singleCapture())
}
while true {
let inst = code[bunny.pc]
// Consuming operations require more input
if bunny.sp == end && inst.isConsuming {
// If there are no more alternatives to try, we failed
guard !stack.isEmpty else { return nil }
// Continue with the next alternative
bunny = stack.restore()
continue
}
switch code[bunny.pc] {
case .nop: bunny.hop()
case .accept:
// If we've matched all of our input, we're done
if bunny.sp == end {
return yieldBunny()
}
// If there are no more alternatives to try, we're done
guard !stack.isEmpty else {
return yieldBunny()
}
// If (TODO?) we want partial matching and we want left-most, we're done
if mode == .partialFromFront {
return yieldBunny()
}
// Continue with the next alternative
bunny = stack.restore()
case .any:
assert(bunny.sp < end)
bunny.nibble(on: input)
bunny.hop()
case .character(let c):
assert(bunny.sp < end)
guard input[bunny.sp] == c else {
// If there are no more alternatives to try, we failed
guard !stack.isEmpty else {
return nil
}
// Continue with the next alternative
bunny = stack.restore()
continue
}
bunny.nibble(on: input)
bunny.hop()
case .unicodeScalar(let u):
assert(bunny.sp < end)
guard input.unicodeScalars[bunny.sp] == u else {
// If there are no more alternatives to try, we failed
guard !stack.isEmpty else {
return nil
}
// Continue with the next alternative
bunny = stack.restore()
continue
}
bunny.nibbleScalar(on: input)
bunny.hop()
case .characterClass(let cc):
assert(bunny.sp < end)
guard let nextSp = cc.matches(in: input, at: bunny.sp) else {
// If there are no more alternatives to try, we failed
guard !stack.isEmpty else {
return nil
}
// Continue with the next alternative
bunny = stack.restore()
continue
}
bunny.nibble(to: nextSp)
bunny.hop()
case .split(let disfavoring):
var disfavoredBunny = bunny
disfavoredBunny.hop(to: code.lookup(disfavoring))
stack.save(disfavoredBunny)
bunny.hop()
case .goto(let label):
bunny.hop(to: code.lookup(label))
case .label(_):
bunny.hop()
case .beginCapture:
bunny.core.beginCapture(bunny.sp)
bunny.hop()
case let .endCapture(transform):
guard bunny.core.endCapture(bunny.sp, transform: transform) else {
return nil
}
bunny.hop()
case .beginGroup:
bunny.core.beginGroup()
bunny.hop()
case .endGroup:
bunny.core.endGroup()
bunny.hop()
case .captureSome:
bunny.core.captureSome()
bunny.hop()
case .captureNil(let childType):
bunny.core.captureNil(childType: childType)
bunny.hop()
case .captureArray(let childType):
bunny.core.captureArray(childType: childType)
bunny.hop()
}
}
}