| Safe Haskell | Safe |
|---|---|
| Language | Haskell2010 |
Language.SexpGrammar
Contents
Description
Write your grammar once and get both parser and pretty-printer, for free.
import GHC.Generics
import Data.Text (Text)
import Language.SexpGrammar
import Language.SexpGrammar.Generic
data Person = Person
{ pName :: Text
, pAddress :: Text
, pAge :: Maybe Int
} deriving (Show, Generic)
instance SexpIso Person where
sexpIso = with $ \person -> -- Person is isomorphic to:
list ( -- a list with
el (sym "person") >>> -- a symbol "person",
el string >>> -- a string, and
props ( -- a property-list with
"address" .: string >>> -- a keyword :address and a string value, and
"age" .:? int)) >>> -- an optional keyword :age with int value.
personSo now we can use this isomorphism to parse S-expessions to Person
record and pretty-print Person records 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)
Synopsis
- type Sexp = Fix (Compose (LocatedBy Position) SexpF)
- data Position
- type SexpGrammar a = forall t. Grammar Position (Sexp :- t) (a :- t)
- data Grammar p a b
- data h :- t
- class SexpIso a where
- sexpIso :: SexpGrammar a
- toSexp :: SexpGrammar a -> a -> Either String Sexp
- encode :: SexpIso a => a -> Either String ByteString
- encodeWith :: SexpGrammar a -> a -> Either String ByteString
- encodePretty :: SexpIso a => a -> Either String ByteString
- encodePrettyWith :: SexpGrammar a -> a -> Either String ByteString
- fromSexp :: SexpGrammar a -> Sexp -> Either String a
- decode :: SexpIso a => ByteString -> Either String a
- decodeWith :: SexpGrammar a -> FilePath -> ByteString -> Either String a
- (>>>) :: Category cat => cat a b -> cat b c -> cat a c
- (<<<) :: Category cat => cat b c -> cat a b -> cat a c
- module Data.InvertibleGrammar.Combinators
- position :: Grammar Position (Sexp :- t) (Position :- (Sexp :- t))
- real :: Grammar Position (Sexp :- t) (Scientific :- t)
- double :: Grammar Position (Sexp :- t) (Double :- t)
- int :: Grammar Position (Sexp :- t) (Int :- t)
- integer :: Grammar Position (Sexp :- t) (Integer :- t)
- string :: Grammar Position (Sexp :- t) (Text :- t)
- symbol :: Grammar Position (Sexp :- t) (Text :- t)
- keyword :: Grammar Position (Sexp :- t) (Text :- t)
- sym :: Text -> Grammar Position (Sexp :- t) t
- kwd :: Text -> Grammar Position (Sexp :- t) t
- data List
- list :: Grammar Position (List :- t) (List :- t') -> Grammar Position (Sexp :- t) t'
- bracketList :: Grammar Position (List :- t) (List :- t') -> Grammar Position (Sexp :- t) t'
- braceList :: Grammar Position (List :- t) (List :- t') -> Grammar Position (Sexp :- t) t'
- el :: Grammar Position (Sexp :- t) t' -> Grammar Position (List :- t) (List :- t')
- rest :: (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (List :- t) (List :- ([a] :- t))
- data PropertyList
- props :: Grammar Position (PropertyList :- t) (PropertyList :- t') -> Grammar Position (List :- t) (List :- t')
- key :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (a :- t))
- optKey :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (Maybe a :- t))
- (.:) :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (a :- t))
- (.:?) :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (Maybe a :- t))
- restKeys :: (forall t. Grammar Position (Sexp :- (Text :- t)) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- ([a] :- t))
- data Prefix
- prefixed :: Prefix -> Grammar Position (Sexp :- t) (a :- t) -> Grammar Position (Sexp :- t) (a :- t)
- quoted :: Grammar Position (Sexp :- t) (a :- t) -> Grammar Position (Sexp :- t) (a :- t)
- hashed :: Grammar Position (Sexp :- t) (a :- t) -> Grammar Position (Sexp :- t) (a :- t)
- data Mismatch
- expected :: Text -> Mismatch
- unexpected :: Text -> Mismatch
Data types
type Sexp = Fix (Compose (LocatedBy Position) SexpF) Source #
S-expression type annotated with positions. Useful for further parsing.
Position: file name, line number, column number
Instances
| Eq Position Source # | |
| Ord Position Source # | |
Defined in Language.Sexp.Types | |
| Show Position Source # | |
| Show Sexp Source # | |
| Generic Position Source # | |
| NFData Position Source # | |
Defined in Language.Sexp.Types | |
| Pretty Position Source # | |
Defined in Language.Sexp.Types | |
| NFData (Fix (Compose (LocatedBy Position) SexpF)) Source # | |
| type Rep Position Source # | |
Defined in Language.Sexp.Types type Rep Position = D1 (MetaData "Position" "Language.Sexp.Types" "sexp-grammar-2.2.1-3ZZuMApZcqcGTL2RunVA7T" False) (C1 (MetaCons "Position" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 FilePath) :*: (S1 (MetaSel (Nothing :: Maybe Symbol) SourceUnpack SourceStrict DecidedStrict) (Rec0 Int) :*: S1 (MetaSel (Nothing :: Maybe Symbol) SourceUnpack SourceStrict DecidedStrict) (Rec0 Int)))) | |
type SexpGrammar a = forall t. Grammar Position (Sexp :- t) (a :- t) Source #
A common type of grammar that operates on S-expressions. This grammar
accepts a single Sexp value and converts it into a value of type
a.
Representation of an invertible grammar -- a grammar that can be run either "forwards" and "backwards".
For a grammar Grammar p a b, running it forwards will take a
value of type a and possibly produce a value of type b. Running
it backwards will take a value of type b and possibly produce an
a. If a value cannot be produced, an error message is generated.
As a common example, running a Grammar forwards corresponds to
parsing and running backwards corresponds to prettyprinting.
That is, the grammar defines a partial isomorphism between two values.
"Cons" pair of a heterogenous list or a stack with potentially
polymophic tail. E.g. "first" :- 2 :- (3,4) :- t
Isomorphic to a tuple with two elments, but is much more convenient for nested pairs.
Instances
| Bitraversable (:-) | |
Defined in Data.InvertibleGrammar.Base Methods bitraverse :: Applicative f => (a -> f c) -> (b -> f d) -> (a :- b) -> f (c :- d) # | |
| Bifoldable (:-) | |
| Bifunctor (:-) | |
| Functor ((:-) h) | |
| Foldable ((:-) h) | |
Defined in Data.InvertibleGrammar.Base Methods fold :: Monoid m => (h :- m) -> m # foldMap :: Monoid m => (a -> m) -> (h :- a) -> m # foldr :: (a -> b -> b) -> b -> (h :- a) -> b # foldr' :: (a -> b -> b) -> b -> (h :- a) -> b # foldl :: (b -> a -> b) -> b -> (h :- a) -> b # foldl' :: (b -> a -> b) -> b -> (h :- a) -> b # foldr1 :: (a -> a -> a) -> (h :- a) -> a # foldl1 :: (a -> a -> a) -> (h :- a) -> a # elem :: Eq a => a -> (h :- a) -> Bool # maximum :: Ord a => (h :- a) -> a # minimum :: Ord a => (h :- a) -> a # | |
| Traversable ((:-) h) | |
| (Eq h, Eq t) => Eq (h :- t) | |
| (Show h, Show t) => Show (h :- t) | |
class SexpIso a where Source #
A class for types that could be converted to and inferred from
s-expressions defined by Sexp.
Methods
sexpIso :: SexpGrammar a Source #
Instances
Encoding
toSexp :: SexpGrammar a -> a -> Either String Sexp Source #
Run grammar in generating (right-to-left) direction
toSexp g = runGrammarString Sexp.dummyPos . backward (sealed g)
encode :: SexpIso a => a -> Either String ByteString Source #
Serialize a value using SexpIso instance
encodeWith :: SexpGrammar a -> a -> Either String ByteString Source #
Serialise a value using a provided grammar
encodePretty :: SexpIso a => a -> Either String ByteString Source #
Serialise and pretty-print a value using its SexpIso instance
encodePrettyWith :: SexpGrammar a -> a -> Either String ByteString Source #
Serialise and pretty-print a value using a provided grammar
Decoding
fromSexp :: SexpGrammar a -> Sexp -> Either String a Source #
Run grammar in parsing (left-to-right) direction
fromSexp g = runGrammarString Sexp.dummyPos . forward (sealed g)
decode :: SexpIso a => ByteString -> Either String a Source #
Deserialise a value using its SexpIso instance
decodeWith :: SexpGrammar a -> FilePath -> ByteString -> Either String a Source #
Deserialise a value using provided grammar, use a provided file name for error messages
Combinators
position :: Grammar Position (Sexp :- t) (Position :- (Sexp :- t)) Source #
Extract/inject a position from/to a Sexp.
Atoms
real :: Grammar Position (Sexp :- t) (Scientific :- t) Source #
Grammar matching fractional number atoms to Scientific values.
>>>encodeWith real (3.141592653589793^3)Right "31.006276680299813114880451174049119330924860257"
double :: Grammar Position (Sexp :- t) (Double :- t) Source #
Grammar matching fractional number atoms to Double values.
>>>encodeWith double (3.141592653589793^3)Right "31.006276680299816"
int :: Grammar Position (Sexp :- t) (Int :- t) Source #
Grammar matching integer number atoms to Int values.
>>>encodeWith int (2^63)Right "-9223372036854775808"
>>>encodeWith int (2^63-1)Right "9223372036854775807"
integer :: Grammar Position (Sexp :- t) (Integer :- t) Source #
Grammar matching integer number atoms to Integer values.
>>>encodeWith integer (2^100)Right "1267650600228229401496703205376"
string :: Grammar Position (Sexp :- t) (Text :- t) Source #
Grammar matching string literal atoms to Text values.
>>>let grammar = list (el string >>> el int) >>> pair>>>encodeWith grammar ("some-string", 42)Right "(\"some-string\" 42)"
symbol :: Grammar Position (Sexp :- t) (Text :- t) Source #
Grammar matching symbol literal atoms to Text values.
>>>encodeWith symbol "some-symbol"Right "some-symbol"
keyword :: Grammar Position (Sexp :- t) (Text :- t) Source #
Grammar matching symbol literal atoms starting with ':' to
Text values without the colon char.
>>>encodeWith keyword "username"Right ":username"
sym :: Text -> Grammar Position (Sexp :- t) t Source #
Grammar matching symbol literal atoms to a specified symbol.
>>>let grammar = list (el (sym "username") >>> el string)>>>encodeWith grammar "Julius Caesar"Right "(username \"Julius Caesar\")"
kwd :: Text -> Grammar Position (Sexp :- t) t Source #
Grammar matching symbol literal atoms to a specified symbol prepended with ':'.
>>>let grammar = list (el (kwd "password") >>> el int)>>>encodeWith grammar 42Right "(:password 42)"
Lists
list :: Grammar Position (List :- t) (List :- t') -> Grammar Position (Sexp :- t) t' Source #
Parenthesis list grammar. Runs a specified grammar on a sequence of S-exps in a parenthesized list.
>>>let grammar = list (el symbol >>> el int) >>> pair>>>encodeWith grammar ("foo", 42)Right "(foo 42)"
bracketList :: Grammar Position (List :- t) (List :- t') -> Grammar Position (Sexp :- t) t' Source #
Bracket list grammar. Runs a specified grammar on a sequence of S-exps in a bracketed list.
>>>let grammar = bracketList (rest int)>>>encodeWith grammar [2, 3, 5, 7, 11, 13]Right "[2 3 5 7 11 13]"
braceList :: Grammar Position (List :- t) (List :- t') -> Grammar Position (Sexp :- t) t' Source #
Brace list grammar. Runs a specified grammar on a sequence of S-exps in a list enclosed in braces.
>>>let grammar = braceList (props (key "x" real >>> key "y" real)) >>> pair>>>encodeWith grammar (3.1415, -1)Right "{:x 3.1415 :y -1}"
el :: Grammar Position (Sexp :- t) t' -> Grammar Position (List :- t) (List :- t') Source #
Element of a sequence grammar. Runs a specified grammar on a next element of a sequence. The underlying grammar can produce zero or more values on the stack.
E.g.:
el (sym "lambda")consumes a symbol "lambda" and produces no values on the stack.el symbolconsumes a symbol and produces aTextvalue corresponding to the symbol.
rest :: (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (List :- t) (List :- ([a] :- t)) Source #
The rest of a sequence grammar. Runs a specified grammar on each of remaining elements of a sequence and collect them. Expects zero or more elements in the sequence.
>>>let grammar = list (el (sym "check-primes") >>> rest int)>>>encodeWith grammar [2, 3, 5, 7, 11, 13]Right "(check-primes 2 3 5 7 11 13)"
Property lists
data PropertyList Source #
Key/value pairs of a property list that is being parsed/constructed.
props :: Grammar Position (PropertyList :- t) (PropertyList :- t') -> Grammar Position (List :- t) (List :- t') Source #
Property list in a sequence grammar. Collects pairs of keywords and S-expressions from remaining sequence elements and runs a specified grammar on them. Expects zero or more pairs in the sequence. If sequence of pairs interrupts with a non-keyword, the rest of this sequence is left untouched.
Collected PropertyList is then available for random-access lookup
combinators key, optKey, .:, .:? or bulk extraction
restKeys combinator.
>>>:{let grammar = braceList ( props (key "real" real >>> key "img" real) >>> onTail pair >>> el (sym "/") >>> props (key "real" real >>> key "img" real) >>> onTail pair) >>> pair in encodeWith grammar ((0, -1), (1, 0)) :} Right "{:real 0 :img -1 / :real 1 :img 0}"
key :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (a :- t)) Source #
Property by a key grammar. Looks up an S-expression by a specified key and runs a specified grammar on it. Expects the key to be present.
Note: performs linear lookup, O(n)
optKey :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (Maybe a :- t)) Source #
(.:) :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (a :- t)) infix 3 Source #
Property by a key grammar. Infix version of key.
(.:?) :: Text -> (forall t. Grammar Position (Sexp :- t) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- (Maybe a :- t)) infix 3 Source #
Optional property by a key grammar. Infix version of optKey.
restKeys :: (forall t. Grammar Position (Sexp :- (Text :- t)) (a :- t)) -> Grammar Position (PropertyList :- t) (PropertyList :- ([a] :- t)) Source #
Remaining properties grammar. Extracts all key-value pairs and applies a grammar on every element.
Quotes, antiquotes, etc
S-expression quotation type
Instances
| Eq Prefix Source # | |
| Ord Prefix Source # | |
| Show Prefix Source # | |
| Generic Prefix Source # | |
| NFData Prefix Source # | |
Defined in Language.Sexp.Types | |
| type Rep Prefix Source # | |
Defined in Language.Sexp.Types type Rep Prefix = D1 (MetaData "Prefix" "Language.Sexp.Types" "sexp-grammar-2.2.1-3ZZuMApZcqcGTL2RunVA7T" False) ((C1 (MetaCons "Quote" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "Backtick" PrefixI False) (U1 :: Type -> Type)) :+: (C1 (MetaCons "Comma" PrefixI False) (U1 :: Type -> Type) :+: (C1 (MetaCons "CommaAt" PrefixI False) (U1 :: Type -> Type) :+: C1 (MetaCons "Hash" PrefixI False) (U1 :: Type -> Type)))) | |
prefixed :: Prefix -> Grammar Position (Sexp :- t) (a :- t) -> Grammar Position (Sexp :- t) (a :- t) Source #
Grammar matching a prefixed S-expression, runs a sub-grammar on a
Sexp under the prefix.
>>>encodeWith (prefixed Backtick symbol) "foo"Right "`foo"
quoted :: Grammar Position (Sexp :- t) (a :- t) -> Grammar Position (Sexp :- t) (a :- t) Source #
Grammar matching a prefixed S-expression, runs a sub-grammar on a
Sexp under the quotation.
>>>encodeWith (quoted symbol) "foo"Right "'foo"
hashed :: Grammar Position (Sexp :- t) (a :- t) -> Grammar Position (Sexp :- t) (a :- t) Source #
Grammar matching a prefixed S-expression, runs a sub-grammar on a
Sexp under the hash prefix.
>>>encodeWith (hashed symbol) "foo"Right "#foo"
Error reporting
Data type to encode mismatches during parsing or generation, kept
abstract. Use expected and unexpected constructors to build a
mismatch report.
expected :: Text -> Mismatch #
Construct a mismatch report with specified expectation. Can be
appended to other expectations and unexpected reports to clarify
a mismatch.
unexpected :: Text -> Mismatch #
Construct a mismatch report with information what occurred during the processing but was not expected.