diff --git a/Parsing.playground/Contents.swift b/Parsing.playground/Contents.swift index 99ace6d159..ab27ea2a59 100644 --- a/Parsing.playground/Contents.swift +++ b/Parsing.playground/Contents.swift @@ -4,21 +4,21 @@ struct Color { let red, green, blue: UInt8 } -let hexPrimary = Prefix(2) - .compactMap { UInt8(Substring($0), radix: 16) } +let hexPrimary = Prefix(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)!) } diff --git a/README.md b/README.md index 1c5fc8d1c8..90c09c0345 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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)`. @@ -122,9 +122,9 @@ 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()) ``` @@ -132,9 +132,9 @@ Currently this will parse a tuple `(Int, Substring, Bool)` from the input, and w ```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) } ``` @@ -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 // => "" diff --git a/Sources/Parsing/Parsers/Literal.swift b/Sources/Parsing/Parsers/Literal.swift new file mode 100644 index 0000000000..1f7b3865cb --- /dev/null +++ b/Sources/Parsing/Parsers/Literal.swift @@ -0,0 +1,35 @@ +extension Array: Parser where Element: Equatable { + @inlinable + public func parse(_ input: inout ArraySlice) -> 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 () + } +} diff --git a/Sources/Parsing/Parsers/Many.swift b/Sources/Parsing/Parsers/Many.swift index 2ec0243226..f526805cd7 100644 --- a/Sources/Parsing/Parsers/Many.swift +++ b/Sources/Parsing/Parsers/Many.swift @@ -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]) /// ``` @@ -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) /// ``` diff --git a/Sources/Parsing/Parsers/Take.swift b/Sources/Parsing/Parsers/Take.swift index 5d70e87b65..a15ef1b90d 100644 --- a/Sources/Parsing/Parsers/Take.swift +++ b/Sources/Parsing/Parsers/Take.swift @@ -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) diff --git a/Sources/swift-parsing-benchmark/ReadmeExample.swift b/Sources/swift-parsing-benchmark/ReadmeExample.swift index d690dbc883..caa7267bd6 100644 --- a/Sources/swift-parsing-benchmark/ReadmeExample.swift +++ b/Sources/swift-parsing-benchmark/ReadmeExample.swift @@ -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", @@ -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", diff --git a/Sources/swift-parsing-benchmark/Routing.swift b/Sources/swift-parsing-benchmark/Routing.swift index 4d3219c9c2..63231b61cf 100644 --- a/Sources/swift-parsing-benchmark/Routing.swift +++ b/Sources/swift-parsing-benchmark/Routing.swift @@ -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:)) )