gdritter repos s-cargot / master Data / SCargot / Repr / Rich.hs
master

Tree @master (Download .tar.gz)

Rich.hs @master

65b9c65
b79d70e
65b9c65
adeaaa4
fffccd6
 
 
 
b79d70e
 
 
fffccd6
d60633f
 
 
 
 
1b5d56d
 
 
94563a1
 
 
56aaebe
 
 
 
82836ac
56aaebe
 
14c258a
 
65b9c65
 
14c258a
 
 
adeaaa4
65b9c65
1b5d56d
82836ac
f55a7c5
82836ac
 
f55a7c5
 
1b5d56d
14c258a
 
 
 
 
1b5d56d
f55a7c5
 
 
 
82836ac
f55a7c5
 
82836ac
f55a7c5
 
1b5d56d
14c258a
 
 
 
 
1b5d56d
14c258a
 
 
 
 
1b5d56d
14c258a
 
 
 
 
1b5d56d
14c258a
 
 
1b5d56d
b79d70e
 
 
 
 
 
 
14c258a
b79d70e
 
 
 
 
 
 
 
 
 
d60633f
 
b79d70e
 
 
eb0baef
2d25c40
eb0baef
b79d70e
14c258a
b79d70e
14c258a
d60633f
 
b79d70e
 
 
eb0baef
2d25c40
eb0baef
2d25c40
d60633f
 
b79d70e
 
 
eb0baef
2d25c40
eb0baef
2d25c40
d60633f
 
b79d70e
 
 
eb0baef
2d25c40
eb0baef
d60633f
 
82836ac
b79d70e
 
 
eb0baef
2d25c40
eb0baef
d60633f
94563a1
82836ac
 
 
 
 
 
 
56aaebe
 
 
94563a1
 
82836ac
 
 
 
 
 
56aaebe
94563a1
 
56aaebe
82836ac
 
 
 
 
56aaebe
14c258a
 
 
82836ac
65680e2
82836ac
 
 
 
 
 
56aaebe
 
14c258a
 
 
 
94563a1
56aaebe
f55a7c5
 
 
 
 
 
56aaebe
 
14c258a
 
 
56aaebe
 
82836ac
 
 
 
 
56aaebe
14c258a
56aaebe
82836ac
14c258a
 
82836ac
 
 
 
 
 
 
 
14c258a
 
 
 
94563a1
56aaebe
82836ac
 
 
 
 
 
56aaebe
14c258a
 
 
94563a1
56aaebe
f55a7c5
 
 
 
 
 
 
 
56aaebe
14c258a
 
94563a1
14c258a
 
 
 
 
56aaebe
 
 
 
 
 
 
 
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}

module Data.SCargot.Repr.Rich
       ( -- * 'RichSExpr' representation
         R.RichSExpr(..)
       , R.toRich
       , R.fromRich
         -- * Constructing and Deconstructing
       , cons
       , uncons
         -- * Useful pattern synonyms
       , pattern (:::)
       , pattern A
       , pattern L
       , pattern DL
       , pattern Nil
         -- * Lenses
       , _car
       , _cdr
         -- * Useful processing functions
       , fromPair
       , fromList
       , fromAtom
       , asPair
       , asList
       , isAtom
       , isNil
       , asAtom
       , asAssoc
       , car
       , cdr
       ) 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") (L [A "one", A "two", A "three"])
-- L [A "elelphant",A "two",A "three"]
-- >>> set _car (L [A "two", A "three"]) (DL [A "one"] "elephant")
-- DL [L[A "two",A "three"]] "elephant"
_car :: Applicative f => (RichSExpr a -> f (RichSExpr a)) -> RichSExpr a -> f (RichSExpr a)
_car f (RSList (x:xs))     = (\ y -> L (y:xs)) `fmap` f x
_car f (RSDotted (x:xs) a) = (\ y -> DL (y:xs) a) `fmap` f x
_car _ (RSAtom a)          = pure (A a)
_car _ (RSList [])         = pure Nil
_car _ (RSDotted [] a)     = pure (A a)

