diff --git a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift index 3394b319..03f4910a 100644 --- a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift +++ b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift @@ -391,8 +391,13 @@ fileprivate extension Compiler.ByteCodeGen { return ccc.guaranteesForwardProgress case .quantification(let amount, _, _): let (atLeast, _) = amount.ast.bounds - guard let atLeast, atLeast > 0 else { return false } - return _guaranteesForwardProgressImpl(list, position: &position) + if let atLeast, atLeast > 0 { + return _guaranteesForwardProgressImpl(list, position: &position) + } else { + list.skipNode(&position) + position += 1 + return false + } case .limitCaptureNesting, .ignoreCapturesInTypedOutput: return _guaranteesForwardProgressImpl(list, position: &position) default: return false diff --git a/Sources/_StringProcessing/Regex/DSLList.swift b/Sources/_StringProcessing/Regex/DSLList.swift index 8e53c87d..98e478de 100644 --- a/Sources/_StringProcessing/Regex/DSLList.swift +++ b/Sources/_StringProcessing/Regex/DSLList.swift @@ -134,37 +134,43 @@ extension DSLTree { } } -extension DSLList { - internal func skipNode(_ position: inout Int) { - guard position < nodes.count else { - return - } - switch nodes[position] { - case let .orderedChoice(children): - let n = children.count - for _ in 0.. { + internal func skipNode(_ position: inout Int) { + guard position < endIndex else { + return } - - case let .concatenation(children): - let n = children.count - for _ in 0.. Int? { switch nodes[position] { case .concatenation(let children): diff --git a/Tests/RegexTests/CompileTests.swift b/Tests/RegexTests/CompileTests.swift index d3129130..62812087 100644 --- a/Tests/RegexTests/CompileTests.swift +++ b/Tests/RegexTests/CompileTests.swift @@ -529,6 +529,8 @@ extension RegexTests { expectProgram(for: #"(?:\w|(?#comment))+"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) expectProgram(for: #"(?:\w|(?#comment)(?i-i:))+"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) expectProgram(for: #"(?:\w|(?i))+"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) + expectProgram(for: #"(?:A*(?:b|c*))*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) + expectProgram(for: #"(?:[^/]*(?:/|$))*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) // Bounded quantification, don't emit position checking expectProgram(for: #"(?:(?=a)){1,4}"#, doesNotContain: [.moveCurrentPosition, .condBranchSamePosition]) diff --git a/Tests/RegexTests/MatchTests.swift b/Tests/RegexTests/MatchTests.swift index 387a71d6..97532ff3 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -2853,6 +2853,9 @@ extension RegexTests { expectCompletion(regex: #"(a?)*"#, in: "aa") expectCompletion(regex: #"(a{,4})*"#, in: "aa") expectCompletion(regex: #"((|)+)*"#, in: "aa") + + expectCompletion(regex: #"(?:A*(?:b|c*))*"#, in: "ABC") + expectCompletion(regex: #"^(?:(?:[^/]*(?:/|$))*)(?:[^/]*)$"#, in: "Sources/main.swift") } func testQuantifyOptimization() throws {