Skip to content

'Regex<Captures>' -> 'Regex<Match>' #68

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

Merged
merged 1 commit into from
Dec 31, 2021
Merged
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
14 changes: 7 additions & 7 deletions Sources/Exercises/Participants/RegexParticipant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ struct RegexLiteralParticipant: Participant {
}

private func extractFromCaptures(
lower: Substring, upper: Substring?, prop: Substring
_ match: Tuple4<Substring, Substring, Substring?, Substring>
) -> GraphemeBreakEntry? {
guard let lowerScalar = Unicode.Scalar(hex: lower),
let upperScalar = upper.map(Unicode.Scalar.init(hex:)) ?? lowerScalar,
let property = Unicode.GraphemeBreakProperty(prop)
guard let lowerScalar = Unicode.Scalar(hex: match.1),
let upperScalar = match.2.map(Unicode.Scalar.init(hex:)) ?? lowerScalar,
let property = Unicode.GraphemeBreakProperty(match.3)
else {
return nil
}
Expand All @@ -48,8 +48,8 @@ private func extractFromCaptures(
private func graphemeBreakPropertyData<RP: RegexProtocol>(
forLine line: String,
using regex: RP
) -> GraphemeBreakEntry? where RP.Capture == (Substring, Substring?, Substring) {
line.match(regex).map(\.captures).flatMap(extractFromCaptures)
) -> GraphemeBreakEntry? where RP.Match == Tuple4<Substring, Substring, Substring?, Substring> {
line.match(regex).map(\.match).flatMap(extractFromCaptures)
}

private func graphemeBreakPropertyData(
Expand All @@ -75,5 +75,5 @@ private func graphemeBreakPropertyDataLiteral(
return graphemeBreakPropertyData(
forLine: line,
using: r(#"([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s+;\s+(\w+).*"#,
capturing: (Substring, Substring?, Substring).self))
matching: Tuple4<Substring, Substring, Substring?, Substring>.self))
}
129 changes: 106 additions & 23 deletions Sources/VariadicsGenerator/VariadicsGenerator.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// swift run VariadicsGenerator --max-arity 7 > Sources/RegexDSL/Concatenation.swift
// swift run VariadicsGenerator --max-arity 7 > Sources/_StringProcessing/RegexDSL/Concatenation.swift

import ArgumentParser

struct Permutation {
let arity: Int
// 1 -> no extra constraint
// 0 -> where T.Capture: NoCaptureProtocol
// 0 -> where T.Match: NoCaptureProtocol
let bits: Int64

func isCaptureless(at index: Int) -> Bool {
Expand Down Expand Up @@ -77,8 +77,6 @@ func outputForEach<C: Collection>(
if let lt = lineTerminator {
let indent = needsSep ? " " : " "
output("\(lt)\n\(indent)")
} else if needsSep {
output(" ")
}
}
}
Expand All @@ -87,12 +85,13 @@ typealias Counter = Int64
let patternProtocolName = "RegexProtocol"
let concatenationStructTypeBaseName = "Concatenate"
let capturingGroupTypeBaseName = "CapturingGroup"
let matchAssociatedTypeName = "Match"
let captureAssociatedTypeName = "Capture"
let patternBuilderTypeName = "RegexBuilder"
let patternProtocolRequirementName = "regex"
let PatternTypeBaseName = "Regex"
let emptyProtocolName = "EmptyProtocol"
let emptyStructName = "Empty"
let emptyProtocolName = "EmptyCaptureProtocol"
let baseMatchTypeName = "Substring"

@main
struct VariadicsGenerator: ParsableCommand {
Expand All @@ -112,8 +111,13 @@ struct VariadicsGenerator: ParsableCommand {

import _MatchingEngine


""")

for arity in 2...maxArity+1 {
emitTupleStruct(arity: arity)
}

for arity in minArity...maxArity {
for permutation in Permutations(arity: arity) {
emitConcatenation(permutation: permutation)
Expand All @@ -124,42 +128,121 @@ struct VariadicsGenerator: ParsableCommand {
output("// END AUTO-GENERATED CONTENT")
}

func emitTupleStruct(arity: Int) {
output("""
@frozen @dynamicMemberLookup
public struct Tuple\(arity)<
""")
outputForEach(0..<arity, separator: ", ") {
"_\($0)"
}
output("> {")
// `public typealias Tuple = (_0, ...)`
output("\n public typealias Tuple = (")
outputForEach(0..<arity, separator: ", ") { "_\($0)" }
output(")")
// `public var tuple: Tuple`
output("\n public var tuple: Tuple\n")
// `subscript(dynamicMember:)`
output("""
public subscript<T>(dynamicMember keyPath: WritableKeyPath<Tuple, T>) -> T {
get { tuple[keyPath: keyPath] }
_modify { yield &tuple[keyPath: keyPath] }
}
""")
output("\n}\n")
output("extension Tuple\(arity): \(emptyProtocolName) where ")
outputForEach(1..<arity, separator: ", ") {
"_\($0): \(emptyProtocolName)"
}
output(" {}\n")
output("extension Tuple\(arity): MatchProtocol {\n")
output(" public typealias Capture = ")
if arity == 2 {
output("_1")
} else {
output("Tuple\(arity-1)<")
outputForEach(1..<arity, separator: ", ") {
"_\($0)"
}
output(">")
}
output("\n public init(_ tuple: Tuple) { self.tuple = tuple }")
// `public init(_0: _0, ...) { ... }`
output("\n public init(")
outputForEach(0..<arity, separator: ", ") {
"_ _\($0): _\($0)"
}
output(") {\n")
output(" self.init((")
outputForEach(0..<arity, separator: ", ") { "_\($0)" }
output("))\n")
output(" }")
output("\n}\n")
// Equatable
output("extension Tuple\(arity): Equatable where ")
outputForEach(0..<arity, separator: ", ") {
"_\($0): Equatable"
}
output(" {\n")
output(" public static func == (lhs: Self, rhs: Self) -> Bool {\n")
output(" ")
outputForEach(0..<arity, separator: " && ") {
"lhs.tuple.\($0) == rhs.tuple.\($0)"
}
output("\n }\n")
output("}\n")
}

func emitConcatenation(permutation: Permutation) {
let arity = permutation.arity

func emitGenericParameters(withConstraints: Bool) {
outputForEach(0..<arity, separator: ", ") {
var base = "T\($0)"
if withConstraints {
base += ": \(patternProtocolName)"
}
return base
}
}

// Emit concatenation type declarations.
// public struct Concatenation{n}_{perm}<...>: RegexProtocol {
// public typealias Capture = ...
// public let regex: Regex
// public typealias Match = ...
// public let regex: Regex<Match>
// public init(...) { ... }
// }
let typeName = "\(concatenationStructTypeBaseName)\(arity)_\(permutation.identifier)"
let typeName =
"\(concatenationStructTypeBaseName)\(arity)_\(permutation.identifier)"
output("public struct \(typeName)<\n ")
outputForEach(0..<arity, separator: ",") { "T\($0): \(patternProtocolName)" }
emitGenericParameters(withConstraints: true)
output("\n>: \(patternProtocolName)")
if permutation.hasCaptureless {
output(" where ")
outputForEach(permutation.capturelessIndices, separator: ",") {
"T\($0).\(captureAssociatedTypeName): \(emptyProtocolName)"
outputForEach(permutation.capturelessIndices, separator: ", ") {
"T\($0).\(matchAssociatedTypeName).\(captureAssociatedTypeName): \(emptyProtocolName)"
}
}
output(" {\n")
let captureIndices = permutation.captureIndices
output(" public typealias \(captureAssociatedTypeName) = ")
output(" public typealias \(matchAssociatedTypeName) = ")
let captureElements = captureIndices
.map { "T\($0).\(captureAssociatedTypeName)" }
.map { "T\($0).\(matchAssociatedTypeName).\(captureAssociatedTypeName)" }
if captureElements.isEmpty {
output(emptyStructName)
output(baseMatchTypeName)
} else {
output("(\(captureElements.joined(separator: ", ")))")
let count = captureElements.count + 1
output("Tuple\(count)<\(baseMatchTypeName), \(captureElements.joined(separator: ", "))>")
}
output("\n")
output(" public let \(patternProtocolRequirementName): \(PatternTypeBaseName)<\(captureAssociatedTypeName)>\n")
output(" public let \(patternProtocolRequirementName): \(PatternTypeBaseName)<\(matchAssociatedTypeName)>\n")
output(" init(")
outputForEach(0..<arity, separator: ",") { "_ x\($0): T\($0)" }
outputForEach(0..<arity, separator: ", ") { "_ x\($0): T\($0)" }
output(") {\n")
output(" \(patternProtocolRequirementName) = .init(ast: concat(\n ")
outputForEach(
0..<arity, separator: ",", lineTerminator: ""
0..<arity, separator: ", ", lineTerminator: ""
) { i in
"x\(i).\(patternProtocolRequirementName).ast"
}
Expand All @@ -169,14 +252,14 @@ struct VariadicsGenerator: ParsableCommand {
// Emit concatenation builders.
output("extension \(patternBuilderTypeName) {\n")
output(" public static func buildBlock<")
outputForEach(0..<arity, separator: ",") { "T\($0)" }
emitGenericParameters(withConstraints: true)
output(">(\n ")
outputForEach(0..<arity, separator: ",") { "_ x\($0): T\($0)" }
outputForEach(0..<arity, separator: ", ") { "_ x\($0): T\($0)" }
output("\n ) -> \(typeName)<")
outputForEach(0..<arity, separator: ",") { "T\($0)" }
emitGenericParameters(withConstraints: false)
output("> {\n")
output(" \(typeName)(")
outputForEach(0..<arity, separator: ",") { "x\($0)" }
outputForEach(0..<arity, separator: ", ") { "x\($0)" }
output(")\n }\n}\n\n")
}
}
36 changes: 31 additions & 5 deletions Sources/_MatchingEngine/Regex/Parse/CaptureStructure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ extension AST {
.reduce(.empty, +)
.map(CaptureStructure.optional)
case .concatenation(let concatenation):
assert(concatenation.children.count > 1)
return concatenation.children.map(\.captureStructure).reduce(.empty, +)
case .group(let group):
let innerCaptures = group.child.captureStructure
Expand Down Expand Up @@ -107,6 +106,27 @@ extension CaptureStructure {
}
return false
}

public func type(withAtomType atomType: Any.Type) -> Any.Type {
switch self {
case .atom:
return atomType
case .array(let child):
return TypeConstruction.arrayType(of: child.type(withAtomType: atomType))
case .optional(let child):
return TypeConstruction.optionalType(of: child.type(withAtomType: atomType))
case .tuple(let children):
return TypeConstruction.tupleType(of: children.map {
$0.type(withAtomType: atomType)
})
}
}

public typealias DefaultAtomType = Substring

public var type: Any.Type {
type(withAtomType: DefaultAtomType.self)
}
}

// MARK: - Serialization
Expand Down Expand Up @@ -142,6 +162,7 @@ extension CaptureStructure {
/// 〚`name: T` (atom)〛 ==> .atom, `name`, '\0'
/// 〚`[T]`〛 ==> 〚`T`〛, .formArray
/// 〚`T?`〛 ==> 〚`T`〛, .formOptional
/// 〚`(T0, T1, ...)` (top level)〛 ==> 〚`T0`〛, 〚`T1`〛, ...
/// 〚`(T0, T1, ...)`〛 ==> .beginTuple, 〚`T0`〛, 〚`T1`〛, ..., .endTuple
/// ```
///
Expand All @@ -163,7 +184,7 @@ extension CaptureStructure {
offset += MemoryLayout<Code>.stride
}
/// Recursively encode the node to the buffer.
func encode(_ node: CaptureStructure) {
func encode(_ node: CaptureStructure, isTopLevel: Bool = false) {
switch node {
// 〚`T` (atom)〛 ==> .atom
case .atom(name: nil):
Expand All @@ -184,17 +205,22 @@ extension CaptureStructure {
case .optional(let child):
encode(child)
append(.formOptional)
// 〚`(T0, T1, ...)` (top level)〛 ==> 〚`T0`〛, 〚`T1`〛, ...
// 〚`(T0, T1, ...)`〛 ==> .beginTuple, 〚`T0`〛, 〚`T1`〛, ..., .endTuple
case .tuple(let children):
append(.beginTuple)
if !isTopLevel {
append(.beginTuple)
}
for child in children {
encode(child)
}
append(.endTuple)
if !isTopLevel {
append(.endTuple)
}
}
}
if !isEmpty {
encode(self)
encode(self, isTopLevel: true)
}
append(.end)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ private func swift_getTupleTypeMetadata3(
proposedWitnesses: UnsafeRawPointer?
) -> (value: Any.Type, state: Int)

enum TypeConstruction {

public enum TypeConstruction {
/// Returns a tuple metatype of the given element types.
static func tupleType<
public static func tupleType<
ElementTypes: BidirectionalCollection
>(
of elementTypes: __owned ElementTypes
Expand Down Expand Up @@ -104,7 +103,7 @@ enum TypeConstruction {
}

/// Creates a type-erased tuple with the given elements.
static func tuple<Elements: BidirectionalCollection>(
public static func tuple<Elements: BidirectionalCollection>(
of elements: __owned Elements
) -> Any where Elements.Element == Any {
// Open existential on the overall tuple type.
Expand Down Expand Up @@ -133,4 +132,18 @@ enum TypeConstruction {
let elementTypes = elements.map { type(of: $0) }
return _openExistential(tupleType(of: elementTypes), do: create)
}

public static func arrayType(of childType: Any.Type) -> Any.Type {
func helper<T>(_: T.Type) -> Any.Type {
[T].self
}
return _openExistential(childType, do: helper)
}

public static func optionalType(of childType: Any.Type) -> Any.Type {
func helper<T>(_: T.Type) -> Any.Type {
T?.self
}
return _openExistential(childType, do: helper)
}
}
Loading