Skip to content

Commit

Permalink
Add "literal" parsers (#56)
Browse files Browse the repository at this point in the history
* Add "literal" parsers

* fix
  • Loading branch information
stephencelis authored Oct 27, 2021
1 parent 9be513c commit 0e4f1bf
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 27 deletions.
10 changes: 5 additions & 5 deletions Parsing.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ struct Color {
let red, green, blue: UInt8
}

let hexPrimary = Prefix<Substring.UTF8View>(2)
.compactMap { UInt8(Substring($0), radix: 16) }
let hexPrimary = Prefix<Substring>(2)
.compactMap { UInt8($0, radix: 16) }

let hexColor = StartsWith("#".utf8)
let hexColor = "#"
.take(hexPrimary)
.take(hexPrimary)
.take(hexPrimary)
.map(Color.init)

do {
var hex = "#000000"[...].utf8
var hex = "#000000"[...]
print(hexColor.parse(&hex)!)
}

do {
var hex = "#FF0000"[...].utf8
var hex = "#FF0000"[...]
print(hexColor.parse(&hex)!)
}
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ We can start by describing what it means to parse a single row, first by parsing

```swift
let user = Int.parser()
.skip(StartsWith(","))
.skip(",")
```

Already this can consume the beginning of the input:
Expand All @@ -111,9 +111,9 @@ Next we want to take everything up until the next comma for the user's name, and

```swift
let user = Int.parser()
.skip(StartsWith(","))
.skip(",")
.take(Prefix { $0 != "," })
.skip(StartsWith(","))
.skip(",")
```

Here the `.take` operator has combined parsed values together into a tuple, `(Int, Substring)`.
Expand All @@ -122,19 +122,19 @@ And then we want to take the boolean at the end of the row for the user's admin

```swift
let user = Int.parser()
.skip(StartsWith(","))
.skip(",")
.take(Prefix { $0 != "," })
.skip(StartsWith(","))
.skip(",")
.take(Bool.parser())
```

Currently this will parse a tuple `(Int, Substring, Bool)` from the input, and we can `.map` on that to turn it into a `User`:

```swift
let user = Int.parser()
.skip(StartsWith(","))
.skip(",")
.take(Prefix { $0 != "," })
.skip(StartsWith(","))
.skip(",")
.take(Bool.parser())
.map { User(id: $0, name: String($1), isAdmin: $2) }
```
Expand All @@ -149,7 +149,7 @@ input // => "\n2,Blob Jr.,false\n3,Blob Sr.,true"
To parse multiple users from the input we can use the `Many` parser:

```swift
let users = Many(user, separator: StartsWith("\n"))
let users = Many(user, separator: "\n")

users.parse(&input) // => [User(id: 1, name: "Blob", isAdmin: true), ...]
input // => ""
Expand Down
35 changes: 35 additions & 0 deletions Sources/Parsing/Parsers/Literal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extension Array: Parser where Element: Equatable {
@inlinable
public func parse(_ input: inout ArraySlice<Element>) -> Void? {
guard input.starts(with: self) else { return nil }
input.removeFirst(self.count)
return ()
}
}

extension String: Parser {
@inlinable
public func parse(_ input: inout Substring) -> Void? {
guard input.starts(with: self) else { return nil }
input.removeFirst(self.count)
return ()
}
}

extension String.UnicodeScalarView: Parser {
@inlinable
public func parse(_ input: inout Substring.UnicodeScalarView) -> Void? {
guard input.starts(with: self) else { return nil }
input.removeFirst(self.count)
return ()
}
}

extension String.UTF8View: Parser {
@inlinable
public func parse(_ input: inout Substring.UTF8View) -> Void? {
guard input.starts(with: self) else { return nil }
input.removeFirst(self.count)
return ()
}
}
4 changes: 2 additions & 2 deletions Sources/Parsing/Parsers/Many.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
///
/// ```swift
/// var input = "1,2,3"[...]
/// let output = Many(Int.parser(), separator: StartsWith(",")).parse(&input)
/// let output = Many(Int.parser(), separator: ",").parse(&input)
/// precondition(input == "")
/// precondition(output == [1, 2, 3])
/// ```
Expand All @@ -23,7 +23,7 @@
/// +=
/// )
/// var input = "1,2,3"[...]
/// let output = Many(Int.parser(), into: 0, separator: StartsWith(",")).parse(&input)
/// let output = Many(Int.parser(), into: 0, separator: ",").parse(&input)
/// precondition(input == "")
/// precondition(output == 6)
/// ```
Expand Down
2 changes: 1 addition & 1 deletion Sources/Parsing/Parsers/Take.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extension Parser {
///
/// var input = "-1.5,1"[...].utf8
/// let output = Double.parser()
/// .skip(StartsWith(","))
/// .skip(",")
/// .take(Double.parser())
/// .map(Point.init)
/// .parse(&input) // => Point(x: -1.5, y: 1)
Expand Down
12 changes: 6 additions & 6 deletions Sources/swift-parsing-benchmark/ReadmeExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ let readmeExampleSuite = BenchmarkSuite(name: "README Example") { suite in

do {
let user = Int.parser()
.skip(StartsWith(","))
.skip(",")
.take(Prefix { $0 != "," })
.skip(StartsWith(","))
.skip(",")
.take(Bool.parser())
.map { User(id: $0, name: String($1), isAdmin: $2) }
let users = Many(user, separator: StartsWith("\n"))
let users = Many(user, separator: "\n")

suite.benchmark(
name: "Parser: Substring",
Expand All @@ -44,12 +44,12 @@ let readmeExampleSuite = BenchmarkSuite(name: "README Example") { suite in

do {
let user = Int.parser(of: Substring.UTF8View.self)
.skip(StartsWith(","[...].utf8))
.skip(",".utf8)
.take(Prefix { $0 != .init(ascii: ",") })
.skip(StartsWith(","[...].utf8))
.skip(",".utf8)
.take(Bool.parser())
.map { User(id: $0, name: String(Substring($1)), isAdmin: $2) }
let users = Many(user, separator: StartsWith("\n"[...].utf8))
let users = Many(user, separator: "\n".utf8)

suite.benchmark(
name: "Parser: UTF8",
Expand Down
10 changes: 5 additions & 5 deletions Sources/swift-parsing-benchmark/Routing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,28 @@ let routingSuite = BenchmarkSuite(name: "Routing") { suite in
.map { Route.home }
.orElse(
Method("GET")
.skip(Path(StartsWith("contact-us".utf8)))
.skip(Path("contact-us".utf8))
.skip(End())
.map { Route.contactUs }
)
.orElse(
Method("GET")
.skip(Path(StartsWith("episodes".utf8)))
.skip(Path("episodes".utf8))
.skip(End())
.map { Route.episodes }
)
.orElse(
Method("GET")
.skip(Path(StartsWith("episodes".utf8)))
.skip(Path("episodes".utf8))
.take(Path(Int.parser()))
.skip(End())
.map(Route.episode(id:))
)
.orElse(
Method("GET")
.skip(Path(StartsWith("episodes".utf8)))
.skip(Path("episodes".utf8))
.take(Path(Int.parser()))
.skip(Path(StartsWith("comments".utf8)))
.skip(Path("comments".utf8))
.skip(End())
.map(Route.episodeComments(id:))
)
Expand Down

0 comments on commit 0e4f1bf

Please sign in to comment.