diff --git a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift index 4abf2b90..4d1290cd 100644 --- a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift +++ b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift @@ -369,6 +369,8 @@ fileprivate extension Compiler.ByteCodeGen { case .nonCapturingGroup(let kind): switch kind.ast { case .lookahead, .negativeLookahead, .lookbehind, .negativeLookbehind: + list.skipNode(&position) + position += 1 return false default: return _guaranteesForwardProgressImpl(list, position: &position) diff --git a/Tests/RegexTests/CompileTests.swift b/Tests/RegexTests/CompileTests.swift index 62812087..1d967c7e 100644 --- a/Tests/RegexTests/CompileTests.swift +++ b/Tests/RegexTests/CompileTests.swift @@ -531,6 +531,9 @@ extension RegexTests { expectProgram(for: #"(?:\w|(?i))+"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) expectProgram(for: #"(?:A*(?:b|c*))*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) expectProgram(for: #"(?:[^/]*(?:/|$))*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) + expectProgram(for: #"(?:(?=a)\d*)*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) + expectProgram(for: #"(?:(?!a)\d*)*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) + expectProgram(for: #"(?:(?!\d)[0-9]*)*"#, 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 97532ff3..34fff746 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -2828,7 +2828,12 @@ extension RegexTests { } } - func expectCompletion(regex: String, in target: String) { + func expectCompletion( + regex: String, + in target: String, + file: StaticString = #file, + line: UInt = #line + ) { let expectation = XCTestExpectation(description: "Run the given regex to completion") Task.init { let r = try! Regex(regex) @@ -2836,7 +2841,14 @@ extension RegexTests { expectation.fulfill() return val } - wait(for: [expectation], timeout: 3.0) + let result = XCTWaiter().wait(for: [expectation], timeout: 3.0) + if result != .completed { + XCTFail(""" + Matching didn't complete within 3 seconds: + regex: \(regex) + input: \(target) + """, file: file, line: line) + } } func testQuantificationForwardProgress() { @@ -2856,6 +2868,12 @@ extension RegexTests { expectCompletion(regex: #"(?:A*(?:b|c*))*"#, in: "ABC") expectCompletion(regex: #"^(?:(?:[^/]*(?:/|$))*)(?:[^/]*)$"#, in: "Sources/main.swift") + + // Repeated lookahead + expectCompletion(regex: #"(?:(?=\w)\d*)*"#, in: "a") + expectCompletion(regex: #"(?:(?=\d)\w*)*"#, in: "a") + expectCompletion(regex: #"(?:(?!b)\d*)*"#, in: "a") + expectCompletion(regex: #"(?:(?!\d)[0-9]*)*"#, in: "a") } func testQuantifyOptimization() throws {