Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Parser combinators from scratch - BFPG

Parser combinators from scratch - BFPG

Fraser writes a simple applicative based parser from scratch showing the concepts. He then extends the applicative parser to be built around Cons to abstract away the stream and piecewise type that the parser is dealing with.

Video: https://www.youtube.com/watch?v=YNGGUvP3Egg

7c0f9b056604fe541691e18aeb679cf7?s=128

Fraser Tweedale

October 13, 2015
Tweet

More Decks by Fraser Tweedale

Other Decks in Programming

Transcript

  1. Parser combinators from scratch Fraser Tweedale @hackuador October 13, 2015

  2. Confession I hardly know anything about parsing But I know

    I don’t like repeating myself And I know a bit about APIs and abstraction
  3. Parser newtype Parser a = Parser { runParser :: String

    -> Maybe (a, String) }
  4. Parser API data Thing = Thing Int Bool -- serialised

    repr: "555t", "7f" -- not valid: "t555", "7 f" -- assumed parseInt :: Parser Int parseBool :: Parser Bool -- we want to define parser thusly parseThing :: Parser Thing parseThing = Thing ‘glue‘ parseInt ‘glue‘ parseBool
  5. Parser API data Thing = Thing Int Bool -- serialised

    repr: "555t", "7f" -- not valid: "t555", "7 f" -- assumed parseInt :: Parser Int parseBool :: Parser Bool -- we shall define parser thusly parseThing :: Parser Thing parseThing = Thing <$> parseInt <*> parseBool
  6. Parser API class Functor (f :: * -> *) where

    fmap :: (a -> b) -> f a -> f b class Functor f => Applicative (f :: * -> *) where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b (*>) :: f a -> f b -> f b -- has default definition (<*) :: f a -> f b -> f a -- has default definition class Applicative f => Alternative (f :: * -> *) where empty :: f a (<|>) :: f a -> f a -> f a
  7. Parser API (<|>) :: Parser a -> Parser a ->

    Parser a many :: Parser a -> Parser [a] many1 :: Parser a -> Parser (NonEmpty a) sepBy :: Parser sep -> Parser a -> Parser [a] between :: Parser l -> Parser r -> Parser a -> Parser a
  8. Let’s write some code!

  9. What if. . . Input type is not String (e.g.

    Text)? Piecewise input type is not Char (e.g. Word8)? Should you have to use a separate library / module?
  10. Better Parser API data Amino = G | A |

    T | C type DNA = [Amino] parseHackerGene :: Parser DNA (NonEmpty Amino) parseHackerGene = many1 (symbol A <|> symbol C)
  11. Better Parser API - enter Cons uncons :: Cons s

    s a a => s -> Maybe (a, s) instance Cons ByteString ByteString Word8 Word8 instance Cons Text Text Char Char instance Cons [a] [b] a b instance Cons (Vector a) (Vector b) a b -- and many more!
  12. Better Parser API import Control.Lens.Cons newtype Parser s a =

    Parser { runParser :: s -> Maybe (a, s) } satisfy :: Cons s s a a => (a -> Bool) -> Parser s a
  13. Let’s refactor some code!

  14. Haskell parsing libs Parsec (also: Megaparsec) Attoparsec Trifecta uu-parsinglib Parsers

    (unifying interface)
  15. Design considerations Ambiguous parses Incremental input Errors and recovery Performance

    Backtracking Continuation passing style Transformers
  16. Resources and related topics Monadic Parsing in Haskell (Functional Pearl)

    www.cs.uwyo.edu/˜jlc/courses/3015/parser pearl.pdf Lexer / parser generators (alex / happy) Invertible Syntax Descriptions Paper: www.informatik.uni-marburg.de/˜rendel/unparse/ Libraries: boomerang, roundtrip, invertible-syntax Prisms, lenses and other optics
  17. Fin Copyright 2015 Fraser Tweedale This work is licensed under

    the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/. Slides https://github.com/frasertweedale/talks/ Email frase@frase.id.au Twitter @hackuador