Basic.hs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. {-# LANGUAGE PatternSynonyms #-}
  2. module Data.SCargot.Repr.Basic
  3. ( -- * Basic 'SExpr' representation
  4. R.SExpr(..)
  5. -- * Shorthand Patterns
  6. , pattern (:::)
  7. , pattern A
  8. , pattern Nil
  9. -- * Lenses
  10. , _car
  11. , _cdr
  12. -- * Useful processing functions
  13. , fromPair
  14. , fromList
  15. , fromAtom
  16. , asPair
  17. , asList
  18. , isAtom
  19. , asAtom
  20. , asAssoc
  21. ) where
  22. import Control.Applicative ((<$>), (<*>), pure)
  23. import Data.SCargot.Repr as R
  24. -- | A traversal with access to the first element of a pair.
  25. --
  26. -- >>> import Lens.Family
  27. -- >>> set _car (A "elephant") (A "one" ::: A "two" ::: A "three" ::: Nil)
  28. -- A "elelphant" ::: A "two" ::: A "three" ::: Nil
  29. -- >>> set _car (A "two" ::: A "three" ::: Nil) (A "one" ::: A "elephant")
  30. -- (A "two" ::: A "three" ::: Nil) ::: A "elephant"
  31. _car :: Applicative f => (SExpr a -> f (SExpr a)) -> SExpr a -> f (SExpr a)
  32. _car f (x ::: xs) = (:::) <$> f x <*> pure xs
  33. _car _ (A a) = pure (A a)
  34. _car _ Nil = pure Nil
  35. -- | A traversal with access to the second element of a pair.
  36. --
  37. -- >>> import Lens.Family
  38. -- >>> set _cdr (A "elephant") (A "one" ::: A "two" ::: A "three" ::: Nil)
  39. -- A "one" ::: A "elephant"
  40. -- >>> set _cdr (A "two" ::: A "three" ::: Nil) (A "one" ::: A "elephant")
  41. -- A "one" ::: A "two" ::: A "three" ::: Nil
  42. _cdr :: Applicative f => (SExpr a -> f (SExpr a)) -> SExpr a -> f (SExpr a)
  43. _cdr f (x ::: xs) = (:::) <$> pure x <*> f xs
  44. _cdr _ (A a) = pure (A a)
  45. _cdr _ Nil = pure Nil
  46. infixr 5 :::
  47. -- | A shorter infix alias for `SCons`
  48. pattern x ::: xs = SCons x xs
  49. -- | A shorter alias for `SAtom`
  50. pattern A x = SAtom x
  51. -- | A (slightly) shorter alias for `SNil`
  52. pattern Nil = SNil
  53. getShape :: SExpr a -> String
  54. getShape Nil = "empty list"
  55. getShape sx = go 0 sx
  56. where go n Nil = "list of length " ++ show n
  57. go n A {} = "dotted list of length " ++ show n
  58. go n (_:::xs) = go (n+1) xs
  59. -- | Utility function for parsing a pair of things.
  60. --
  61. -- >>> fromPair (isAtom "pachy") (asAtom return) (A "pachy" ::: A "derm" ::: Nil)
  62. -- Right ((), "derm")
  63. -- >>> fromPair (isAtom "pachy") fromAtom (A "pachy" ::: Nil)
  64. -- Left "Expected two-element list"
  65. fromPair :: (SExpr t -> Either String a)
  66. -> (SExpr t -> Either String b)
  67. -> SExpr t -> Either String (a, b)
  68. fromPair pl pr (l ::: r ::: Nil) = (,) <$> pl l <*> pr r
  69. fromPair _ _ sx = Left ("fromPair: expected two-element list; found " ++ getShape sx)
  70. -- | Utility function for parsing a list of things.
  71. fromList :: (SExpr t -> Either String a) -> SExpr t -> Either String [a]
  72. fromList p (s ::: ss) = (:) <$> p s <*> fromList p ss
  73. fromList p Nil = pure []
  74. fromList _ sx = Left ("fromList: expected list; found " ++ getShape sx)
  75. -- | Utility function for parsing a single atom
  76. fromAtom :: SExpr t -> Either String t
  77. fromAtom (A a) = return a
  78. fromAtom sx = Left ("fromAtom: expected atom; found list" ++ getShape sx)
  79. gatherList :: SExpr t -> Either String [SExpr t]
  80. gatherList (x ::: xs) = (:) <$> pure x <*> gatherList xs
  81. gatherList Nil = pure []
  82. gatherList sx = Left ("gatherList: expected list; found " ++ getShape sx)
  83. -- | Parse a two-element list (NOT a dotted pair) using the
  84. -- provided function.
  85. --
  86. -- >>> let go (A l) (A r) = return (l ++ r); go _ _ = Left "expected atoms"
  87. -- >>> asPair go (A "pachy" ::: A "derm" ::: Nil)
  88. -- Right "pachyderm"
  89. -- >>> asPair go (A "elephant" ::: Nil)
  90. -- Left "asPair: expected two-element list; found list of length 1"
  91. asPair :: ((SExpr t, SExpr t) -> Either String a)
  92. -> SExpr t -> Either String a
  93. asPair f (l ::: r ::: SNil) = f (l, r)
  94. asPair _ sx = Left ("asPair: expected two-element list; found " ++ getShape sx)
  95. -- | Parse an arbitrary-length list using the provided function.
  96. --
  97. -- >>> let go xs = concat <$> mapM fromAtom xs
  98. -- >>> asList go (A "el" ::: A "eph" ::: A "ant" ::: Nil)
  99. -- Right "elephant"
  100. -- >>> asList go (A "el" ::: A "eph" ::: A "ant")
  101. -- Left "asList: expected list; found dotted list of length 3"
  102. asList :: ([SExpr t] -> Either String a) -> SExpr t -> Either String a
  103. asList f ls = gatherList ls >>= f
  104. -- | Match a given literal atom, failing otherwise.
  105. --
  106. -- >>> isAtom "elephant" (A "elephant")
  107. -- Right ()
  108. -- >>> isAtom "elephant" (A "elephant" ::: Nil)
  109. -- Left "isAtom: expected atom; found list"
  110. isAtom :: Eq t => t -> SExpr t -> Either String ()
  111. isAtom s (A s')
  112. | s == s' = return ()
  113. | otherwise = Left "isAtom: failed to match atom"
  114. isAtom _ sx = Left ("isAtom: expected atom; found " ++ getShape sx)
  115. -- | Parse an atom using the provided function.
  116. --
  117. -- >>> import Data.Char (toUpper)
  118. -- >>> asAtom (return . map toUpper) (A "elephant")
  119. -- Right "ELEPHANT"
  120. -- >>> asAtom (return . map toUpper) Nil
  121. -- Left "asAtom: expected atom; found empty list"
  122. asAtom :: (t -> Either String a) -> SExpr t -> Either String a
  123. asAtom f (A s) = f s
  124. asAtom _ sx = Left ("asAtom: expected atom; found " ++ getShape sx)
  125. -- | Parse an assoc-list using the provided function.
  126. --
  127. -- >>> let def (x, y) = do { a <- fromAtom x; b <- fromAtom y; return (a ++ ": " ++ b) }
  128. -- >>> let defList xs = do { defs <- mapM def xs; return (unlines defs) }
  129. -- >>> asAssoc defList ((A "legs" ::: A "four" ::: Nil) ::: (A "trunk" ::: A "one" ::: Nil) ::: Nil)
  130. -- Right "legs: four\ntrunk: one\n"
  131. -- >>> asAssoc defList ((A "legs" ::: A "four" ::: Nil) ::: (A "elephant") ::: Nil)
  132. -- Left "asAssoc: expected pair; found list of length 1"
  133. asAssoc :: ([(SExpr t, SExpr t)] -> Either String a)
  134. -> SExpr t -> Either String a
  135. asAssoc f ss = gatherList ss >>= mapM go >>= f
  136. where go (a ::: b ::: Nil) = return (a, b)
  137. go sx = Left ("asAssoc: expected pair; found " ++ getShape sx)