diff --git a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift index 4d1290cd0..0eca86a89 100644 --- a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift +++ b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift @@ -424,7 +424,10 @@ fileprivate extension Compiler.ByteCodeGen { } switch (low, high) { case (_, 0): - try skipNode(&list) + // `{0}` / `{0,0}` never runs the operand. Drop the body, but still + // allocate any nested captures so they remain unset (nil) rather than + // recording an empty match at the current position. + try skipNode(&list, preservingCaptures: false, allocatingCaptures: true) return case let (n, m?) where n > m: // TODO: Should error out earlier, maybe DSL and parser @@ -848,18 +851,25 @@ fileprivate extension Compiler.ByteCodeGen { extension Compiler.ByteCodeGen { mutating func skipNode( _ list: inout ArraySlice, - preservingCaptures: Bool = true + preservingCaptures: Bool = true, + allocatingCaptures: Bool = false ) throws { guard let node = list.popFirst() else { return } switch node { case let .orderedChoice(n): for _ in 0..