Skip to content

Commit 76b6fb6

Browse files
committed
DSL quantifier types -> quantifier functions
Change quantifiers in the DSL from structure types to top-level functions. This allows for a lot more flexibility with overloading a quantifier based on the input `Match` type. The immediate benefit of this is getting rid of void and nested void types (see example below). A more important benefit (IMO) is being able to get rid of nominal tuples and switch back to Swift tuples. ----- Before: ```swift let r0 = OneOrMore(.digit) // => `.Match == Tuple2<Substring, [()]>` let r1 = Optionally(.digit) // => `.Match == Tuple2<Substring, ()?>` let r2 = OneOrMore(Repeat(Optionally(.digit))) // => `.Match == Tuple2<Substring, [[()?]]>` "123".match(r2) // => `RegexMatch<Tuple2<Substring, [[()?]]>>?` ``` After: ```swift let r0 = oneOrMore(.digit) // => `.Match == Substring` let r1 = optionally(.digit) // => `.Match == Substring` let r2 = OneOrMore(Repeat(Optionally(.digit))) // => `.Match == Substring` "123".match(r2) // => `RegexMatch<Substring>` ```
1 parent 97f4edc commit 76b6fb6

File tree

7 files changed

+1197
-148
lines changed

7 files changed

+1197
-148
lines changed

