Rich.hs 8.8 KB

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