Skip to content

Commit 8c11752

Browse files
committed
Heterogeneous alternation
----- Example: ```swift oneOf { "a".capture() "b".capture() "c" } => `.Match = (Substring, Substring?, Substring?)` ```
1 parent 12c7e6e commit 8c11752

File tree

5 files changed

+1351
-24
lines changed

5 files changed

+1351
-24
lines changed

Sources/VariadicsGenerator/VariadicsGenerator.swift

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ struct VariadicsGenerator: ParsableCommand {
160160
print(to: &standardError)
161161
}
162162

163+
print("Generating alternation overloads...", to: &standardError)
164+
for (leftArity, rightArity) in Permutations(totalArity: maxArity) {
165+
print(
166+
" Left arity: \(leftArity) Right arity: \(rightArity)",
167+
to: &standardError)
168+
emitAlternation(leftArity: leftArity, rightArity: rightArity)
169+
}
170+
171+
print("Generating 'AlternationBuilder.buildBlock(_:)' overloads...", to: &standardError)
172+
for arity in 1..<maxArity {
173+
print(" Capture arity: \(arity)", to: &standardError)
174+
emitUnaryAlternationBuildBlock(arity: arity)
175+
}
176+
163177
output("\n\n")
164178

165179
output("// END AUTO-GENERATED CONTENT\n")
@@ -444,4 +458,90 @@ struct VariadicsGenerator: ParsableCommand {
444458
445459
""")
446460
}
461+
462+
func emitAlternation(leftArity: Int, rightArity: Int) {
463+
let typeName = "_Alternation_\(leftArity)_\(rightArity)"
464+
let leftGenParams: String = {
465+
if leftArity == 0 {
466+
return "R0"
467+
}
468+
return "R0, W0, " + (0..<leftArity).map { "C\($0)" }.joined(separator: ", ")
469+
}()
470+
let rightGenParams: String = {
471+
if rightArity == 0 {
472+
return "R1"
473+
}
474+
return "R1, W1, " + (leftArity..<leftArity+rightArity).map { "C\($0)" }.joined(separator: ", ")
475+
}()
476+
let genericParams = leftGenParams + ", " + rightGenParams
477+
let whereClause: String = {
478+
var result = "where R0: \(regexProtocolName), R1: \(regexProtocolName)"
479+
if leftArity > 0 {
480+
result += ", R0.\(matchAssociatedTypeName) == (W0, \((0..<leftArity).map { "C\($0)" }.joined(separator: ", ")))"
481+
}
482+
if rightArity > 0 {
483+
result += ", R1.\(matchAssociatedTypeName) == (W1, \((leftArity..<leftArity+rightArity).map { "C\($0)" }.joined(separator: ", ")))"
484+
}
485+
return result
486+
}()
487+
let resultCaptures: String = {
488+
var result = (0..<leftArity).map { "C\($0)" }.joined(separator: ", ")
489+
if leftArity > 0, rightArity > 0 {
490+
result += ", "
491+
}
492+
result += (leftArity..<leftArity+rightArity).map { "C\($0)?" }.joined(separator: ", ")
493+
return result
494+
}()
495+
let matchType: String = {
496+
if leftArity == 0, rightArity == 0 {
497+
return baseMatchTypeName
498+
}
499+
return "(\(baseMatchTypeName), \(resultCaptures))"
500+
}()
501+
output("""
502+
public struct \(typeName)<\(genericParams)>: \(regexProtocolName) \(whereClause) {
503+
public typealias Match = \(matchType)
504+
public let regex: Regex<Match>
505+
506+
public init(_ left: R0, _ right: R1) {
507+
self.regex = .init(ast: alt(left.regex.ast, right.regex.ast))
508+
}
509+
}
510+
511+
extension AlternationBuilder {
512+
public static func buildBlock<\(genericParams)>(combining next: R1, into combined: R0) -> \(typeName)<\(genericParams)> {
513+
.init(combined, next)
514+
}
515+
}
516+
517+
public func | <\(genericParams)>(lhs: R0, rhs: R1) -> \(typeName)<\(genericParams)> {
518+
.init(lhs, rhs)
519+
}
520+
521+
""")
522+
}
523+
524+
func emitUnaryAlternationBuildBlock(arity: Int) {
525+
assert(arity > 0)
526+
let captures = (0..<arity).map { "C\($0)" }.joined(separator: ", ")
527+
let genericParams: String = {
528+
if arity == 0 {
529+
return "R"
530+
}
531+
return "R, W, " + captures
532+
}()
533+
let whereClause: String = """
534+
where R: \(regexProtocolName), \
535+
R.\(matchAssociatedTypeName) == (W, \(captures))
536+
"""
537+
let resultCaptures = (0..<arity).map { "C\($0)?" }.joined(separator: ", ")
538+
output("""
539+
extension AlternationBuilder {
540+
public static func buildBlock<\(genericParams)>(_ regex: R) -> Regex<(W, \(resultCaptures))> \(whereClause) {
541+
.init(ast: alt(regex.regex.ast))
542+
}
543+
}
544+
545+
""")
546+
}
447547
}

Sources/_MatchingEngine/Regex/AST/AST.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ extension AST {
130130
public init(_ mems: [AST], pipes: [SourceLocation]) {
131131
// An alternation must have at least two branches (though the branches
132132
// may be empty AST nodes), and n - 1 pipes.
133-
precondition(mems.count >= 2)
133+
//precondition(mems.count >= 2)
134134
precondition(pipes.count == mems.count - 1)
135135

136136
self.children = mems

0 commit comments

Comments
 (0)