Skip to content

Commit 074ee9f

Browse files
authored
Merge pull request #130 from hamishknight/one-of-a-kind
2 parents fbdc959 + 7ca9a9a commit 074ee9f

23 files changed

+1773
-1709
lines changed

Sources/VariadicsGenerator/VariadicsGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ struct VariadicsGenerator: ParsableCommand {
255255
outputForEach(
256256
0..<arity, separator: ", ", lineTerminator: ""
257257
) { i in
258-
"x\(i).\(patternProtocolRequirementName).ast"
258+
"x\(i).\(patternProtocolRequirementName).ast.root"
259259
}
260260
output("))\n")
261261
output(" }\n}\n\n")

Sources/_MatchingEngine/Regex/AST/AST.swift

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,64 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12-
/// A regex abstract syntax tree
13-
@frozen
14-
public indirect enum AST:
15-
Hashable/*, _ASTPrintable ASTValue, ASTAction*/
16-
{
17-
/// ... | ... | ...
18-
case alternation(Alternation)
12+
/// A regex abstract syntax tree. This is a top-level type that stores the root
13+
/// node.
14+
public struct AST: Hashable {
15+
public var root: AST.Node
16+
public init(_ root: AST.Node) {
17+
self.root = root
18+
}
19+
}
1920

20-
/// ... ...
21-
case concatenation(Concatenation)
21+
extension AST {
22+
/// Whether this AST tree has nested somewhere inside it a capture.
23+
public var hasCapture: Bool { root.hasCapture }
2224

23-
/// (...)
24-
case group(Group)
25+
/// The capture structure of this AST tree.
26+
public var captureStructure: CaptureStructure { root.captureStructure }
27+
}
2528

26-
/// (?(cond) true-branch | false-branch)
27-
case conditional(Conditional)
29+
extension AST {
30+
/// A node in the regex AST.
31+
@frozen
32+
public indirect enum Node:
33+
Hashable/*, _ASTPrintable ASTValue, ASTAction*/
34+
{
35+
/// ... | ... | ...
36+
case alternation(Alternation)
2837

29-
case quantification(Quantification)
38+
/// ... ...
39+
case concatenation(Concatenation)
3040

31-
/// \Q...\E
32-
case quote(Quote)
41+
/// (...)
42+
case group(Group)
3343

34-
/// Comments, non-semantic whitespace, etc
35-
case trivia(Trivia)
44+
/// (?(cond) true-branch | false-branch)
45+
case conditional(Conditional)
3646

37-
case atom(Atom)
47+
case quantification(Quantification)
3848

39-
case customCharacterClass(CustomCharacterClass)
49+
/// \Q...\E
50+
case quote(Quote)
4051

41-
case absentFunction(AbsentFunction)
52+
/// Comments, non-semantic whitespace, etc
53+
case trivia(Trivia)
4254

43-
case empty(Empty)
55+
case atom(Atom)
4456

45-
// FIXME: Move off the regex literal AST
46-
case groupTransform(
47-
Group, transform: CaptureTransform)
48-
}
57+
case customCharacterClass(CustomCharacterClass)
4958

50-
// TODO: Do we want something that holds the AST and stored global options?
59+
case absentFunction(AbsentFunction)
5160

52-
extension AST {
61+
case empty(Empty)
62+
63+
// FIXME: Move off the regex literal AST
64+
case groupTransform(
65+
Group, transform: CaptureTransform)
66+
}
67+
}
68+
69+
extension AST.Node {
5370
// :-(
5471
//
5572
// Existential-based programming is highly prone to silent
@@ -79,7 +96,7 @@ extension AST {
7996
}
8097

8198
/// If this node is a parent node, access its children
82-
public var children: [AST]? {
99+
public var children: [AST.Node]? {
83100
return (_associatedValue as? _ASTParent)?.children
84101
}
85102

@@ -127,10 +144,10 @@ extension AST {
127144
extension AST {
128145

129146
public struct Alternation: Hashable, _ASTNode {
130-
public let children: [AST]
147+
public let children: [AST.Node]
131148
public let pipes: [SourceLocation]
132149

133-
public init(_ mems: [AST], pipes: [SourceLocation]) {
150+
public init(_ mems: [AST.Node], pipes: [SourceLocation]) {
134151
// An alternation must have at least two branches (though the branches
135152
// may be empty AST nodes), and n - 1 pipes.
136153
precondition(mems.count >= 2)
@@ -146,10 +163,10 @@ extension AST {
146163
}
147164

148165
public struct Concatenation: Hashable, _ASTNode {
149-
public let children: [AST]
166+
public let children: [AST.Node]
150167
public let location: SourceLocation
151168

152-
public init(_ mems: [AST], _ location: SourceLocation) {
169+
public init(_ mems: [AST.Node], _ location: SourceLocation) {
153170
self.children = mems
154171
self.location = location
155172
}
@@ -201,16 +218,16 @@ extension AST {
201218
public enum Kind: Hashable {
202219
/// An absent repeater `(?~absent)`. This is equivalent to `(?~|absent|.*)`
203220
/// and therefore matches as long as the pattern `absent` is not matched.
204-
case repeater(AST)
221+
case repeater(AST.Node)
205222

206223
/// An absent expression `(?~|absent|expr)`, which defines an `absent`
207224
/// pattern which must not be matched against while the pattern `expr` is
208225
/// matched.
209-
case expression(absentee: AST, pipe: SourceLocation, expr: AST)
226+
case expression(absentee: AST.Node, pipe: SourceLocation, expr: AST.Node)
210227

211228
/// An absent stopper `(?~|absent)`, which prevents matching against
212229
/// `absent` until the end of the regex, or until it is cleared.
213-
case stopper(AST)
230+
case stopper(AST.Node)
214231

215232
/// An absent clearer `(?~|)` which cancels the effect of an absent
216233
/// stopper.

Sources/_MatchingEngine/Regex/AST/ASTProtocols.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ extension _ASTNode {
2828
}
2929

3030
protocol _ASTParent: _ASTNode {
31-
var children: [AST] { get }
31+
var children: [AST.Node] { get }
3232
}
3333

3434
extension AST.Concatenation: _ASTParent {}
3535
extension AST.Alternation: _ASTParent {}
3636

3737
extension AST.Group: _ASTParent {
38-
var children: [AST] { [child] }
38+
var children: [AST.Node] { [child] }
3939
}
4040
extension AST.Quantification: _ASTParent {
41-
var children: [AST] { [child] }
41+
var children: [AST.Node] { [child] }
4242
}
4343
extension AST.AbsentFunction: _ASTParent {
44-
var children: [AST] {
44+
var children: [AST.Node] {
4545
switch kind {
4646
case .repeater(let a), .stopper(let a): return [a]
4747
case .expression(let a, _, let c): return [a, c]

Sources/_MatchingEngine/Regex/AST/Atom.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ extension AST.Atom {
689689
}
690690
}
691691

692-
extension AST {
692+
extension AST.Node {
693693
public var literalStringValue: String? {
694694
switch self {
695695
case .atom(let a): return a.literalStringValue

Sources/_MatchingEngine/Regex/AST/Conditional.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ extension AST {
1414
public var location: SourceLocation
1515
public var condition: Condition
1616

17-
public var trueBranch: AST
17+
public var trueBranch: AST.Node
1818
public var pipe: SourceLocation?
19-
public var falseBranch: AST
19+
public var falseBranch: AST.Node
2020

2121
public init(
22-
_ condition: Condition, trueBranch: AST, pipe: SourceLocation?,
23-
falseBranch: AST, _ location: SourceLocation
22+
_ condition: Condition, trueBranch: AST.Node, pipe: SourceLocation?,
23+
falseBranch: AST.Node, _ location: SourceLocation
2424
) {
2525
self.location = location
2626
self.condition = condition

Sources/_MatchingEngine/Regex/AST/Group.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
extension AST {
1313
public struct Group: Hashable {
1414
public let kind: Located<Kind>
15-
public let child: AST
15+
public let child: AST.Node
1616

1717
public let location: SourceLocation
1818

1919
public init(
20-
_ kind: Located<Kind>, _ child: AST, _ r: SourceLocation
20+
_ kind: Located<Kind>, _ child: AST.Node, _ r: SourceLocation
2121
) {
2222
self.kind = kind
2323
self.child = child

Sources/_MatchingEngine/Regex/AST/Quantification.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ extension AST {
1414
public let amount: Located<Amount>
1515
public let kind: Located<Kind>
1616

17-
public let child: AST
17+
public let child: AST.Node
1818
public let location: SourceLocation
1919

2020
public init(
2121
_ amount: Located<Amount>,
2222
_ kind: Located<Kind>,
23-
_ child: AST,
23+
_ child: AST.Node,
2424
_ r: SourceLocation
2525
) {
2626
self.amount = amount

Sources/_MatchingEngine/Regex/Parse/CaptureStructure.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public enum CaptureStructure: Equatable {
2525
}
2626
}
2727

28-
extension AST {
28+
extension AST.Node {
2929
public var captureStructure: CaptureStructure {
3030
// Note: This implementation could be more optimized.
3131
switch self {
@@ -68,7 +68,7 @@ extension AST {
6868
var captures = CaptureStructure.empty
6969
switch c.condition.kind {
7070
case .group(let g):
71-
captures = captures + AST.group(g).captureStructure
71+
captures = captures + AST.Node.group(g).captureStructure
7272
default:
7373
break
7474
}

Sources/_MatchingEngine/Regex/Parse/Parse.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ extension Parser {
127127
}
128128
fatalError("Unhandled termination condition")
129129
}
130-
return ast
130+
return .init(ast)
131131
}
132132

133133
/// Parse a regular expression node. This should be used instead of `parse()`
@@ -136,7 +136,7 @@ extension Parser {
136136
/// RegexNode -> '' | Alternation
137137
/// Alternation -> Concatenation ('|' Concatenation)*
138138
///
139-
mutating func parseNode() throws -> AST {
139+
mutating func parseNode() throws -> AST.Node {
140140
let _start = source.currentPosition
141141

142142
if source.isEmpty { return .empty(.init(loc(_start))) }
@@ -163,8 +163,8 @@ extension Parser {
163163
/// ConcatComponent -> Trivia | Quote | Quantification
164164
/// Quantification -> QuantOperand Quantifier?
165165
///
166-
mutating func parseConcatenation() throws -> AST {
167-
var result = Array<AST>()
166+
mutating func parseConcatenation() throws -> AST.Node {
167+
var result = [AST.Node]()
168168
let _start = source.currentPosition
169169

170170
while true {
@@ -219,9 +219,9 @@ extension Parser {
219219
/// Perform a recursive parse for the branches of a conditional.
220220
mutating func parseConditionalBranches(
221221
start: Source.Position, _ cond: AST.Conditional.Condition
222-
) throws -> AST {
222+
) throws -> AST.Node {
223223
let child = try parseNode()
224-
let trueBranch: AST, falseBranch: AST, pipe: SourceLocation?
224+
let trueBranch: AST.Node, falseBranch: AST.Node, pipe: SourceLocation?
225225
switch child {
226226
case .alternation(let a):
227227
// If we have an alternation child, we only accept 2 branches.
@@ -316,7 +316,7 @@ extension Parser {
316316
/// Conditional -> ConditionalStart Concatenation ('|' Concatenation)? ')'
317317
/// ConditionalStart -> KnownConditionalStart | GroupConditionalStart
318318
///
319-
mutating func parseQuantifierOperand() throws -> AST? {
319+
mutating func parseQuantifierOperand() throws -> AST.Node? {
320320
assert(!source.isEmpty)
321321

322322
let _start = source.currentPosition

Sources/_MatchingEngine/Regex/Printing/DumpAST.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ extension _ASTPrintable {
2626
public var description: String { _print() }
2727
public var debugDescription: String { _dump() }
2828

29-
var _children: [AST]? {
29+
var _children: [AST.Node]? {
3030
if let children = (self as? _ASTParent)?.children {
3131
return children
3232
}
33-
if let children = (self as? AST)?.children {
33+
if let children = (self as? AST.Node)?.children {
3434
return children
3535
}
3636
return nil
@@ -57,6 +57,12 @@ extension _ASTPrintable {
5757
}
5858

5959
extension AST: _ASTPrintable {
60+
public var _dumpBase: String {
61+
root._dumpBase
62+
}
63+
}
64+
65+
extension AST.Node: _ASTPrintable {
6066
public var _dumpBase: String {
6167
_associatedValue._dumpBase
6268
}

Sources/_MatchingEngine/Regex/Printing/PrintAsCanonical.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,38 @@ extension AST {
2626
}
2727
}
2828

29+
extension AST.Node {
30+
/// Render using Swift's preferred regex literal syntax
31+
public func renderAsCanonical(
32+
showDelimiters delimiters: Bool = false,
33+
terminateLine: Bool = false
34+
) -> String {
35+
var printer = PrettyPrinter()
36+
printer.printAsCanonical(
37+
self,
38+
delimiters: delimiters,
39+
terminateLine: terminateLine)
40+
return printer.finish()
41+
}
42+
}
43+
2944
extension PrettyPrinter {
3045
/// Will output `ast` in canonical form, taking care to
3146
/// also indent and terminate the line (updating internal state)
3247
mutating func printAsCanonical(
3348
_ ast: AST,
3449
delimiters: Bool = false,
3550
terminateLine terminate: Bool = true
51+
) {
52+
printAsCanonical(ast.root, delimiters: delimiters, terminateLine: terminate)
53+
}
54+
55+
/// Will output `ast` in canonical form, taking care to
56+
/// also indent and terminate the line (updating internal state)
57+
mutating func printAsCanonical(
58+
_ ast: AST.Node,
59+
delimiters: Bool = false,
60+
terminateLine terminate: Bool = true
3661
) {
3762
indent()
3863
if delimiters { output("'/") }
@@ -45,7 +70,7 @@ extension PrettyPrinter {
4570

4671
/// Output the `ast` in canonical form, does not indent, terminate,
4772
/// or affect internal state
48-
mutating func outputAsCanonical(_ ast: AST) {
73+
mutating func outputAsCanonical(_ ast: AST.Node) {
4974
switch ast {
5075
case let .alternation(a):
5176
for idx in a.children.indices {

0 commit comments

Comments
 (0)