packages feed

lhae-0.0.2: src/Util/Sexpable.hs

{-# OPTIONS_GHC -XFlexibleInstances -XOverlappingInstances #-}
module Util.Sexpable ( Sexpable (..),ParseSexpError,PSE
                     , unexpected,unknownAtom
                     , recordToSexp,recordFromSexp)
    where

import Control.Monad.Error

import Util.Sexp (Sexp (..))

data ParseSexpError = Unexpected Sexp
                    | UnknownAtom String
                    | UnknownError String

type PSE = Either ParseSexpError

instance Show ParseSexpError where
    show (Unexpected sexp) = "Unexpected s-expression " ++ (show sexp)
    show (UnknownAtom atom) = "Unknown atom " ++ atom
    show (UnknownError msg) = "Unknown error: " ++ msg

instance Error ParseSexpError where
    noMsg = UnknownError "Unknown s-expression parsing error"
    strMsg msg = UnknownError msg

unexpected :: Sexp -> PSE a
unexpected = throwError . Unexpected

unknownAtom :: String -> PSE a
unknownAtom = throwError . UnknownAtom

class Sexpable a where
    toSexp :: a -> Sexp
    fromSexp :: Sexp -> PSE a

instance Sexpable Char where
    toSexp c = Atom [c]
    fromSexp (Atom (a:[])) = return a
    fromSexp (Atom a) = throwError $ UnknownError $ "Can not parse character from " ++ (show $ Atom a)
    fromSexp sexp = unexpected sexp

instance Sexpable [Char] where
    toSexp = Atom
    fromSexp (Atom a) = return a
    fromSexp sexp = unexpected sexp

instance (Sexpable a) => Sexpable [a] where
    toSexp = List . map toSexp
    fromSexp (List a) = forM a fromSexp
    fromSexp sexp = unexpected sexp

instance Sexpable Int where
    toSexp = Atom . show
    fromSexp (Atom a) = return $ read a
    fromSexp sexp = unexpected sexp

instance Sexpable Double where
    toSexp = Atom . show
    fromSexp (Atom a) = return $ read a
    fromSexp sexp = unexpected sexp

instance (Sexpable a) => Sexpable (Maybe a) where
    toSexp Nothing = List [Atom "optional"]
    toSexp (Just a) = List [Atom "optional",toSexp a]
    fromSexp (List [Atom "optional"]) = return Nothing
    fromSexp (List [Atom "optional",sexp]) = 
        fromSexp sexp >>= return . Just
    fromSexp sexp = unexpected sexp

instance Sexpable Bool where
    toSexp True = Atom "true"
    toSexp False = Atom "false"
    fromSexp (Atom "true") = return True
    fromSexp (Atom "false") = return False
    fromSexp sexp = unexpected sexp

recordToSexp :: [(String, b -> Sexp)] -> b -> Sexp
recordToSexp assocs record = 
    List $ (map (\(name, field) ->
                     List [Atom name, field record]
                )
           ) assocs

recordFromSexp :: (a -> Sexp -> PSE a) -> a -> Sexp -> PSE a
recordFromSexp f init (List options) = foldM f init options
recordFromSexp _ _ sexp = unexpected sexp