> {
241 | lhs.first / (lhs.second / rhs)
242 | }
243 |
244 | // MARK: Common patterns.
245 |
246 | /// Succeeds anywhere except for at the end of input, and consumes 1 element.
247 | public let any = OneOf(description: "any", regex: #"[.\p{Zl}]"#,
248 | contains: { _ in true })
249 | /// Matches one character representing a letter, i.e. where `Character.isLetter` is `true`.
250 | public let letter = OneOf(description: "letter", regex: #"\p{Alphabetic}"#,
251 | contains: { $0.isLetter })
252 | /// Matches one character representing a lowercase character, i.e. where `Character.isLowercase` is `true`.
253 | public let lowercase = OneOf(description: "lowercase", regex: #"\p{Ll}"#,
254 | contains: { $0.isLowercase })
255 | /// Matches one character representing an uppercase character, i.e. where `Character.isUppercase` is `true`.
256 | public let uppercase = OneOf(description: "uppercase", regex: #"\p{Lu}"#,
257 | contains: { $0.isUppercase })
258 | /// Matches one character representing a whole number, i.e. where `Character.isWholeNumber` is `true`.
259 | public let digit = OneOf(description: "digit", regex: #"\p{Nd}"#,
260 | contains: { $0.isWholeNumber })
261 | /// Matches one letter or one digit.
262 | public let alphanumeric = OneOf(description: "alphanumeric", regex: #"(?:\p{Alphabetic}|\p{Nd})"#,
263 | contains: { $0.isWholeNumber || $0.isLetter })
264 | /// Matches one character representing a newline, i.e. where `Character.isNewline` is `true`.
265 | public let newline = OneOf(description: "newline", regex: #"\p{Zl}"#,
266 | contains: { $0.isNewline })
267 | /// Matches one character representing whitespace (including newlines), i.e. where `Character.isWhitespace` is `true`.
268 | public let whitespace = OneOf(description: "whitespace", regex: #"\p{White_Space}"#,
269 | contains: { $0.isWhitespace })
270 | /// Matches one character representing punctuation, i.e. where `Character.isPunctuation` is `true`.
271 | public let punctuation = OneOf(description: "punctuation", regex: #"\p{P}"#,
272 | contains: { $0.isPunctuation })
273 | /// Matches one character representing a symbol, i.e. where `Character.isSymbol` is `true`.
274 | public let symbol = OneOf(description: "symbol", regex: #"\p{S}"#,
275 | contains: { $0.isSymbol })
276 | /// Matches one character representing a hexadecimal digit, i.e. where `Character.isHexDigit` is `true`.
277 | public let hexDigit = OneOf(description: "hexDigit", regex: #"\p{Hex_Digit}"#,
278 | contains: { $0.isHexDigit })
279 | /// Matches one ASCII character, i.e. where `Character.isASCII` is `true`.
280 | public let ascii = OneOf(description: "ascii", regex: #"[[:ascii:]]"#,
281 | contains: { $0.isASCII }) // regex might also be [ -~] or [\x00-\x7F]
282 | /// Matches one character representing a mathematical symbol, i.e. where `Character.isMathSymbol` is `true`.
283 | public let mathSymbol = OneOf(description: "mathSymbol", regex: #"\p{Sm}"#,
284 | contains: { $0.isMathSymbol })
285 | /// Matches one character representing a currency symbol, i.e. where `Character.isCurrencySymbol` is `true`.
286 | public let currencySymbol = OneOf(description: "currencySymbol", regex: #"\p{Sc}"#,
287 | contains: { $0.isCurrencySymbol })
288 |
289 | extension OneOf where Input == String {
290 | /// Predefined OneOf patterns.
291 | public static var patterns: [OneOf] {
292 | [alphanumeric, letter, lowercase, uppercase, punctuation, whitespace, newline, hexDigit, digit,
293 | ascii, symbol, mathSymbol, currencySymbol]
294 | }
295 |
296 | /// All the predefined OneOf patterns that match `element`.
297 | public static func patterns(for element: Input.Element) -> [OneOf] {
298 | OneOf.patterns.filter { $0.group.contains(element) }
299 | }
300 |
301 | /// The predefined OneOf patterns that match _all_ the elements in `sequence`.
302 | public static func patterns(for sequence: S) -> [OneOf] where S.Element == Input.Element {
303 | let sequence = ContiguousArray(sequence)
304 | return OneOf.patterns.filter { $0.group.contains(contentsOf: sequence) }
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/Sources/Patterns/Decoder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Decoder.swift
3 | // Patterns
4 | //
5 | // Created by Kåre Morstøl on 14/08/2019.
6 | //
7 |
8 | extension Parser where Input == String {
9 | /// Decodes all matches found in `string` into an array of `T`.
10 | @inlinable
11 | public func decode(_ type: [T].Type, from string: String) throws -> [T] where T: Decodable {
12 | try matches(in: string).map { try $0.decode(type.Element.self, from: string) }
13 | }
14 |
15 | /// Decodes the first match found in `string` into a value of type `type`.
16 | @inlinable
17 | public func decodeFirst(_ type: T.Type, from string: String) throws -> T? where T: Decodable {
18 | try match(in: string, at: string.startIndex).map { try $0.decode(type.self, from: string) }
19 | }
20 | }
21 |
22 | extension Parser.Match where Input == String {
23 | /// Decodes this match found in `string` into a value of type `type`.
24 | @inlinable
25 | public func decode(_ type: T.Type, from string: String) throws -> T where T: Decodable {
26 | try type.init(from: MatchDecoder(match: self, string: string))
27 | }
28 |
29 | public struct MatchDecoder: Decoder {
30 | @usableFromInline
31 | let match: Parser.Match
32 | @usableFromInline
33 | let string: String
34 |
35 | public let codingPath: [CodingKey]
36 | public var userInfo: [CodingUserInfoKey: Any] { [:] }
37 |
38 | @inlinable
39 | init(match: Parser.Match, string: String, codingPath: [CodingKey] = []) {
40 | let namePrefix = codingPath.first.map { $0.stringValue }
41 | let captures = namePrefix.map { namePrefix in
42 | match.captures.flatMap { name, range in
43 | name?.hasPrefix(namePrefix) ?? false ? [(String(name!.dropFirst(namePrefix.count)), range)] : []
44 | }
45 | } ?? match.captures
46 |
47 | self.match = Parser.Match(endIndex: match.endIndex, captures: captures)
48 | self.string = string
49 | self.codingPath = codingPath
50 | }
51 |
52 | @inlinable
53 | public func container(keyedBy _: Key.Type) throws -> KeyedDecodingContainer where Key: CodingKey {
54 | KeyedDecodingContainer(KDC(codingPath: codingPath, matchDecoder: self))
55 | }
56 |
57 | @inlinable
58 | public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
59 | UDC(codingPath: codingPath, values: match.captures.map { $0.range }, string: string)
60 | }
61 |
62 | @inlinable
63 | public func singleValueContainer() throws -> SingleValueDecodingContainer {
64 | guard match.captures.count < 2 else {
65 | let property = codingPath.map { "\($0.stringValue)" }.joined(separator: ".")
66 | throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription:
67 | "Property '\(property)' needs a single value, but multiple captures exists."))
68 | }
69 | let range = match.captures.first?.range ?? match.endIndex ..< match.endIndex
70 | return StringDecoder(string: String(string[range]), codingPath: codingPath)
71 | }
72 |
73 | @usableFromInline
74 | struct UDC: UnkeyedDecodingContainer {
75 | @usableFromInline
76 | var codingPath: [CodingKey]
77 | @usableFromInline
78 | let values: [Range]
79 | @usableFromInline
80 | let string: String
81 |
82 | @usableFromInline
83 | init(codingPath: [CodingKey], values: [Range], string: String) {
84 | self.codingPath = codingPath
85 | self.values = values
86 | self.string = string
87 | }
88 |
89 | @usableFromInline
90 | var count: Int? { values.count }
91 | @usableFromInline
92 | var isAtEnd: Bool { currentIndex >= values.endIndex }
93 | @usableFromInline
94 | var currentIndex: Int = 0
95 |
96 | @usableFromInline
97 | mutating func decodeNil() throws -> Bool { false }
98 |
99 | @usableFromInline
100 | mutating func nestedContainer(keyedBy type: NestedKey.Type)
101 | throws -> KeyedDecodingContainer where NestedKey: CodingKey {
102 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
103 | }
104 |
105 | @usableFromInline
106 | mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
107 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
108 | }
109 |
110 | @usableFromInline
111 | mutating func superDecoder() throws -> Decoder {
112 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
113 | }
114 |
115 | @usableFromInline
116 | mutating func decode(_ type: T.Type) throws -> T where T: Decodable {
117 | defer { currentIndex += 1 }
118 | let text = String(string[values[currentIndex]])
119 | return try type.init(from: StringDecoder(string: text, codingPath: codingPath))
120 | }
121 |
122 | @usableFromInline
123 | mutating func decode(_ type: T.Type) throws -> T where T: Decodable & LosslessStringConvertible {
124 | guard let value = type.init(String(string[values[currentIndex]])) else {
125 | throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: ""))
126 | }
127 | currentIndex += 1
128 | return value
129 | }
130 | }
131 |
132 | @usableFromInline
133 | struct KDC: KeyedDecodingContainerProtocol {
134 | @usableFromInline
135 | var codingPath: [CodingKey] = []
136 | @usableFromInline
137 | var allKeys: [Key] {
138 | matchDecoder.match.captureNames.compactMap(Key.init(stringValue:))
139 | }
140 |
141 | @usableFromInline
142 | let matchDecoder: MatchDecoder
143 |
144 | @usableFromInline
145 | init(codingPath: [CodingKey] = [], matchDecoder: MatchDecoder) {
146 | self.codingPath = codingPath
147 | self.matchDecoder = matchDecoder
148 | }
149 |
150 | @usableFromInline
151 | func capture(for key: CodingKey) throws -> String {
152 | guard let range = matchDecoder.match[one: key.stringValue] else {
153 | throw DecodingError.keyNotFound(key, DecodingError.Context(codingPath: codingPath, debugDescription: ""))
154 | }
155 | return String(matchDecoder.string[range])
156 | }
157 |
158 | @usableFromInline
159 | func contains(_ key: Key) -> Bool {
160 | matchDecoder.match[one: key.stringValue] == nil
161 | }
162 |
163 | @usableFromInline
164 | func decodeNil(forKey key: Key) throws -> Bool {
165 | contains(key)
166 | }
167 |
168 | @usableFromInline
169 | func decode(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable {
170 | return try type.init(from:
171 | MatchDecoder(match: matchDecoder.match, string: matchDecoder.string, codingPath: codingPath + [key]))
172 | }
173 |
174 | @usableFromInline
175 | func decode(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable & LosslessStringConvertible {
176 | guard let value = type.init(try capture(for: key)) else {
177 | throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: [key], debugDescription: ""))
178 | }
179 | return value
180 | }
181 |
182 | @usableFromInline
183 | func nestedContainer(keyedBy _: NestedKey.Type, forKey _: Key)
184 | throws -> KeyedDecodingContainer where NestedKey: CodingKey {
185 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
186 | }
187 |
188 | @usableFromInline
189 | func nestedUnkeyedContainer(forKey _: Key) throws -> UnkeyedDecodingContainer {
190 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
191 | }
192 |
193 | @usableFromInline
194 | func superDecoder() throws -> Decoder {
195 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
196 | }
197 |
198 | @usableFromInline
199 | func superDecoder(forKey _: Key) throws -> Decoder {
200 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
201 | }
202 | }
203 | }
204 | }
205 |
206 | @usableFromInline
207 | struct StringDecoder: Decoder, SingleValueDecodingContainer {
208 | @usableFromInline
209 | let string: String
210 | @usableFromInline
211 | let codingPath: [CodingKey]
212 | @usableFromInline
213 | var userInfo: [CodingUserInfoKey: Any] = [:]
214 |
215 | @usableFromInline
216 | init(string: String, codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any] = [:]) {
217 | self.string = string
218 | self.codingPath = codingPath
219 | self.userInfo = userInfo
220 | }
221 |
222 | @usableFromInline
223 | func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key: CodingKey {
224 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
225 | }
226 |
227 | @usableFromInline
228 | func unkeyedContainer() throws -> UnkeyedDecodingContainer {
229 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
230 | }
231 |
232 | @usableFromInline
233 | func singleValueContainer() throws -> SingleValueDecodingContainer { self }
234 |
235 | @usableFromInline
236 | func decodeNil() -> Bool { false }
237 |
238 | @usableFromInline
239 | func decode(_ type: T.Type) throws -> T where T: Decodable {
240 | fatalError("Not implemented yet. If you want to help with that, go to https://github.com/kareman/Patterns")
241 | }
242 |
243 | @usableFromInline
244 | func decode(_ type: T.Type) throws -> T where T: Decodable & LosslessStringConvertible {
245 | guard let value = type.init(string) else {
246 | throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath, debugDescription: ""))
247 | }
248 | return value
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/Sources/Patterns/General/General.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collections.swift
3 | // Patterns
4 | //
5 | // Created by Kåre Morstøl on 24/09/16.
6 | //
7 |
8 | @usableFromInline
9 | struct SearchCache {
10 | @usableFromInline
11 | let skipTable: [Element: Int]
12 | @usableFromInline
13 | let target: [Element]
14 |
15 | @usableFromInline
16 | init(_ target: Target) where Target.Element == Element {
17 | let newtarget = Array(target)
18 | var skipTable = [Element: Int](minimumCapacity: newtarget.count)
19 | for (i, c) in newtarget[...].dropLast().enumerated() {
20 | skipTable[c] = newtarget.count - i - 1
21 | }
22 | self.skipTable = skipTable
23 | self.target = newtarget
24 | }
25 | }
26 |
27 | extension BidirectionalCollection where Element: Hashable {
28 | /// Finds the next occurrence of `target` in this collection.
29 | /// - Parameters:
30 | /// - target: The sequence of elements to search for.
31 | /// - start: Where to start the search from.
32 | /// - Returns: The range where `target` was found, or nil if not found.
33 | @inlinable
34 | func range(of target: Target, from start: Index? = nil) -> Range?
35 | where Target.Element == Element {
36 | self.range(of: SearchCache(target), from: start)
37 | }
38 |
39 | /// Finds the next occurrence of `cache.target` in this collection, using the pre-created `cache`.
40 | /// - Parameters:
41 | /// - cache: When searching for the same sequence multiple times, use a SearchCache for improved performance.
42 | /// - start: Where to start the search from.
43 | /// - Returns: The range where `target` was found, or nil if not found.
44 | @inlinable
45 | func range(of cache: SearchCache, from start: Index? = nil) -> Range? {
46 | // https://en.wikipedia.org/wiki/Boyer–Moore–Horspool_algorithm
47 | let target = cache.target
48 | guard !target.isEmpty else { return nil }
49 |
50 | var pos = self.index(start ?? self.startIndex, offsetBy: target.count - 1, limitedBy: endIndex) ?? endIndex
51 |
52 | while pos < endIndex {
53 | var i = pos
54 | var p = target.index(before: target.endIndex)
55 |
56 | while self[i] == target[p] {
57 | if p == target.startIndex {
58 | return i ..< index(after: pos)
59 | } else {
60 | self.formIndex(before: &i)
61 | target.formIndex(before: &p)
62 | }
63 | }
64 |
65 | let advance = cache.skipTable[self[pos]] ?? target.count
66 | pos = self.index(pos, offsetBy: advance, limitedBy: endIndex) ?? endIndex
67 | }
68 |
69 | return nil
70 | }
71 | }
72 |
73 | extension Collection {
74 | /// Returns the results of passing leading elements to `transform` until it returns nil.
75 | /// - Parameter transform: transforms each element, returns nil when it wants to stop.
76 | /// - Throws: Whatever `transform` throws.
77 | /// - Returns: An array of the transformed elements, not including the first `nil`.
78 | @inlinable
79 | func mapPrefix(transform: (Element) throws -> T?) rethrows -> [T] {
80 | var result = [T]()
81 | for e in self {
82 | guard let transformed = try transform(e) else {
83 | return result
84 | }
85 | result.append(transformed)
86 | }
87 | return result
88 | }
89 | }
90 |
91 | extension Sequence {
92 | /// Returns an array containing the entire sequence.
93 | func array() -> [Element] { Array(self) }
94 |
95 | /// Returns the result of combining the elements using the given closure, if there are no nil elements.
96 | /// - Parameters:
97 | /// - initialResult: The value to use as the initial accumulating value.
98 | /// - updateAccumulatingResult: A closure that updates the accumulating value with an element of the sequence.
99 | /// - partialResult: The accumulating value.
100 | /// - unwrappedElement: An unwrapped element.
101 | /// - Returns: The final accumulated value, or nil if there were any nil elements.
102 | /// If the sequence has no elements, the result is initialResult.
103 | @inlinable
104 | func reduceIfNoNils(
105 | into initialResult: Result,
106 | _ updateAccumulatingResult: (_ partialResult: inout Result, _ unwrappedElement: T) throws -> Void)
107 | rethrows -> Result? where Element == Optional {
108 | var accumulator = initialResult
109 | for element in self {
110 | guard let element = element else { return nil }
111 | try updateAccumulatingResult(&accumulator, element)
112 | }
113 | return accumulator
114 | }
115 | }
116 |
117 | /// Used like e.g. `let a = optional ?? fatalError("Message")`.
118 | @inlinable
119 | func ?? (b: T?, a: @autoclosure () -> Never) -> T {
120 | if let b = b {
121 | return b
122 | }
123 | a()
124 | }
125 |
126 | /// Used like e.g. `let a = try optional ?? AnError()`.
127 | @inlinable
128 | func ?? (b: T?, a: @autoclosure () -> (E)) throws -> T {
129 | if let b = b {
130 | return b
131 | } else {
132 | throw a()
133 | }
134 | }
135 |
136 | extension BidirectionalCollection {
137 | /// Returns an index that is the specified distance from the given index, or nil if that index would be invalid.
138 | /// Never returns `endIndex`.
139 | @inlinable
140 | func validIndex(_ i: Index, offsetBy distance: Int) -> Index? {
141 | if distance < 0 {
142 | return index(i, offsetBy: distance, limitedBy: startIndex)
143 | }
144 | let newI = index(i, offsetBy: distance, limitedBy: endIndex)
145 | return newI == endIndex ? nil : newI
146 | }
147 |
148 | /// Offsets the given index by the specified distance, limited by `startIndex...endIndex`.
149 | /// - Returns: true if `index` has been offset by exactly `distance` steps; otherwise, false. When the return value is false, `index` is either `startIndex` or `endIndex`.
150 | @inlinable
151 | func formIndexSafely(_ index: inout Index, offsetBy distance: Int) -> Bool {
152 | if distance > 0 {
153 | return formIndex(&index, offsetBy: distance, limitedBy: endIndex)
154 | }
155 | return formIndex(&index, offsetBy: distance, limitedBy: startIndex)
156 | }
157 | }
158 |
159 | extension RangeReplaceableCollection where SubSequence == Self, Self: BidirectionalCollection {
160 | /// Removes the trailing range of elements for which `predicate` returns true.
161 | /// Stops as soon as `predicate` returns false.
162 | @inlinable
163 | mutating func removeSuffix(where predicate: (Element) -> Bool) {
164 | guard !isEmpty else { return }
165 | var i = index(before: endIndex)
166 | guard predicate(self[i]) else { return }
167 | while i > startIndex {
168 | formIndex(before: &i)
169 | if !predicate(self[i]) {
170 | self = self[...i]
171 | return
172 | }
173 | }
174 | removeAll()
175 | }
176 |
177 | @inlinable
178 | mutating func removeSuffix(from index: Index) {
179 | guard index < endIndex else { return }
180 | removeLast(distance(from: index, to: endIndex))
181 | }
182 | }
183 |
184 | extension RangeReplaceableCollection {
185 | /// Shortcut for creating a RangeReplaceableCollection.
186 | ///
187 | /// Example:
188 | /// ```
189 | /// let longIdentifier = Array {
190 | /// $0.append(...)
191 | /// $0.append(contentsOf:...)
192 | /// }
193 | /// ```
194 | @inlinable
195 | init(compose: (inout Self) throws -> Void) rethrows {
196 | self.init()
197 | try compose(&self)
198 | }
199 |
200 | /// Shortcut for appending to a RangeReplaceableCollection.
201 | ///
202 | /// Example:
203 | /// ```
204 | /// longIdentifier.append {
205 | /// $0.append(...)
206 | /// $0.append(contentsOf:...)
207 | /// }
208 | /// ```
209 | @inlinable
210 | mutating func append(compose: (inout Self) throws -> Void) rethrows {
211 | try compose(&self)
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/Sources/Patterns/General/Group.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Group.swift
3 | // Patterns
4 | //
5 | // Created by Kåre Morstøl on 23/04/2019.
6 | //
7 |
8 | /// Works like a set, except it cannot list its contents.
9 | /// It can only tell whether or not it contains a specific element.
10 | @usableFromInline
11 | struct Group {
12 | /// Returns true if this group contains `element`.
13 | @usableFromInline
14 | let contains: (Element) -> Bool
15 |
16 | /// A new group containing only elements for which `contains` returns true.
17 | @usableFromInline
18 | init(contains: @escaping (Element) -> Bool) {
19 | self.contains = contains
20 | }
21 |
22 | /// Returns true if this group contains all the elements in `sequence`.
23 | @usableFromInline
24 | func contains(contentsOf sequence: S) -> Bool where S.Element == Element {
25 | sequence.allSatisfy(contains)
26 | }
27 | }
28 |
29 | extension Group {
30 | /// Returns a group which contains all the elements of `self` and `other`.
31 | @usableFromInline
32 | func union(_ other: Group) -> Group {
33 | Group { self.contains($0) || other.contains($0) }
34 | }
35 |
36 | @usableFromInline
37 | static func || (a: Group, b: Group) -> Group {
38 | a.union(b)
39 | }
40 |
41 | /// Returns a group containing only elements that are both in `self` and `other`.
42 | @usableFromInline
43 | func intersection(_ other: Group) -> Group {
44 | Group { self.contains($0) && other.contains($0) }
45 | }
46 |
47 | /// Returns a group containing only elements that are in `self` but not `other`.
48 | @usableFromInline
49 | func subtracting(_ other: Group) -> Group {
50 | Group { self.contains($0) && !other.contains($0) }
51 | }
52 |
53 | /// Returns a group containing only elements that _not_ in `self`.
54 | @usableFromInline
55 | func inverted() -> Group {
56 | Group { !self.contains($0) }
57 | }
58 | }
59 |
60 | extension Group where Element: Hashable {
61 | /// A new group containing only elements that are in `set`.
62 | @usableFromInline
63 | init(contentsOf set: Set) {
64 | contains = set.contains
65 | }
66 |
67 | /// A new group containing only elements that are in `sequence`.
68 | @usableFromInline
69 | init(contentsOf sequence: S) where S: Sequence, Element == S.Element {
70 | self.init(contentsOf: Set(sequence))
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Sources/Patterns/Grammar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Grammar.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 27/05/2020.
6 | //
7 |
8 | /// Allows for recursive patterns, also indirectly.
9 | ///
10 | /// Define subpatterns using `<-`, like this arithmetic pattern:
11 | /// ```
12 | /// let g = Grammar { g in
13 | /// g.all <- g.expr • !any
14 | /// g.expr <- g.sum
15 | /// g.sum <- g.product • (("+" / "-") • g.product)*
16 | /// g.product <- g.power • (("*" / "/") • g.power)*
17 | /// g.power <- g.value • ("^" • g.power)¿
18 | /// g.value <- digit+ / "(" • g.expr • ")"
19 | /// }
20 | /// ```
21 | /// This recognises e.g. "1+2-3*(4+3)"
22 | ///
23 | /// - warning: Does not support left recursion:
24 | /// ```
25 | /// g.a <- g.a • g.b
26 | /// ```
27 | /// will lead to infinite recursion.
28 | @dynamicMemberLookup
29 | public class Grammar: Pattern where Input.Element: Hashable {
30 | /// Calls another subpattern in a grammar.
31 | public struct CallPattern: Pattern {
32 | /// The grammar that contains the subpattern being called.
33 | public let grammar: Grammar
34 | /// The name of the subpattern being called.
35 | public let name: String
36 | public var description: String { "<\(name)>" }
37 |
38 | @inlinable
39 | init(grammar: Grammar, name: String) {
40 | self.grammar = grammar
41 | self.name = name
42 | }
43 |
44 | @inlinable
45 | public func createInstructions(_ instructions: inout ContiguousArray>) {
46 | instructions.append(.openCall(name: name))
47 | }
48 | }
49 |
50 | public var description: String { "Grammar" } // TODO:
51 |
52 | /// All the subpatterns and their names.
53 | public internal(set) var patterns: [(name: String, pattern: AnyPattern)] = []
54 |
55 | /// The main subpattern, which will be called when this Grammar is being used.
56 | public var firstPattern: String? { patterns.first?.name }
57 |
58 | @inlinable
59 | public init() {}
60 |
61 | @inlinable
62 | public init() where Input == String {}
63 |
64 | @inlinable
65 | public convenience init(_ closure: (Grammar) -> Void) {
66 | self.init()
67 | closure(self)
68 | }
69 |
70 | @inlinable
71 | public convenience init(_ closure: (Grammar) -> Void) where Input == String {
72 | self.init()
73 | closure(self)
74 | }
75 |
76 | @inlinable
77 | /// Allows the use of e.g. `g.a` to refer to subpatterns.
78 | public subscript(dynamicMember name: String) -> CallPattern {
79 | CallPattern(grammar: self, name: name)
80 | }
81 |
82 | @inlinable
83 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
84 | // We begin with a call to the first subpattern, followed by a jump to the end.
85 | // This enables this grammar to be used inside other patterns (including other grammars).
86 |
87 | let startIndex = instructions.endIndex
88 | instructions.append(
89 | .openCall(name: try firstPattern ?? Parser.PatternError.message("Grammar is empty.")))
90 | instructions.append(.jump(offset: .max)) // replaced later
91 | var callTable = [String: Range]()
92 |
93 | // Create instructions for all subpatterns. Store their positions in `callTable`.
94 | for (name, pattern) in patterns {
95 | let startIndex = instructions.endIndex
96 | try pattern.createInstructions(&instructions)
97 | instructions.append(.return)
98 | guard (startIndex ..< instructions.endIndex).count > 1 else {
99 | throw Parser.PatternError.message("Pattern '\(name) <- \(pattern)' was empty.")
100 | }
101 | callTable[name] = startIndex ..< instructions.endIndex
102 | }
103 |
104 | // Replace all `.openCall` with `.call(offset)` and the correct offsets.
105 | for i in instructions.indices[startIndex...] {
106 | if case let .openCall(name) = instructions[i] {
107 | guard let subpatternRange = callTable[name] else {
108 | throw Parser.PatternError.message("Pattern '\(name)' was never defined with ´<-´ operator.")
109 | }
110 | // If the last non-dummy (i.e. not .choiceEnd) instruction in a subpattern is a call to itself we
111 | // perform a tail call optimisation by jumping directly instead.
112 | // The very last instruction is a .return, so skip that.
113 | if subpatternRange.upperBound - 2 == i
114 | || (subpatternRange.upperBound - 3 == i && instructions[i + 1].doesNotDoAnything) {
115 | instructions[i] = .jump(offset: subpatternRange.lowerBound - i)
116 | } else {
117 | instructions[i] = .call(offset: subpatternRange.lowerBound - i)
118 | }
119 | }
120 | }
121 |
122 | instructions[startIndex + 1] = .jump(offset: instructions.endIndex - startIndex - 1)
123 | }
124 |
125 | public static func == (lhs: Grammar, rhs: Grammar) -> Bool {
126 | lhs.patterns.elementsEqual(rhs.patterns, by: { $0.name == $1.name && $0.pattern == $1.pattern })
127 | }
128 | }
129 |
130 | infix operator <-: AssignmentPrecedence
131 |
132 | /// Used by grammars to define subpatterns with `g.a <- ...`.
133 | public func <- (call: Grammar.CallPattern, pattern: P) {
134 | call.grammar.patterns.append((call.name, AnyPattern(pattern)))
135 | }
136 |
137 | /// In case of `g.name <- Capture(...)`, names the nameless Capture "name".
138 | public func <- (call: Grammar.CallPattern, capture: Capture
) {
139 | let newPattern = capture.name == nil
140 | ? Capture(name: call.name, capture.wrapped)
141 | : capture
142 | call.grammar.patterns.append((call.name, AnyPattern(newPattern)))
143 | }
144 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/And.swift:
--------------------------------------------------------------------------------
1 | //
2 | // And.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 04/06/2020.
6 | //
7 |
8 | /// A pattern which matches the `wrapped` pattern, without consuming any input.
9 | public struct AndPattern: Pattern {
10 | public typealias Input = Wrapped.Input
11 | public let wrapped: Wrapped
12 | public var description: String { "&\(wrapped)" }
13 |
14 | @usableFromInline
15 | init(_ wrapped: Wrapped) {
16 | self.wrapped = wrapped
17 | }
18 |
19 | @inlinable
20 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
21 | let wrappedInstructions = try wrapped.createInstructions()
22 | if let indexMovedBy = wrappedInstructions.movesIndexBy() {
23 | instructions.append(contentsOf: wrappedInstructions)
24 | instructions.append(.moveIndex(offset: -indexMovedBy))
25 | } else {
26 | instructions.append { // TODO: test. And keep any captures.
27 | $0.append(.choice(offset: wrappedInstructions.count + 4))
28 | $0.append(.choice(offset: wrappedInstructions.count + 1))
29 | $0.append(contentsOf: wrappedInstructions)
30 | $0.append(.commit)
31 | $0.append(.fail)
32 | }
33 | }
34 | }
35 | }
36 |
37 | prefix operator &&
38 |
39 | extension Pattern {
40 | /// Matches the following pattern without consuming any input.
41 | ///
42 | /// - note: in standard PEG this operator is `&`, but that is not allowed in Swift.
43 | @inlinable
44 | public static prefix func && (me: Self) -> AndPattern {
45 | AndPattern(me)
46 | }
47 | }
48 |
49 | extension Literal {
50 | /// Matches the following pattern without consuming any input.
51 | ///
52 | /// - note: in standard PEG this operator is `&`, but that is not allowed in Swift.
53 | @inlinable
54 | public static prefix func && (me: Literal) -> AndPattern {
55 | AndPattern(me)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/AnyPattern.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringInterpolation.swift
3 | // Patterns
4 | //
5 | // Created by Kåre Morstøl on 11/08/2019.
6 | //
7 |
8 | /// A type erased wrapper around a pattern.
9 | /// Can be used to store patterns in arrays and non-generic variables.
10 | public struct AnyPattern: Pattern where Input.Element: Hashable {
11 | @usableFromInline
12 | let _instructions: (inout ContiguousArray>) throws -> Void
13 |
14 | @inlinable
15 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
16 | try _instructions(&instructions)
17 | }
18 |
19 | private let _description: () -> String
20 | public var description: String { _description() }
21 |
22 | /// The wrapped pattern. If you know the exact type you can unwrap it again.
23 | public let wrapped: Any
24 |
25 | public init(_ p: P) where Input == P.Input {
26 | _instructions = p.createInstructions
27 | _description = { p.description }
28 | wrapped = p
29 | }
30 |
31 | @inlinable
32 | public init(_ p: AnyPattern) {
33 | self = p
34 | }
35 |
36 | public init(_ p: Literal) {
37 | _instructions = p.createInstructions
38 | _description = { p.description }
39 | wrapped = p
40 | }
41 |
42 | public static func == (lhs: AnyPattern, rhs: AnyPattern) -> Bool {
43 | lhs.description == rhs.description
44 | }
45 | }
46 |
47 | extension AnyPattern: ExpressibleByUnicodeScalarLiteral where Input == String {
48 | @inlinable
49 | public init(unicodeScalarLiteral value: String) {
50 | self.init(stringLiteral: String(describing: value))
51 | }
52 | }
53 |
54 | extension AnyPattern: ExpressibleByExtendedGraphemeClusterLiteral where Input == String {
55 | public typealias ExtendedGraphemeClusterLiteralType = String
56 | }
57 |
58 | extension AnyPattern: ExpressibleByStringLiteral where Input == String {
59 | public typealias StringLiteralType = String
60 | }
61 |
62 | /// Allows AnyPattern to be defined by a string with patterns in interpolations.
63 | ///
64 | /// `let p: AnyPattern = "hi\(whitespace)there"`
65 | /// is the same as `"hi" • whitespace • "there"`.
66 | extension AnyPattern: ExpressibleByStringInterpolation where Input == String {
67 | public struct StringInterpolation: StringInterpolationProtocol {
68 | @usableFromInline
69 | var pattern = AnyPattern("")
70 |
71 | @inlinable
72 | public init(literalCapacity: Int, interpolationCount: Int) {}
73 |
74 | @inlinable
75 | public mutating func appendLiteral(_ literal: String) {
76 | if !literal.isEmpty {
77 | pattern = AnyPattern(pattern • Literal(literal))
78 | }
79 | }
80 |
81 | @inlinable
82 | public mutating func appendInterpolation(_ newpattern: P) where P.Input == Input {
83 | pattern = AnyPattern(pattern • newpattern)
84 | }
85 | }
86 |
87 | @inlinable
88 | public init(stringLiteral value: String) {
89 | self.init(Literal(value))
90 | }
91 |
92 | @inlinable
93 | public init(stringInterpolation: StringInterpolation) {
94 | self.init(stringInterpolation.pattern)
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/Capture.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Capture.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 25/05/2020.
6 | //
7 |
8 | /// Captures the current position as a range.
9 | ///
10 | /// It can be retrieved in `Parser.Match.captures` or used for decoding into Decodables.
11 | public struct Capture: Pattern {
12 | public typealias Input = Wrapped.Input
13 | public var description: String {
14 | let result: String
15 | switch (name, wrapped) {
16 | case (nil, is NoPattern):
17 | result = ""
18 | case let (name?, is NoPattern):
19 | result = "name: \(name)"
20 | case let (name?, wrapped):
21 | result = "name: \(name), \(wrapped)"
22 | case let (nil, wrapped):
23 | result = wrapped.description
24 | }
25 | return "Capture(\(result))"
26 | }
27 |
28 | public let name: String?
29 | public let wrapped: Wrapped
30 |
31 | /// Captures the position of `wrapped` as a range.
32 | /// - Parameters:
33 | /// - name: optional name
34 | @inlinable
35 | public init(name: String? = nil, _ wrapped: Wrapped) {
36 | self.name = name
37 | self.wrapped = wrapped
38 | }
39 |
40 | @inlinable
41 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
42 | instructions.append(.captureStart(name: name))
43 | try wrapped.createInstructions(&instructions)
44 | instructions.append(.captureEnd)
45 | }
46 | }
47 |
48 | extension Capture {
49 | /// Captures the current input position as an empty range.
50 | /// - Parameter name: optional name
51 | @inlinable
52 | public init(name: String? = nil) where Wrapped == NoPattern {
53 | self.wrapped = NoPattern()
54 | self.name = name
55 | }
56 |
57 | /// Captures the current input position as an empty range.
58 | /// - Parameter name: optional name
59 | @inlinable
60 | public init(name: String? = nil) where Wrapped == NoPattern {
61 | self.wrapped = NoPattern()
62 | self.name = name
63 | }
64 |
65 | /// Captures the position of `wrapped` as a range.
66 | /// - Parameter name: optional name
67 | @inlinable
68 | public init(name: String? = nil, _ wrapped: Literal) where Wrapped == Literal {
69 | self.wrapped = wrapped
70 | self.name = name
71 | }
72 | }
73 |
74 | /// A pattern that does absolutely nothing.
75 | public struct NoPattern: Pattern where Input.Element: Hashable {
76 | public var description: String { "" }
77 |
78 | @inlinable
79 | public init() {}
80 |
81 | @inlinable
82 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {}
83 | }
84 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/Choice.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftPattern.swift
3 | // Patterns
4 | //
5 | // Created by Kåre Morstøl on 20/03/2017.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /// A pattern which first tries the `first` pattern,
12 | /// if that fails it tries the `second` pattern from the same position.
13 | public struct OrPattern: Pattern where First.Input == Second.Input {
14 | public typealias Input = First.Input
15 | public let first: First
16 | public let second: Second
17 |
18 | @inlinable
19 | init(_ first: First, or second: Second) {
20 | self.first = first
21 | self.second = second
22 | }
23 |
24 | public var description: String {
25 | "(\(first) / \(second))"
26 | }
27 |
28 | @inlinable
29 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
30 | let inst1 = try first.createInstructions()
31 | let inst2 = try second.createInstructions()
32 | instructions.append(.choice(offset: inst1.count + 3))
33 | instructions.append(contentsOf: inst1)
34 | instructions.append(.commit)
35 | instructions.append(.jump(offset: inst2.count + 2))
36 | instructions.append(contentsOf: inst2)
37 | instructions.append(.choiceEnd)
38 | }
39 | }
40 |
41 | /// First tries the pattern to the left,
42 | /// if that fails it tries the pattern to the right from the same position.
43 | @inlinable
44 | public func / (p1: First, p2: Second) -> OrPattern {
45 | OrPattern(p1, or: p2)
46 | }
47 |
48 | /// First tries the pattern to the left,
49 | /// if that fails it tries the pattern to the right from the same position.
50 | @inlinable
51 | public func / (p1: Literal, p2: Second) -> OrPattern, Second> {
52 | OrPattern(p1, or: p2)
53 | }
54 |
55 | /// First tries the pattern to the left,
56 | /// if that fails it tries the pattern to the right from the same position.
57 | @inlinable
58 | public func / (p1: First, p2: Literal) -> OrPattern> {
59 | OrPattern(p1, or: p2)
60 | }
61 |
62 | /// First tries the pattern to the left,
63 | /// if that fails it tries the pattern to the right from the same position.
64 | @inlinable
65 | public func / (p1: Literal, p2: Literal) -> OrPattern, Literal> {
66 | OrPattern(p1, or: p2)
67 | }
68 |
69 | /// First tries the pattern to the left,
70 | /// if that fails it tries the pattern to the right from the same position.
71 | @inlinable
72 | public func / (p1: Literal, p2: Literal) -> OrPattern, Literal> {
73 | OrPattern(p1, or: p2)
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/Concatenation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Concatenation.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 21/05/2020.
6 | //
7 |
8 | precedencegroup PatternConcatenationPrecedence {
9 | associativity: left
10 | higherThan: MultiplicationPrecedence // `/` has this
11 | }
12 |
13 | infix operator •: PatternConcatenationPrecedence
14 |
15 | /// A pattern which first tries the `first` pattern,
16 | /// if that succeeds it continues with the `second` pattern.
17 | public struct Concat: Pattern where First.Input == Second.Input {
18 | public typealias Input = First.Input
19 | public let first: First
20 | public let second: Second
21 | public var description: String { "\(first) \(second)" }
22 |
23 | @inlinable
24 | init(_ first: First, _ second: Second) {
25 | self.first = first
26 | self.second = second
27 | }
28 |
29 | @inlinable
30 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
31 | try first.createInstructions(&instructions)
32 | try second.createInstructions(&instructions)
33 | }
34 | }
35 |
36 | /// First tries the pattern to the left, if that succeeds it tries the pattern to the right.
37 | @inlinable
38 | public func • (lhs: Left, rhs: Right) -> Concat where Left.Input == Right.Input {
39 | Concat(lhs, rhs)
40 | }
41 |
42 | /// First tries the pattern to the left, if that succeeds it tries the pattern to the right.
43 | @inlinable
44 | public func • (lhs: Literal, rhs: Right) -> Concat, Right> {
45 | Concat(lhs, rhs)
46 | }
47 |
48 | /// First tries the pattern to the left, if that succeeds it tries the pattern to the right.
49 | @inlinable
50 | public func • (lhs: Left, rhs: Literal) -> Concat> {
51 | Concat(lhs, rhs)
52 | }
53 |
54 | /// First tries the pattern to the left, if that succeeds it tries the pattern to the right.
55 | @inlinable
56 | public func • (lhs: Literal, rhs: Literal) -> Concat, Literal> {
57 | Concat(lhs, rhs)
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/Not.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Negation.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 25/05/2020.
6 | //
7 |
8 | /// A pattern which only succeeds if the `wrapped` pattern fails.
9 | /// The next pattern will continue from where `wrapped` started.
10 | public struct NotPattern: Pattern {
11 | public typealias Input = Wrapped.Input
12 | public let wrapped: Wrapped
13 | public var description: String { "!\(wrapped)" }
14 |
15 | @inlinable
16 | init(_ wrapped: Wrapped) {
17 | self.wrapped = wrapped
18 | }
19 |
20 | @inlinable
21 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
22 | let wrappedInstructions = try wrapped.createInstructions()
23 | instructions.append(.choice(offset: wrappedInstructions.count + 3))
24 | instructions.append(contentsOf: wrappedInstructions)
25 | instructions.append(.commit)
26 | instructions.append(.fail)
27 | }
28 | }
29 |
30 | /// Will only succeed if the following pattern fails. Does not consume any input.
31 | @inlinable
32 | public prefix func ! (pattern: P) -> NotPattern
{
33 | NotPattern(pattern)
34 | }
35 |
36 | /// Will only succeed if the following pattern fails. Does not consume any input.
37 | @inlinable
38 | public prefix func ! (pattern: Literal) -> NotPattern> {
39 | NotPattern(pattern)
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/Repetition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Repetition.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 25/05/2020.
6 | //
7 |
8 | /// Repeats the `wrapped` pattern `min` times, then repeats it optionally `max-min` times.
9 | /// Or an unlimited number of times if max is nil.
10 | ///
11 | /// Used by operators `*+¿`.
12 | public struct RepeatPattern: Pattern {
13 | public typealias Input = Wrapped.Input
14 | public let wrapped: Wrapped
15 | public let min: Int
16 | public let max: Int?
17 |
18 | @inlinable
19 | init(_ wrapped: Wrapped, range: R) where R.Bound == Int {
20 | let actualRange = range.relative(to: Int.zero ..< Int.max)
21 | self.wrapped = wrapped
22 | self.min = actualRange.lowerBound
23 | self.max = actualRange.upperBound == Int.max ? nil : actualRange.upperBound - 1
24 | }
25 |
26 | public var description: String {
27 | "\(wrapped){\(min)...\(max.map(String.init) ?? "")}"
28 | }
29 |
30 | @inlinable
31 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
32 | let repeatedInstructions = try wrapped.createInstructions()
33 | for _ in 0 ..< min { instructions.append(contentsOf: repeatedInstructions) }
34 | if let max = max {
35 | let optionalRepeatedInstructions = ContiguousArray> {
36 | $0.append(.choice(offset: repeatedInstructions.count + 2))
37 | $0.append(contentsOf: repeatedInstructions)
38 | $0.append(.commit)
39 | }
40 | instructions.append(contentsOf: repeatElement(optionalRepeatedInstructions, count: max - min).lazy.flatMap { $0 })
41 | } else {
42 | instructions.append {
43 | $0.append(.choice(offset: repeatedInstructions.count + 3))
44 | $0.append(contentsOf: repeatedInstructions)
45 | $0.append(.commit)
46 | $0.append(.jump(offset: -repeatedInstructions.count - 2))
47 | }
48 | }
49 | }
50 | }
51 |
52 | extension Pattern {
53 | /// Repeats this pattern from `range.lowerBound` to `range.upperBound` times.
54 | @inlinable
55 | public func `repeat`(_ range: R) -> RepeatPattern where R.Bound == Int {
56 | return RepeatPattern(self, range: range)
57 | }
58 |
59 | /// Repeats this pattern `count` times.
60 | @inlinable
61 | public func `repeat`(_ count: Int) -> RepeatPattern {
62 | RepeatPattern(self, range: count ... count)
63 | }
64 | }
65 |
66 | postfix operator *
67 |
68 | /// Repeats the preceding pattern 0 or more times.
69 | @inlinable
70 | public postfix func * (me: P) -> RepeatPattern
{
71 | me.repeat(0...)
72 | }
73 |
74 | /// Repeats the preceding pattern 0 or more times.
75 | @inlinable
76 | public postfix func * (me: Literal) -> RepeatPattern> {
77 | me.repeat(0...)
78 | }
79 |
80 | postfix operator +
81 |
82 | /// Repeats the preceding pattern 1 or more times.
83 | @inlinable
84 | public postfix func + (me: P) -> RepeatPattern
{
85 | me.repeat(1...)
86 | }
87 |
88 | /// Repeats the preceding pattern 1 or more times.
89 | @inlinable
90 | public postfix func + (me: Literal) -> RepeatPattern> {
91 | me.repeat(1...)
92 | }
93 |
94 | postfix operator ¿
95 |
96 | /// Tries the preceding pattern, and continues even if it fails.
97 | ///
98 | /// - note: in standard PEG this operator is `?`, but that is not allowed in Swift.
99 | @inlinable
100 | public postfix func ¿ (me: P) -> RepeatPattern
{
101 | me.repeat(0 ... 1)
102 | }
103 |
104 | /// Tries the preceding pattern, and continues even if it fails.
105 | ///
106 | /// - note: in standard PEG this operator is `?`, but that is not allowed in Swift.
107 | @inlinable
108 | public postfix func ¿ (me: Literal) -> RepeatPattern> {
109 | me.repeat(0 ... 1)
110 | }
111 |
--------------------------------------------------------------------------------
/Sources/Patterns/Operations on Patterns/Skip.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Skip.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 25/05/2020.
6 | //
7 |
8 | /// Skips 0 or more elements until a match for the next patterns is found.
9 | ///
10 | /// ```swift
11 | /// let s = Skip() • a
12 | /// ```
13 | /// is the same as `|S <- A / . |` in standard PEG.
14 | ///
15 | /// - note:
16 | /// If `Skip` is at the end of a pattern, it just succeeds without consuming input. So it will be pointless.
17 | ///
18 | /// But this works:
19 | /// ```swift
20 | /// let s = Skip()
21 | /// let p = s • " "
22 | /// ```
23 | /// because here the `s` pattern is "inlined".
24 | ///
25 | /// This, however, does not work:
26 | /// ```swift
27 | /// let g = Grammar { g in
28 | /// g.nextSpace <- g.skip • " "
29 | /// g.skip <- Skip()
30 | /// }
31 | /// ```
32 | /// because in grammars the subexpressions are _called_, like functions, not "_inlined_", like Swift variables.
33 | /// So the `Skip()` in `g.skip` can't tell what will come after it.
34 | public struct Skip: Pattern where Input.Element: Hashable {
35 | public var description: String { "Skip()" }
36 |
37 | @inlinable
38 | public init() {}
39 |
40 | @inlinable
41 | public init() where Input == String {}
42 |
43 | @inlinable
44 | public func createInstructions(_ instructions: inout ContiguousArray>) throws {
45 | instructions.append(.skip)
46 | }
47 | }
48 |
49 | import SE0270_RangeSet
50 |
51 | extension ContiguousArray {
52 | /// Replaces all placeholder `.skip` instructions.
53 | @_specialize(where Input == String, Element == Instruction) // doesn't happen automatically (swiftlang-1200.0.28.1).
54 | @_specialize(where Input == String.UTF8View, Element == Instruction)
55 | @usableFromInline
56 | mutating func replaceSkips() where Element == Instruction {
57 | // `setupSkip(at: i)` adds 1 new instruction somewhere after `ì`, so we cant loop over self.indices directly.
58 | var i = self.startIndex
59 | while i < self.endIndex {
60 | switch self[i] {
61 | case .skip:
62 | self.setupSkip(at: i)
63 | default: break
64 | }
65 | self.formIndex(after: &i)
66 | }
67 | }
68 |
69 | /// Replaces the dummy `.skip` instruction at `skipIndex` with one that will search using the instructions
70 | /// right after `skipIndex`.
71 | ///
72 | /// In other words we look at the instructions right after the .skip and see if they can be searched for
73 | /// efficiently.
74 | ///
75 | /// Also places a .choice right after the search instruction replacing the .skip, and a corresponding .commit
76 | /// somewhere after that again. So if the search succeeds, but a later instruction fails, we can start a new
77 | /// search one step ahead from where the previous search succeeded.
78 | /// In the sub-pattern `Skip() • "abc" • letter • Skip() • "xyz"`, if "abc" succeeds, but there is no
79 | /// letter afterwards, we search for "abc" again from the "b". But if there is "abc" and another letter,
80 | /// we don't search for "abc" again because the next instruction is another .skip, and if we can't find "xyz"
81 | /// further on there's no point in searching for "abc" again.
82 | ///
83 | /// See `placeSkipCommit` for more.
84 | @usableFromInline
85 | mutating func setupSkip(at skipIndex: Index) where Element == Instruction {
86 | let afterSkip = skipIndex + 1
87 | switch self[afterSkip] {
88 | case let .checkIndex(function, atIndexOffset: 0):
89 | self[skipIndex] = .search { input, index in
90 | input[index...].indices.first(where: { function(input, $0) })
91 | ?? (function(input, input.endIndex) ? input.endIndex : nil)
92 | }
93 | self[afterSkip] = .choice(offset: -1, atIndexOffset: +1)
94 | case .checkIndex(_, atIndexOffset: _):
95 | // A `.checkIndex` will only have a non-zero offset if it has been moved by `moveMovablesForward`,
96 | // and that will never move anything beyond a `.skip`.
97 | fatalError("A `.checkIndex` with a non-zero offset can't be located right after a `.skip` instruction.")
98 | case let .checkElement(test):
99 | self[skipIndex] = .search { input, index in
100 | input[index...].firstIndex(where: test)
101 | .map(input.index(after:))
102 | }
103 | self[afterSkip] = .choice(offset: -1, atIndexOffset: 0)
104 | case .elementEquals:
105 | let elements: [Input.Element] = self[afterSkip...]
106 | .mapPrefix {
107 | switch $0 {
108 | case let .elementEquals(element):
109 | return element
110 | default:
111 | return nil
112 | }
113 | }
114 | if elements.count == 1 {
115 | self[skipIndex] = .search { input, index in
116 | input[index...].firstIndex(of: elements[0])
117 | .map(input.index(after:))
118 | }
119 | self[afterSkip] = .choice(offset: -1, atIndexOffset: 0)
120 | } else {
121 | // More than one literal, use Boyer–Moore–Horspool search.
122 | let cache = SearchCache(elements)
123 | self[skipIndex] = .search { input, index in
124 | input.range(of: cache, from: index)?.upperBound
125 | }
126 | self[afterSkip] = .choice(offset: -1, atIndexOffset: (-elements.count) + 1)
127 | self[afterSkip + 1] = .jump(offset: elements.count - 1)
128 | }
129 | default:
130 | // Could not find instructions to search for efficiently,
131 | // so we just try them and if they fail we move one step forward and try again.
132 | self[skipIndex] = .choice(offset: 0, atIndexOffset: +1)
133 | self.placeSkipCommit(startSearchFrom: skipIndex + 1)
134 | return
135 | }
136 | self.placeSkipCommit(startSearchFrom: skipIndex + 2)
137 | }
138 |
139 | /// Places a .commit after replacing a .skip .
140 | ///
141 | /// Any instruction replacing a .skip will have a .choice right after it.
142 | /// We place the corresponding .commit as far after it as possible.
143 | /// As always we have to make sure that no pairs of corresponding .choice (or other instruction) and .commit
144 | /// intersect with any other pair.
145 | ///
146 | /// So we have to jump over any optional repetition (`¿+*` and `.repeat(range)`) and any `/` choice patterns.
147 | /// All of them use the `.choice` instruction.
148 | /// If we are inside any of these we put the .commit at the end of our part of the pattern.
149 | @usableFromInline
150 | mutating func placeSkipCommit(startSearchFrom: Index) where Element == Instruction {
151 | var i = startSearchFrom
152 | loop: while true {
153 | switch self[i] {
154 | case let .choice(_, indexOffset) where indexOffset < 0:
155 | fatalError("Not implemented.")
156 | case let .choice(offset, _):
157 | // We jump over this entire sub-pattern.
158 | // If one step back there is a jump forwards, then it's a '/' pattern. So follow that jump too.
159 | if case let .jump(jumpOffset) = self[i + offset - 1], jumpOffset > 0 {
160 | i += offset - 1 + jumpOffset
161 | } else {
162 | i += offset
163 | }
164 | case let .jump(offset) where offset > 0: // If we jump backwards we are likely to enter an infinite loop.
165 | i += offset
166 | case .elementEquals, .checkElement, .checkIndex, .moveIndex, .captureStart, .captureEnd, .call, .jump:
167 | i += 1
168 | case .commit, .choiceEnd, .return, .match, .skip, .search, .fail:
169 | // This is as far as we can go.
170 | insertInstructions(.commit, at: i)
171 | return
172 | case .openCall:
173 | fatalError("`.openCall` instruction should have been replaced.")
174 | }
175 | }
176 | }
177 |
178 | /// Inserts `newInstructions` at `location`. Adjusts the offsets of the other instructions accordingly.
179 | ///
180 | /// Since all offsets are relative to the positions of their instructions,
181 | /// if `location` lies between an instruction with an offset and where that offset leads to,
182 | /// the offset needs to be increased by the length of `newInstructions`.
183 | @usableFromInline
184 | mutating func insertInstructions(_ newInstructions: Element..., at location: Index)
185 | where Element == Instruction {
186 | insert(contentsOf: newInstructions, at: location)
187 | let insertedRange = location ..< (location + newInstructions.count + 1)
188 | // instruction ... location ... offsetTarget
189 | for i in startIndex ..< insertedRange.lowerBound {
190 | switch self[i] {
191 | case let .call(offset) where offset > (location - i):
192 | self[i] = .call(offset: offset + newInstructions.count)
193 | case let .jump(offset) where offset > (location - i):
194 | self[i] = .jump(offset: offset + newInstructions.count)
195 | case let .choice(offset, atIndexOffset) where offset > (location - i):
196 | self[i] = .choice(offset: offset + newInstructions.count, atIndexOffset: atIndexOffset)
197 | default:
198 | break
199 | }
200 | }
201 | // offsetTarget ... location ... instruction
202 | for i in insertedRange.upperBound ..< endIndex {
203 | switch self[i] {
204 | case let .call(offset) where offset < (location - i):
205 | self[i] = .call(offset: offset - newInstructions.count)
206 | case let .jump(offset) where offset < (location - i):
207 | self[i] = .jump(offset: offset - newInstructions.count)
208 | case let .choice(offset, atIndexOffset) where offset < (location - i):
209 | self[i] = .choice(offset: offset - newInstructions.count, atIndexOffset: atIndexOffset)
210 | default:
211 | break
212 | }
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/Sources/Patterns/Optimise Instructions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Optimise Instructions.swift
3 | //
4 | // Created by Kåre Morstøl on 17/06/2020.
5 | //
6 |
7 | private extension Instruction {
8 | /// Can this instruction be moved by `moveMovablesForward`?
9 | var isMovable: Bool {
10 | switch self {
11 | case .checkIndex, .captureStart, .captureEnd:
12 | return true
13 | default:
14 | return false
15 | }
16 | }
17 |
18 | /// Does this instruction prohibit `moveMovablesForward` from moving anything past it?
19 | var stopsMovables: Bool {
20 | switch self {
21 | case .elementEquals, .checkElement:
22 | return false
23 | default:
24 | return true
25 | }
26 | }
27 | }
28 |
29 | import SE0270_RangeSet
30 |
31 | extension MutableCollection where Self: RandomAccessCollection, Index == Int {
32 | /// Moves any `.checkIndex`, `.captureStart`, `.captureEnd` past any `.elementEquals`, `.checkElement`.
33 | ///
34 | /// Improves performance noticeably.
35 | @usableFromInline
36 | mutating func moveMovablesForward() where Element == Instruction {
37 | var movables = ContiguousArray()
38 | for i in indices {
39 | if self[i].isMovable {
40 | movables.append(i)
41 | } else if !movables.isEmpty, self[i].stopsMovables {
42 | let moved = moveSubranges(RangeSet(movables, within: self), to: i)
43 | var checkIndexIndices = RangeSet()
44 | for (movedIndex, oldPosition) in zip(moved, movables) {
45 | let distanceMoved = (movedIndex - oldPosition)
46 | switch self[movedIndex] {
47 | case let .captureStart(name, offset):
48 | self[movedIndex] = .captureStart(name: name, atIndexOffset: offset - distanceMoved)
49 | case let .captureEnd(offset):
50 | self[movedIndex] = .captureEnd(atIndexOffset: offset - distanceMoved)
51 | case let .checkIndex(test, offset):
52 | self[movedIndex] = .checkIndex(test, atIndexOffset: offset - distanceMoved)
53 | checkIndexIndices.insert(movedIndex, within: self)
54 | default:
55 | fatalError("'\(self[movedIndex])' is not a 'movable'.")
56 | }
57 | }
58 | movables.removeAll()
59 |
60 | // All `.checkIndex` should be first. If they fail there is no point in capturing anything.
61 | moveSubranges(checkIndexIndices, to: moved.lowerBound)
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Sources/Patterns/Parser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Parser.swift
3 | // Patterns
4 | //
5 | // Created by Kåre Morstøl on 23/10/2018.
6 | //
7 |
8 | /// Takes a pattern, optimises it and tries to match it over an input.
9 | public struct Parser where Input.Element: Hashable {
10 | /// Indicates a problem with a malformed pattern.
11 | public enum PatternError: Error, CustomStringConvertible {
12 | /// The error message from the parser.
13 | case message(String)
14 |
15 | public var description: String {
16 | switch self {
17 | case let .message(string):
18 | return string
19 | }
20 | }
21 | }
22 |
23 | @usableFromInline
24 | let matcher: VMEngine
25 |
26 | /// A parser which matches `pattern` _at_ a given position.
27 | @inlinable
28 | public init(_ pattern: P) throws where P.Input == Input {
29 | self.matcher = try VMEngine(pattern)
30 | }
31 |
32 | /// A parser which searches for `pattern` _from_ a given position.
33 | ///
34 | /// Is the same as `Parser(Skip() • pattern)`.
35 | @inlinable
36 | public init(search pattern: P) throws where P.Input == Input {
37 | try self.init(Skip() • pattern)
38 | }
39 |
40 | /// Contains information about a patterns successfully completed match.
41 | public struct Match: Equatable {
42 | /// The position in the input when the pattern completed.
43 | ///
44 | /// - note: If the last part of the pattern is a `!` or `&&`,
45 | /// `endIndex` is the position when that last part _started_.
46 | public let endIndex: Input.Index
47 |
48 | /// The names and ranges of all captures.
49 | public let captures: [(name: String?, range: Range)]
50 |
51 | @inlinable
52 | init(endIndex: Input.Index, captures: [(name: String?, range: Range)]) {
53 | self.endIndex = endIndex
54 | self.captures = captures
55 | }
56 |
57 | @inlinable
58 | public static func == (lhs: Parser.Match, rhs: Parser.Match) -> Bool {
59 | lhs.endIndex == rhs.endIndex
60 | && lhs.captures.elementsEqual(rhs.captures, by: { left, right in
61 | left.range == right.range && left.name == right.name
62 | })
63 | }
64 |
65 | /// The range from the beginning of the first capture to the end of the last one.
66 | /// If there are no captures, the empty range at the `endIndex` of this Match.
67 | @inlinable
68 | public var range: Range {
69 | // TODO: Is `captures.last!.range.upperBound` always the highest captured index?
70 | // What if there is one large range and a smaller inside that?
71 | captures.isEmpty
72 | ? endIndex ..< endIndex
73 | : captures.first!.range.lowerBound ..< captures.last!.range.upperBound
74 | }
75 |
76 | public func description(using input: Input) -> String {
77 | """
78 | endIndex: "\(endIndex == input.endIndex ? "EOF" : String(describing: input[endIndex]))"
79 | \(captures.map { "\($0.name.map { $0 + ":" } ?? "") \(input[$0.range])" }.joined(separator: "\n"))
80 |
81 | """
82 | }
83 |
84 | /// Returns the first capture named `name`.
85 | @inlinable
86 | public subscript(one name: String) -> Range? {
87 | captures.first(where: { $0.name == name })?.range
88 | }
89 |
90 | /// Returns all captures named `name`.
91 | @inlinable
92 | public subscript(multiple name: String) -> [Range] {
93 | captures.filter { $0.name == name }.map { $0.range }
94 | }
95 |
96 | /// The names of all the captures.
97 | @inlinable
98 | public var captureNames: Set { Set(captures.compactMap { $0.name }) }
99 | }
100 |
101 | /// Tries to match the pattern in `input` at `index`.
102 | /// - Parameters:
103 | /// - index: The position to match at, if not provided the beginning of input will be used.
104 | @inlinable
105 | public func match(in input: Input, at index: Input.Index? = nil) -> Match? {
106 | matcher.match(in: input, at: index ?? input.startIndex)
107 | }
108 |
109 | /// A lazily generated sequence of consecutive matches of the pattern in `input`.
110 | ///
111 | /// Each match attempt starts at the `.range.upperBound` of the previous match,
112 | /// so the matches can be overlapping.
113 | ///
114 | /// You can dictate where the next match should start by where you place the last capture.
115 | ///
116 | /// - Parameters:
117 | /// - startindex: The position to match from, if not provided the beginning of input will be used.
118 | @inlinable
119 | public func matches(in input: Input, from startindex: Input.Index? = nil)
120 | -> UnfoldSequence {
121 | var stop = false
122 | var lastMatch: Match?
123 | return sequence(state: startindex ?? input.startIndex, next: { (index: inout Input.Index) in
124 | guard var match = self.match(in: input, at: index), !stop else { return nil }
125 | if match == lastMatch {
126 | guard index != input.endIndex else { return nil }
127 | input.formIndex(after: &index)
128 | guard let newMatch = self.match(in: input, at: index) else { return nil }
129 | match = newMatch
130 | }
131 | lastMatch = match
132 | let matchEnd = match.range.upperBound
133 | if matchEnd == index {
134 | guard matchEnd != input.endIndex else {
135 | stop = true
136 | return match
137 | }
138 | input.formIndex(after: &index)
139 | } else {
140 | index = matchEnd
141 | }
142 | return match
143 | })
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/Sources/Patterns/Pattern And Instruction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Pattern And Instruction.swift
3 | //
4 | //
5 | // Created by Kåre Morstøl on 06/06/2020.
6 | //
7 |
8 | /// Something that can create Instructions for the Parser.
9 | public protocol Pattern: CustomStringConvertible {
10 | associatedtype Input: BidirectionalCollection where Input.Element: Hashable
11 | typealias ParsedRange = Range
12 | // Ideally this should be used by all implementors, but that sometimes causes a compiler crash (Swift 5.3 beta)
13 | typealias Instructions = ContiguousArray>
14 |
15 | /// Appends Instructions for the Parser to `instructions`.
16 | @inlinable
17 | func createInstructions(_ instructions: inout Instructions) throws
18 | /// Returns Instructions for the Parser.
19 | @inlinable
20 | func createInstructions() throws -> Instructions
21 | }
22 |
23 | extension Pattern {
24 | /// Returns Instructions for the Parser.
25 | @inlinable
26 | public func createInstructions() throws -> Instructions {
27 | var instructions = Instructions()
28 | try self.createInstructions(&instructions)
29 | return instructions
30 | }
31 | }
32 |
33 | /// The instructions used by patterns in `createInstructions`.
34 | ///
35 | /// Unless otherwise noted, each instruction moves on to the next instruction after it has finished.
36 | public enum Instruction where Input.Element: Hashable {
37 | public typealias Distance = Int
38 |
39 | /// Succeeds if the current element equals this element. Advances index to the next element.
40 | case elementEquals(Input.Element)
41 | /// Succeeds if the closure returns true when passed the current element. Advances index to the next element.
42 | case checkElement((Input.Element) -> Bool)
43 | /// Succeeds if the closure returns true when passed the input and the input index + `atIndexOffset`.
44 | case checkIndex((Input, Input.Index) -> Bool, atIndexOffset: Int)
45 |
46 | /// Moves the input index by `offset`.
47 | case moveIndex(offset: Distance)
48 | /// Continues with the instruction at `offset` relative to this instruction.
49 | case jump(offset: Distance)
50 |
51 | /// Sets the input index to the output from the closure.
52 | /// If the output is nil, the instruction fails.
53 | case search((Input, Input.Index) -> Input.Index?)
54 |
55 | /// Stores (current input index - `atIndexOffset`) as the beginning of capture `name`
56 | case captureStart(name: String?, atIndexOffset: Int)
57 | /// Stores (current input index - `atIndexOffset`) as the end of the most recently started capture.
58 | case captureEnd(atIndexOffset: Int)
59 |
60 | /// Stores a snapshot of the current state, with input index set to (current + `atIndexOffset`).
61 | ///
62 | /// If there is a future failure the snapshot will be restored
63 | /// and the instruction at `offset` (relative to this instruction) will be called.
64 | case choice(offset: Distance, atIndexOffset: Int)
65 | /// Signals the end of a choice. Doesn't do anything else.
66 | /// Used as a barrier across which instructions cannot be moved.
67 | case choiceEnd
68 | /// Discards the state saved by previous `.choice`, because the instructions since then have completed
69 | /// successfully and the alternative instructions at the previous `.choice` are no longer needed.
70 | case commit
71 |
72 | /// Will be replaced by .call in preprocessing. Is never executed.
73 | case openCall(name: String)
74 | /// Goes to the subpattern at `offset` relative to this instruction.
75 | /// When the subpattern finishes we move on to the instruction after this.
76 | case call(offset: Distance)
77 | /// Returns from this subpattern to the instruction after where this was called from.
78 | case `return`
79 |
80 | /// Signals a failure.
81 | ///
82 | /// The snapshot from the previous `.choice` is restored, if there aren't any left we stop matching altogether.
83 | case fail
84 | /// A match has been successfully completed!
85 | ///
86 | /// Will not continue with further instructions.
87 | case match
88 |
89 | /// Will be replaced in preprocessing. Is never executed.
90 | case skip
91 |
92 | /// Succeeds anywhere except at the end of the input.
93 | @inlinable
94 | public static var any: Self { Self.checkElement { _ in true } } // TODO: make its own instruction
95 |
96 | /// Stores the current input index as the beginning of capture `name`
97 | @inlinable
98 | public static func captureStart(name: String?) -> Self {
99 | .captureStart(name: name, atIndexOffset: 0)
100 | }
101 |
102 | /// Stores the current input index as the end of the most recently started capture.
103 | @inlinable
104 | public static var captureEnd: Self {
105 | .captureEnd(atIndexOffset: 0)
106 | }
107 |
108 | /// Succeeds if the closure returns true when passed the input and the input index.
109 | @inlinable
110 | public static func checkIndex(_ test: @escaping (Input, Input.Index) -> Bool) -> Self {
111 | .checkIndex(test, atIndexOffset: 0)
112 | }
113 |
114 | /// Stores a snapshot of the current state.
115 | ///
116 | /// If there is a future failure the snapshot will be restored
117 | /// and the instruction at `offset` (relative to this instruction) will be called.
118 | @inlinable
119 | public static func choice(offset: Int) -> Instruction {
120 | .choice(offset: offset, atIndexOffset: 0)
121 | }
122 |
123 | /// The offset by which this instruction will move the input index.
124 | @usableFromInline
125 | var movesIndexBy: Int? {
126 | switch self {
127 | case .checkIndex, .captureStart, .captureEnd, .commit, .match, .choiceEnd:
128 | return 0
129 | case .elementEquals, .checkElement:
130 | return 1
131 | case let .moveIndex(offset):
132 | return offset
133 | case .search, .choice, .jump, .openCall, .call, .return, .fail, .skip:
134 | return nil
135 | }
136 | }
137 |
138 | /// Returns false only if instruction has no effect.
139 | @usableFromInline
140 | var doesNotDoAnything: Bool {
141 | switch self {
142 | case .choiceEnd, .jump(+1):
143 | return true
144 | default:
145 | return false
146 | }
147 | }
148 | }
149 |
150 | extension Sequence {
151 | /// The offset by which these instructions will move the input index.
152 | @usableFromInline
153 | func movesIndexBy