WellFormed.hs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. {-# LANGUAGE PatternSynonyms #-}
  2. {-# LANGUAGE ViewPatterns #-}
  3. module Data.SCargot.Repr.WellFormed
  4. ( -- * 'WellFormedSExpr' representation
  5. R.WellFormedSExpr(..)
  6. , R.toWellFormed
  7. , R.fromWellFormed
  8. -- * Constructing and Deconstructing
  9. , cons
  10. , uncons
  11. -- * Useful pattern synonyms
  12. , pattern (:::)
  13. , pattern L
  14. , pattern A
  15. , pattern Nil
  16. -- * Useful processing functions
  17. , fromPair
  18. , fromList
  19. , fromAtom
  20. , asPair
  21. , asList
  22. , isAtom
  23. , isNil
  24. , asAtom
  25. , asAssoc
  26. , car
  27. , cdr
  28. ) where
  29. #if !MIN_VERSION_base(4,8,0)
  30. import Control.Applicative ((<$>), (<*>), pure)
  31. #endif
  32. import Data.SCargot.Repr as R
  33. -- | Produce the head and tail of the s-expression (if possible).
  34. --
  35. -- >>> uncons (L [A "el", A "eph", A "ant"])
  36. -- Just (WFSAtom "el",WFSList [WFSAtom "eph",WFSAtom "ant"])
  37. uncons :: WellFormedSExpr a -> Maybe (WellFormedSExpr a, WellFormedSExpr a)
  38. uncons R.WFSAtom {} = Nothing
  39. uncons (R.WFSList []) = Nothing
  40. uncons (R.WFSList (x:xs)) = Just (x, R.WFSList xs)
  41. -- | Combine the two-expressions into a new one. This will return
  42. -- @Nothing@ if the resulting s-expression is not well-formed.
  43. --
  44. -- >>> cons (A "el") (L [A "eph", A "ant"])
  45. -- Just (WFSList [WFSAtom "el",WFSAtom "eph",WFSAtom "ant"])
  46. -- >>> cons (A "pachy") (A "derm"))
  47. -- Nothing
  48. cons :: WellFormedSExpr a -> WellFormedSExpr a -> Maybe (WellFormedSExpr a)
  49. cons _ (R.WFSAtom {}) = Nothing
  50. cons x (R.WFSList xs) = Just (R.WFSList (x:xs))
  51. -- | A shorter infix alias to grab the head and tail of a `WFSList`. This
  52. -- pattern is unidirectional, because it cannot be guaranteed that it
  53. -- is used to construct well-formed s-expressions; use the function "cons"
  54. -- instead.
  55. --
  56. -- >>> let sum (x ::: xs) = x + sum xs; sum Nil = 0
  57. #if MIN_VERSION_base(4,8,0)
  58. pattern (:::) :: WellFormedSExpr a -> WellFormedSExpr a -> WellFormedSExpr a
  59. #endif
  60. pattern x ::: xs <- (uncons -> Just (x, xs))
  61. -- | A shorter alias for `WFSList`
  62. --
  63. -- >>> L [A "pachy", A "derm"]
  64. -- WFSList [WFSAtom "pachy",WFSAtom "derm"]
  65. #if MIN_VERSION_base(4,8,0)
  66. pattern L :: [WellFormedSExpr t] -> WellFormedSExpr t
  67. #endif
  68. pattern L xs = R.WFSList xs
  69. -- | A shorter alias for `WFSAtom`
  70. --
  71. -- >>> A "elephant"
  72. -- WFSAtom "elephant"
  73. #if MIN_VERSION_base(4,8,0)
  74. pattern A :: t -> WellFormedSExpr t
  75. #endif
  76. pattern A a = R.WFSAtom a
  77. -- | A shorter alias for `WFSList` @[]@
  78. --
  79. -- >>> Nil
  80. -- WFSList []
  81. #if MIN_VERSION_base(4,8,0)
  82. pattern Nil :: WellFormedSExpr t
  83. #endif
  84. pattern Nil = R.WFSList []
  85. getShape :: WellFormedSExpr a -> String
  86. getShape WFSAtom {} = "atom"
  87. getShape (WFSList []) = "empty list"
  88. getShape (WFSList sx) = "list of length " ++ show (length sx)
  89. -- | Utility function for parsing a pair of things.
  90. --
  91. -- >>> fromPair (isAtom "pachy") (asAtom return) (L [A "pachy", A "derm"])
  92. -- Right ((), "derm")
  93. -- >>> fromPair (isAtom "pachy") fromAtom (L [A "pachy"])
  94. -- Left "Expected two-element list"
  95. fromPair :: (WellFormedSExpr t -> Either String a)
  96. -> (WellFormedSExpr t -> Either String b)
  97. -> WellFormedSExpr t -> Either String (a, b)
  98. fromPair pl pr (L [l, r]) = (,) <$> pl l <*> pr r
  99. fromPair _ _ sx = Left ("fromPair: expected two-element list; found " ++ getShape sx)
  100. -- | Utility function for parsing a list of things.
  101. --
  102. -- >>> fromList fromAtom (L [A "this", A "that", A "the-other"])
  103. -- Right ["this","that","the-other"]
  104. -- >>> fromList fromAtom (A "pachyderm")
  105. -- Left "asList: expected proper list; found dotted list"
  106. fromList :: (WellFormedSExpr t -> Either String a)
  107. -> WellFormedSExpr t -> Either String [a]
  108. fromList p (L ss) = mapM p ss
  109. fromList _ sx = Left ("fromList: expected list; found " ++ getShape sx)
  110. -- | Utility function for parsing a single atom
  111. --
  112. -- >>> fromAtom (A "elephant")
  113. -- Right "elephant"
  114. -- >>> fromAtom (L [A "elephant"])
  115. -- Left "fromAtom: expected atom; found list"
  116. fromAtom :: WellFormedSExpr t -> Either String t
  117. fromAtom (A a) = return a
  118. fromAtom sx = Left ("fromAtom: expected atom; found " ++ getShape sx)
  119. -- | Parses a two-element list using the provided function.
  120. --
  121. -- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
  122. -- >>> asPair go (L [A "pachy", A "derm"])
  123. -- Right "pachyderm"
  124. -- >>> asPair go (L [A "elephant"])
  125. -- Left "asPair: expected two-element list; found list of length 1"
  126. asPair :: ((WellFormedSExpr t, WellFormedSExpr t) -> Either String a)
  127. -> WellFormedSExpr t -> Either String a
  128. asPair f (L [l, r]) = f (l, r)
  129. asPair _ sx = Left ("asPair: expected two-element list; found " ++ getShape sx)
  130. -- | Parse an arbitrary-length list using the provided function.
  131. --
  132. -- >>> let go xs = concat <$> mapM fromAtom xs
  133. -- >>> asList go (L [A "el", A "eph", A "ant"])
  134. -- Right "elephant"
  135. -- >>> asList go (A "pachyderm")
  136. -- Left "asList: expected list; found atom"
  137. asList :: ([WellFormedSExpr t] -> Either String a)
  138. -> WellFormedSExpr t -> Either String a
  139. asList f (L ls) = f ls
  140. asList _ sx = Left ("asList: expected list; found " ++ getShape sx)
  141. -- | Match a given literal atom, failing otherwise.
  142. --
  143. -- >>> isAtom "elephant" (A "elephant")
  144. -- Right ()
  145. -- >>> isAtom "elephant" (L [A "elephant"])
  146. -- Left "isAtom: expected atom; found list"
  147. isAtom :: Eq t => t -> WellFormedSExpr t -> Either String ()
  148. isAtom s (A s')
  149. | s == s' = return ()
  150. | otherwise = Left "isAtom: failed to match atom"
  151. isAtom _ sx = Left ("isAtom: expected atom; found " ++ getShape sx)
  152. -- | Match an empty list, failing otherwise.
  153. --
  154. -- >>> isNil (L [])
  155. -- Right ()
  156. -- >>> isNil (A "elephant")
  157. -- Left "isNil: expected nil; found atom"
  158. isNil :: WellFormedSExpr t -> Either String ()
  159. isNil Nil = return ()
  160. isNil sx = Left ("isNil: expected nil; found " ++ getShape sx)
  161. -- | Parse an atom using the provided function.
  162. --
  163. -- >>> import Data.Char (toUpper)
  164. -- >>> asAtom (return . map toUpper) (A "elephant")
  165. -- Right "ELEPHANT"
  166. -- >>> asAtom (return . map toUpper) (L [])
  167. -- Left "asAtom: expected atom; found list"
  168. asAtom :: (t -> Either String a) -> WellFormedSExpr t -> Either String a
  169. asAtom f (A s) = f s
  170. asAtom _ sx = Left ("asAtom: expected atom; found " ++ getShape sx)
  171. -- | Parse an assoc-list using the provided function.
  172. --
  173. -- >>> let def (x, y) = do { a <- fromAtom x; b <- fromAtom y; return (a ++ ": " ++ b) }
  174. -- >>> let defList xs = do { defs <- mapM def xs; return (unlines defs) }
  175. -- >>> asAssoc defList (L [ L [A "legs", A "four"], L [ A "trunk", A "one"] ])
  176. -- Right "legs: four\ntrunk: one\n"
  177. -- >>> asAssoc defList (L [ L [A "legs", A "four"], L [ A "elephant"] ])
  178. -- Left "asAssoc: expected pair; found list of length 1"
  179. asAssoc :: ([(WellFormedSExpr t, WellFormedSExpr t)] -> Either String a)
  180. -> WellFormedSExpr t -> Either String a
  181. asAssoc f (L ss) = gatherPairs ss >>= f
  182. where gatherPairs (L [a, b] : ts) = (:) <$> pure (a, b) <*> gatherPairs ts
  183. gatherPairs [] = pure []
  184. gatherPairs (sx:_) = Left ("asAssoc: expected pair; found " ++ getShape sx)
  185. asAssoc _ sx = Left ("asAssoc: expected list; found " ++ getShape sx)
  186. -- | Run the parser on the first element of a Haskell list of "WellFormedSExpr" values,
  187. -- failing if the list is empty. This is useful in conjunction with the `asList`
  188. -- function.
  189. car :: (WellFormedSExpr t -> Either String t')
  190. -> [WellFormedSExpr t] -> Either String t'
  191. car f (x:_) = f x
  192. car _ [] = Left "car: Taking car of zero-element list"
  193. -- | Run the parser on all but the first element of a Haskell list of "WellFormedSExpr" values,
  194. -- failing if the list is empty. This is useful in conjunction with the `asList`
  195. -- function.
  196. cdr :: ([WellFormedSExpr t] -> Either String t')
  197. -> [WellFormedSExpr t] -> Either String t'
  198. cdr f (_:xs) = f xs
  199. cdr _ [] = Left "cdr: Taking cdr of zero-element list"