gdritter repos s-cargot / v0.1.0.0 Data / SCargot / Repr.hs
v0.1.0.0

Tree @v0.1.0.0 (Download .tar.gz)

Repr.hs @v0.1.0.0

2d2a81e
 
bd3629c
2d2a81e
ec88103
bd3629c
9343e1b
352b374
 
d8ba37b
 
95a2d84
 
 
d8ba37b
95a2d84
 
 
 
 
2d2a81e
e950f75
 
2d2a81e
ec88103
c843acc
719d0fe
 
 
 
95a2d84
0afe6a4
 
95a2d84
 
 
 
 
2d2a81e
95a2d84
c843acc
 
 
ec88103
 
 
e950f75
 
 
 
 
 
95a2d84
0afe6a4
 
95a2d84
d8ba37b
352b374
 
d8ba37b
 
 
95a2d84
 
 
 
2d2a81e
95a2d84
c843acc
 
 
ec88103
 
 
 
 
 
 
d8ba37b
0afe6a4
d8ba37b
0afe6a4
 
 
d8ba37b
95a2d84
 
d8ba37b
 
 
719d0fe
 
95a2d84
0afe6a4
95a2d84
 
 
 
 
 
 
 
 
 
 
 
 
2d2a81e
95a2d84
ec88103
 
 
 
 
 
c843acc
 
 
d8ba37b
95a2d84
0afe6a4
d8ba37b
 
 
 
c843acc
d8ba37b
c843acc
0afe6a4
d8ba37b
0afe6a4
95a2d84
 
d8ba37b
719d0fe
d8ba37b
719d0fe
 
 
95a2d84
 
 
 
 
183a85f
352b374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}

module Data.SCargot.Repr
       ( -- $reprs
         -- * Elementary SExpr representation
         SExpr(..)
         -- * Rich SExpr representation
       , RichSExpr(..)
       , toRich
       , fromRich
         -- * Well-Formed SExpr representation
       , WellFormedSExpr(..)
       , toWellFormed
       , fromWellFormed
       ) where

import Data.Data (Data)
import Data.Foldable (Foldable(..))
import Data.Traversable (Traversable(..))
import Data.Typeable (Typeable)
import GHC.Exts (IsList(..), IsString(..))

#if !MIN_VERSION_base(4,8,0)
import Prelude hiding (foldr)
#endif

-- | All S-Expressions can be understood as a sequence
--   of @cons@ cells (represented here by 'SCons'), the
--   empty list @nil@ (represented by 'SNil') or an
--   @atom@.
data SExpr atom
  = SCons (SExpr atom) (SExpr atom)
  | SAtom atom
  | SNil
    deriving (Eq, Show, Read, Functor, Data, Typeable, Foldable, Traversable)

instance IsString atom => IsString (SExpr atom) where
  fromString = SAtom . fromString

instance IsList (SExpr atom) where
  type Item (SExpr atom) = SExpr atom
  fromList = foldr SCons SNil
  toList   = go
    where go (SCons x xs) = x : go xs
          go SNil         = []
          go (SAtom {})   = error "Unable to turn atom into list"

-- | Sometimes the cons-based interface is too low
--   level, and we'd rather have the lists themselves
--   exposed. In this case, we have 'RSList' to
--   represent a well-formed cons list, and 'RSDotted'
--   to represent an improper list of the form
--   @(a b c . d)@. This representation is based on
--   the structure of the parsed S-Expression, and not on
--   how it was originally represented: thus, @(a . (b))@ is going to
--   be represented as @RSList[RSAtom a, RSAtom b]@
--   despite having been originally represented as a
--   dotted list.
data RichSExpr atom
  = RSList [RichSExpr atom]
  | RSDotted [RichSExpr atom] atom
  | RSAtom atom
    deriving (Eq, Show, Read, Functor, Data, Typeable, Foldable, Traversable)

instance IsString atom => IsString (RichSExpr atom) where
  fromString = RSAtom . fromString

instance IsList (RichSExpr atom) where
  type Item (RichSExpr atom) = RichSExpr atom
  fromList = RSList
  toList (RSList xs)   = xs
  toList (RSDotted {}) = error "Unable to turn dotted list into haskell list"
  toList (RSAtom {})   = error "Unable to turn atom into Haskell list"

