Safe Haskell | Safe |
---|---|
Language | Haskell2010 |
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. person
So 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
- (>>>) :: forall k cat (a :: k) (b :: k) (c :: k). Category cat => cat a b -> cat b c -> cat a c
- (<<<) :: forall k cat (b :: k) (c :: k) (a :: k). 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) t' -> Grammar Position (Sexp :- t) t'
- quoted :: Grammar Position (Sexp :- t) t' -> Grammar Position (Sexp :- t) t'
- hashed :: Grammar Position (Sexp :- t) t' -> Grammar Position (Sexp :- t) 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 | |
NFData1 (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.3.0-JCnumEn9wOUGbR0nPzOSB9" '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 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 fold :: Monoid m => (h :- m) -> m # foldMap :: Monoid m => (a -> m) -> (h :- a) -> 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
.
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
(>>>) :: forall k cat (a :: k) (b :: k) (c :: k). Category cat => cat a b -> cat b c -> cat a c infixr 1 #
Left-to-right composition
(<<<) :: forall k cat (b :: k) (c :: k) (a :: k). Category cat => cat b c -> cat a b -> cat a c infixr 1 #
Right-to-left composition
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 42
Right "(: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 symbol
consumes a symbol and produces aText
value 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.3.0-JCnumEn9wOUGbR0nPzOSB9" '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) t' -> Grammar Position (Sexp :- t) 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) t' -> Grammar Position (Sexp :- t) 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) t' -> Grammar Position (Sexp :- t) 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.