Sources/Exercises/Participants/RegexParticipant.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,16 @@ private func graphemeBreakPropertyData(
8080
forLine line: String
8181
) -> GraphemeBreakEntry? {
8282
line.match {
83-
OneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
84-
Optionally {
83+
oneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
84+
optionally {
8585
".."
86-
OneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
86+
oneOrMore(.hexDigit).tryCapture(Unicode.Scalar.init(hex:))
8787
}
88-
OneOrMore(.whitespace)
88+
oneOrMore(.whitespace)
8989
";"
90-
OneOrMore(.whitespace)
91-
OneOrMore(.word).tryCapture(Unicode.GraphemeBreakProperty.init)
92-
Repeat(.any)
90+
oneOrMore(.whitespace)
91+
oneOrMore(.word).tryCapture(Unicode.GraphemeBreakProperty.init)
92+
many(.any)
9393
}.map {
9494
let (_, lower, upper, property) = $0.match.tuple
9595
return GraphemeBreakEntry(lower...(upper ?? lower), property)

Sources/VariadicsGenerator/VariadicsGenerator.swift

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct StandardErrorStream: TextOutputStream {
8989
var standardError = StandardErrorStream()
9090

9191
typealias Counter = Int64
92-
let patternProtocolName = "RegexProtocol"
92+
let regexProtocolName = "RegexProtocol"
9393
let concatenationStructTypeBaseName = "Concatenate"
9494
let capturingGroupTypeBaseName = "CapturingGroup"
9595
let matchAssociatedTypeName = "Match"
@@ -132,9 +132,10 @@ struct VariadicsGenerator: ParsableCommand {
132132
emitTupleStruct(arity: arity)
133133
}
134134

135+
print("Generating concatenation overloads...", to: &standardError)
135136
for (leftArity, rightArity) in Permutations(totalArity: maxArity) {
136137
print(
137-
"Left arity: \(leftArity) Right arity: \(rightArity)",
138+
" Left arity: \(leftArity) Right arity: \(rightArity)",
138139
to: &standardError)
139140
emitConcatenation(leftArity: leftArity, rightArity: rightArity)
140141
}
@@ -144,7 +145,20 @@ struct VariadicsGenerator: ParsableCommand {
144145
}
145146

146147
output("\n\n")
147-
output("// END AUTO-GENERATED CONTENT")
148+
149+
print("Generating quantifiers...", to: &standardError)
150+
for arity in 0..<maxArity {
151+
print(" Arity \(arity): ", terminator: "", to: &standardError)
152+
for kind in QuantifierKind.allCases {
153+
print("\(kind.rawValue) ", terminator: "", to: &standardError)
154+
emitQuantifier(kind: kind, arity: arity)
155+
}
156+
print(to: &standardError)
157+
}
158+
159+
output("\n\n")
160+
161+
output("// END AUTO-GENERATED CONTENT\n")
148162

149163
print("Done!", to: &standardError)
150164
}
@@ -231,7 +245,7 @@ struct VariadicsGenerator: ParsableCommand {
231245
}
232246
output(", ")
233247
if withConstraints {
234-
output("R0: \(patternProtocolName), R1: \(patternProtocolName)")
248+
output("R0: \(regexProtocolName), R1: \(regexProtocolName)")
235249
} else {
236250
output("R0, R1")
237251
}
@@ -268,7 +282,7 @@ struct VariadicsGenerator: ParsableCommand {
268282
let typeName = "\(concatenationStructTypeBaseName)_\(leftArity)_\(rightArity)"
269283
output("public struct \(typeName)<\n ")
270284
emitGenericParameters(withConstraints: true)
271-
output("\n>: \(patternProtocolName)")
285+
output("\n>: \(regexProtocolName)")
272286
output(" where ")
273287
output("R0.Match == ")
274288
if leftArity == 0 {
@@ -343,7 +357,7 @@ struct VariadicsGenerator: ParsableCommand {
343357
", C\($0)"
344358
}
345359
output("""
346-
, R0: \(patternProtocolName), R1: \(patternProtocolName)>(
360+
, R0: \(regexProtocolName), R1: \(regexProtocolName)>(
347361
combining next: R1, into combined: R0
348362
) -> Regex<
349363
""")
@@ -374,4 +388,106 @@ struct VariadicsGenerator: ParsableCommand {
374388
375389
""")
376390
}
391+
392+
enum QuantifierKind: String, CaseIterable {
393+
case zeroOrOne = "optionally"
394+
case zeroOrMore = "many"
395+
case oneOrMore = "oneOrMore"
396+
397+
var typeName: String {
398+
switch self {
399+
case .zeroOrOne: return "_ZeroOrOne"
400+
case .zeroOrMore: return "_ZeroOrMore"
401+
case .oneOrMore: return "_OneOrMore"
402+
}
403+
}
404+
405+
var operatorName: String {
406+
switch self {
407+
case .zeroOrOne: return ".?"
408+
case .zeroOrMore: return ".+"
409+
case .oneOrMore: return ".*"
410+
}
411+
}
412+
413+
var astQuantifierAmount: String {
414+
switch self {
415+
case .zeroOrOne: return "zeroOrOne"
416+
case .zeroOrMore: return "zeroOrMore"
417+
case .oneOrMore: return "oneOrMore"
418+
}
419+
}
420+
}
421+
422+
func emitQuantifier(kind: QuantifierKind, arity: Int) {
423+
assert(arity >= 0)
424+
func genericParameters(withConstraints: Bool) -> String {
425+
var result = ""
426+
if arity > 0 {
427+
result += "W"
428+
result += (0..<arity).map { ", C\($0)" }.joined()
429+
result += ", "
430+
}
431+
result += "Component"
432+
if withConstraints {
433+
result += ": \(regexProtocolName)"
434+
}
435+
return result
436+
}
437+
let captures = (0..<arity).map { "C\($0)" }.joined(separator: ", ")
438+
let capturesTupled = arity == 1 ? captures : "Tuple\(arity)<\(captures)>"
439+
let componentConstraint: String = arity == 0 ? "" :
440+
"where Component.Match == Tuple\(arity+1)<W, \(captures)>"
441+
let quantifiedCaptures: String = {
442+
switch kind {
443+
case .zeroOrOne:
444+
return "\(capturesTupled)?"
445+
case .zeroOrMore, .oneOrMore:
446+
return "[\(capturesTupled)]"
447+
}
448+
}()
449+
let matchType = arity == 0 ? baseMatchTypeName : "Tuple2<\(baseMatchTypeName), \(quantifiedCaptures)>"
450+
output("""
451+
public struct \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: true))>: \(regexProtocolName) \(componentConstraint) {
452+
public typealias \(matchAssociatedTypeName) = \(matchType)
453+
public let regex: Regex<\(matchAssociatedTypeName)>
454+
public init(component: Component) {
455+
self.regex = .init(ast: \(kind.astQuantifierAmount)(.eager, component.regex.ast))
456+
}
457+
}
458+
459+
\(arity == 0 ? "@_disfavoredOverload" : "")
460+
public func \(kind.rawValue)<\(genericParameters(withConstraints: true))>(
461+
_ component: Component
462+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
463+
.init(component: component)
464+
}
465+
466+
\(arity == 0 ? "@_disfavoredOverload" : "")
467+
public func \(kind.rawValue)<\(genericParameters(withConstraints: true))>(
468+
@RegexBuilder _ component: () -> Component
469+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
470+
\(kind.rawValue)(component())
471+
}
472+
473+
\(arity == 0 ? "@_disfavoredOverload" : "")
474+
public postfix func \(kind.operatorName)<\(genericParameters(withConstraints: true))>(
475+
_ component: Component
476+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
477+
\(kind.rawValue)(component)
478+
}
479+
480+
\(kind == .zeroOrOne ?
481+
"""
482+
extension RegexBuilder {
483+
public static func buildLimitedAvailability<\(genericParameters(withConstraints: true))>(
484+
_ component: Component
485+
) -> \(kind.typeName)_\(arity)<\(genericParameters(withConstraints: false))> {
486+
\(kind.rawValue)(component)
487+
}
488+
}
489+
""" : "")
490+
491+
""")
492+
}
377493
}

Sources/_StringProcessing/RegexDSL/Builder.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,4 @@ public enum RegexBuilder {
3333
public static func buildEither<R: RegexProtocol>(second component: R) -> R {
3434
component
3535
}
36-
37-
public static func buildLimitedAvailability<R: RegexProtocol>(
38-
_ component: R
39-
) -> Optionally<R> {
40-
.init(component)
41-
}
4236
}

0 commit comments

Comments
 (0)