diff --git a/0062-parser-combinators-pt1/ParserCombinators.playground/Contents.swift b/0062-parser-combinators-pt1/ParserCombinators.playground/Contents.swift new file mode 100644 index 00000000..16e9982c --- /dev/null +++ b/0062-parser-combinators-pt1/ParserCombinators.playground/Contents.swift @@ -0,0 +1,344 @@ + +struct Parser { + let run: (inout Substring) -> A? +} + + + + + + + + + + + + + + + + + +let int = Parser { str in + let prefix = str.prefix(while: { $0.isNumber }) + let match = Int(prefix) + str.removeFirst(prefix.count) + return match +} + +let double = Parser { str in + let prefix = str.prefix(while: { $0.isNumber || $0 == "." }) + let match = Double(prefix) + str.removeFirst(prefix.count) + return match +} + +let char = Parser { str in + guard !str.isEmpty else { return nil } + return str.removeFirst() +} + +func literal(_ p: String) -> Parser { + return Parser { str in + guard str.hasPrefix(p) else { return nil } + str.removeFirst(p.count) + return () + } +} + + + + + + + + + + + + + + + + + + + + + +func always(_ a: A) -> Parser { + return Parser { _ in a } +} + +extension Parser { + static var never: Parser { + return Parser { _ in nil } + } +} + + + + + + + + + + + + + + + + + + + + + +extension Parser { + func map(_ f: @escaping (A) -> B) -> Parser { + return Parser { str -> B? in + self.run(&str).map(f) + } + } + + func flatMap(_ f: @escaping (A) -> Parser) -> Parser { + return Parser { str -> B? in + let original = str + let matchA = self.run(&str) + let parserB = matchA.map(f) + guard let matchB = parserB?.run(&str) else { + str = original + return nil + } + return matchB + } + } +} + +func zip(_ a: Parser, _ b: Parser) -> Parser<(A, B)> { + return Parser<(A, B)> { str -> (A, B)? in + let original = str + guard let matchA = a.run(&str) else { return nil } + guard let matchB = b.run(&str) else { + str = original + return nil + } + return (matchA, matchB) + } +} + + + + + +func zip( + _ a: Parser, + _ b: Parser, + _ c: Parser + ) -> Parser<(A, B, C)> { + return zip(a, zip(b, c)) + .map { a, bc in (a, bc.0, bc.1) } +} +func zip( + _ a: Parser, + _ b: Parser, + _ c: Parser, + _ d: Parser + ) -> Parser<(A, B, C, D)> { + return zip(a, zip(b, c, d)) + .map { a, bcd in (a, bcd.0, bcd.1, bcd.2) } +} +func zip( + _ a: Parser, + _ b: Parser, + _ c: Parser, + _ d: Parser, + _ e: Parser + ) -> Parser<(A, B, C, D, E)> { + + return zip(a, zip(b, c, d, e)) + .map { a, bcde in (a, bcde.0, bcde.1, bcde.2, bcde.3) } +} +func zip( + _ a: Parser, + _ b: Parser, + _ c: Parser, + _ d: Parser, + _ e: Parser, + _ f: Parser + ) -> Parser<(A, B, C, D, E, F)> { + return zip(a, zip(b, c, d, e, f)) + .map { a, bcdef in (a, bcdef.0, bcdef.1, bcdef.2, bcdef.3, bcdef.4) } +} +func zip( + _ a: Parser, + _ b: Parser, + _ c: Parser, + _ d: Parser, + _ e: Parser, + _ f: Parser, + _ g: Parser + ) -> Parser<(A, B, C, D, E, F, G)> { + return zip(a, zip(b, c, d, e, f, g)) + .map { a, bcdefg in (a, bcdefg.0, bcdefg.1, bcdefg.2, bcdefg.3, bcdefg.4, bcdefg.5) } +} + + + + + + + + + + + + + + + + + + + + + +extension Parser { + func run(_ str: String) -> (match: A?, rest: Substring) { + var str = str[...] + let match = self.run(&str) + return (match, str) + } +} + + + + + + + + + + + + + + + + + + + + + +// 40.446° N, 79.982° W +struct Coordinate { + let latitude: Double + let longitude: Double +} + +func prefix(while p: @escaping (Character) -> Bool) -> Parser { + return Parser { str in + let prefix = str.prefix(while: p) + str.removeFirst(prefix.count) + return prefix + } +} + +let zeroOrMoreSpaces = prefix( + while: { $0 == " " }) + .map { _ in () } +// Parser { str -> Void? in +// let prefix = str.prefix(while: { $0 == " " }) +// str.removeFirst(prefix.count) +// return () +//} +let oneOrMoreSpaces = prefix( + while: { $0 == " " }) + .flatMap { + $0.isEmpty + ? .never + : always(()) +} +// Parser { str -> Void? in +// let prefix = str.prefix(while: { $0 == " " }) +// guard !prefix.isEmpty else { return nil } +// str.removeFirst(prefix.count) +// return () +//} + + + +let northSouth = char + .flatMap { + $0 == "N" ? always(1.0) + : $0 == "S" ? always(-1) + : .never +} +let eastWest = char + .flatMap { + $0 == "E" ? always(1.0) + : $0 == "W" ? always(-1) + : .never +} +let latitude = zip( + double, + literal("°"), + oneOrMoreSpaces, + northSouth + ) + .map { lat, _, _, latSign in lat * latSign } +let longitude = zip( + double, + literal("°"), + oneOrMoreSpaces, + eastWest + ) + .map { long, _, _, longSign in long * longSign } +let coord = zip( + zeroOrMoreSpaces, + latitude, + literal(","), + oneOrMoreSpaces, + longitude + ) + .map { _, lat, _, _, long in + Coordinate( + latitude: lat, + longitude: long + ) +} + + + +coord.run("40.446° N, 79.982° W") + + +coord.run("40.446° N, 79.982° W") +coord.run("40.446° N, 79.982° W ") +coord.run(" 40.446° N, 79.982° W ") + + +import Foundation + +let df = DateFormatter() +df.dateStyle = .medium + +df.date(from: "Jan 29, 2018") +df.date(from: "Jan 29, 2018") +df.date(from: " Jan 29, 2018") + + + +try NSRegularExpression(pattern: " *") + + + +Scanner().charactersToBeSkipped = .whitespaces + + + +oneOrMoreSpaces.run(" Hello, world!") +oneOrMoreSpaces.run("Hello, world!") diff --git a/0062-parser-combinators-pt1/ParserCombinators.playground/contents.xcplayground b/0062-parser-combinators-pt1/ParserCombinators.playground/contents.xcplayground new file mode 100644 index 00000000..63b6dd8d --- /dev/null +++ b/0062-parser-combinators-pt1/ParserCombinators.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/0062-parser-combinators-pt1/README.md b/0062-parser-combinators-pt1/README.md new file mode 100644 index 00000000..1bf16cbe --- /dev/null +++ b/0062-parser-combinators-pt1/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Parser Combinators: Part 1](https://www.pointfree.co/episodes/ep62-parser-combinators-pt1) +> +> Even though `map`, `flatMap` and `zip` pack a punch, there are still many parsing operations that can't be done using them alone. This is where "parser combinators" come into play. Let's look at a few common parsing problems and solve them using parser combinators!