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

Fraser Tweedale

October 13, 2015
Tweet

More Decks by Fraser Tweedale

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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?
  7. Better Parser API data Amino = G | A |

    T | C type DNA = [Amino] parseHackerGene :: Parser DNA (NonEmpty Amino) parseHackerGene = many1 (symbol A <|> symbol C)
  8. 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!
  9. 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
  10. 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
  11. 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 [email protected] Twitter @hackuador