Skip to content

Commit c6e2241

Browse files
stephencelisJaap WijnenmbrandonwKaiOelfke
authored
Swift 5.8 Support (#289)
* Swift 5.8 Support Co-authored-by: Jaap Wijnen <[email protected]> * wip * wip * wip * wip * wip * omit some benchmarks from swift 5.7 * wip * FromSubstring ParseBuilder needs Substring as Input (#292) * Downstream ParseBuilder should use Downstream.Input as Input (#291) * Downstream ParseBuilder should use Downstream.Input as Input The ParseBuilder is for Downstream so it should use it’s input. Or Upstream.Output as pipe runs the downstream parser on the output of the upstream parser. Which is why Upstream.Output == Downstream.Input. With the 5.8 changes pipe uses the wrong input here. This only causes issues, if Upstream and Downstream have a different Input type. * Pipe - Use Upstream.Output instead of Downstream.Input * wip * wip * wip * wip * wip * fix * wip --------- Co-authored-by: Jaap Wijnen <[email protected]> Co-authored-by: Brandon Williams <[email protected]> Co-authored-by: Kai Oelfke <[email protected]>
1 parent 5ac4b09 commit c6e2241

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2089
-12766
lines changed

.github/workflows/ci.yml

+6-15
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ on:
1111

1212
jobs:
1313
macos_tests:
14-
runs-on: macos-11
14+
runs-on: macos-12
1515
strategy:
1616
matrix:
1717
xcode:
18-
- "13.2.1" # Swift 5.5
18+
- "14.2" # Swift 5.7.2
1919
command:
2020
- test
21-
- benchmarks
21+
# - benchmarks
2222
steps:
23-
- uses: actions/checkout@v2
23+
- uses: actions/checkout@v3
2424
- name: Select Xcode ${{ matrix.xcode }}
2525
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
2626
- name: System
@@ -31,22 +31,13 @@ jobs:
3131
ubuntu_tests:
3232
strategy:
3333
matrix:
34-
os: [ubuntu-18.04, ubuntu-20.04]
34+
os: [ubuntu-20.04]
3535

3636
runs-on: ${{ matrix.os }}
3737

3838
steps:
39-
- uses: actions/checkout@v2
39+
- uses: actions/checkout@v3
4040
- name: Build
4141
run: swift build
4242
- name: Run tests
4343
run: swift test
44-
45-
windows_tests:
46-
runs-on: windows-2019
47-
48-
steps:
49-
- uses: actions/checkout@v2
50-
- uses: MaxDesiatov/swift-windows-action@v1
51-
with:
52-
swift-version: "5.5.1"

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PLATFORM_IOS = iOS Simulator,name=iPhone 11 Pro
22
PLATFORM_MACOS = macOS
3-
PLATFORM_TVOS = tvOS Simulator,name=Apple TV 4K (at 1080p)
3+
PLATFORM_TVOS = tvOS Simulator,name=Apple TV
44

55
default: test
66

Package.swift

+4-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.5
1+
// swift-tools-version:5.7
22

33
import PackageDescription
44

@@ -18,9 +18,10 @@ let package = Package(
1818
],
1919
dependencies: [
2020
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.5.0"),
21+
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
2122
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "0.8.0"),
2223
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "0.2.1"),
23-
.package(name: "Benchmark", url: "https://github.com/google/swift-benchmark", from: "0.1.1"),
24+
.package(url: "https://github.com/google/swift-benchmark", from: "0.1.1"),
2425
],
2526
targets: [
2627
.target(
@@ -37,19 +38,8 @@ let package = Package(
3738
name: "swift-parsing-benchmark",
3839
dependencies: [
3940
"Parsing",
40-
.product(name: "Benchmark", package: "Benchmark"),
41+
.product(name: "Benchmark", package: "swift-benchmark"),
4142
]
4243
),
43-
.executableTarget(
44-
name: "variadics-generator",
45-
dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser")]
46-
),
4744
]
4845
)
49-
50-
#if swift(>=5.6)
51-
// Add the documentation compiler plugin if possible
52-
package.dependencies.append(
53-
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0")
54-
)
55-
#endif

