gdritter repos s-cargot / master Data / SCargot / Repr.hs
master

Tree @master (Download .tar.gz)

Repr.hs @master

350aa7d
 
d115597
350aa7d
d51c85a
d115597
adeaaa4
ed1b3db
 
d01604d
 
65b9c65
 
 
d01604d
65b9c65
 
 
 
 
350aa7d
6196b93
 
350aa7d
d51c85a
94563a1
14c258a
 
 
 
65b9c65
c9a1514
 
65b9c65
 
 
 
 
350aa7d
65b9c65
94563a1
 
 
d51c85a
 
 
6196b93
 
 
 
 
 
65b9c65
c9a1514
 
65b9c65
d01604d
ed1b3db
 
d01604d
 
 
65b9c65
 
 
 
350aa7d
65b9c65
94563a1
 
 
d51c85a
 
 
 
 
 
 
d01604d
c9a1514
d01604d
c9a1514
 
 
d01604d
65b9c65
 
d01604d
 
 
14c258a
 
65b9c65
c9a1514
65b9c65
 
 
 
 
 
 
 
 
 
 
 
 
350aa7d
65b9c65
d51c85a
 
 
 
 
 
94563a1
 
 
d01604d
65b9c65
c9a1514
d01604d
 
 
 
94563a1
d01604d
94563a1
c9a1514
d01604d
c9a1514
65b9c65
 
d01604d
14c258a
d01604d
14c258a
 
 
65b9c65
 
 
 
 
1b92e49
ed1b3db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{-# 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".
-}