Skip to content

Commit aa83aec

Browse files
committed
Implement .as for Regex
1 parent 838bdfe commit aa83aec

File tree

5 files changed

+119
-66
lines changed

5 files changed

+119
-66
lines changed

Sources/_RegexParser/Utility/TypeConstruction.swift

Lines changed: 42 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,7 @@
1717
// const Metadata * const *elements,
1818
// const char *labels,
1919
// const ValueWitnessTable *proposedWitnesses);
20-
//
21-
// SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
22-
// MetadataResponse
23-
// swift_getTupleTypeMetadata2(MetadataRequest request,
24-
// const Metadata *elt0, const Metadata *elt1,
25-
// const char *labels,
26-
// const ValueWitnessTable *proposedWitnesses);
27-
// SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
28-
// MetadataResponse
29-
// swift_getTupleTypeMetadata3(MetadataRequest request,
30-
// const Metadata *elt0, const Metadata *elt1,
31-
// const Metadata *elt2, const char *labels,
32-
// const ValueWitnessTable *proposedWitnesses);
20+
3321

3422
@_silgen_name("swift_getTupleTypeMetadata")
3523
private func swift_getTupleTypeMetadata(
@@ -40,31 +28,13 @@ private func swift_getTupleTypeMetadata(
4028
proposedWitnesses: UnsafeRawPointer?
4129
) -> (value: Any.Type, state: Int)
4230

43-
@_silgen_name("swift_getTupleTypeMetadata2")
44-
private func swift_getTupleTypeMetadata2(
45-
request: Int,
46-
element1: Any.Type,
47-
element2: Any.Type,
48-
labels: UnsafePointer<Int8>?,
49-
proposedWitnesses: UnsafeRawPointer?
50-
) -> (value: Any.Type, state: Int)
51-
52-
@_silgen_name("swift_getTupleTypeMetadata3")
53-
private func swift_getTupleTypeMetadata3(
54-
request: Int,
55-
element1: Any.Type,
56-
element2: Any.Type,
57-
element3: Any.Type,
58-
labels: UnsafePointer<Int8>?,
59-
proposedWitnesses: UnsafeRawPointer?
60-
) -> (value: Any.Type, state: Int)
61-
6231
public enum TypeConstruction {
6332
/// Returns a tuple metatype of the given element types.
6433
public static func tupleType<
6534
ElementTypes: BidirectionalCollection
6635
>(
67-
of elementTypes: __owned ElementTypes
36+
of elementTypes: __owned ElementTypes,
37+
labels: String? = nil
6838
) -> Any.Type where ElementTypes.Element == Any.Type {
6939
// From swift/ABI/Metadata.h:
7040
// template <typename int_type>
@@ -78,39 +48,50 @@ public enum TypeConstruction {
7848
let elementCountFlag = 0x0000FFFF
7949
assert(elementTypes.count != 1, "A one-element tuple is not a realistic Swift type")
8050
assert(elementTypes.count <= elementCountFlag, "Tuple size exceeded \(elementCountFlag)")
81-
switch elementTypes.count {
82-
case 2:
83-
return swift_getTupleTypeMetadata2(
84-
request: 0,
85-
element1: elementTypes[elementTypes.startIndex],
86-
element2: elementTypes[elementTypes.index(elementTypes.startIndex, offsetBy: 1)],
87-
labels: nil,
88-
proposedWitnesses: nil).value
89-
case 3:
90-
return swift_getTupleTypeMetadata3(
91-
request: 0,
92-
element1: elementTypes[elementTypes.startIndex],
93-
element2: elementTypes[elementTypes.index(elementTypes.startIndex, offsetBy: 1)],
94-
element3: elementTypes[elementTypes.index(elementTypes.startIndex, offsetBy: 2)],
95-
labels: nil,
96-
proposedWitnesses: nil).value
97-
default:
98-
let result = elementTypes.withContiguousStorageIfAvailable { elementTypesBuffer in
99-
swift_getTupleTypeMetadata(
51+
52+
var flags = elementTypes.count
53+
54+
// If we have labels to provide, then say the label pointer is not constant
55+
// because the lifetime of said pointer will only be vaild for the lifetime
56+
// of the 'swift_getTupleTypeMetadata' call. If we don't have labels, then
57+
// our label pointer will be empty and constant.
58+
if labels != nil {
59+
// Has non constant labels
60+
flags |= 0x10000
61+
}
62+
63+
let result = elementTypes.withContiguousStorageIfAvailable { elementTypesBuffer in
64+
if let labels = labels {
65+
return labels.withCString { labelsPtr in
66+
swift_getTupleTypeMetadata(
67+
request: 0,
68+
flags: flags,
69+
elements: elementTypesBuffer.baseAddress,
70+
labels: labelsPtr,
71+
proposedWitnesses: nil
72+
)
73+
}
74+
} else {
75+
return swift_getTupleTypeMetadata(
10076
request: 0,
101-
flags: elementTypesBuffer.count,
77+
flags: flags,
10278
elements: elementTypesBuffer.baseAddress,
10379
labels: nil,
104-
proposedWitnesses: nil).value
80+
proposedWitnesses: nil
81+
)
10582
}
106-
guard let result = result else {
107-
fatalError("""
108-
The collection of element types does not support an internal representation of
109-
contiguous storage
110-
""")
111-
}
112-
return result
11383
}
84+
85+
guard let result = result else {
86+
fatalError(
87+
"""
88+
The collection of element types does not support an internal representation of
89+
contiguous storage
90+
"""
91+
)
92+
}
93+
94+
return result.value
11495
}
11596

11697
/// Creates a type-erased tuple with the given elements.

Sources/_StringProcessing/Regex/AnyRegexOutput.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,8 @@ extension Regex where Output == AnyRegexOutput {
222222
///
223223
/// Use this initializer to fit a regex with strongly typed captures into the
224224
/// use site of a dynamic regex, i.e. one that was created from a string.
225-
public init<Output>(_ match: Regex<Output>) {
226-
fatalError("FIXME: Not implemented")
225+
public init<Output>(_ regex: Regex<Output>) {
226+
self.init(node: regex.root)
227227
}
228228

229229
/// Returns a typed regex by converting the underlying types.
@@ -232,6 +232,12 @@ extension Regex where Output == AnyRegexOutput {
232232
/// - Returns: A regex generic over the output type if the underlying types can be converted.
233233
/// Returns `nil` otherwise.
234234
public func `as`<Output>(_ type: Output.Type) -> Regex<Output>? {
235-
fatalError("FIXME: Not implemented")
235+
let result = Regex<Output>(node: root)
236+
237+
guard result._verifyType() else {
238+
return nil
239+
}
240+
241+
return result
236242
}
237243
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
@_implementationOnly import _RegexParser
13+
14+
@available(SwiftStdlib 5.7, *)
15+
extension Regex {
16+
internal func _verifyType() -> Bool {
17+
var tupleElements: [Any.Type] = [Substring.self]
18+
var labels = " "
19+
20+
for capture in program.tree.root._captureList.captures {
21+
var captureType: Any.Type = capture.type ?? Substring.self
22+
var i = capture.optionalDepth
23+
24+
while i != 0 {
25+
captureType = TypeConstruction.optionalType(of: captureType)
26+
i -= 1
27+
}
28+
29+
tupleElements.append(captureType)
30+
31+
if let name = capture.name {
32+
labels += name
33+
}
34+
35+
labels.unicodeScalars.append(" ")
36+
}
37+
38+
// If we have no captures, then our Regex must be Regex<Substring>.
39+
if tupleElements.count == 1 {
40+
return Output.self == Substring.self
41+
}
42+
43+
let createdType = TypeConstruction.tupleType(
44+
of: tupleElements,
45+
labels: labels
46+
)
47+
48+
return Output.self == createdType
49+
}
50+
}

Tests/RegexTests/CaptureTests.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,23 @@ extension RegexTests {
451451
// TODO: "((a|b)|c)*"
452452

453453
}
454-
454+
455+
func testTypeVerification() throws {
456+
let opaque1 = try Regex("abc")
457+
let concrete1 = try XCTUnwrap(opaque1.as(Substring.self))
458+
XCTAssertNil(opaque1.as((Substring, Substring).self))
459+
XCTAssertNil(opaque1.as(Int.self))
460+
461+
let opaque2 = try Regex("(abc)")
462+
let concrete2 = try XCTUnwrap(opaque2.as((Substring, Substring).self))
463+
XCTAssertNil(opaque2.as(Substring.self))
464+
XCTAssertNil(opaque2.as((Substring, Int).self))
465+
466+
let opaque3 = try Regex("(?<someLabel>abc)")
467+
let concrete3 = try XCTUnwrap(opaque3.as((Substring, someLabel: Substring).self))
468+
XCTAssertNil(opaque3.as((Substring, Substring).self))
469+
XCTAssertNil(opaque3.as(Substring.self))
470+
}
455471
}
456472

457473

Tests/RegexTests/MatchTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ extension Executor {
3535
in: start..<input.endIndex,
3636
.partialFromFront
3737
) {
38-
let caps = result.rawCaptures.slices(from: input)
38+
let caps = result.anyRegexOutput.slices(from: input)
3939
return (input[result.range], caps)
4040
} else if start == input.endIndex {
4141
throw MatchError("match not found for \(regex) in \(input)")

0 commit comments

Comments
 (0)