Rich.hs 7.7 KB

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