-- | A traversal with access to the second element of a pair. Using
--   this to modify an s-expression may result in changing the
--   constructor used, changing a list to a dotted list or vice
--   versa.
--
-- >>> import Lens.Family
-- >>> set _cdr (A "elephant") (L [A "one", A "two", A "three"])
-- DL [A "one"] "elephant"
-- >>> set _cdr (L [A "two", A "three"]) (DL [A "one"] "elephant")
-- L [A "one",A "two",A "three"]
_cdr :: Applicative f => (RichSExpr a -> f (RichSExpr a)) -> RichSExpr a -> f (RichSExpr a)
_cdr f (RSList (x:xs)) =
  let go (RSList [])      = L [x]
      go (RSAtom a)       = DL [x] a
      go (RSList xs')     = L (x:xs')
      go (RSDotted ys a') = DL (x:ys) a'
  in go `fmap` f (L xs)
_cdr f (RSDotted [x] a) =
  let go (RSList [])      = L [x]
      go (RSAtom a')      = DL [x] a'
      go (RSList xs)      = L (x:xs)
      go (RSDotted ys a') = DL (x:ys) a'
  in go `fmap` f (A a)
_cdr f (RSDotted (x:xs) a) =
  let go (RSList [])      = L [x]
      go (RSAtom a')      = DL [x] a'
      go (RSList ys)      = L (x:ys)
      go (RSDotted ys a') = DL (x:ys) a'
  in go `fmap` f (DL xs a)
_cdr _ (RSAtom a)         = pure (A a)
_cdr _ (RSList [])        = pure Nil
_cdr _ (RSDotted [] a)    = pure (A a)

-- | Produce the head and tail of the s-expression (if possible).
--
-- >>> uncons (L [A "el", A "eph", A "ant"])
-- Just (A "el",L [A "eph",A "ant"])
uncons :: RichSExpr a -> Maybe (RichSExpr a, RichSExpr a)
uncons (R.RSList (x:xs))     = Just (x, R.RSList xs)
uncons (R.RSDotted (x:xs) a) = Just (x, R.RSDotted xs a)
uncons _                     = Nothing

-- | Combine the two s-expressions into a new one.
--
-- >>> cons (A "el") (L [A "eph", A "ant"])
-- L [A "el",A "eph",A "ant"]
cons :: RichSExpr a -> RichSExpr a -> RichSExpr a
cons x (R.RSList xs)     = R.RSList (x:xs)
cons x (R.RSDotted xs a) = R.RSDotted (x:xs) a
cons x (R.RSAtom a)      = R.RSDotted [x] a

-- | A shorter infix alias to grab the head
--   and tail of an `RSList`.
--
-- >>> A "one" ::: L [A "two", A "three"]
-- RSList [RSAtom "one",RSAtom "two",RSAtom "three"]
#if MIN_VERSION_base(4,8,0)
pattern (:::) :: RichSExpr a -> RichSExpr a -> RichSExpr a
#endif
pattern x ::: xs <- (uncons -> Just (x, xs))
#if MIN_VERSION_base(4,8,0)
  where x ::: xs = cons x xs
#endif

-- | A shorter alias for `RSAtom`
--
-- >>> A "elephant"
-- RSAtom "elephant"
#if MIN_VERSION_base(4,8,0)
pattern A :: a -> RichSExpr a
#endif
pattern A a = R.RSAtom a

-- | A shorter alias for `RSList`
--
-- >>> L [A "pachy", A "derm"]
-- RSList [RSAtom "pachy",RSAtom "derm"]
#if MIN_VERSION_base(4,8,0)
pattern L :: [RichSExpr a] -> RichSExpr a
#endif
pattern L xs = R.RSList xs

-- | A shorter alias for `RSDotted`
--
-- >>> DL [A "pachy"] "derm"
-- RSDotted [RSAtom "pachy"] "derm"
#if MIN_VERSION_base(4,8,0)
pattern DL :: [RichSExpr a] -> a -> RichSExpr a
#endif
pattern DL xs x = R.RSDotted xs x

-- | A shorter alias for `RSList` @[]@
--
-- >>> Nil
-- RSList []
#if MIN_VERSION_base(4,8,0)
pattern Nil :: RichSExpr a
#endif
pattern Nil = R.RSList []

-- | Utility function for parsing a pair of things: this parses a two-element list,
--   and not a cons pair.
--
-- >>> fromPair (isAtom "pachy") (asAtom return) (L [A "pachy", A "derm"])
-- Right ((), "derm")
-- >>> fromPair (isAtom "pachy") fromAtom (L [A "pachy"])
-- Left "Expected two-element list"
fromPair :: (RichSExpr t -> Either String a)
         -> (RichSExpr t -> Either String b)
         -> RichSExpr t -> Either String (a, b)
fromPair pl pr = asPair $ \(l,r) -> (,) <$> pl l <*> pr r

-- | Utility function for parsing a proper list of things.
--
-- >>> fromList fromAtom (L [A "this", A "that", A "the-other"])
-- Right ["this","that","the-other"]
-- >>> fromList fromAtom (DL [A "this", A "that"] "the-other"])
-- Left "asList: expected proper list; found dotted list"
fromList :: (RichSExpr t -> Either String a) -> RichSExpr t -> Either String [a]
fromList p = asList $ \ss -> mapM p ss

-- | Utility function for parsing a single atom
--
-- >>> fromAtom (A "elephant")
-- Right "elephant"
-- >>> fromAtom (L [A "elephant"])
-- Left "fromAtom: expected atom; found list"
fromAtom :: RichSExpr t -> Either String t
fromAtom (RSList _)     = Left "fromAtom: expected atom; found list"
fromAtom (RSDotted _ _) = Left "fromAtom: expected atom; found dotted list"
fromAtom (RSAtom a)     = return a

-- | Parses a two-element list using the provided function.
--
-- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
-- >>> asPair go (L [A "pachy", A "derm"])
-- Right "pachyderm"
-- >>> asPair go (L [A "elephant"])
-- Left "asPair: expected two-element list; found list of length 1"
asPair :: ((RichSExpr t, RichSExpr t) -> Either String a)
       -> RichSExpr t -> Either String a
asPair f (RSList [l, r]) = f (l, r)
asPair _ (RSList ls)     = Left ("asPair: expected two-element list; found list of lenght " ++ show (length ls))
asPair _ RSDotted {}     = Left ("asPair: expected two-element list; found dotted list")
asPair _ RSAtom {}       = Left ("asPair: expected two-element list; found atom")

-- | Parse an arbitrary-length list using the provided function.
--
-- >>> let go xs = concat <$> mapM fromAtom xs
-- >>> asList go (L [A "el", A "eph", A "ant"])
-- Right "elephant"
-- >>> asList go (DL [A "el", A "eph"] "ant")
-- Left "asList: expected list; found dotted list"
asList :: ([RichSExpr t] -> Either String a)
       -> RichSExpr t -> Either String a
asList f (RSList ls) = f ls
asList _ RSDotted {} = Left ("asList: expected list; found dotted list")
asList _ RSAtom { }  = Left ("asList: expected list; found dotted list")

-- | Match a given literal atom, failing otherwise.
--
-- >>> isAtom "elephant" (A "elephant")
-- Right ()
-- >>> isAtom "elephant" (L [A "elephant"])
-- Left "isAtom: expected atom; found list"
isAtom :: Eq t => t -> RichSExpr t -> Either String ()
isAtom s (RSAtom s')
  | s == s'   = return ()
  | otherwise = Left "isAtom: failed to match atom"
isAtom _ RSList {}  = Left "isAtom: expected atom; found list"
isAtom _ RSDotted {} = Left "isAtom: expected atom; found dotted list"

-- | Match an empty list, failing otherwise.
--
-- >>> isNil (L [])
-- Right ()
-- >>> isNil (A "elephant")
-- Left "isNil: expected nil; found atom"
isNil :: RichSExpr t -> Either String ()
isNil (RSList []) = return ()
isNil RSList {}   = Left "isNil: expected nil; found non-nil list"
isNil RSDotted {} = Left "isNil: expected nil; found dotted list"
isNil RSAtom {}   = Left "isNil: expected nil; found atom"

-- | Parse an atom using the provided function.
--
-- >>> import Data.Char (toUpper)
-- >>> asAtom (return . map toUpper) (A "elephant")
-- Right "ELEPHANT"
-- >>> asAtom (return . map toUpper) (L [])
-- Left "asAtom: expected atom; found list"
asAtom :: (t -> Either String a) -> RichSExpr t -> Either String a
asAtom f (RSAtom s)  = f s
asAtom _ RSList {}   = Left ("asAtom: expected atom; found list")
asAtom _ RSDotted {} = Left ("asAtom: expected atom; found dotted list")

-- | 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 (L [ L [A "legs", A "four"], L [ A "trunk", A "one"] ])
-- Right "legs: four\ntrunk: one\n"
-- >>> asAssoc defList (L [ L [A "legs", A "four"], L [ A "elephant"] ])
-- Left "asAssoc: expected pair; found list of length 1"
asAssoc :: ([(RichSExpr t, RichSExpr t)] -> Either String a)
        -> RichSExpr t -> Either String a
asAssoc f (RSList ss) = gatherPairs ss >>= f
  where gatherPairs (RSList [a, b] : ts) = (:) <$> pure (a, b) <*> gatherPairs ts
        gatherPairs []              = pure []
        gatherPairs (RSAtom {} : _)      = Left ("asAssoc: expected pair; found atom")
        gatherPairs (RSDotted {} : _)     = Left ("asAssoc: expected pair; found dotted list")
        gatherPairs (RSList ls : _)      = Left ("asAssoc: expected pair; found list of length " ++ show (length ls))
asAssoc _ RSDotted {} = Left "asAssoc: expected assoc list; found dotted list"
asAssoc _ RSAtom {}   = Left "asAssoc: expected assoc list; found atom"

car :: (RichSExpr t -> Either String t') -> [RichSExpr t] -> Either String t'
car f (x:_) = f x
car _ []    = Left "car: Taking car of zero-element list"

cdr :: ([RichSExpr t] -> Either String t') -> [RichSExpr t] -> Either String t'
cdr f (_:xs) = f xs
cdr _ []     = Left "cdr: Taking cdr of zero-element list"