README.md

+52-47
Original file line numberDiff line numberDiff line change
@@ -103,22 +103,26 @@ It would be more straightforward and efficient to instead describe how to consum
103103
We can start by describing what it means to parse a single row, first by parsing an integer off the front of the string, and then parsing a comma. We can do this by using the `Parse` type, which acts as an entry point into describing a list of parsers that you want to run one after the other to consume from an input:
104104

105105
```swift
106-
let user = Parse {
106+
let user = Parse(input: Substring.self) {
107107
Int.parser()
108108
","
109109
}
110110
```
111111

112+
Note that this parsing library is quite general, allowing one to parse _any_ kind of input into
113+
_any_ kind of output. For this reason we sometimes need to specify the exact input type the parser
114+
can process, in this case substrings.
115+
112116
Already this can consume the beginning of the input:
113117

114118
```swift
115-
try user.parse("1,") // 1
119+
try user.parse("1,") // 1
116120
```
117121

118122
Next we want to take everything up until the next comma for the user's name, and then consume the comma:
119123

120124
```swift
121-
let user = Parse {
125+
let user = Parse(input: Substring.self) {
122126
Int.parser()
123127
","
124128
Prefix { $0 != "," }
@@ -129,7 +133,7 @@ let user = Parse {
129133
And then we want to take the boolean at the end of the row for the user's admin status:
130134

131135
```swift
132-
let user = Parse {
136+
let user = Parse(input: Substring.self) {
133137
Int.parser()
134138
","
135139
Prefix { $0 != "," }
@@ -141,7 +145,7 @@ let user = Parse {
141145
Currently this will parse a tuple `(Int, Substring, Bool)` from the input, and we can `.map` on that to turn it into a `User`:
142146

143147
```swift
144-
let user = Parse {
148+
let user = Parse(input: Substring.self) {
145149
Int.parser()
146150
","
147151
Prefix { $0 != "," }
@@ -154,7 +158,7 @@ let user = Parse {
154158
To make the data we are parsing to more prominent, we can instead pass the transform closure as the first argument to `Parse`:
155159

156160
```swift
157-
let user = Parse {
161+
let user = Parse(input: Substring.self) {
158162
User(id: $0, name: String($1), isAdmin: $2)
159163
} with: {
160164
Int.parser()
@@ -168,7 +172,7 @@ let user = Parse {
168172
Or we can pass the `User` initializer to `Parse` in a point-free style by transforming the `Prefix` parser's output from a `Substring` to ` String` first:
169173

170174
```swift
171-
let user = Parse(User.init(id:name:isAdmin:)) {
175+
let user = Parse(input: Substring.self, User.init(id:name:isAdmin:)) {
172176
Int.parser()
173177
","
174178
Prefix { $0 != "," }.map(String.init)
@@ -305,46 +309,47 @@ Apple M1 Pro (10 cores, 8 performance and 2 efficiency)
305309
306310
name time std iterations
307311
----------------------------------------------------------------------------------
308-
Arithmetic.Parser 8042.000 ns ± 5.91 % 174657
309-
BinaryData.Parser 42.000 ns ± 56.81 % 1000000
310-
Bool.Bool.init 41.000 ns ± 60.69 % 1000000
311-
Bool.Bool.parser 42.000 ns ± 57.28 % 1000000
312-
Bool.Scanner.scanBool 1041.000 ns ± 25.98 % 1000000
313-
Color.Parser 209.000 ns ± 13.68 % 1000000
314-
CSV.Parser 4047750.000 ns ± 1.18 % 349
315-
CSV.Ad hoc mutating methods 898604.000 ns ± 1.49 % 1596
316-
Date.Parser 6416.000 ns ± 2.56 % 219218
317-
Date.DateFormatter 25625.000 ns ± 2.19 % 54110
318-
Date.ISO8601DateFormatter 35125.000 ns ± 1.71 % 39758
319-
HTTP.HTTP 9709.000 ns ± 3.81 % 138868
320-
JSON.Parser 32292.000 ns ± 3.18 % 41890
321-
JSON.JSONSerialization 1833.000 ns ± 8.58 % 764057
322-
Numerics.Int.init 41.000 ns ± 84.54 % 1000000
323-
Numerics.Int.parser 42.000 ns ± 72.17 % 1000000
324-
Numerics.Scanner.scanInt 125.000 ns ± 20.26 % 1000000
325-
Numerics.Comma separated: Int.parser 8096459.000 ns ± 0.44 % 173
326-
Numerics.Comma separated: Scanner.scanInt 49178770.500 ns ± 0.24 % 28
327-
Numerics.Comma separated: String.split 14922583.500 ns ± 0.67 % 94
328-
Numerics.Double.init 42.000 ns ± 72.61 % 1000000
329-
Numerics.Double.parser 125.000 ns ± 58.57 % 1000000
330-
Numerics.Scanner.scanDouble 167.000 ns ± 18.84 % 1000000
331-
Numerics.Comma separated: Double.parser 11313395.500 ns ± 0.96 % 124
332-
Numerics.Comma separated: Scanner.scanDouble 50431521.000 ns ± 0.19 % 28
333-
Numerics.Comma separated: String.split 18744125.000 ns ± 0.46 % 75
334-
PrefixUpTo.Parser: Substring 249958.000 ns ± 0.88 % 5595
335-
PrefixUpTo.Parser: UTF8 13250.000 ns ± 2.96 % 105812
336-
PrefixUpTo.String.range(of:) 43084.000 ns ± 1.57 % 32439
337-
PrefixUpTo.Scanner.scanUpToString 47500.000 ns ± 1.27 % 29444
338-
Race.Parser 34417.000 ns ± 2.73 % 40502
339-
README Example.Parser: Substring 4000.000 ns ± 3.79 % 347868
340-
README Example.Parser: UTF8 1125.000 ns ± 7.92 % 1000000
341-
README Example.Ad hoc 3542.000 ns ± 4.13 % 394248
342-
README Example.Scanner 14292.000 ns ± 2.82 % 97922
343-
String Abstractions.Substring 934167.000 ns ± 0.60 % 1505
344-
String Abstractions.UTF8 158750.000 ns ± 1.36 % 8816
345-
UUID.UUID.init 209.000 ns ± 15.02 % 1000000
346-
UUID.UUID.parser 208.000 ns ± 24.17 % 1000000
347-
Xcode Logs.Parser 3768437.500 ns ± 0.56 % 372
312+
Arithmetic.Parser 6166.000 ns ± 10.73 % 228888
313+
BinaryData.Parser 208.000 ns ± 39.64 % 1000000
314+
Bool.Bool.init 41.000 ns ± 84.71 % 1000000
315+
Bool.Bool.parser 42.000 ns ± 87.86 % 1000000
316+
Bool.Scanner.scanBool 916.000 ns ± 30.55 % 1000000
317+
Color.Parser 208.000 ns ± 28.34 % 1000000
318+
CSV.Parser 3675250.000 ns ± 1.16 % 380
319+
CSV.Ad hoc mutating methods 651333.000 ns ± 1.00 % 2143
320+
Date.Parser 5833.000 ns ± 5.65 % 238924
321+
Date.DateFormatter 23542.000 ns ± 5.50 % 58766
322+
Date.ISO8601DateFormatter 29041.000 ns ± 3.31 % 48028
323+
HTTP.HTTP 10250.000 ns ± 6.24 % 135657
324+
JSON.Parser 38167.000 ns ± 3.26 % 36423
325+
JSON.JSONSerialization 1792.000 ns ± 54.14 % 753770
326+
Numerics.Int.init 0.000 ns ± inf % 1000000
327+
Numerics.Int.parser 83.000 ns ± 67.28 % 1000000
328+
Numerics.Scanner.scanInt 125.000 ns ± 38.65 % 1000000
329+
Numerics.Digits 83.000 ns ± 65.03 % 1000000
330+
Numerics.Comma separated: Int.parser 15364583.000 ns ± 0.63 % 91
331+
Numerics.Comma separated: Scanner.scanInt 50654458.500 ns ± 0.30 % 28
332+
Numerics.Comma separated: String.split 15452542.000 ns ± 1.30 % 90
333+
Numerics.Double.init 42.000 ns ± 152.57 % 1000000
334+
Numerics.Double.parser 166.000 ns ± 45.23 % 1000000
335+
Numerics.Scanner.scanDouble 167.000 ns ± 42.36 % 1000000
336+
Numerics.Comma separated: Double.parser 18539833.000 ns ± 0.57 % 75
337+
Numerics.Comma separated: Scanner.scanDouble 55239167.000 ns ± 0.46 % 25
338+
Numerics.Comma separated: String.split 17636000.000 ns ± 1.34 % 78
339+
PrefixUpTo.Parser: Substring 182041.000 ns ± 1.78 % 7643
340+
PrefixUpTo.Parser: UTF8 40417.000 ns ± 2.71 % 34379
341+
PrefixUpTo.String.range(of:) 49792.000 ns ± 2.70 % 27891
342+
PrefixUpTo.Scanner.scanUpToString 53959.000 ns ± 3.87 % 25745
343+
Race.Parser 59583.000 ns ± 2.78 % 23333
344+
README Example.Parser: Substring 2834.000 ns ± 12.87 % 488264
345+
README Example.Parser: UTF8 1291.000 ns ± 22.65 % 1000000
346+
README Example.Ad hoc 2459.000 ns ± 20.61 % 561930
347+
README Example.Scanner 12084.000 ns ± 5.53 % 115388
348+
String Abstractions.Substring 472083.500 ns ± 1.38 % 2962
349+
String Abstractions.UTF8 196041.000 ns ± 3.38 % 7059
350+
UUID.UUID.init 208.000 ns ± 43.60 % 1000000
351+
UUID.UUID.parser 167.000 ns ± 42.00 % 1000000
352+
Xcode Logs.Parser 4511625.500 ns ± 0.58 % 226
348353
```
349354

350355
## Documentation

Sources/Parsing/Builders/OneOfBuilder.swift

+56-16
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
/// try currency.parse("$100") // (.usd, 100)
1818
/// ```
1919
@resultBuilder
20-
public enum OneOfBuilder {
20+
public enum OneOfBuilder<Input, Output> {
2121
/// Provides support for `for`-`in` loops in ``OneOfBuilder`` blocks.
2222
///
2323
/// Useful for building up a parser from a dynamic source, like for a case-iterable enum:
@@ -36,23 +36,23 @@ public enum OneOfBuilder {
3636
/// }
3737
/// ```
3838
@inlinable
39-
public static func buildArray<P>(_ parsers: [P]) -> Parsers.OneOfMany<P> {
39+
public static func buildArray<P>(_ parsers: [P]) -> Parsers.OneOfMany<P>
40+
where P.Input == Input, P.Output == Output {
4041
.init(parsers)
4142
}
4243

44+
@inlinable
45+
static public func buildBlock() -> Fail<Input, Output> {
46+
Fail()
47+
}
48+
4349
/// Provides support for specifying a parser in ``OneOfBuilder`` blocks.
4450
@inlinable
45-
static public func buildBlock<P: Parser>(_ parser: P) -> P {
51+
static public func buildBlock<P: Parser>(_ parser: P) -> P
52+
where P.Input == Input, P.Output == Output {
4653
parser
4754
}
4855

49-
#if swift(<5.7)
50-
@inlinable
51-
static public func buildBlock<P0: Parser, P1: Parser>(_ p0: P0, _ p1: P1) -> OneOf2<P0, P1> {
52-
OneOf2(p0, p1)
53-
}
54-
#endif
55-
5656
/// Provides support for `if`-`else` statements in ``OneOfBuilder`` blocks, producing a
5757
/// conditional parser for the `if` branch.
5858
///
@@ -68,7 +68,13 @@ public enum OneOfBuilder {
6868
@inlinable
6969
public static func buildEither<TrueParser, FalseParser>(
7070
first parser: TrueParser
71-
) -> Parsers.Conditional<TrueParser, FalseParser> {
71+
) -> Parsers.Conditional<TrueParser, FalseParser>
72+
where
73+
TrueParser.Input == Input,
74+
TrueParser.Output == Output,
75+
FalseParser.Input == Input,
76+
FalseParser.Output == Output
77+
{
7278
.first(parser)
7379
}
7480

@@ -87,10 +93,22 @@ public enum OneOfBuilder {
8793
@inlinable
8894
public static func buildEither<TrueParser, FalseParser>(
8995
second parser: FalseParser
90-
) -> Parsers.Conditional<TrueParser, FalseParser> {
96+
) -> Parsers.Conditional<TrueParser, FalseParser>
97+
where
98+
TrueParser.Input == Input,
99+
TrueParser.Output == Output,
100+
FalseParser.Input == Input,
101+
FalseParser.Output == Output
102+
{
91103
.second(parser)
92104
}
93105

106+
@inlinable
107+
public static func buildExpression<P: Parser>(_ parser: P) -> P
108+
where P.Input == Input, P.Output == Output {
109+
parser
110+
}
111+
94112
/// Provides support for `if` statements in ``OneOfBuilder`` blocks, producing an optional parser.
95113
///
96114
/// ```swift
@@ -106,24 +124,27 @@ public enum OneOfBuilder {
106124
/// }
107125
/// ```
108126
@inlinable
109-
public static func buildIf<P>(_ parser: P?) -> OptionalOneOf<P> {
127+
public static func buildIf<P>(_ parser: P?) -> OptionalOneOf<P> where P.Input == Input {
110128
.init(wrapped: parser)
111129
}
112130

113131
/// Provides support for `if #available` statements in ``OneOfBuilder`` blocks, producing an
114132
/// optional parser.
115133
@inlinable
116-
public static func buildLimitedAvailability<P>(_ parser: P?) -> OptionalOneOf<P> {
134+
public static func buildLimitedAvailability<P>(_ parser: P?) -> OptionalOneOf<P>
135+
where P.Input == Input, P.Output == Output {
117136
.init(wrapped: parser)
118137
}
119138

120139
@inlinable
121-
public static func buildPartialBlock<P0: Parser>(first: P0) -> P0 {
140+
public static func buildPartialBlock<P: Parser>(first: P) -> P
141+
where P.Input == Input, P.Output == Output {
122142
first
123143
}
124144

125145
@inlinable
126-
public static func buildPartialBlock<P0, P1>(accumulated: P0, next: P1) -> OneOf2<P0, P1> {
146+
public static func buildPartialBlock<P0, P1>(accumulated: P0, next: P1) -> OneOf2<P0, P1>
147+
where P0.Input == Input, P0.Output == Output, P1.Input == Input, P1.Output == Output {
127148
.init(accumulated, next)
128149
}
129150

@@ -211,3 +232,22 @@ extension OneOfBuilder.OptionalOneOf: ParserPrinter where Wrapped: ParserPrinter
211232
try wrapped.print(output, into: &input)
212233
}
213234
}
235+
236+
extension OneOfBuilder where Input == Substring {
237+
@_disfavoredOverload
238+
public static func buildExpression<P: Parser>(_ parser: P)
239+
-> From<Conversions.SubstringToUTF8View, Substring.UTF8View, P>
240+
where P.Input == Substring.UTF8View {
241+
From(.utf8) {
242+
parser
243+
}
244+
}
245+
}
246+
247+
extension OneOfBuilder where Input == Substring.UTF8View {
248+
@_disfavoredOverload
249+
public static func buildExpression<P: Parser>(_ parser: P) -> P
250+
where P.Input == Substring.UTF8View {
251+
parser
252+
}
253+
}

0 commit comments

Comments
 (0)