Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Experiment] Trivialize save points #688

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 21 additions & 23 deletions Sources/_StringProcessing/Engine/Backtracking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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))
}
}

Expand Down
8 changes: 8 additions & 0 deletions Sources/_StringProcessing/Engine/MECapture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@


extension Processor {

struct SavedState {
var captureEnds: [_StoredCapture]
var intRegisters: [Int]
var posRegisters: [Input.Index]
}


struct _StoredCapture {
var range: Range<Position>? = nil

Expand Down
20 changes: 20 additions & 0 deletions Sources/_StringProcessing/Engine/MEQuantify.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -124,13 +136,21 @@ extension Processor {
}

if savePoint.rangeIsEmpty {
// TODO: Refactor to make logic smoother...
// TODO: Skip this unnecessary saved state traffic
rectifySaveState(savePoint.savedStateIndex)

signalFailure()
return false
}
// The last save point has saved the current position, so it's unneeded
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
}
Expand Down
54 changes: 39 additions & 15 deletions Sources/_StringProcessing/Engine/Processor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ struct Processor {

var callStack: [InstructionAddress] = []

var savedState: [SavedState] = []

var storedCaptures: Array<_StoredCapture>

var wordIndexCache: Set<String.Index>? = nil
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -319,41 +322,54 @@ 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)
// If we have a quantifier save point, move the next range position into pos
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()
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -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()
}
}
Expand Down