Skip to content

Commit 04927a7

Browse files
authored
Merge pull request #39 from kareman/simplify
Simplify, add documentation and @inlineable.
2 parents 2e4f3a3 + 4a4c43f commit 04927a7

27 files changed

+631
-330
lines changed

Sources/Patterns/Atomic Patterns/Line.swift

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,49 @@
55
// Created by Kåre Morstøl on 25/05/2020.
66
//
77

8+
/// Matches one line, not including newline characters.
89
public struct Line: Pattern {
9-
public let description: String = "line"
10+
public init() {}
11+
12+
public var description: String { "Line()" }
1013

11-
public let pattern: Pattern
1214
public static let start = Start()
1315
public static let end = End()
1416

15-
public init() {
16-
pattern = Start() Skip() End()
17-
}
18-
17+
@inlinable
1918
public func createInstructions(_ instructions: inout Instructions) throws {
20-
try pattern.createInstructions(&instructions)
19+
try (Start() Skip() End()).createInstructions(&instructions)
2120
}
2221

22+
/// Matches the start of a line, including the start of input.
2323
public struct Start: Pattern {
2424
public init() {}
2525

26-
public var description: String { "line.start" }
26+
public var description: String { "Line.start" }
2727

28-
public func parse(_ input: Input, at index: Input.Index) -> Bool {
28+
@inlinable
29+
func parse(_ input: Input, at index: Input.Index) -> Bool {
2930
(index == input.startIndex) || input[input.index(before: index)].isNewline
3031
}
3132

33+
@inlinable
3234
public func createInstructions(_ instructions: inout Instructions) {
3335
instructions.append(.checkIndex(self.parse(_:at:)))
3436
}
3537
}
3638

39+
/// Matches the end of a line, including the end of input.
3740
public struct End: Pattern {
3841
public init() {}
3942

40-
public var description: String { "line.end" }
43+
public var description: String { "Line.end" }
4144

42-
public func parse(_ input: Input, at index: Input.Index) -> Bool {
45+
@inlinable
46+
func parse(_ input: Input, at index: Input.Index) -> Bool {
4347
index == input.endIndex || input[index].isNewline
4448
}
4549

50+
@inlinable
4651
public func createInstructions(_ instructions: inout Instructions) {
4752
instructions.append(.checkIndex(self.parse(_:at:)))
4853
}

Sources/Patterns/Atomic Patterns/Literal.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,36 @@
77

88
import Foundation
99

10+
/// Matches a sequence of elements.
11+
///
12+
/// If empty, it will always succeed without consuming any input.
1013
public struct Literal: Pattern {
11-
public let substring: Input
14+
public let elements: Input
1215

1316
public var description: String {
14-
#""\#(String(substring).replacingOccurrences(of: "\n", with: "\\n"))""#
17+
#""\#(String(elements).replacingOccurrences(of: "\n", with: "\\n"))""#
1518
}
1619

20+
/// Matches `sequence`.
21+
@inlinable
1722
public init<S: Sequence>(_ sequence: S) where S.Element == Pattern.Input.Element {
18-
self.substring = Pattern.Input(sequence)
23+
self.elements = Pattern.Input(sequence)
1924
}
2025

26+
/// Matches this character.
27+
@inlinable
2128
public init(_ character: Character) {
2229
self.init(String(character))
2330
}
2431

32+
@inlinable
2533
public func createInstructions(_ instructions: inout Instructions) {
26-
instructions.append(contentsOf: substring.map(Instruction.elementEquals))
34+
instructions.append(contentsOf: elements.map(Instruction.elementEquals))
2735
}
2836
}
2937

3038
extension Literal: ExpressibleByStringLiteral {
39+
@inlinable
3140
public init(stringLiteral value: StaticString) {
3241
self.init(String(describing: value))
3342
}

Sources/Patterns/Atomic Patterns/OneOf.swift

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Foundation
99

10+
/// Matches and consumes a single element.
1011
public struct OneOf: Pattern, RegexConvertible {
1112
@usableFromInline
1213
let group: Group<Input.Element>
@@ -25,33 +26,44 @@ public struct OneOf: Pattern, RegexConvertible {
2526
self._regex = regex
2627
}
2728

29+
/// Matches any element for which `contains` returns `true`.
30+
/// - Parameters:
31+
/// - description: A descriptive identifier for textual representation of the pattern.
32+
/// - regex: An optional regex matching the same elements.
33+
/// - contains: A closure returning true for any element that matches.
2834
@inlinable
2935
public init(description: String, regex: String? = nil, contains: @escaping (Input.Element) -> Bool) {
3036
self.init(description: description, regex: regex, group: Group(contains: contains))
3137
}
3238

39+
/// Matches any elements in `elements`.
40+
/// - Parameter elements: A sequence of elements to match.
3341
@inlinable
34-
public init<S: Sequence>(_ characters: S) where S.Element == Input.Element {
35-
group = Group(contentsOf: characters)
36-
description = #"["\#(String(characters))"]"#
37-
_regex = "[\(NSRegularExpression.escapedPattern(for: characters.map(String.init(describing:)).joined()))]"
42+
public init<S: Sequence>(_ elements: S) where S.Element == Input.Element {
43+
group = Group(contentsOf: elements)
44+
description = #"[\#(String(elements))]"#
45+
_regex = "[\(NSRegularExpression.escapedPattern(for: elements.map(String.init(describing:)).joined()))]"
3846
}
3947

48+
/// Matches any elements _not_ in `elements`.
49+
/// - Parameter elements: A sequence of elements _not_ to match.
4050
@inlinable
41-
public init<S: Sequence>(not characters: S) where S.Element == Input.Element {
42-
group = Group(contentsOf: characters).inverted()
43-
description = #"[^"\#(String(characters))"]"#
44-
_regex = "[^\(NSRegularExpression.escapedPattern(for: characters.map(String.init(describing:)).joined()))]"
51+
public init<S: Sequence>(not elements: S) where S.Element == Input.Element {
52+
group = Group(contentsOf: elements).inverted()
53+
description = #"[^\#(String(elements))]"#
54+
_regex = "[^\(NSRegularExpression.escapedPattern(for: elements.map(String.init(describing:)).joined()))]"
4555
}
4656

57+
/// Matches any of the provided elements.
4758
@inlinable
4859
public init(_ oneofs: OneOfConvertible...) {
4960
let closures = oneofs.map { $0.contains(_:) }
5061
group = Group(contains: { char in closures.contains(where: { $0(char) }) })
51-
description = #"[\#(oneofs.map(String.init(describing:)).joined(separator: ","))]"#
62+
description = "[\(oneofs.map(String.init(describing:)).joined(separator: ","))]"
5263
_regex = nil
5364
}
5465

66+
/// Matches anything that is _not_ among the provided elements.
5567
@inlinable
5668
public init(not oneofs: OneOfConvertible...) {
5769
let closures = oneofs.map { $0.contains(_:) }
@@ -68,67 +80,80 @@ public struct OneOf: Pattern, RegexConvertible {
6880

6981
// MARK: OneOfConvertible
7082

83+
/// A type that `OneOf` can use.
7184
public protocol OneOfConvertible {
72-
func contains(_: Character) -> Bool
85+
@inlinable
86+
func contains(_: Pattern.Input.Element) -> Bool
7387
}
7488

7589
extension Character: OneOfConvertible {
76-
public func contains(_ char: Character) -> Bool { char == self }
90+
@inlinable
91+
public func contains(_ char: Pattern.Input.Element) -> Bool { char == self }
7792
}
7893

7994
extension String: OneOfConvertible {}
8095
extension Substring: OneOfConvertible {}
8196

97+
@inlinable
8298
public func ... (lhs: Character, rhs: Character) -> ClosedRange<Character> {
83-
precondition(lhs <= rhs, "The left side of the '...' operator must be less than or equal to the right side")
99+
precondition(lhs <= rhs, "The left side of the '...' operator must be less than or equal to the right side.")
84100
return ClosedRange(uncheckedBounds: (lower: lhs, upper: rhs))
85101
}
86102

87103
extension ClosedRange: OneOfConvertible where Bound == Character {}
88104

105+
@inlinable
89106
public func ..< (lhs: Character, rhs: Character) -> Range<Character> {
90-
precondition(lhs <= rhs, "The left side of the '..<' operator must be less than or equal to the right side")
107+
precondition(lhs <= rhs, "The left side of the '..<' operator must be less than or equal to the right side.")
91108
return Range(uncheckedBounds: (lower: lhs, upper: rhs))
92109
}
93110

94111
extension Range: OneOfConvertible where Bound == Character {}
95112

96113
extension OneOf: OneOfConvertible {
97-
public func contains(_ char: Character) -> Bool { group.contains(char) }
114+
@inlinable
115+
public func contains(_ char: Pattern.Input.Element) -> Bool { group.contains(char) }
98116
}
99117

100118
// MARK: Join `&&OneOf • OneOf` into one.
101119

120+
@inlinable
102121
public func (lhs: AndPattern<OneOf>, rhs: OneOf) -> OneOf {
103122
OneOf(description: "\(lhs) \(rhs)", group: lhs.wrapped.group.intersection(rhs.group))
104123
}
105124

106-
public func <P: Pattern>(lhs: AndPattern<OneOf>, rhs: Concat<OneOf, P>) -> Concat<OneOf, P> {
107-
(lhs rhs.left) rhs.right
125+
@inlinable
126+
public func <P: Pattern>(lhs: Concat<P, AndPattern<OneOf>>, rhs: OneOf) -> Concat<P, OneOf> {
127+
lhs.first (lhs.second rhs)
108128
}
109129

110130
// MARK: Join `!OneOf • Oneof` into one.
111131

132+
@inlinable
112133
public func (lhs: NotPattern<OneOf>, rhs: OneOf) -> OneOf {
113134
OneOf(description: "\(lhs) \(rhs)", group: rhs.group.subtracting(lhs.wrapped.group))
114135
}
115136

116-
public func <P: Pattern>(lhs: NotPattern<OneOf>, rhs: Concat<OneOf, P>) -> Concat<OneOf, P> {
117-
(lhs rhs.left) rhs.right
137+
@inlinable
138+
public func <P: Pattern>(lhs: Concat<P, NotPattern<OneOf>>, rhs: OneOf) -> Concat<P, OneOf> {
139+
lhs.first (lhs.second rhs)
118140
}
119141

120142
// MARK: Join `OneOf / OneOf` into one.
121143

144+
@inlinable
122145
public func / (lhs: OneOf, rhs: OneOf) -> OneOf {
123146
OneOf(description: "\(lhs) / \(rhs)", group: lhs.group.union(rhs.group))
124147
}
125148

149+
@inlinable
126150
public func / <P: Pattern>(lhs: OrPattern<P, OneOf>, rhs: OneOf) -> OrPattern<P, OneOf> {
127151
lhs.first / (lhs.second / rhs)
128152
}
129153

130154
// MARK: Common patterns.
131155

156+
/// Succeeds anywhere except for the end of input, and consumes 1 element.
132157
public let any = OneOf(description: "any", regex: #"[.\p{Zl}]"#,
133158
contains: { _ in true })
134159
public let alphanumeric = OneOf(description: "alphanumeric", regex: #"(?:\p{Alphabetic}|\p{Nd})"#,
@@ -159,16 +184,20 @@ public let currencySymbol = OneOf(description: "currencySymbol", regex: #"\p{Sc}
159184
contains: { $0.isCurrencySymbol })
160185

161186
extension OneOf {
162-
public static let basePatterns: [OneOf] = [
163-
any, alphanumeric, letter, lowercase, uppercase, punctuation, whitespace, newline, hexDigit, digit,
187+
/// Predefined OneOf patterns.
188+
public static let patterns: [OneOf] = [
189+
alphanumeric, letter, lowercase, uppercase, punctuation, whitespace, newline, hexDigit, digit,
164190
ascii, symbol, mathSymbol, currencySymbol,
165191
]
166192

167-
public static func patterns(for c: Input.Element) -> [Pattern] {
168-
OneOf.basePatterns.filter { $0.group.contains(c) }
193+
/// All the predefined OneOf patterns that match `element`.
194+
public static func patterns(for element: Input.Element) -> [OneOf] {
195+
OneOf.patterns.filter { $0.group.contains(element) }
169196
}
170197

171-
public static func patterns<S: Sequence>(for s: S) -> [Pattern] where S.Element == Input.Element {
172-
OneOf.basePatterns.filter { $0.group.contains(contentsOf: s) }
198+
/// The predefined OneOf patterns that match _all_ the elements in `sequence`.
199+
public static func patterns<S: Sequence>(for sequence: S) -> [OneOf] where S.Element == Input.Element {
200+
let sequence = ContiguousArray(sequence)
201+
return OneOf.patterns.filter { $0.group.contains(contentsOf: sequence) }
173202
}
174203
}

Sources/Patterns/Atomic Patterns/Word.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@
66
//
77

88
public struct Word {
9+
/// Detects boundaries between words.
10+
///
11+
/// Uses rules from https://www.unicode.org/reports/tr29/#Word_Boundary_Rules .
912
public static let boundary = Boundary()
1013

14+
/// Detects boundaries between words.
15+
///
16+
/// Uses rules from https://www.unicode.org/reports/tr29/#Word_Boundary_Rules .
1117
public struct Boundary: Pattern {
1218
public let description: String = "Word.boundary"
1319

14-
//TODO: return bool
15-
public func parse(_ input: Input, at index: Input.Index) -> ParsedRange? {
20+
// TODO: Should use UnicodeScalars. And return bool.
21+
@usableFromInline
22+
func parse(_ input: Input, at index: Input.Index) -> ParsedRange? {
1623
let success = index ..< index
1724
guard index != input.endIndex, index != input.startIndex else { return success }
1825

@@ -37,7 +44,7 @@ public struct Word {
3744
a1.contains(char1After) && char2After.map(a2.contains) ?? false
3845
}
3946

40-
let char2Before = input.validIndex(index, offsetBy: -2).map { input[$0] }
47+
let char2Before = input.index(index, offsetBy: -2, limitedBy: input.startIndex).map { input[$0] }
4148
func before2(_ b2: Group<UInt32>, _ b1: Group<UInt32>) -> Bool {
4249
b1.contains(char1Before) && (char2Before.map(b2.contains) ?? false)
4350
}
@@ -64,6 +71,7 @@ public struct Word {
6471
return success
6572
}
6673

74+
@inlinable
6775
public func createInstructions(_ instructions: inout Instructions) {
6876
instructions.append(.checkIndex { (input, index) -> Bool in
6977
self.parse(input, at: index) != nil

0 commit comments

Comments
 (0)