Skip to content

Commit

Permalink
58
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed May 19, 2019
1 parent cbae3ca commit 5f4ef37
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 0 deletions.
248 changes: 248 additions & 0 deletions 0058-what-is-a-parser-pt3/Parsers.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@

Int("42")
Int("42-")
Double("42")
Double("42.32435")
Bool("true")
Bool("false")
Bool("f")

import Foundation

UUID.init(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF")
UUID.init(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEE")
UUID.init(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEZ")

URL.init(string: "https://www.pointfree.co")
URL.init(string: "^https://www.pointfree.co")

let components = URLComponents.init(string: "https://www.pointfree.co?ref=twitter")
components?.queryItems

let df = DateFormatter()
df.timeStyle = .none
df.dateStyle = .short
type(of: df.date(from: "1/29/17"))
df.date(from: "-1/29/17")


let emailRegexp = try NSRegularExpression(pattern: #"\S+@\S+"#)
let emailString = "You're logged in as [email protected]"
let emailRange = emailString.startIndex..<emailString.endIndex
let match = emailRegexp.firstMatch(
in: emailString,
range: NSRange(emailRange, in: emailString)
)!
emailString[Range(match.range(at: 0), in: emailString)!]

//let scanner = Scanner.init(string: "A42 Hello World")
//var int = 0
//scanner.scanInt(&int)
//int

// 40.6782° N, 73.9442° W
struct Coordinate {
let latitude: Double
let longitude: Double
}

//typealias Parser<A> = (String) -> A

struct Parser<A> {
// let run: (String) -> A?
// let run: (String) -> (match: A?, rest: String)
// let run: (inout String) -> A?
let run: (inout Substring) -> A?

func run(_ str: String) -> (match: A?, rest: Substring) {
var str = str[...]
let match = self.run(&str)
return (match, str)
}
}

let int = Parser<Int> { str in
let prefix = str.prefix(while: { $0.isNumber })
guard let int = Int(prefix) else { return nil }
str.removeFirst(prefix.count)
return int
}

//Substring


int.run("42")
int.run("42 Hello World")
int.run("Hello World")

let double = Parser<Double> { str in
let prefix = str.prefix(while: { $0.isNumber || $0 == "." })
guard let match = Double(prefix) else { return nil }
str.removeFirst(prefix.count)
return match
}

double.run("42")
double.run("42.87432893247")
double.run("42.87432 Hello World")
double.run("42.4.1.4.6")

func literal(_ literal: String) -> Parser<Void> {
return Parser<Void> { str in
guard str.hasPrefix(literal) else { return nil }
str.removeFirst(literal.count)
return ()
}
}

literal("cat").run("cat dog")
literal("cat").run("dog cat")

func always<A>(_ a: A) -> Parser<A> {
return Parser<A> { _ in a }
}

always("cat").run("dog")

// let never<A> = Parser { ... }
func never<A>() -> Parser<A> {
return Parser<A> { _ in nil }
}
extension Parser {
static var never: Parser {
return Parser { _ in nil }
}
}
(never() as Parser<Int>).run("dog")
Parser<Int>.never.run("dog")


// (A) -> A
// (inout A) -> Void



enum Route {
case home
case profile
case episodes
case episode(id: Int)
}

let router = Parser<Route> { str in
fatalError()
}

//router.run("/") // .home
//router.run("/episodes/42") // .episode(42)

//switch router.run("/episodes/42") {
//case .none:
//case .some(.home):
//case .some(.profile):
//case .some(.episodes):
//case let .some(.episode(id)):
//}

enum EnumPropertyGenerator {
case help
case version
case invoke(urls: [URL], dryRun: Bool)
}

let cli = Parser<EnumPropertyGenerator> { str in
fatalError()
}

//cli.run("generate-enum-properties --version") // .version
//cli.run("generate-enum-properties --help") // .help
//cli.run("generate-enum-properties --dry-run /path/to/file.swift") // .invoke(["/path/to/file.swift"], dryRun: true)
//
//switch cli.run("generate-enum-properties --dry-run /path/to/file.swift") {
//case .help:
//case .version:
//case .invoke:
//case nil:
//}

let northSouth = Parser<Double> { str in
guard
let cardinal = str.first,
cardinal == "N" || cardinal == "S"
else { return nil }
str.removeFirst(1)
return cardinal == "N" ? 1 : -1
}
let eastWest = Parser<Double> { 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
)


// let parts = str.split(separator: " ")
// guard parts.count == 4 else { return nil }
// guard
// let lat = Double(parts[0].dropLast()),
// let long = Double(parts[2].dropLast())
// else { return nil }
// let latCard = parts[1].dropLast()
// guard latCard == "N" || latCard == "S" else { return nil }
// let longCard = parts[3]
// guard longCard == "E" || longCard == "W" else { return nil }
// let latSign = latCard == "N" ? 1.0 : -1
// let longSign = longCard == "E" ? 1.0 : -1
// return Coordinate(latitude: lat * latSign, longitude: long * longSign)
}

print(parseLatLong("40.6782° N, 73.9442° W"))




func parseLatLongWithScanner(_ string: String) -> Coordinate? {
let scanner = Scanner(string: string)

var lat: Double = 0
guard scanner.scanDouble(&lat) else { return nil }

guard scanner.scanString("° ", into: nil) else { return nil }

var northSouth: NSString? = ""
guard scanner.scanCharacters(from: ["N", "S"], into: &northSouth) else { return nil }
let latSign = northSouth == "N" ? 1.0 : -1

guard scanner.scanString(", ", into: nil) else { return nil }

var long: Double = 0
guard scanner.scanDouble(&long) else { return nil }

guard scanner.scanString("° ", into: nil) else { return nil }

var eastWest: NSString? = ""
guard scanner.scanCharacters(from: ["E", "W"], into: &eastWest) else { return nil }
let longSign = eastWest == "E" ? 1.0 : -1

return Coordinate(latitude: lat * latSign, longitude: long * longSign)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='macos'>
<timeline fileName='timeline.xctimeline'/>
</playground>
5 changes: 5 additions & 0 deletions 0058-what-is-a-parser-pt3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## [Point-Free](https://www.pointfree.co)

> #### This directory contains code from Point-Free Episode: [What Is a Parser?: Part 3](https://www.pointfree.co/episodes/ep58-what-is-a-parser-part-3)
>
> Now that we've looked at a bunch of parsers that are at our disposal, let's ask ourselves what a parser really is from the perspective of functional programming and functions. We'll take a multi-step journey and optimize using Swift language features.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@ This repository is the home of code written on episodes of
1. [Swift Syntax Command Line Tool](0055-swift-syntax-command-line-tool)
1. [What Is a Parser?: Part 1](0056-what-is-a-parser-pt1)
1. [What Is a Parser?: Part 2](0057-what-is-a-parser-pt2)
1. [What Is a Parser?: Part 3](0058-what-is-a-parser-pt3)

0 comments on commit 5f4ef37

Please sign in to comment.