mirror of
https://github.com/frasertweedale/hs-fresnel.git
synced 2024-07-07 07:36:29 +03:00
Prism-based parsers and pretty printers
bench | ||
src/Data | ||
test | ||
.gitignore | ||
.travis.yml | ||
agpl-3.0.txt | ||
fresnel.cabal | ||
README.rst | ||
Setup.hs | ||
stack.yaml |
Prism-based unified parsers and pretty printers =============================================== Synopsis -------- Defining a grammar: .. code:: haskell {-# LANGUAGE TemplateHaskell #-} import Data.Fresnel import Data.Fresnel.Char import Data.Fresnel.TH -- | A contrived and poorly-constrained type for phone numbers -- data PhoneNumber = PhoneNumber { phoneAreaCode :: String , phoneNumber :: String } makeIso ''PhoneNumber phoneNumberG :: Cons s s Char Char => Grammar s PhoneNumber phoneNumberG = _PhoneNumber <<$>> literal '(' *>> replicate 2 digit <<* literal ')' <<* match (many space) " " <<*>> replicate 8 (match (many space) "" *>> digit) Using a grammar:: λ> parse phoneNumberG ("(07)3456 78 9 0" :: String) Just (PhoneNumber "07" "34567890") λ> print phoneNumberG (PhoneNumber "07" "34567890") :: String "(07) 34567890" Library overview ---------------- *fresnel* is a combinator library for building first class unified parser/printers out of prisms and isos. A parser/printer is a ``Prism``, but most functions reference the ``Grammar`` type alias: .. code:: haskell type Grammar s a = Prism' s (a, s) The prism can be previewed to parse, and reviewed to print. The ``parse`` and ``print`` functions are provided for convenience. .. code:: haskell parse :: Grammar s b -> s -> Maybe b parse g s = fst <$> preview g s print :: Monoid s => Grammar s b -> b -> s print g b = review g (b, mempty) Many familiar combinators are provided. Some have the ``Control.Lens.Cons.Cons`` type class constraint which provides for generic parser/printer that work with many types that can be (de)constructed "piecewise". .. code:: haskell satisfy :: Cons s s a a => (a -> Bool) -> Grammar s a symbol :: (Cons s s a a, Eq a) => a -> Grammar s a eof :: Cons s s a a => Grammar s () between :: Grammar s () -> Grammar s () -> Grammar s a -> Grammar s a many :: Grammar s a -> Grammar s [a] many1 :: Grammar s a -> Grammar s (NonEmpty a) replicate :: Natural -> Grammar s a -> Grammar s [a] The analogue of ``fmap``/``(<$>)`` is ``(<<$>>)`` which composes an ``Iso`` into the grammar. .. code:: haskell (<<$>>) :: Iso' a b -> Grammar s a -> Grammar s b Product types (records) and sum types (choices) can be handled with special combinators in conjunction with isos. *Template Haskell* code is provided for automatically generating appropriate ``Iso`` values for your types. .. code:: haskell (<<*>>) :: Grammar s a -> Grammar s b -> Grammar s (a, b) (<<+>>) :: Grammar s a -> Grammar s b -> Grammar s (Either a b) Combinators for sequencing and an analogue of ``(>>=)`` are also provided. .. code:: haskell (<<*) :: Grammar s a -> Grammar s () -> Grammar s a (*>>) :: Grammar s () -> Grammar s a -> Grammar s a bind :: Grammar s a -> (a -> Grammar s b) -> (b -> a) -> Grammar s b By the way, I lied about the type of ``(<<$>>)``. It can also be used with any old ``Prism'``. A failed ``preview`` is a parse failure. Isos are just prisms that never fail! Here's the real type: .. code:: haskell (<<$>>) :: Prism' a b -> Grammar s a -> Grammar s b You can consume and print a literal value, or match an arbitrary grammar while printing a "canonical" value on review. .. code:: haskell literal :: (Cons s s a a, Eq a) => a -> Grammar s () match :: Grammar s a -> a -> Grammar s () Mapping to/from custom data types --------------------------------- You can automatically generate ``Iso`` values for custom data types via the ``makeIso`` Template Haskell function. .. code:: haskell {-# LANGUAGE TemplateHaskell #-} import Data.Fresnel.TH (makeIso) data Foo = A Int Char | B Bool makeIso ''Foo This will create the ``Iso``: .. code:: haskell _Foo :: Iso' (Either (Int, Char) Bool) Foo