Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
..
Failed to load latest commit information.
src
ChangeLog.md
LICENSE
Parsing.cabal
Readme.md
Setup.hs
custom.css
index.html
stack.yaml

Readme.md

title subtitle author date to standalone mathjax revealjs-url css height width controls progress history theme transition
Parsing
Martin Heuschober
Vienna, Mar. 9th 2015,
License: CC-BY-SA-4.0
revealjs
true
true
../javascript/revealjs
custom.css
'90%'
'80%'
true
true
true
solarized
default
  • JSON
  • BSON
  • attoparsec
  • parsec

What is parsing?

The process of transforming a

Stringvalue

where value usually means a custom datatype, describing a problem.

Before we start

This presentation is accompanied by a haskell module, execute

stack ghci --ghci-options -XOverloadedStrings

to play around with it

Value

The values we will have a look at in the following chapters will be

Seaman.hs

data Seaman = Captain  { name   :: Text
                       , vessel :: Text
                       , age    :: Int }
            | Mate { name         :: Text
                   , nextDutyTime :: UTCTime }

for the aeson example.

Body.hs

data Body = Body { bodyID :: Int
                 , brandID :: Int }

for the bson example.

Commands.hs

data Command = POST {_message :: Message}
             | READ {_userName :: UserName }
             | FOLLOW {_who :: UserName , _whom :: UserName}
             | WALL {_userName :: UserName}
             deriving (Show, Eq)

for the attoparsec example

FilmDB.hs

type UserRatings = (String, Int)
type Title = String
type Director = String
type Year = Int
type Film = (Title, Director, Year, [UserRatings])

Aeson

API

class ToJSON a where
    toJSON :: a -> Value
    toEncoding :: a -> Encoding
class FromJSON a where
    parseJSON :: Value -> Parser a

Using aeson

The simple case

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Seaman where

import           Data.Aeson
import           GHC.Generics
-- import some more

data Seaman = Captain  { name   :: Text
                       , vessel :: Text
                       , age    :: Int }
            | Mate { name         :: Text
                   , nextDutyTime :: UTCTime }
            deriving (Show, Generic)

instance FromJSON Seaman
instance ToJSON Seaman

Example

λ> encode mrd
λ> eitherDecode mrdFail

BSON

API

class (Typeable a, Show a, Eq a) => Val a where
    val :: a -> Value
    ...
    cast' :: Value -> Maybe a
    ...

val

instance Val Body where
  val (Body body brand) = Doc [ "bodyID"  := (Int64 $ fromIntegral body)
                              , "brandID" := (Int64 $ fromIntegral brand)]

cast'

cast' (Doc bson) = case (bson !? "bodyID") of
            Just (Int64 x) ->
               case (bson !? "brandID") of
                     Just (Int64 y) -> Just (Body (fromIntegral x)
                                  (fromIntegral y))
                     Just _  -> Nothing
                     Nothing -> Nothing
            Just _ -> Nothing
            Nothing -> Nothing
cast' (Array _) = undefined
cast' _ = Nothing

or simpler

val + cast'

instance Val Body where
  val (Body body brand) = Doc [ "bodyID"  := (val body)
                              , "brandID" := (val brand)]
  cast' (Doc bson) = do body  <- (bson !? "bodyID")
                        brand <- (bson !? "brandID")
                        Body <$> cast' body <*> cast' brand

  cast' _ = Nothing

Example

λ> val (Body 30 4)
λ> cast' $ val (Body 30 4) :: Maybe Body

Attoparsec

Value

data Command = POST {_message :: Message}
             | READ {_userName :: UserName }
             | FOLLOW {_who :: UserName , _whom :: UserName}
             | WALL {_userName :: UserName}

from the social networking kata

Parsing

command :: Parser Command
command = choice $ map (skipSpace >>)[post_, wall_, follow_, read_]
  where post_ :: Parser Command
        post_ = do _author <- T.strip  <$> X.takeWhile (/= '-')
                   string "->"
                   _content <- T.strip <$> takeText
                   return (POST Message{..})
        ..

Example

λ> parseOnly command ..

Parsec

Value

type UserRatings = (String, Int)
type Title = String
type Director = String
type Year = Int
type Film = (Title, Director, Year, [UserRatings])

Helpers

str :: Parser String
str = many1 (noneOf ",")

int :: Parser Int
int = read <$> many1 digit

userRating :: Parser UserRatings
userRating = do user <- str
                comma
                rating <- int
                return (user, rating)

comma :: Parser Char
comma = char ','

Parsing the Film

film :: Parser Film
film = do title <- str
          comma
          director <- str
          comma
          year <- int
          comma
          ratings <- userRating `sepBy` comma
          eof
          return (title, director, year, ratings)

Example

λ> myEitherParse film "message string" testString