-- |  It should always be true that
--
--   > fromRich (toRich x) == x
--
--   and that
--
--   > toRich (fromRich x) == x
toRich :: SExpr atom -> RichSExpr atom
toRich (SAtom a) = RSAtom a
toRich (SCons x xs) = go xs (toRich x:)
  where go (SAtom a) rs    = RSDotted (rs []) a
        go SNil rs         = RSList (rs [])
        go (SCons y ys) rs = go ys (rs . (toRich y:))
toRich SNil = RSList []

-- | This follows the same laws as 'toRich'.
fromRich :: RichSExpr atom -> SExpr atom
fromRich (RSAtom a) = SAtom a
fromRich (RSList xs) = foldr SCons SNil (map fromRich xs)
fromRich (RSDotted xs x) = foldr SCons (SAtom x) (map fromRich xs)

-- | A well-formed s-expression is one which does not
--   contain any dotted lists. This means that not
--   every value of @SExpr a@ can be converted to a
--   @WellFormedSExpr a@, although the opposite is
--   fine.
data WellFormedSExpr atom
  = WFSList [WellFormedSExpr atom]
  | WFSAtom atom
    deriving (Eq, Show, Read, Functor, Data, Typeable, Foldable, Traversable)

instance IsList (WellFormedSExpr atom) where
  type Item (WellFormedSExpr atom) = WellFormedSExpr atom
  fromList = WFSList
  toList (WFSList xs) = xs
  toList (WFSAtom {}) = error "Unable to turn atom into Haskell list"

instance IsString atom => IsString (WellFormedSExpr atom) where
  fromString = WFSAtom . fromString

-- | This will be @Nothing@ if the argument contains an
--   improper list. It should hold that
--
--   > toWellFormed (fromWellFormed x) == Right x
--
--   and also (more tediously) that
--
--   > case toWellFormed x of
--   >   Left _  -> True
--   >   Right y -> x == fromWellFormed y
toWellFormed :: SExpr atom -> Either String (WellFormedSExpr atom)
toWellFormed SNil      = return (WFSList [])
toWellFormed (SAtom a) = return (WFSAtom a)
toWellFormed (SCons x xs) = do
  x' <- toWellFormed x
  go xs (x':)
  where go (SAtom _) _  = Left "Found atom in cdr position"
        go SNil rs      = return (WFSList (rs []))
        go (SCons y ys) rs = do
          y' <- toWellFormed y
          go ys (rs . (y':))

-- | Convert a WellFormedSExpr back into a SExpr.
fromWellFormed :: WellFormedSExpr atom -> SExpr atom
fromWellFormed (WFSAtom a)  = SAtom a
fromWellFormed (WFSList xs) =
  foldr SCons SNil (map fromWellFormed xs)

{- $reprs

This module contains several different representations for
s-expressions. The s-cargot library underlying uses the
'SExpr' type as its representation type, which is a binary
tree representation with an arbitrary type for its leaves.

This type is not always convenient to manipulate in Haskell
code, this module defines two alternate representations
which turn a sequence of nested right-branching cons pairs
into Haskell lists: that is to say, they transform between

@
SCons a (SCons b (SCons c SNil))  \<=\>  RSList [a, b, c]
@

These two types differ in how they handle non-well-formed
lists, i.e. lists that end with an atom. The 'RichSExpr'
format handles this with a special constructor for lists
that end in an atom:

@
SCons a (SCons b (SAtom c))  \<=\>  RSDotted [a, b] c
@

On the other hand, the 'WellFormedSExpr' type elects
not to handle this case. This is unusual for Lisp source code,
but is a reasonable choice for configuration or data
storage formats that use s-expressions, where
non-well-formed lists would be an unnecessary
complication.

To make working with these types less verbose, there are other
modules that export pattern aliases and helper functions: these
can be found at "Data.SCargot.Repr.Basic",
"Data.SCargot.Repr.Rich", and "Data.SCargot.Repr.WellFormed".
-}