Skip to content

Commit 17adc2c

Browse files
authored
Store integer registers in SavePoint (swiftlang#181)
Without saving the int registers, quantifications can't accurately keep track of their loop count when backtracking, so some patterns were incorrectly matching. For example, /(a|a){3}a/ incorrectly matched against `"aaaxyz"`, because after failing to match the final `a` against the `"x"`, the engine backtracked to the second alternation without resetting the number of times the quantification had been satisfied.
1 parent ad4966c commit 17adc2c

File tree

3 files changed

+16
-5
lines changed

3 files changed

+16
-5
lines changed

Sources/_StringProcessing/Engine/Backtracking.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ extension Processor {
2929
// perhaps current start)
3030
var captureEnds: [_StoredCapture]
3131

32+
// The int registers store values that can be relevant to
33+
// backtracking, such as the number of trips in a quantification.
34+
var intRegisters: [Int]
35+
3236
var destructure: (
3337
pc: InstructionAddress,
3438
pos: Position?,
3539
stackEnd: CallStackAddress,
36-
captureEnds: [_StoredCapture]
40+
captureEnds: [_StoredCapture],
41+
intRegisters: [Int]
3742
) {
38-
(pc, pos, stackEnd, captureEnds)
43+
(pc, pos, stackEnd, captureEnds, intRegisters)
3944
}
4045
}
4146

@@ -47,7 +52,8 @@ extension Processor {
4752
pc: pc,
4853
pos: addressOnly ? nil : currentPosition,
4954
stackEnd: .init(callStack.count),
50-
captureEnds: storedCaptures)
55+
captureEnds: storedCaptures,
56+
intRegisters: registers.ints)
5157
}
5258
}
5359

Sources/_StringProcessing/Engine/Processor.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ extension Processor {
163163
}
164164

165165
mutating func signalFailure() {
166-
guard let (pc, pos, stackEnd, capEnds) =
166+
guard let (pc, pos, stackEnd, capEnds, intRegisters) =
167167
savePoints.popLast()?.destructure
168168
else {
169169
state = .fail
@@ -175,7 +175,8 @@ extension Processor {
175175
controller.pc = pc
176176
currentPosition = pos ?? currentPosition
177177
callStack.removeLast(callStack.count - stackEnd.rawValue)
178-
storedCaptures = capEnds
178+
storedCaptures = capEnds
179+
registers.ints = intRegisters
179180
}
180181

181182
mutating func tryAccept() {

Tests/RegexTests/MatchTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ extension RegexTests {
316316
#"xa{0}y"#, input: "123aaaxyz", match: "xy")
317317
firstMatchTest(
318318
#"xa{0,0}y"#, input: "123aaaxyz", match: "xy")
319+
firstMatchTest(
320+
#"(a|a){2}a"#, input: "123aaaxyz", match: "aaa")
321+
firstMatchTest(
322+
#"(a|a){3}a"#, input: "123aaaxyz", match: nil)
319323

320324
firstMatchTest("a.*", input: "dcba", match: "a")
321325

0 commit comments

Comments
 (0)