Safe Haskell | None |
---|---|
Language | Haskell2010 |
Write your grammar once and get both parser and pretty-printer, for free.
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.
(person "John Doe" :address "42 Whatever str." :age 25)
will parse into:
Person {pName = "John Doe", pAddress = "42 Whatever str.", pAge = Just 25}
and the record will pretty-print back into:
(person "John Doe" :address "42 Whatever str." :age 25)
Grammar types diagram:
-------------------------------------- | AtomGrammar | -------------------------------------- ^ | atomic grammar combinators v ------------------------------------------------------ | SexpGrammar | ------------------------------------------------------ | list, vect ^ ^ v | el, rest | ---------------------------------- | | SeqGrammar | | ---------------------------------- | (.:) | props | (.:?) v | ------------------------------------- | PropGrammar | -------------------------------------
- data Grammar g t t'
- type SexpG a = forall t. Grammar SexpGrammar (Sexp :- t) (a :- t)
- type SexpG_ = forall t. Grammar SexpGrammar (Sexp :- t) t
- iso :: (a -> b) -> (b -> a) -> Grammar g (a :- t) (b :- t)
- embedPrism :: StackPrism a b -> Grammar g (a :- t) (b :- t)
- embedParsePrism :: String -> StackPrism b a -> Grammar g (a :- t) (b :- t)
- push :: Eq a => a -> Grammar g t (a :- t)
- pushForget :: a -> Grammar g t (a :- t)
- bool :: SexpG Bool
- integer :: SexpG Integer
- int :: SexpG Int
- real :: SexpG Scientific
- double :: SexpG Double
- string :: SexpG Text
- symbol :: SexpG Text
- keyword :: SexpG Kw
- string' :: SexpG String
- symbol' :: SexpG String
- enum :: (Enum a, Bounded a, Eq a, Data a) => SexpG a
- sym :: Text -> SexpG_
- kw :: Kw -> SexpG_
- list :: Grammar SeqGrammar t t' -> Grammar SexpGrammar (Sexp :- t) t'
- vect :: Grammar SeqGrammar t t' -> Grammar SexpGrammar (Sexp :- t) t'
- 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
- (.:) :: 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)
- pair :: Grammar g (b :- (a :- t)) ((a, b) :- t)
- unpair :: Grammar g ((a, b) :- t) (b :- (a :- t))
- swap :: Grammar g (b :- (a :- t)) (a :- (b :- t))
- coproduct :: [Grammar g a b] -> Grammar g a b
- grammarFor :: Name -> ExpQ
- data SexpGrammar a b
- data AtomGrammar a b
- data SeqGrammar a b
- data PropGrammar a b
- parseFromString :: SexpG a -> String -> Either String a
- parseFromFile :: SexpG a -> FilePath -> IO (Either String a)
- prettyToText :: SexpG a -> a -> Either String Text
- prettyToFile :: FilePath -> SexpG a -> a -> IO (Either String ())
- parse :: (Functor m, MonadPlus m, MonadError String m, InvertibleGrammar m g) => Grammar g (Sexp :- ()) (a :- ()) -> Sexp -> m a
- gen :: (Functor m, MonadPlus m, MonadError String m, InvertibleGrammar m g) => Grammar g (Sexp :- ()) (a :- ()) -> a -> m Sexp
- class SexpIso a where
- type StackPrism a b = forall p f. (Choice p, Applicative f) => p a (f a) -> p b (f b)
- data h :- t :: * -> * -> * = h :- t
Documentation
type SexpG a = forall t. Grammar SexpGrammar (Sexp :- t) (a :- t) Source
Grammar which matches Sexp to a value of type a and vice versa.
type SexpG_ = forall t. Grammar SexpGrammar (Sexp :- t) t Source
Grammar which pattern matches Sexp and produces nothing, or consumes nothing but generates some Sexp.
Combinators
Primitive grammars
iso :: (a -> b) -> (b -> a) -> Grammar g (a :- t) (b :- t) Source
Make a grammar from a total isomorphism on top element of stack
embedPrism :: StackPrism a b -> Grammar g (a :- t) (b :- t) Source
Make a grammar from a prism which can fail during generation
embedParsePrism :: String -> StackPrism b a -> Grammar g (a :- t) (b :- t) Source
Make a grammar from a prism which can fail during parsing
push :: Eq a => a -> Grammar g t (a :- t) Source
Unconditionally push given value on stack, i.e. it does not consume anything on parsing. However such grammar expects the same value as given one on stack during generation.
pushForget :: a -> Grammar g t (a :- t) Source
Same as push
except it does not check the value on stack during
generation. Potentially unsafe as it "forgets" some data.
Atom grammars
real :: SexpG Scientific Source
Define an atomic real number (Scientific) grammar
Define an atomic double precision floating point number (Double) grammar
enum :: (Enum a, Bounded a, Eq a, Data a) => SexpG a Source
Define a grammar for an enumeration type. Automatically derives all symbol names from data constructor names and "lispifies" them.
Complex grammars
list :: Grammar SeqGrammar t t' -> Grammar SexpGrammar (Sexp :- t) t' Source
Define a sequence grammar inside a list
vect :: Grammar SeqGrammar t t' -> Grammar SexpGrammar (Sexp :- t) t' Source
Define a sequence grammar inside a vector
Sequence grammars
el :: Grammar SexpGrammar (Sexp :- a) b -> Grammar SeqGrammar a b Source
Define a sequence element grammar
rest :: Grammar SexpGrammar (Sexp :- a) (b :- a) -> Grammar SeqGrammar a ([b] :- a) Source
Define a grammar for rest of the sequence
props :: Grammar PropGrammar a b -> Grammar SeqGrammar a b Source
Define a property list grammar on the rest of the sequence. The remaining sequence must be empty or start with a keyword and its corresponding value and continue with the sequence built by the same rules.
E.g.
:kw1 <val1> :kw2 <val2> ... :kwN <valN>
Property grammars
(.:) :: Kw -> Grammar SexpGrammar (Sexp :- t) (a :- t) -> Grammar PropGrammar t (a :- t) Source
Define property pair grammar
(.:?) :: Kw -> Grammar SexpGrammar (Sexp :- t) (a :- t) -> Grammar PropGrammar t (Maybe a :- t) Source
Define optional property pair grammar
Utility grammars
unpair :: Grammar g ((a, b) :- t) (b :- (a :- t)) Source
Deconstruct pair into two top elements of stack
swap :: Grammar g (b :- (a :- t)) (a :- (b :- t)) Source
Swap two top elements of stack. Useful for defining grammars for data constructors with inconvenient field order.
E.g. consider a data type, which has field order different from what would like to display to user:
data Command = Command { args :: [String], executable :: FilePath }
In S-expression executable should go first:
commandGrammar = $(grammarFor 'Command) . list ( el (sym "call") >>> -- symbol "call" el string' >>> -- executable name rest string' >>> -- arguments swap )
coproduct :: [Grammar g a b] -> Grammar g a b Source
Combine several alternative grammars into one grammar. Useful for defining grammars for sum types.
E.g. consider a data type:
data Maybe a = Nothing | Just a
A total grammar which would handle both cases should be constructed
with coproduct
combinator or with Semigroup
's instance.
maybeGrammar :: SexpG a -> SexpG (Maybe a) maybeGrammar g = coproduct [ $(grammarFor 'Nothing) . kw (Kw "nil") , $(grammarFor 'Just) . g ]
TemplateHaskell helpers
grammarFor :: Name -> ExpQ Source
Build a prism and the corresponding grammar that will match on the given constructor and convert it to reverse sequence of :- stacks.
E.g. consider a data type:
data FooBar a b c = Foo a b c | Bar
For constructor Foo
fooGrammar = $(grammarFor 'Foo)
will expand into
fooGrammar = GenPrism "Foo" $ stackPrism (\(c :- b :- a :- t) -> Foo a b c :- t) (\case { Foo a b c :- t -> Just $ c :- b :- a :- t; _ -> Nothing })
Note the order of elements on the stack:
ghci> :t fooGrammar fooGrammar :: Grammar g (c :- (b :- (a :- t))) (FooBar a b c :- t)
Grammar types
data SexpGrammar a b Source
data AtomGrammar a b Source
data SeqGrammar a b Source
data PropGrammar a b Source
Parsing and printing
Low-level printing and parsing
parse :: (Functor m, MonadPlus m, MonadError String m, InvertibleGrammar m g) => Grammar g (Sexp :- ()) (a :- ()) -> Sexp -> m a Source
gen :: (Functor m, MonadPlus m, MonadError String m, InvertibleGrammar m g) => Grammar g (Sexp :- ()) (a :- ()) -> a -> m Sexp Source
Typeclass for Sexp grammars
Nothing
SexpIso Bool Source | |
SexpIso Double Source | |
SexpIso Int Source | |
SexpIso Integer Source | |
SexpIso Scientific Source | |
SexpIso Text Source | |
SexpIso a => SexpIso [a] Source | |
SexpIso a => SexpIso (Maybe a) Source | |
(Ord a, SexpIso a) => SexpIso (Set a) Source | |
SexpIso a => SexpIso (NonEmpty a) Source | |
(SexpIso a, SexpIso b) => SexpIso (a, b) Source | |
(Ord k, SexpIso k, SexpIso v) => SexpIso (Map k v) Source |
Re-exported from stack-prism
type StackPrism a b = forall p f. (Choice p, Applicative f) => p a (f a) -> p b (f b)