Rich.hs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. {-# LANGUAGE PatternSynonyms #-}
  2. module Data.SCargot.Repr.Rich
  3. ( -- * 'RichSExpr' representation
  4. R.RichSExpr(..)
  5. , R.toRich
  6. , R.fromRich
  7. -- * Useful pattern synonyms
  8. , pattern (:::)
  9. , pattern A
  10. , pattern L
  11. , pattern DL
  12. , pattern Nil
  13. -- * Lenses
  14. , _car
  15. , _cdr
  16. -- * Useful processing functions
  17. , fromPair
  18. , fromList
  19. , fromAtom
  20. , asPair
  21. , asList
  22. , isAtom
  23. , isNil
  24. , asAtom
  25. , asAssoc
  26. ) where
  27. import Control.Applicative ((<$>), (<*>), pure)
  28. import Data.SCargot.Repr as R
  29. -- | A traversal with access to the first element of a pair.
  30. --
  31. -- >>> set _car (A "elephant") (L [A "one", A "two", A "three"])
  32. -- L [A "elelphant",A "two",A "three"]
  33. _car :: Applicative f => (RichSExpr a -> f (RichSExpr a)) -> RichSExpr a -> f (RichSExpr a)
  34. _car f (L (x:xs)) = (\ x -> L (x:xs)) `fmap` f x
  35. _car f (DL (x:xs) a) = (\ x -> DL (x:xs) a) `fmap` f x
  36. _car _ (A a) = pure (A a)
  37. _car _ Nil = pure Nil
  38. -- | A traversal with access to the second element of a pair.
  39. --
  40. -- >>> set _car (A "elephant") (L [A "one", A "two", A "three"])
  41. -- DL [A "one"] "elephant"
  42. _cdr :: Applicative f => (RichSExpr a -> f (RichSExpr a)) -> RichSExpr a -> f (RichSExpr a)
  43. _cdr f (L (x:xs)) =
  44. let go Nil = L [x]
  45. go (A a) = DL [x] a
  46. go (L xs') = L (x:xs')
  47. in go `fmap` f (L xs)
  48. _cdr f (DL [x] a) =
  49. let go Nil = L [x]
  50. go (A a') = DL [x] a'
  51. go (L xs) = L (x:xs)
  52. in go `fmap` f (A a)
  53. _cdr f (DL (x:xs) a) =
  54. let go Nil = L [x]
  55. go (A a') = DL [x] a'
  56. go (L xs) = L (x:xs)
  57. in go `fmap` f (DL xs a)
  58. _cdr _ (A a) = pure (A a)
  59. _cdr _ Nil = pure Nil
  60. -- | A shorter infix alias to grab the head
  61. -- and tail of an `RSList`.
  62. pattern x ::: xs = R.RSList (x : xs)
  63. -- | A shorter alias for `RSAtom`
  64. pattern A a = R.RSAtom a
  65. -- | A shorter alias for `RSList`
  66. pattern L xs = R.RSList xs
  67. -- | A shorter alias for `RSDotted`
  68. pattern DL xs x = R.RSDotted xs x
  69. -- | A shorter alias for `RSList` @[]@
  70. pattern Nil = R.RSList []
  71. -- | Utility function for parsing a pair of things: this parses a two-element list,
  72. -- and not a cons pair.
  73. --
  74. -- >>> fromPair (isAtom "pachy") (asAtom return) (L [A "pachy", A "derm"])
  75. -- Right ((), "derm")
  76. -- >>> fromPair (isAtom "pachy") fromAtom (L [A "pachy"])
  77. -- Left "Expected two-element list"
  78. fromPair :: (RichSExpr t -> Either String a)
  79. -> (RichSExpr t -> Either String b)
  80. -> RichSExpr t -> Either String (a, b)
  81. fromPair pl pr = asPair $ \(l,r) -> (,) <$> pl l <*> pr r
  82. -- | Utility function for parsing a proper list of things.
  83. --
  84. -- >>> fromList fromAtom (L [A "this", A "that", A "the-other"])
  85. -- Right ["this","that","the-other"]
  86. -- >>> fromList fromAtom (DL [A "this", A "that"] "the-other"])
  87. -- Left "asList: expected proper list; found dotted list"
  88. fromList :: (RichSExpr t -> Either String a) -> RichSExpr t -> Either String [a]
  89. fromList p = asList $ \ss -> mapM p ss
  90. -- | Utility function for parsing a single atom
  91. --
  92. -- >>> fromAtom (A "elephant")
  93. -- Right "elephant"
  94. -- >>> fromAtom (L [A "elephant"])
  95. -- Left "fromAtom: expected atom; found list"
  96. fromAtom :: RichSExpr t -> Either String t
  97. fromAtom (L _) = Left "fromAtom: expected atom; found list"
  98. fromAtom (DL _ _) = Left "fromAtom: expected atom; found dotted list"
  99. fromAtom (A a) = return a
  100. -- | Parses a two -element list using the provided function.
  101. --
  102. -- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
  103. -- >>> asPair go (L [A "pachy", A "derm"])
  104. -- Right "pachyderm"
  105. -- >>> asPair go (L [A "elephant"])
  106. -- Left "asPair: expected two-element list; found list of length 1"
  107. asPair :: ((RichSExpr t, RichSExpr t) -> Either String a)
  108. -> RichSExpr t -> Either String a
  109. asPair f (L [l, r]) = f (l, r)
  110. asPair _ (L ls) = Left ("asPair: expected two-element list; found list of lenght " ++ show (length ls))
  111. asPair _ DL {} = Left ("asPair: expected two-element list; found dotted list")
  112. asPair _ A {} = Left ("asPair: expected two-element list; found atom")
  113. -- | Parse an arbitrary-length list using the provided function.
  114. asList :: ([RichSExpr t] -> Either String a)
  115. -> RichSExpr t -> Either String a
  116. asList f (L ls) = f ls
  117. asList _ DL {} = Left ("asList: expected list; found dotted list")
  118. asList _ A { } = Left ("asList: expected list; found dotted list")
  119. -- | Match a given literal atom, failing otherwise.
  120. --
  121. -- >>> isAtom "elephant" (A "elephant")
  122. -- Right ()
  123. -- >>> isAtom "elephant" (L [A "elephant"])
  124. -- Left "isAtom: expected atom; found list"
  125. isAtom :: Eq t => t -> RichSExpr t -> Either String ()
  126. isAtom s (A s')
  127. | s == s' = return ()
  128. | otherwise = Left "isAtom: failed to match atom"
  129. isAtom _ L {} = Left "isAtom: expected atom; found list"
  130. isAtom _ DL {} = Left "isAtom: expected atom; found dotted list"
  131. -- | Match an empty list, failing otherwise.
  132. --
  133. -- >>> isNil (L [])
  134. -- Right ()
  135. -- >>> isNil (A "elephant")
  136. -- Left "isNil: expected nil; found atom"
  137. isNil :: RichSExpr t -> Either String ()
  138. isNil Nil = return ()
  139. isNil L {} = Left "isNil: expected nil; found non-nil list"
  140. isNil DL {} = Left "isNil: expected nil; found dotted list"
  141. isNil A {} = Left "isNil: expected nil; found atom"
  142. -- | Parse an atom using the provided function.
  143. --
  144. -- >>> import Data.Char (toUpper)
  145. -- >>> asAtom (return . map toUpper) (A "elephant")
  146. -- Right "ELEPHANT"
  147. -- >>> asAtom (return . map toUpper) (L [])
  148. -- Left "asAtom: expected atom; found list"
  149. asAtom :: (t -> Either String a) -> RichSExpr t -> Either String a
  150. asAtom f (A s) = f s
  151. asAtom _ L {} = Left ("asAtom: expected atom; found list")
  152. asAtom _ DL {} = Left ("asAtom: expected atom; found dotted list")
  153. -- | Parse an assoc-list using the provided function.
  154. asAssoc :: Show t => ([(RichSExpr t, RichSExpr t)] -> Either String a)
  155. -> RichSExpr t -> Either String a
  156. asAssoc f (L ss) = gatherPairs ss >>= f
  157. where gatherPairs (L [a, b] : ss) = (:) <$> pure (a, b) <*> gatherPairs ss
  158. gatherPairs [] = pure []
  159. gatherPairs _ = Left "..."
  160. asAssoc _ sx = Left ("Expected assoc list; got " ++ show sx)
  161. car :: (RichSExpr t -> Either String t') -> [RichSExpr t] -> Either String t'
  162. car f (x:_) = f x
  163. car _ [] = Left "car: Taking car of zero-element list"
  164. cdr :: ([RichSExpr t] -> Either String t') -> [RichSExpr t] -> Either String t'
  165. cdr f (_:xs) = f xs
  166. cdr _ [] = Left "cdr: Taking cdr of zero-element list"