packages feed

sexp-grammar-1.1.0: README.md

sexp-grammar
============

Invertible syntax library for serializing and deserializing Haskell
structures into S-expressions. Just write a grammar once and get
both parser and pretty-printer, for free.

The package is heavily inspired by the paper
[Invertible syntax descriptions: Unifying parsing and pretty printing]
(http://www.informatik.uni-marburg.de/~rendel/unparse/) and a similar
implementation of invertible grammar approach for JSON, library by
Martijn van Steenbergen called
[JsonGrammar2](https://github.com/MedeaMelana/JsonGrammar2).

Let's take a look at example:

```haskell
data Person = Person
  { pName    :: String
  , pAddress :: String
  , pAge     :: Maybe Int
  } deriving (Show)

personGrammar :: SexpG Person
personGrammar =
  $(grammarFor 'Person) .               -- construct Person from
    list (                              -- a list with
      el (sym "person") >>>             -- symbol "person",
      el string'        >>>             -- some string,
      props (                           -- and properties
        Kw "address" .: string' >>>     -- :address with string value,
        Kw "age" .:? int))              -- and optional :age int proprety
```

So now we can use `personGrammar` to parse S-expessions to `Person`
record and pretty-print any `Person` back to S-expression:

```haskell
ghci> :m Control.Category Language.SexpGrammar
ghci> parseFromString personGrammar <$> getLine
(person "John Doe" :address "42 Whatever str." :age 25)
Right (Person {pName = "John Doe", pAddress = "42 Whatever str.", pAge = Just 25})
ghci> let (Right person) = it
ghci> prettyToText personGrammar person
(person
 "John Doe"
 :address
 "42 Whatever str."
 :age
 25)
```

The grammars are described in terms of isomorphisms and stack
manipulations.

The simplest primitive grammars are atom grammars, which match `Sexp`
atoms with Haskell counterparts:

```haskell
                             --               grammar type   | consumes     | produces
                             --    --------------------------+--------------+-------------------
bool    :: SexpG Bool        -- or Grammar    SexpGrammar      (Sexp :- t)    (Bool       :- t)
integer :: SexpG Integer     -- or Grammar    SexpGrammar      (Sexp :- t)    (Integer    :- t)
int     :: SexpG Int         -- or Grammar    SexpGrammar      (Sexp :- t)    (Int        :- t)
real    :: SexpG Scientific  -- or Grammar    SexpGrammar      (Sexp :- t)    (Scientific :- t)
double  :: SexpG Double      -- or Grammar    SexpGrammar      (Sexp :- t)    (Double     :- t)
string  :: SexpG Text        -- or Grammar    SexpGrammar      (Sexp :- t)    (Text       :- t)
string' :: SexpG String      -- or Grammar    SexpGrammar      (Sexp :- t)    (String     :- t)
symbol  :: SexpG Text        -- or Grammar    SexpGrammar      (Sexp :- t)    (Text       :- t)
symbol' :: SexpG String      -- or Grammar    SexpGrammar      (Sexp :- t)    (String     :- t)
keyword :: SexpG Kw          -- or Grammar    SexpGrammar      (Sexp :- t)    (Kw         :- t)
sym     :: Text -> SexpG_    -- or Grammar    SexpGrammar      (Sexp :- t)    t
kw      :: Kw   -> SexpG_    -- or Grammar    SexpGrammar      (Sexp :- t)    t
```

Grammars matching lists and vectors can be defined using an auxiliary
grammar type `SeqGrammar`. The following primitives embed
`SeqGrammar`s into main `SexpGrammar` context:

```haskell
list  :: Grammar SeqGrammar t t' -> Grammar SexpGrammar (Sexp :- t) t'
vect  :: Grammar SeqGrammar t t' -> Grammar SexpGrammar (Sexp :- t) t'
```

Grammar type `SeqGrammar` basically describes the sequence of elements
in a `Sexp` list (or vector). Single element grammar is defined with
`el`, "match rest of the sequence as list" grammar could be defined
with `rest` combinator. If the rest of the sequence is a property
list, `props` combinator should be used.

```haskell
el    :: Grammar SexpGrammar (Sexp :- a)  b       -> Grammar SeqGrammar a b
rest  :: Grammar SexpGrammar (Sexp :- a) (b :- a) -> Grammar SeqGrammar a ([b] :- a)
props :: Grammar PropGrammar a b                  -> Grammar SeqGrammar a b
```

`props` combinator embeds properties grammar `PropGrammar` into a
`SeqGrammar` context. `PropGrammar` describes what keys and values to
match.

```haskell
(.:)  :: Kw
      -> Grammar SexpGrammar (Sexp :- t) (a :- t)
      -> Grammar PropGrammar t (a :- t)

(.:?) :: Kw
      -> Grammar SexpGrammar (Sexp :- t) (a :- t)
      -> Grammar PropGrammar t (Maybe a :- t)
```

Please refer to Haddock on [Hackage](http://hackage.haskell.org/package/sexp-grammar)
for API documentation.

Diagram of grammar contexts:

```

     --------------------------------------
     |              AtomGrammar           |
     --------------------------------------
         ^
         |  atomic grammar combinators
         v
 ------------------------------------------------------
 |                      SexpGrammar                   |
 ------------------------------------------------------
         | list, vect     ^              ^
         v                | el, rest     |
     ----------------------------------  |
     |           SeqGrammar           |  |
     ----------------------------------  | (.:)
              | props                    | (.:?)
              v                          |
          -------------------------------------
          |             PropGrammar           |
          -------------------------------------

```