diff --git a/0059-composable-parsing-map/Composable Parsers.playground/Contents.swift b/0059-composable-parsing-map/Composable Parsers.playground/Contents.swift new file mode 100644 index 00000000..9bd5f2c6 --- /dev/null +++ b/0059-composable-parsing-map/Composable Parsers.playground/Contents.swift @@ -0,0 +1,149 @@ +import Foundation + +struct Parser { + let run: (inout Substring) -> A? +} + +let int = Parser { str in + let prefix = str.prefix(while: { $0.isNumber }) + guard let int = Int(prefix) else { return nil } + str.removeFirst(prefix.count) + return int +} + +let double = Parser { str in + let prefix = str.prefix(while: { $0.isNumber || $0 == "." }) + guard let match = Double(prefix) else { return nil } + str.removeFirst(prefix.count) + return match +} + +func literal(_ literal: String) -> Parser { + return Parser { str in + guard str.hasPrefix(literal) else { return nil } + str.removeFirst(literal.count) + return () + } +} + +func always(_ a: A) -> Parser { + return Parser { _ in a } +} + +extension Parser { + static var never: Parser { + return Parser { _ in nil } + } +} + +struct Coordinate { + let latitude: Double + let longitude: Double +} + + +extension Parser { + func run(_ str: String) -> (match: A?, rest: Substring) { + var str = str[...] + let match = self.run(&str) + return (match, str) + } +} + + +// map: ((A) -> B) -> (F) -> F + +// F = Parser +// map: ((A) -> B) -> (Parser) -> Parser + +// map(id) = id + +[1, 2, 3] + .map { $0 } + +Optional("Blob") + .map { $0 } + + +// map: (Parser, (A) -> B) -> Parser + +extension Parser { + func map(_ f: @escaping (A) -> B) -> Parser { + return Parser { str -> B? in + self.run(&str).map(f) + } + } + + func fakeMap(_ f: @escaping (A) -> B) -> Parser { + return Parser { _ in nil } + } + func fakeMap2(_ f: @escaping (A) -> B) -> Parser { + return Parser { str in + let matchB = self.run(&str).map(f) + str = "" + return matchB + } + } +} + +int.map { $0 } +int.fakeMap { $0 }.run("123") +int + .fakeMap2 { $0 }.run("123 Hello World") +int + .run("123 Hello World") + +let even = int.map { $0 % 2 == 0 } + +even.run("123 Hello World") +even.run("42 Hello World") + +let char = Parser { str in + guard !str.isEmpty else { return nil } + return str.removeFirst() +} + +//let northSouth = Parser { str in +// guard +// let cardinal = str.first, +// cardinal == "N" || cardinal == "S" +// else { return nil } +// str.removeFirst(1) +// return cardinal == "N" ? 1 : -1 +//} +let northSouth = char + .map { + $0 == "N" ? always(1.0) + : $0 == "S" ? always(-1) + : .never +} + +let eastWest = Parser { str in + guard + let cardinal = str.first, + cardinal == "E" || cardinal == "W" + else { return nil } + str.removeFirst(1) + return cardinal == "E" ? 1 : -1 +} + +func parseLatLong(_ str: String) -> Coordinate? { + var str = str[...] + + guard + let lat = double.run(&str), + literal("° ").run(&str) != nil, + let latSign = northSouth.run(&str), + literal(", ").run(&str) != nil, + let long = double.run(&str), + literal("° ").run(&str) != nil, + let longSign = eastWest.run(&str) + else { return nil } + + return Coordinate( + latitude: lat * latSign, + longitude: long * longSign + ) +} + +print(String(describing: parseLatLong("40.6782° N, 73.9442° W"))) diff --git a/0059-composable-parsing-map/Composable Parsers.playground/contents.xcplayground b/0059-composable-parsing-map/Composable Parsers.playground/contents.xcplayground new file mode 100644 index 00000000..63b6dd8d --- /dev/null +++ b/0059-composable-parsing-map/Composable Parsers.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/0059-composable-parsing-map/README.md b/0059-composable-parsing-map/README.md new file mode 100644 index 00000000..ab4ef201 --- /dev/null +++ b/0059-composable-parsing-map/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Composable Parsing: Map](https://www.pointfree.co/episodes/ep59-composable-parsing-map) +> +> We now have a precise, efficent definition for parsing, but we haven’t even scratched the surface of its relation to functional programming. In this episode we begin to show how all of the functional operators we know and love come into play, starting with map.