$30 off During Our Annual Pro Sale. View Details »

Parser Combinators

yhkaplan
December 03, 2019

Parser Combinators

yhkaplan

December 03, 2019
Tweet

More Decks by yhkaplan

Other Decks in Programming

Transcript

  1. Parser Combinators
    1

    View Slide

  2. Self Intro
    » Name: Joshua Kaplan
    » Interests:
    !
    λ
    "
    » Company: GMO Pepabo
    » Service: minne
    2

    View Slide

  3. What are Parser Combinators?
    3

    View Slide

  4. » Higher-order function that takes multiple parsers
    and /combines/ them into a new parser
    » CS theory
    » Parsec, Haskell
    4

    View Slide

  5. In Swift
    struct Parser {
    let run: (inout Substring) throws -> A
    }
    5

    View Slide

  6. Characteristics
    » Monadic
    » Composable
    » Generic
    » Immutable
    6

    View Slide

  7. Use
    let int = Parser { input in
    let num = input.prefix(while: { $0.isNumber })
    guard let number = Int(num) else { throw ParserError.IntError.notANumber(num) }
    input.removeFirst(num.count)
    return number
    }
    7

    View Slide

  8. func removingLiteral(_ string: String) -> Parser {
    return Parser { input in
    guard input.hasPrefix(string) else {
    throw ParserError.StringError.literalNotFound(string[...])
    }
    input.removeFirst(string.count)
    }
    }
    8

    View Slide

  9. Higher order functions
    » map
    » flatMap (bind, >>=)
    » zip
    9

    View Slide

  10. struct Coordinate { let x, y: Int }
    let str = "1,2"
    let coordinateParser = zip(
    int,
    removingLiteral(","),
    int
    ).map { x, _, y in Coordinate(x: x, y: y) }
    let (coordinate, _) = try coordinateParser.run(str[...])
    ▿ Coordinate
    - x: 1
    - y: 2
    10

    View Slide

  11. func substring(while predicate: @escaping (Character) -> Bool) -> Parser {
    return Parser { input in
    let p = input.prefix(while: predicate)
    input.removeFirst(p.count)
    return p
    }
    }
    11

    View Slide

  12. Let's make another parser!
    struct Person { let name: String; let age: Int }
    let str = "name: John, age: 90"
    12

    View Slide

  13. Name and age parsers
    let nameParser = zip(
    removingLiteral("name: "),
    substring(while: { $0.isLetter })
    ).map { _, name in return String(name) }
    let ageParser = zip(
    removingLiteral("age: "),
    int
    ).map { _, age in return age }
    13

    View Slide

  14. Person parser
    let personParser = zip(
    nameParser,
    removingLiteral(", "),
    ageParser
    ).map { name, _, age in return Person(name: name, age: age) }
    let (person, _) = try personParser.run(str[...])
    ▿ Person
    - name: "John"
    - age: 90
    14

    View Slide

  15. Comparison
    » By hand
    » Scanner
    15

    View Slide

  16. Why and when?
    16

    View Slide

  17. References
    » https://github.com/pointfreeco/episode-code-
    samples/tree/master/0064-parser-combinators-pt3
    » https://talk.objc.io/episodes/S01E13-parsing-
    techniques
    » https://github.com/johnpatrickmorgan/Sparse
    » https://github.com/davedufresne/SwiftParsec
    » https://github.com/thoughtbot/Argo
    » https://github.com/tryswift/TryParsec
    17

    View Slide