Repr.hs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. {-# LANGUAGE DeriveDataTypeable #-}
  2. {-# LANGUAGE DeriveFoldable #-}
  3. {-# LANGUAGE DeriveFunctor #-}
  4. {-# LANGUAGE DeriveTraversable #-}
  5. {-# LANGUAGE TypeFamilies #-}
  6. module Data.SCargot.Repr
  7. ( -- $reprs
  8. -- * Elementary SExpr representation
  9. SExpr(..)
  10. -- * Rich SExpr representation
  11. , RichSExpr(..)
  12. , toRich
  13. , fromRich
  14. -- * Well-Formed SExpr representation
  15. , WellFormedSExpr(..)
  16. , toWellFormed
  17. , fromWellFormed
  18. ) where
  19. import Data.Data (Data)
  20. import Data.Foldable (Foldable(..))
  21. import Data.Traversable (Traversable(..))
  22. import Data.Typeable (Typeable)
  23. import GHC.Exts (IsList(..), IsString(..))
  24. #if !MIN_VERSION_base(4,8,0)
  25. import Prelude hiding (foldr)
  26. #endif
  27. -- | All S-Expressions can be understood as a sequence
  28. -- of @cons@ cells (represented here by 'SCons'), the
  29. -- empty list @nil@ (represented by 'SNil') or an
  30. -- @atom@.
  31. data SExpr atom
  32. = SCons (SExpr atom) (SExpr atom)
  33. | SAtom atom
  34. | SNil
  35. deriving (Eq, Show, Read, Functor, Data, Typeable, Foldable, Traversable)
  36. instance IsString atom => IsString (SExpr atom) where
  37. fromString = SAtom . fromString
  38. instance IsList (SExpr atom) where
  39. type Item (SExpr atom) = SExpr atom
  40. fromList = foldr SCons SNil
  41. toList = go
  42. where go (SCons x xs) = x : go xs
  43. go SNil = []
  44. go (SAtom {}) = error "Unable to turn atom into list"
  45. -- | Sometimes the cons-based interface is too low
  46. -- level, and we'd rather have the lists themselves
  47. -- exposed. In this case, we have 'RSList' to
  48. -- represent a well-formed cons list, and 'RSDotted'
  49. -- to represent an improper list of the form
  50. -- @(a b c . d)@. This representation is based on
  51. -- the structure of the parsed S-Expression, and not on
  52. -- how it was originally represented: thus, @(a . (b))@ is going to
  53. -- be represented as @RSList[RSAtom a, RSAtom b]@
  54. -- despite having been originally represented as a
  55. -- dotted list.
  56. data RichSExpr atom
  57. = RSList [RichSExpr atom]
  58. | RSDotted [RichSExpr atom] atom
  59. | RSAtom atom
  60. deriving (Eq, Show, Read, Functor, Data, Typeable, Foldable, Traversable)
  61. instance IsString atom => IsString (RichSExpr atom) where
  62. fromString = RSAtom . fromString
  63. instance IsList (RichSExpr atom) where
  64. type Item (RichSExpr atom) = RichSExpr atom
  65. fromList = RSList
  66. toList (RSList xs) = xs
  67. toList (RSDotted {}) = error "Unable to turn dotted list into haskell list"
  68. toList (RSAtom {}) = error "Unable to turn atom into Haskell list"
  69. -- | It should always be true that
  70. --
  71. -- > fromRich (toRich x) == x
  72. --
  73. -- and that
  74. --
  75. -- > toRich (fromRich x) == x
  76. toRich :: SExpr atom -> RichSExpr atom
  77. toRich (SAtom a) = RSAtom a
  78. toRich (SCons x xs) = go xs (toRich x:)
  79. where go (SAtom a) rs = RSDotted (rs []) a
  80. go SNil rs = RSList (rs [])
  81. go (SCons y ys) rs = go ys (rs . (toRich y:))
  82. toRich SNil = RSList []
  83. -- | This follows the same laws as 'toRich'.
  84. fromRich :: RichSExpr atom -> SExpr atom
  85. fromRich (RSAtom a) = SAtom a
  86. fromRich (RSList xs) = foldr SCons SNil (map fromRich xs)
  87. fromRich (RSDotted xs x) = foldr SCons (SAtom x) (map fromRich xs)
  88. -- | A well-formed s-expression is one which does not
  89. -- contain any dotted lists. This means that not
  90. -- every value of @SExpr a@ can be converted to a
  91. -- @WellFormedSExpr a@, although the opposite is
  92. -- fine.
  93. data WellFormedSExpr atom
  94. = WFSList [WellFormedSExpr atom]
  95. | WFSAtom atom
  96. deriving (Eq, Show, Read, Functor, Data, Typeable, Foldable, Traversable)
  97. instance IsList (WellFormedSExpr atom) where
  98. type Item (WellFormedSExpr atom) = WellFormedSExpr atom
  99. fromList = WFSList
  100. toList (WFSList xs) = xs
  101. toList (WFSAtom {}) = error "Unable to turn atom into Haskell list"
  102. instance IsString atom => IsString (WellFormedSExpr atom) where
  103. fromString = WFSAtom . fromString
  104. -- | This will be @Nothing@ if the argument contains an
  105. -- improper list. It should hold that
  106. --
  107. -- > toWellFormed (fromWellFormed x) == Right x
  108. --
  109. -- and also (more tediously) that
  110. --
  111. -- > case toWellFormed x of
  112. -- > Left _ -> True
  113. -- > Right y -> x == fromWellFormed y
  114. toWellFormed :: SExpr atom -> Either String (WellFormedSExpr atom)
  115. toWellFormed SNil = return (WFSList [])
  116. toWellFormed (SAtom a) = return (WFSAtom a)
  117. toWellFormed (SCons x xs) = do
  118. x' <- toWellFormed x
  119. go xs (x':)
  120. where go (SAtom _) _ = Left "Found atom in cdr position"
  121. go SNil rs = return (WFSList (rs []))
  122. go (SCons y ys) rs = do
  123. y' <- toWellFormed y
  124. go ys (rs . (y':))
  125. -- | Convert a WellFormedSExpr back into a SExpr.
  126. fromWellFormed :: WellFormedSExpr atom -> SExpr atom
  127. fromWellFormed (WFSAtom a) = SAtom a
  128. fromWellFormed (WFSList xs) =
  129. foldr SCons SNil (map fromWellFormed xs)
  130. {- $reprs
  131. This module contains several different representations for
  132. s-expressions. The s-cargot library underlying uses the
  133. 'SExpr' type as its representation type, which is a binary
  134. tree representation with an arbitrary type for its leaves.
  135. This type is not always convenient to manipulate in Haskell
  136. code, this module defines two alternate representations
  137. which turn a sequence of nested right-branching cons pairs
  138. into Haskell lists: that is to say, they transform between
  139. @
  140. SCons a (SCons b (SCons c SNil)) \<=\> RSList [a, b, c]
  141. @
  142. These two types differ in how they handle non-well-formed
  143. lists, i.e. lists that end with an atom. The 'RichSExpr'
  144. format handles this with a special constructor for lists
  145. that end in an atom:
  146. @
  147. SCons a (SCons b (SAtom c)) \<=\> RSDotted [a, b] c
  148. @
  149. On the other hand, the 'WellFormedSExpr' type elects
  150. not to handle this case. This is unusual for Lisp source code,
  151. but is a reasonable choice for configuration or data
  152. storage formats that use s-expressions, where
  153. non-well-formed lists would be an unnecessary
  154. complication.
  155. To make working with these types less verbose, there are other
  156. modules that export pattern aliases and helper functions: these
  157. can be found at "Data.SCargot.Repr.Basic",
  158. "Data.SCargot.Repr.Rich", and "Data.SCargot.Repr.WellFormed".
  159. -}