Rich.hs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. {-# LANGUAGE PatternSynonyms #-}
  2. {-# LANGUAGE ViewPatterns #-}
  3. module Data.SCargot.Repr.Rich
  4. ( -- * 'RichSExpr' representation
  5. R.RichSExpr(..)
  6. , R.toRich
  7. , R.fromRich
  8. -- * Constructing and Deconstructing
  9. , cons
  10. , uncons
  11. -- * Useful pattern synonyms
  12. , pattern (:::)
  13. , pattern A
  14. , pattern L
  15. , pattern DL
  16. , pattern Nil
  17. -- * Lenses
  18. , _car
  19. , _cdr
  20. -- * Useful processing functions
  21. , fromPair
  22. , fromList
  23. , fromAtom
  24. , asPair
  25. , asList
  26. , isAtom
  27. , isNil
  28. , asAtom
  29. , asAssoc
  30. , car
  31. , cdr
  32. ) where
  33. #if !MIN_VERSION_base(4,8,0)
  34. import Control.Applicative (Applicative, (<$>), (<*>), pure)
  35. #endif
  36. import Data.SCargot.Repr as R
  37. -- | A traversal with access to the first element of a pair.
  38. --
  39. -- >>> import Lens.Family
  40. -- >>> set _car (A "elephant") (L [A "one", A "two", A "three"])
  41. -- L [A "elelphant",A "two",A "three"]
  42. -- >>> set _car (L [A "two", A "three"]) (DL [A "one"] "elephant")
  43. -- DL [L[A "two",A "three"]] "elephant"
  44. _car :: Applicative f => (RichSExpr a -> f (RichSExpr a)) -> RichSExpr a -> f (RichSExpr a)
  45. _car f (RSList (x:xs)) = (\ y -> L (y:xs)) `fmap` f x
  46. _car f (RSDotted (x:xs) a) = (\ y -> DL (y:xs) a) `fmap` f x
  47. _car _ (RSAtom a) = pure (A a)
  48. _car _ (RSList []) = pure Nil
  49. _car _ (RSDotted [] a) = pure (A a)
  50. -- | A traversal with access to the second element of a pair. Using
  51. -- this to modify an s-expression may result in changing the
  52. -- constructor used, changing a list to a dotted list or vice
  53. -- versa.
  54. --
  55. -- >>> import Lens.Family
  56. -- >>> set _cdr (A "elephant") (L [A "one", A "two", A "three"])
  57. -- DL [A "one"] "elephant"
  58. -- >>> set _cdr (L [A "two", A "three"]) (DL [A "one"] "elephant")
  59. -- L [A "one",A "two",A "three"]
  60. _cdr :: Applicative f => (RichSExpr a -> f (RichSExpr a)) -> RichSExpr a -> f (RichSExpr a)
  61. _cdr f (RSList (x:xs)) =
  62. let go (RSList []) = L [x]
  63. go (RSAtom a) = DL [x] a
  64. go (RSList xs') = L (x:xs')
  65. go (RSDotted ys a') = DL (x:ys) a'
  66. in go `fmap` f (L xs)
  67. _cdr f (RSDotted [x] a) =
  68. let go (RSList []) = L [x]
  69. go (RSAtom a') = DL [x] a'
  70. go (RSList xs) = L (x:xs)
  71. go (RSDotted ys a') = DL (x:ys) a'
  72. in go `fmap` f (A a)
  73. _cdr f (RSDotted (x:xs) a) =
  74. let go (RSList []) = L [x]
  75. go (RSAtom a') = DL [x] a'
  76. go (RSList ys) = L (x:ys)
  77. go (RSDotted ys a') = DL (x:ys) a'
  78. in go `fmap` f (DL xs a)
  79. _cdr _ (RSAtom a) = pure (A a)
  80. _cdr _ (RSList []) = pure Nil
  81. _cdr _ (RSDotted [] a) = pure (A a)
  82. -- | Produce the head and tail of the s-expression (if possible).
  83. --
  84. -- >>> uncons (L [A "el", A "eph", A "ant"])
  85. -- Just (A "el",L [A "eph",A "ant"])
  86. uncons :: RichSExpr a -> Maybe (RichSExpr a, RichSExpr a)
  87. uncons (R.RSList (x:xs)) = Just (x, R.RSList xs)
  88. uncons (R.RSDotted (x:xs) a) = Just (x, R.RSDotted xs a)
  89. uncons _ = Nothing
  90. -- | Combine the two s-expressions into a new one.
  91. --
  92. -- >>> cons (A "el") (L [A "eph", A "ant"])
  93. -- L [A "el",A "eph",A "ant"]
  94. cons :: RichSExpr a -> RichSExpr a -> RichSExpr a
  95. cons x (R.RSList xs) = R.RSList (x:xs)
  96. cons x (R.RSDotted xs a) = R.RSDotted (x:xs) a
  97. cons x (R.RSAtom a) = R.RSDotted [x] a
  98. -- | A shorter infix alias to grab the head
  99. -- and tail of an `RSList`.
  100. --
  101. -- >>> A "one" ::: L [A "two", A "three"]
  102. -- RSList [RSAtom "one",RSAtom "two",RSAtom "three"]
  103. pattern x ::: xs <- (uncons -> Just (x, xs))
  104. #if MIN_VERSION_base(4,8,0)
  105. where x ::: xs = cons x xs
  106. #endif
  107. -- | A shorter alias for `RSAtom`
  108. --
  109. -- >>> A "elephant"
  110. -- RSAtom "elephant"
  111. pattern A a = R.RSAtom a
  112. -- | A shorter alias for `RSList`
  113. --
  114. -- >>> L [A "pachy", A "derm"]
  115. -- RSList [RSAtom "pachy",RSAtom "derm"]
  116. pattern L xs = R.RSList xs
  117. -- | A shorter alias for `RSDotted`
  118. --
  119. -- >>> DL [A "pachy"] "derm"
  120. -- RSDotted [RSAtom "pachy"] "derm"
  121. pattern DL xs x = R.RSDotted xs x
  122. -- | A shorter alias for `RSList` @[]@
  123. --
  124. -- >>> Nil
  125. -- RSList []
  126. pattern Nil = R.RSList []
  127. -- | Utility function for parsing a pair of things: this parses a two-element list,
  128. -- and not a cons pair.
  129. --
  130. -- >>> fromPair (isAtom "pachy") (asAtom return) (L [A "pachy", A "derm"])
  131. -- Right ((), "derm")
  132. -- >>> fromPair (isAtom "pachy") fromAtom (L [A "pachy"])
  133. -- Left "Expected two-element list"
  134. fromPair :: (RichSExpr t -> Either String a)
  135. -> (RichSExpr t -> Either String b)
  136. -> RichSExpr t -> Either String (a, b)
  137. fromPair pl pr = asPair $ \(l,r) -> (,) <$> pl l <*> pr r
  138. -- | Utility function for parsing a proper list of things.
  139. --
  140. -- >>> fromList fromAtom (L [A "this", A "that", A "the-other"])
  141. -- Right ["this","that","the-other"]
  142. -- >>> fromList fromAtom (DL [A "this", A "that"] "the-other"])
  143. -- Left "asList: expected proper list; found dotted list"
  144. fromList :: (RichSExpr t -> Either String a) -> RichSExpr t -> Either String [a]
  145. fromList p = asList $ \ss -> mapM p ss
  146. -- | Utility function for parsing a single atom
  147. --
  148. -- >>> fromAtom (A "elephant")
  149. -- Right "elephant"
  150. -- >>> fromAtom (L [A "elephant"])
  151. -- Left "fromAtom: expected atom; found list"
  152. fromAtom :: RichSExpr t -> Either String t
  153. fromAtom (RSList _) = Left "fromAtom: expected atom; found list"
  154. fromAtom (RSDotted _ _) = Left "fromAtom: expected atom; found dotted list"
  155. fromAtom (RSAtom a) = return a
  156. -- | Parses a two-element list using the provided function.
  157. --
  158. -- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
  159. -- >>> asPair go (L [A "pachy", A "derm"])
  160. -- Right "pachyderm"
  161. -- >>> asPair go (L [A "elephant"])
  162. -- Left "asPair: expected two-element list; found list of length 1"
  163. asPair :: ((RichSExpr t, RichSExpr t) -> Either String a)
  164. -> RichSExpr t -> Either String a
  165. asPair f (RSList [l, r]) = f (l, r)
  166. asPair _ (RSList ls) = Left ("asPair: expected two-element list; found list of lenght " ++ show (length ls))
  167. asPair _ RSDotted {} = Left ("asPair: expected two-element list; found dotted list")
  168. asPair _ RSAtom {} = Left ("asPair: expected two-element list; found atom")
  169. -- | Parse an arbitrary-length list using the provided function.
  170. --
  171. -- >>> let go xs = concat <$> mapM fromAtom xs
  172. -- >>> asList go (L [A "el", A "eph", A "ant"])
  173. -- Right "elephant"
  174. -- >>> asList go (DL [A "el", A "eph"] "ant")
  175. -- Left "asList: expected list; found dotted list"
  176. asList :: ([RichSExpr t] -> Either String a)
  177. -> RichSExpr t -> Either String a
  178. asList f (RSList ls) = f ls
  179. asList _ RSDotted {} = Left ("asList: expected list; found dotted list")
  180. asList _ RSAtom { } = Left ("asList: expected list; found dotted list")
  181. -- | Match a given literal atom, failing otherwise.
  182. --
  183. -- >>> isAtom "elephant" (A "elephant")
  184. -- Right ()
  185. -- >>> isAtom "elephant" (L [A "elephant"])
  186. -- Left "isAtom: expected atom; found list"
  187. isAtom :: Eq t => t -> RichSExpr t -> Either String ()
  188. isAtom s (RSAtom s')
  189. | s == s' = return ()
  190. | otherwise = Left "isAtom: failed to match atom"
  191. isAtom _ RSList {} = Left "isAtom: expected atom; found list"
  192. isAtom _ RSDotted {} = Left "isAtom: expected atom; found dotted list"
  193. -- | Match an empty list, failing otherwise.
  194. --
  195. -- >>> isNil (L [])
  196. -- Right ()
  197. -- >>> isNil (A "elephant")
  198. -- Left "isNil: expected nil; found atom"
  199. isNil :: RichSExpr t -> Either String ()
  200. isNil (RSList []) = return ()
  201. isNil RSList {} = Left "isNil: expected nil; found non-nil list"
  202. isNil RSDotted {} = Left "isNil: expected nil; found dotted list"
  203. isNil RSAtom {} = Left "isNil: expected nil; found atom"
  204. -- | Parse an atom using the provided function.
  205. --
  206. -- >>> import Data.Char (toUpper)
  207. -- >>> asAtom (return . map toUpper) (A "elephant")
  208. -- Right "ELEPHANT"
  209. -- >>> asAtom (return . map toUpper) (L [])
  210. -- Left "asAtom: expected atom; found list"
  211. asAtom :: (t -> Either String a) -> RichSExpr t -> Either String a
  212. asAtom f (RSAtom s) = f s
  213. asAtom _ RSList {} = Left ("asAtom: expected atom; found list")
  214. asAtom _ RSDotted {} = Left ("asAtom: expected atom; found dotted list")
  215. -- | Parse an assoc-list using the provided function.
  216. --
  217. -- >>> let def (x, y) = do { a <- fromAtom x; b <- fromAtom y; return (a ++ ": " ++ b) }
  218. -- >>> let defList xs = do { defs <- mapM def xs; return (unlines defs) }
  219. -- >>> asAssoc defList (L [ L [A "legs", A "four"], L [ A "trunk", A "one"] ])
  220. -- Right "legs: four\ntrunk: one\n"
  221. -- >>> asAssoc defList (L [ L [A "legs", A "four"], L [ A "elephant"] ])
  222. -- Left "asAssoc: expected pair; found list of length 1"
  223. asAssoc :: ([(RichSExpr t, RichSExpr t)] -> Either String a)
  224. -> RichSExpr t -> Either String a
  225. asAssoc f (RSList ss) = gatherPairs ss >>= f
  226. where gatherPairs (RSList [a, b] : ts) = (:) <$> pure (a, b) <*> gatherPairs ts
  227. gatherPairs [] = pure []
  228. gatherPairs (RSAtom {} : _) = Left ("asAssoc: expected pair; found atom")
  229. gatherPairs (RSDotted {} : _) = Left ("asAssoc: expected pair; found dotted list")
  230. gatherPairs (RSList ls : _) = Left ("asAssoc: expected pair; found list of length " ++ show (length ls))
  231. asAssoc _ RSDotted {} = Left "asAssoc: expected assoc list; found dotted list"
  232. asAssoc _ RSAtom {} = Left "asAssoc: expected assoc list; found atom"
  233. car :: (RichSExpr t -> Either String t') -> [RichSExpr t] -> Either String t'
  234. car f (x:_) = f x
  235. car _ [] = Left "car: Taking car of zero-element list"
  236. cdr :: ([RichSExpr t] -> Either String t') -> [RichSExpr t] -> Either String t'
  237. cdr f (_:xs) = f xs
  238. cdr _ [] = Left "cdr: Taking cdr of zero-element list"