123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- {-# LANGUAGE PatternSynonyms #-}
- {-# LANGUAGE ScopedTypeVariables #-}
- {-# LANGUAGE ViewPatterns #-}
- module Data.SCargot.Repr.Basic
- ( -- * Basic 'SExpr' representation
- R.SExpr(..)
- -- * Constructing and Deconstructing
- , cons
- , uncons
- -- * Shorthand Patterns
- , pattern (:::)
- , pattern A
- , pattern L
- , pattern DL
- , pattern Nil
- -- * Lenses
- , _car
- , _cdr
- -- * Useful processing functions
- , fromPair
- , fromList
- , fromAtom
- , asPair
- , asList
- , isAtom
- , asAtom
- , asAssoc
- ) where
- #if !MIN_VERSION_base(4,8,0)
- import Control.Applicative (Applicative, (<$>), (<*>), pure)
- #endif
- import Data.SCargot.Repr as R
- -- | A traversal with access to the first element of a pair.
- --
- -- >>> import Lens.Family
- -- >>> set _car (A "elephant") (A "one" ::: A "two" ::: A "three" ::: Nil)
- -- A "elelphant" ::: A "two" ::: A "three" ::: Nil
- -- >>> set _car (A "two" ::: A "three" ::: Nil) (A "one" ::: A "elephant")
- -- (A "two" ::: A "three" ::: Nil) ::: A "elephant"
- _car :: Applicative f => (SExpr a -> f (SExpr a)) -> SExpr a -> f (SExpr a)
- _car f (SCons x xs) = (:::) <$> f x <*> pure xs
- _car _ (SAtom a) = pure (A a)
- _car _ SNil = pure SNil
- -- | A traversal with access to the second element of a pair.
- --
- -- >>> import Lens.Family
- -- >>> set _cdr (A "elephant") (A "one" ::: A "two" ::: A "three" ::: Nil)
- -- A "one" ::: A "elephant"
- -- >>> set _cdr (A "two" ::: A "three" ::: Nil) (A "one" ::: A "elephant")
- -- A "one" ::: A "two" ::: A "three" ::: Nil
- _cdr :: Applicative f => (SExpr a -> f (SExpr a)) -> SExpr a -> f (SExpr a)
- _cdr f (SCons x xs) = (:::) <$> pure x <*> f xs
- _cdr _ (SAtom a) = pure (A a)
- _cdr _ SNil = pure Nil
- -- | Produce the head and tail of the s-expression (if possible).
- --
- -- >>> uncons (A "el" ::: A "eph" ::: A "ant" ::: Nil)
- -- Just (A "el",SCons (SAtom "eph") (SCons (SAtom "ant") SNil))
- uncons :: SExpr a -> Maybe (SExpr a, SExpr a)
- uncons (SCons x xs) = Just (x, xs)
- uncons _ = Nothing
- -- | Combine the two s-expressions into a new one.
- --
- -- >>> cons (A "el") (L ["eph", A "ant"])
- -- SCons (SAtom "el) (SCons (SAtom "eph") (SCons (SAtom "ant") SNil))
- cons :: SExpr a -> SExpr a -> SExpr a
- cons = SCons
- gatherDList :: SExpr a -> Maybe ([SExpr a], a)
- gatherDList SNil = Nothing
- gatherDList SAtom {} = Nothing
- gatherDList sx = go sx
- where go SNil = Nothing
- go (SAtom a) = return ([], a)
- go (SCons x xs) = do
- (ys, a) <- go xs
- return (x:ys, a)
- infixr 5 :::
- -- | A shorter infix alias for `SCons`
- --
- -- >>> A "pachy" ::: A "derm"
- -- SCons (SAtom "pachy") (SAtom "derm")
- #if MIN_VERSION_base(4,8,0)
- pattern (:::) :: SExpr a -> SExpr a -> SExpr a
- #endif
- pattern x ::: xs = SCons x xs
- -- | A shorter alias for `SAtom`
- --
- -- >>> A "elephant"
- -- SAtom "elephant"
- #if MIN_VERSION_base(4,8,0)
- pattern A :: a -> SExpr a
- #endif
- pattern A x = SAtom x
- -- | A (slightly) shorter alias for `SNil`
- --
- -- >>> Nil
- -- SNil
- #if MIN_VERSION_base(4,8,0)
- pattern Nil :: SExpr a
- #endif
- pattern Nil = SNil
- -- | An alias for matching a proper list.
- --
- -- >>> L [A "pachy", A "derm"]
- -- SExpr (SAtom "pachy") (SExpr (SAtom "derm") SNil)
- #if MIN_VERSION_base(4,8,0)
- pattern L :: [SExpr a] -> SExpr a
- #endif
- pattern L xs <- (gatherList -> Right xs)
- #if MIN_VERSION_base(4,8,0)
- where L [] = SNil
- L (x:xs) = SCons x (L xs)
- #endif
- -- | An alias for matching a dotted list.
- --
- -- >>> DL [A "pachy"] A "derm"
- -- SExpr (SAtom "pachy") (SAtom "derm")
- #if MIN_VERSION_base(4,8,0)
- pattern DL :: [SExpr a] -> a -> SExpr a
- #endif
- pattern DL xs x <- (gatherDList -> Just (xs, x))
- #if MIN_VERSION_base(4,8,0)
- where DL [] a = SAtom a
- DL (x:xs) a = SCons x (DL xs a)
- #endif
- getShape :: SExpr a -> String
- getShape Nil = "empty list"
- getShape sx = go (0 :: Int) sx
- where go n SNil = "list of length " ++ show n
- go n SAtom {} = "dotted list of length " ++ show n
- go n (SCons _ xs) = go (n+1) xs
- -- | Utility function for parsing a pair of things.
- --
- -- >>> fromPair (isAtom "pachy") (asAtom return) (A "pachy" ::: A "derm" ::: Nil)
- -- Right ((), "derm")
- -- >>> fromPair (isAtom "pachy") fromAtom (A "pachy" ::: Nil)
- -- Left "Expected two-element list"
- fromPair :: (SExpr t -> Either String a)
- -> (SExpr t -> Either String b)
- -> SExpr t -> Either String (a, b)
- fromPair pl pr (l ::: r ::: Nil) = (,) <$> pl l <*> pr r
- fromPair _ _ sx = Left ("fromPair: expected two-element list; found " ++ getShape sx)
- -- | Utility function for parsing a list of things.
- fromList :: (SExpr t -> Either String a) -> SExpr t -> Either String [a]
- fromList p (s ::: ss) = (:) <$> p s <*> fromList p ss
- fromList _ Nil = pure []
- fromList _ sx = Left ("fromList: expected list; found " ++ getShape sx)
- -- | Utility function for parsing a single atom
- fromAtom :: SExpr t -> Either String t
- fromAtom (A a) = return a
- fromAtom sx = Left ("fromAtom: expected atom; found list" ++ getShape sx)
- gatherList :: SExpr t -> Either String [SExpr t]
- gatherList (x ::: xs) = (:) <$> pure x <*> gatherList xs
- gatherList Nil = pure []
- gatherList sx = Left ("gatherList: expected list; found " ++ getShape sx)
- -- | Parse a two-element list (NOT a dotted pair) using the
- -- provided function.
- --
- -- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
- -- >>> asPair go (A "pachy" ::: A "derm" ::: Nil)
- -- Right "pachyderm"
- -- >>> asPair go (A "elephant" ::: Nil)
- -- Left "asPair: expected two-element list; found list of length 1"
- asPair :: ((SExpr t, SExpr t) -> Either String a)
- -> SExpr t -> Either String a
- asPair f (l ::: r ::: SNil) = f (l, r)
- asPair _ sx = Left ("asPair: expected two-element list; found " ++ getShape sx)
- -- | Parse an arbitrary-length list using the provided function.
- --
- -- >>> let go xs = concat <$> mapM fromAtom xs
- -- >>> asList go (A "el" ::: A "eph" ::: A "ant" ::: Nil)
- -- Right "elephant"
- -- >>> asList go (A "el" ::: A "eph" ::: A "ant")
- -- Left "asList: expected list; found dotted list of length 3"
- asList :: ([SExpr t] -> Either String a) -> SExpr t -> Either String a
- asList f ls = gatherList ls >>= f
- -- | Match a given literal atom, failing otherwise.
- --
- -- >>> isAtom "elephant" (A "elephant")
- -- Right ()
- -- >>> isAtom "elephant" (A "elephant" ::: Nil)
- -- Left "isAtom: expected atom; found list"
- isAtom :: Eq t => t -> SExpr t -> Either String ()
- isAtom s (A s')
- | s == s' = return ()
- | otherwise = Left "isAtom: failed to match atom"
- isAtom _ sx = Left ("isAtom: expected atom; found " ++ getShape sx)
- -- | Parse an atom using the provided function.
- --
- -- >>> import Data.Char (toUpper)
- -- >>> asAtom (return . map toUpper) (A "elephant")
- -- Right "ELEPHANT"
- -- >>> asAtom (return . map toUpper) Nil
- -- Left "asAtom: expected atom; found empty list"
- asAtom :: (t -> Either String a) -> SExpr t -> Either String a
- asAtom f (A s) = f s
- asAtom _ sx = Left ("asAtom: expected atom; found " ++ getShape sx)
- -- | Parse an assoc-list using the provided function.
- --
- -- >>> let def (x, y) = do { a <- fromAtom x; b <- fromAtom y; return (a ++ ": " ++ b) }
- -- >>> let defList xs = do { defs <- mapM def xs; return (unlines defs) }
- -- >>> asAssoc defList ((A "legs" ::: A "four" ::: Nil) ::: (A "trunk" ::: A "one" ::: Nil) ::: Nil)
- -- Right "legs: four\ntrunk: one\n"
- -- >>> asAssoc defList ((A "legs" ::: A "four" ::: Nil) ::: (A "elephant") ::: Nil)
- -- Left "asAssoc: expected pair; found list of length 1"
- asAssoc :: ([(SExpr t, SExpr t)] -> Either String a)
- -> SExpr t -> Either String a
- asAssoc f ss = gatherList ss >>= mapM go >>= f
- where go (a ::: b ::: Nil) = return (a, b)
- go sx = Left ("asAssoc: expected pair; found " ++ getShape sx)
|