Skip to content

Commit 6bed883

Browse files
authored
Quick bug fix / workaround for whole-match values (#191)
1 parent 2eb7e4e commit 6bed883

File tree

5 files changed

+51
-48
lines changed

5 files changed

+51
-48
lines changed

Sources/_StringProcessing/Capture.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,3 @@ extension Sequence where Element == StructuredCapture {
9696
self.map { $0.slice(from: input) }
9797
}
9898
}
99-
100-

Sources/_StringProcessing/Engine/Consume.swift

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,17 @@ extension Engine {
2424
}
2525
}
2626

27-
extension Engine where Input == String {
28-
func consume(
29-
_ input: Input,
30-
in range: Range<Input.Index>,
31-
matchMode: MatchMode
32-
) -> (Input.Index, CaptureList)? {
33-
if enableTracing {
34-
print("Consume: \(input)")
35-
}
36-
37-
var cpu = makeProcessor(input: input, bounds: range, matchMode: matchMode)
38-
let result: Input.Index? = {
39-
while true {
40-
switch cpu.state {
41-
case .accept:
42-
return cpu.currentPosition
43-
case .fail:
44-
return nil
45-
case .inProgress: cpu.cycle()
46-
}
47-
}
48-
}()
49-
50-
if enableTracing {
51-
if let idx = result {
52-
print("Result: \(input[..<idx]) | \(input[idx...])")
53-
} else {
54-
print("Result: nil")
27+
extension Processor where Input == String {
28+
mutating func consume() -> Input.Index? {
29+
while true {
30+
switch self.state {
31+
case .accept:
32+
return self.currentPosition
33+
case .fail:
34+
return nil
35+
case .inProgress: self.cycle()
5536
}
5637
}
57-
guard let result = result else { return nil }
58-
59-
let capList = cpu.storedCaptures
60-
return (result, CaptureList(
61-
values: capList, referencedCaptureOffsets: program.referencedCaptureOffsets))
6238
}
6339
}
6440

Sources/_StringProcessing/Executor.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,41 @@ struct Executor {
2424
in inputRange: Range<String.Index>,
2525
_ mode: MatchMode
2626
) throws -> RegexMatch<Match>? {
27-
guard let (endIdx, capList) = engine.consume(
28-
input, in: inputRange, matchMode: mode
29-
) else {
27+
var cpu = engine.makeProcessor(
28+
input: input, bounds: inputRange, matchMode: mode)
29+
30+
guard let endIdx = cpu.consume() else {
3031
return nil
3132
}
33+
34+
let capList = CaptureList(
35+
values: cpu.storedCaptures,
36+
referencedCaptureOffsets: engine.program.referencedCaptureOffsets)
37+
3238
let capStruct = engine.program.captureStructure
3339
let range = inputRange.lowerBound..<endIdx
3440
let caps = try capStruct.structuralize(
3541
capList, input)
3642

43+
// FIXME: This is a workaround for not tracking (or
44+
// specially compiling) whole-match values.
45+
let value: Any?
46+
if Match.self != Substring.self,
47+
Match.self != (Substring, DynamicCaptures).self,
48+
caps.isEmpty
49+
{
50+
value = cpu.registers.values.first
51+
assert(value != nil, "hmm, what would this mean?")
52+
} else {
53+
value = nil
54+
}
55+
3756
return RegexMatch(
3857
input: input,
3958
range: range,
4059
rawCaptures: caps,
41-
referencedCaptureOffsets: capList.referencedCaptureOffsets)
60+
referencedCaptureOffsets: capList.referencedCaptureOffsets,
61+
value: value)
4262
}
4363

4464
func dynamicMatch(

Sources/_StringProcessing/RegexDSL/Match.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public struct RegexMatch<Match> {
1616
let rawCaptures: [StructuredCapture]
1717
let referencedCaptureOffsets: [ReferenceID: Int]
1818

19+
let value: Any?
20+
1921
public var match: Match {
2022
if Match.self == (Substring, DynamicCaptures).self {
2123
// FIXME(rdar://89449323): Compiler assertion
@@ -25,7 +27,15 @@ public struct RegexMatch<Match> {
2527
} else if Match.self == Substring.self {
2628
// FIXME: Plumb whole match (`.0`) through the matching engine.
2729
return input[range] as! Match
30+
} else if rawCaptures.isEmpty, value != nil {
31+
// FIXME: This is a workaround for whole-match values not
32+
// being modeled as part of captures. We might want to
33+
// switch to a model where results are alongside captures
34+
return value! as! Match
2835
} else {
36+
guard value == nil else {
37+
fatalError("FIXME: what would this mean?")
38+
}
2939
let typeErasedMatch = rawCaptures.existentialMatch(from: input[range])
3040
return typeErasedMatch as! Match
3141
}

Tests/RegexTests/CustomTests.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,14 @@ extension RegexTests {
8383
("55z", .match, nil),
8484
("55z", .firstMatch, "55"))
8585

86-
// FIXME: Requires we return a value instead of a range
87-
// customTest(
88-
// Regex {
89-
// Numbler()
90-
// },
91-
// ("ab123c", .firstMatch, 1),
92-
// ("abc", .firstMatch, nil),
93-
// ("55z", .match, nil),
94-
// ("55z", .firstMatch, 5))
86+
customTest(
87+
Regex {
88+
Numbler()
89+
},
90+
("ab123c", .firstMatch, 1),
91+
("abc", .firstMatch, nil),
92+
("55z", .match, nil),
93+
("55z", .firstMatch, 5))
9594

9695
// TODO: Convert below tests to better infra. Right now
9796
// it's hard because `Match` is constrained to be

0 commit comments

Comments
 (0)