11
11
12
12
@_implementationOnly import _RegexParser
13
13
14
- @available ( SwiftStdlib 5 . 7 , * )
15
- extension Regex where Output == AnyRegexOutput {
16
- /// Parses and compiles a regular expression, resulting in an existentially-typed capture list.
17
- ///
18
- /// - Parameter pattern: The regular expression.
19
- public init ( _ pattern: String ) throws {
20
- self . init ( ast: try parse ( pattern, . semantic, . traditional) )
21
- }
22
- }
23
-
24
- @available ( SwiftStdlib 5 . 7 , * )
25
- extension Regex {
26
- /// Parses and compiles a regular expression.
27
- ///
28
- /// - Parameter pattern: The regular expression.
29
- /// - Parameter as: The desired type for the output.
30
- public init (
31
- _ pattern: String ,
32
- as: Output . Type = Output . self
33
- ) throws {
34
- self . init ( ast: try parse ( pattern, . semantic, . traditional) )
35
- }
36
- }
37
-
38
- @available ( SwiftStdlib 5 . 7 , * )
39
- extension Regex . Match where Output == AnyRegexOutput {
40
- /// Accesses the whole match using the `.0` syntax.
41
- public subscript(
42
- dynamicMember keyPath: KeyPath < ( Substring , _doNotUse: ( ) ) , Substring >
43
- ) -> Substring {
44
- anyRegexOutput. input [ range]
45
- }
46
-
47
- public subscript( name: String ) -> AnyRegexOutput . Element ? {
48
- anyRegexOutput. first {
49
- $0. name == name
50
- }
51
- }
52
- }
53
-
54
14
/// A type-erased regex output.
55
15
@available ( SwiftStdlib 5 . 7 , * )
56
16
public struct AnyRegexOutput {
57
- let input : String
58
- let _elements : [ ElementRepresentation ]
59
-
60
- /// The underlying representation of the element of a type-erased regex
61
- /// output.
62
- internal struct ElementRepresentation {
63
- /// The depth of `Optioals`s wrapping the underlying value. For example,
64
- /// `Substring` has optional depth `0`, and `Int??` has optional depth `2`.
65
- let optionalDepth : Int
66
-
67
- /// The bounds of the output element.
68
- let bounds : Range < String . Index > ?
69
-
70
- /// The name of the capture.
71
- var name : String ? = nil
72
-
73
- /// The capture reference this element refers to.
74
- var referenceID : ReferenceID ? = nil
75
-
76
- /// If the output vaule is strongly typed, then this will be set.
77
- var value : Any ? = nil
78
- }
17
+ internal let input : String
18
+ internal let _elements : [ ElementRepresentation ]
79
19
}
80
20
81
21
@available ( SwiftStdlib 5 . 7 , * )
82
22
extension AnyRegexOutput {
83
- /// Creates a type-erased regex output from an existing output .
23
+ /// Creates a type-erased regex output from an existing match .
84
24
///
85
- /// Use this initializer to fit a regex with strongly typed captures into the
86
- /// use site of a dynamic regex, like one that was created from a string .
25
+ /// Use this initializer to fit a strongly- typed regex match into the
26
+ /// use site of a type-erased regex output .
87
27
public init < Output> ( _ match: Regex < Output > . Match ) {
88
28
self = match. anyRegexOutput
89
29
}
90
30
91
- /// Returns a typed output by converting the underlying value to the specified
92
- /// type.
31
+ /// Returns a strongly-typed output by converting type-erased values to the specified type.
93
32
///
94
33
/// - Parameter type: The expected output type.
95
34
/// - Returns: The output, if the underlying value can be converted to the
96
35
/// output type; otherwise `nil`.
97
- public func `as`< Output> ( _ type: Output . Type = Output . self) -> Output ? {
36
+ public func extractValues< Output> (
37
+ as type: Output . Type = Output . self
38
+ ) -> Output ? {
98
39
let elements = map {
99
40
$0. existentialOutputComponent ( from: input [ ... ] )
100
41
}
101
42
return TypeConstruction . tuple ( of: elements) as? Output
102
43
}
103
44
}
104
45
105
- @available ( SwiftStdlib 5 . 7 , * )
106
- extension AnyRegexOutput {
107
- internal init ( input: String , elements: [ ElementRepresentation ] ) {
108
- self . init (
109
- input: input,
110
- _elements: elements
111
- )
112
- }
113
- }
114
-
115
- @available ( SwiftStdlib 5 . 7 , * )
116
- extension AnyRegexOutput . ElementRepresentation {
117
- func value( forInput input: String ) -> Any {
118
- // Ok for now because `existentialMatchComponent`
119
- // wont slice the input if there's no range to slice with
120
- //
121
- // FIXME: This is ugly :-/
122
- let input = bounds. map { input [ $0] } ?? " "
123
-
124
- return constructExistentialOutputComponent (
125
- from: input,
126
- in: bounds,
127
- value: nil ,
128
- optionalCount: optionalDepth
129
- )
130
- }
131
- }
132
-
133
46
@available ( SwiftStdlib 5 . 7 , * )
134
47
extension AnyRegexOutput : RandomAccessCollection {
48
+ /// An individual type-erased output value.
135
49
public struct Element {
136
- fileprivate let representation : ElementRepresentation
137
- let input : String
138
-
139
- var optionalDepth : Int {
140
- representation. optionalDepth
141
- }
142
-
143
- var name : String ? {
144
- representation. name
145
- }
146
-
50
+ internal let representation : ElementRepresentation
51
+ internal let input : String
52
+
147
53
/// The range over which a value was captured. `nil` for no-capture.
148
54
public var range : Range < String . Index > ? {
149
55
representation. bounds
150
56
}
151
-
152
- var referenceID : ReferenceID ? {
153
- representation. referenceID
154
- }
155
-
57
+
156
58
/// The slice of the input over which a value was captured. `nil` for no-capture.
157
59
public var substring : Substring ? {
158
60
range. map { input [ $0] }
159
61
}
160
62
161
63
/// The captured value, `nil` for no-capture
162
64
public var value : Any ? {
65
+ // FIXME: Should this return the substring for default-typed
66
+ // values?
163
67
representation. value
164
68
}
69
+
70
+ /// The name of this capture, if it has one, otherwise `nil`.
71
+ public var name : String ? {
72
+ representation. name
73
+ }
74
+
75
+ // TODO: Consider making API, and figure out how
76
+ // DSL and this would work together...
77
+ /// Whether this capture is considered optional by the regex. I.e.,
78
+ /// whether it is inside an alternation or zero-or-n quantification.
79
+ var isOptional : Bool {
80
+ representation. optionalDepth != 0
81
+ }
165
82
}
166
83
167
84
public var startIndex : Int {
@@ -191,6 +108,7 @@ extension AnyRegexOutput: RandomAccessCollection {
191
108
192
109
@available ( SwiftStdlib 5 . 7 , * )
193
110
extension AnyRegexOutput {
111
+ /// Access a capture by name. Returns `nil` if no capture with that name was present in the Regex.
194
112
public subscript( name: String ) -> Element ? {
195
113
first {
196
114
$0. name == name
@@ -200,21 +118,52 @@ extension AnyRegexOutput {
200
118
201
119
@available ( SwiftStdlib 5 . 7 , * )
202
120
extension Regex . Match where Output == AnyRegexOutput {
203
- /// Creates a type-erased regex match from an existing match.
121
+ /// Accesses the whole match using the `.0` syntax.
122
+ public subscript(
123
+ dynamicMember keyPath: KeyPath < ( Substring , _doNotUse: ( ) ) , Substring >
124
+ ) -> Substring {
125
+ anyRegexOutput. input [ range]
126
+ }
127
+
128
+ /// Access a capture by name. Returns `nil` if there's no capture with that name.
129
+ public subscript( name: String ) -> AnyRegexOutput . Element ? {
130
+ anyRegexOutput. first {
131
+ $0. name == name
132
+ }
133
+ }
134
+ }
135
+
136
+ // MARK: - Run-time regex creation and queries
137
+
138
+ @available ( SwiftStdlib 5 . 7 , * )
139
+ extension Regex where Output == AnyRegexOutput {
140
+ /// Parses and compiles a regular expression, resulting in a type-erased capture list.
204
141
///
205
- /// Use this initializer to fit a regex match with strongly typed captures into the
206
- /// use site of a dynamic regex match, like one that was created from a string.
207
- public init < Output> ( _ match: Regex < Output > . Match ) {
208
- self . init (
209
- anyRegexOutput: match. anyRegexOutput,
210
- range: match. range,
211
- value: match. value
212
- )
142
+ /// - Parameter pattern: The regular expression.
143
+ public init ( _ pattern: String ) throws {
144
+ self . init ( ast: try parse ( pattern, . semantic, . traditional) )
213
145
}
214
146
}
215
147
216
148
@available ( SwiftStdlib 5 . 7 , * )
217
149
extension Regex {
150
+ /// Parses and compiles a regular expression.
151
+ ///
152
+ /// - Parameter pattern: The regular expression.
153
+ /// - Parameter as: The desired type for the output.
154
+ public init (
155
+ _ pattern: String ,
156
+ as: Output . Type = Output . self
157
+ ) throws {
158
+ self . init ( ast: try parse ( pattern, . semantic, . traditional) )
159
+ }
160
+
161
+ /// Produces a regex that matches `verbatim` exactly, as though every
162
+ /// metacharacter in it was escaped.
163
+ public init ( verbatim: String ) {
164
+ self . init ( node: . quotedLiteral( verbatim) )
165
+ }
166
+
218
167
/// Returns whether a named-capture with `name` exists
219
168
public func contains( captureNamed name: String ) -> Bool {
220
169
program. tree. root. _captureList. captures. contains ( where: {
@@ -223,30 +172,95 @@ extension Regex {
223
172
}
224
173
}
225
174
175
+ // MARK: - Converting to/from ARO
176
+
226
177
@available ( SwiftStdlib 5 . 7 , * )
227
178
extension Regex where Output == AnyRegexOutput {
228
179
/// Creates a type-erased regex from an existing regex.
229
180
///
230
- /// Use this initializer to fit a regex with strongly typed captures into the
231
- /// use site of a dynamic regex, i.e. one that was created from a string.
181
+ /// Use this initializer to fit a regex with strongly- typed captures into the
182
+ /// use site of a type-erased regex, i.e. one that was created from a string.
232
183
public init < Output> ( _ regex: Regex < Output > ) {
233
184
self . init ( node: regex. root)
234
185
}
186
+ }
235
187
236
- /// Returns a typed regex by converting the underlying types.
188
+ @available ( SwiftStdlib 5 . 7 , * )
189
+ extension Regex . Match where Output == AnyRegexOutput {
190
+ /// Creates a type-erased regex match from an existing match.
237
191
///
238
- /// - Parameter type: The expected output type.
239
- /// - Returns: A regex generic over the output type if the underlying types can be converted.
240
- /// Returns `nil` otherwise.
241
- public func `as`< Output> (
242
- _ type: Output . Type = Output . self
243
- ) -> Regex < Output > ? {
244
- let result = Regex < Output > ( node: root)
245
-
246
- guard result. _verifyType ( ) else {
192
+ /// Use this initializer to fit a regex match with strongly-typed captures into the
193
+ /// use site of a type-erased regex match.
194
+ public init < Output> ( _ match: Regex < Output > . Match ) {
195
+ self . init (
196
+ anyRegexOutput: match. anyRegexOutput,
197
+ range: match. range,
198
+ value: match. value
199
+ )
200
+ }
201
+ }
202
+
203
+ @available ( SwiftStdlib 5 . 7 , * )
204
+ extension Regex {
205
+ /// Creates a strongly-typed regex from a type-erased regex.
206
+ ///
207
+ /// Use this initializer to create a strongly-typed regex from
208
+ /// one that was created from a string. Returns `nil` if the types
209
+ /// don't match.
210
+ public init ? (
211
+ _ erased: Regex < AnyRegexOutput > ,
212
+ as: Output . Type = Output . self
213
+ ) {
214
+ self . init ( node: erased. root)
215
+ guard self . _verifyType ( ) else {
247
216
return nil
248
217
}
249
-
250
- return result
218
+ }
219
+ }
220
+
221
+ // MARK: - Internals
222
+
223
+ @available ( SwiftStdlib 5 . 7 , * )
224
+ extension AnyRegexOutput {
225
+ /// The underlying representation of the element of a type-erased regex
226
+ /// output.
227
+ internal struct ElementRepresentation {
228
+ /// The depth of `Optioals`s wrapping the underlying value. For example,
229
+ /// `Substring` has optional depth `0`, and `Int??` has optional depth `2`.
230
+ let optionalDepth : Int
231
+
232
+ /// The bounds of the output element.
233
+ let bounds : Range < String . Index > ?
234
+
235
+ /// The name of the capture.
236
+ var name : String ? = nil
237
+
238
+ /// The capture reference this element refers to.
239
+ var referenceID : ReferenceID ? = nil
240
+
241
+ /// If the output vaule is strongly typed, then this will be set.
242
+ var value : Any ? = nil
243
+ }
244
+
245
+ internal init ( input: String , elements: [ ElementRepresentation ] ) {
246
+ self . init ( input: input, _elements: elements)
247
+ }
248
+ }
249
+
250
+ @available ( SwiftStdlib 5 . 7 , * )
251
+ extension AnyRegexOutput . ElementRepresentation {
252
+ fileprivate func value( forInput input: String ) -> Any {
253
+ // Ok for now because `existentialMatchComponent`
254
+ // wont slice the input if there's no range to slice with
255
+ //
256
+ // FIXME: This is ugly :-/
257
+ let input = bounds. map { input [ $0] } ?? " "
258
+
259
+ return constructExistentialOutputComponent (
260
+ from: input,
261
+ in: bounds,
262
+ value: nil ,
263
+ optionalCount: optionalDepth
264
+ )
251
265
}
252
266
}
0 commit comments