diff --git a/Sources/_StringProcessing/Engine/Backtracking.swift b/Sources/_StringProcessing/Engine/Backtracking.swift index 48470ce91..ff099f35e 100644 --- a/Sources/_StringProcessing/Engine/Backtracking.swift +++ b/Sources/_StringProcessing/Engine/Backtracking.swift @@ -28,25 +28,15 @@ extension Processor { // save point stack var stackEnd: CallStackAddress - // FIXME: Save minimal info (e.g. stack position and - // perhaps current start) - var captureEnds: [_StoredCapture] - - // The int registers store values that can be relevant to - // backtracking, such as the number of trips in a quantification. - var intRegisters: [Int] - // Same with position registers - var posRegisters: [Input.Index] + var savedStateIndex: Array.Index var destructure: ( pc: InstructionAddress, pos: Position?, stackEnd: CallStackAddress, - captureEnds: [_StoredCapture], - intRegisters: [Int], - PositionRegister: [Input.Index] + savedStateIndex: Array.Index ) { - return (pc, pos, stackEnd, captureEnds, intRegisters, posRegisters) + return (pc, pos, stackEnd, savedStateIndex) } var rangeIsEmpty: Bool { rangeEnd == nil } @@ -82,36 +72,44 @@ extension Processor { } } - func makeSavePoint( + mutating func makeSavePoint( _ pc: InstructionAddress, addressOnly: Bool = false ) -> SavePoint { - SavePoint( + // TODO: Only push back if there's been a change in state + savedState.append(.init( + captureEnds: storedCaptures, + intRegisters: registers.ints, + posRegisters: registers.positions)) + + return SavePoint( pc: pc, pos: addressOnly ? nil : currentPosition, rangeStart: nil, rangeEnd: nil, isScalarSemantics: false, // FIXME: refactor away stackEnd: .init(callStack.count), - captureEnds: storedCaptures, - intRegisters: registers.ints, - posRegisters: registers.positions) + savedStateIndex: savedState.index(before: savedState.endIndex)) } - func startQuantifierSavePoint( + mutating func startQuantifierSavePoint( isScalarSemantics: Bool ) -> SavePoint { + // TODO: Only push back if there's been a change in state + savedState.append(.init( + captureEnds: storedCaptures, + intRegisters: registers.ints, + posRegisters: registers.positions)) + // Restores to the instruction AFTER the current quantifier instruction - SavePoint( + return SavePoint( pc: controller.pc + 1, pos: nil, rangeStart: nil, rangeEnd: nil, isScalarSemantics: isScalarSemantics, stackEnd: .init(callStack.count), - captureEnds: storedCaptures, - intRegisters: registers.ints, - posRegisters: registers.positions) + savedStateIndex: savedState.index(before: savedState.endIndex)) } } diff --git a/Sources/_StringProcessing/Engine/MECapture.swift b/Sources/_StringProcessing/Engine/MECapture.swift index 4bea21133..e9354e43e 100644 --- a/Sources/_StringProcessing/Engine/MECapture.swift +++ b/Sources/_StringProcessing/Engine/MECapture.swift @@ -31,6 +31,14 @@ extension Processor { + + struct SavedState { + var captureEnds: [_StoredCapture] + var intRegisters: [Int] + var posRegisters: [Input.Index] + } + + struct _StoredCapture { var range: Range? = nil diff --git a/Sources/_StringProcessing/Engine/MEQuantify.swift b/Sources/_StringProcessing/Engine/MEQuantify.swift index 1ff734ccd..b2daacec3 100644 --- a/Sources/_StringProcessing/Engine/MEQuantify.swift +++ b/Sources/_StringProcessing/Engine/MEQuantify.swift @@ -74,12 +74,20 @@ extension Processor { } if trips < payload.minTrips { + // TODO: Refactor to make logic smoother... + // TODO: Skip this unnecessary saved state traffic + rectifySaveState(savePoint.savedStateIndex) + signalFailure() return false } if !savePoint.rangeIsEmpty { savePoints.append(savePoint) + } else { + // TODO: Refactor to make logic smoother... + // TODO: Skip this unnecessary saved state traffic + rectifySaveState(savePoint.savedStateIndex) } return true } @@ -104,6 +112,10 @@ extension Processor { savePoint.shrinkRange(input) if !savePoint.rangeIsEmpty { savePoints.append(savePoint) + } else { + // TODO: Refactor to make logic smoother... + // TODO: Skip this unnecessary saved state traffic + rectifySaveState(savePoint.savedStateIndex) } return true } @@ -124,6 +136,10 @@ extension Processor { } if savePoint.rangeIsEmpty { + // TODO: Refactor to make logic smoother... + // TODO: Skip this unnecessary saved state traffic + rectifySaveState(savePoint.savedStateIndex) + signalFailure() return false } @@ -131,6 +147,10 @@ extension Processor { savePoint.shrinkRange(input) if !savePoint.rangeIsEmpty { savePoints.append(savePoint) + } else { + // TODO: Refactor to make logic smoother... + // TODO: Skip this unnecessary saved state traffic + rectifySaveState(savePoint.savedStateIndex) } return true } diff --git a/Sources/_StringProcessing/Engine/Processor.swift b/Sources/_StringProcessing/Engine/Processor.swift index 0350a37db..cf11ad04a 100644 --- a/Sources/_StringProcessing/Engine/Processor.swift +++ b/Sources/_StringProcessing/Engine/Processor.swift @@ -77,6 +77,8 @@ struct Processor { var callStack: [InstructionAddress] = [] + var savedState: [SavedState] = [] + var storedCaptures: Array<_StoredCapture> var wordIndexCache: Set? = nil @@ -135,6 +137,7 @@ extension Processor { self.registers.reset(sentinel: searchBounds.upperBound) self.savePoints.removeAll(keepingCapacity: true) + self.savedState.removeAll(keepingCapacity: true) self.callStack.removeAll(keepingCapacity: true) for idx in storedCaptures.indices { @@ -319,18 +322,23 @@ extension Processor { return true } + // TODO: better name / design, come up with something applicable for both + // signalFailure and clearThrough + mutating func rectifySaveState(_ idx: Array.Index) { + assert(idx == savedState.index(before: savedState.endIndex)) + _ = savedState.removeLast() + } + mutating func signalFailure() { guard !savePoints.isEmpty else { state = .fail return } - let (pc, pos, stackEnd, capEnds, intRegisters, posRegisters): ( + let (pc, pos, stackEnd, savedStateIndex): ( pc: InstructionAddress, pos: Position?, stackEnd: CallStackAddress, - captureEnds: [_StoredCapture], - intRegisters: [Int], - PositionRegister: [Input.Index] + savedStateIndex: Array.Index ) let idx = savePoints.index(before: savePoints.endIndex) @@ -338,22 +346,30 @@ extension Processor { if !savePoints[idx].rangeIsEmpty { savePoints[idx].takePositionFromRange(input) } + + // TODO: clean up the quantifier save point logic some + let shouldRemoveSavePoint = savePoints[idx].rangeIsEmpty + // If we have a normal save point or an empty quantifier save point, remove it - if savePoints[idx].rangeIsEmpty { - (pc, pos, stackEnd, capEnds, intRegisters, posRegisters) = savePoints.removeLast().destructure + if shouldRemoveSavePoint { + (pc, pos, stackEnd, savedStateIndex) = savePoints.removeLast().destructure } else { - (pc, pos, stackEnd, capEnds, intRegisters, posRegisters) = savePoints[idx].destructure + (pc, pos, stackEnd, savedStateIndex) = savePoints[idx].destructure } assert(stackEnd.rawValue <= callStack.count) - assert(capEnds.count == storedCaptures.count) + assert(savedState[savedStateIndex].captureEnds.count == storedCaptures.count) controller.pc = pc currentPosition = pos ?? currentPosition callStack.removeLast(callStack.count - stackEnd.rawValue) - storedCaptures = capEnds - registers.ints = intRegisters - registers.positions = posRegisters + storedCaptures = savedState[savedStateIndex].captureEnds + registers.ints = savedState[savedStateIndex].intRegisters + registers.positions = savedState[savedStateIndex].posRegisters + + if shouldRemoveSavePoint { + rectifySaveState(savedStateIndex) + } metrics.addBacktrack() } @@ -381,6 +397,7 @@ extension Processor { mutating func clearThrough(_ address: InstructionAddress) { while let sp = savePoints.popLast() { + rectifySaveState(sp.savedStateIndex) if sp.pc == address { controller.step() return @@ -450,7 +467,8 @@ extension Processor { controller.pc = nextPC case .clear: - if let _ = savePoints.popLast() { + if let sp = savePoints.popLast() { + rectifySaveState(sp.savedStateIndex) controller.step() } else { // TODO: What should we do here? @@ -630,9 +648,15 @@ extension Processor { let (val, cap) = payload.pairedValueCapture let value = registers[val] let capNum = Int(asserting: cap.rawValue) - let sp = makeSavePoint(self.currentPC) - storedCaptures[capNum].registerValue( - value, overwriteInitial: sp) + + // FIXME: Why is this overwriteInitial in here? It's not used inside + // FIXME: registerValue at all + + // let sp = makeSavePoint(self.currentPC) + // storedCaptures[capNum].registerValue( + // value, overwriteInitial: sp) + + storedCaptures[capNum].registerValue(value) controller.step() } }