packages feed

atto-lisp 0.1 → 0.2

raw patch · 2 files changed

+248/−43 lines, 2 filesdep +containersdep ~attoparsecdep ~blaze-textualPVP ok

version bump matches the API change (PVP)

Dependencies added: containers

Dependency ranges changed: attoparsec, blaze-textual

API changes (from Hackage documentation)

+ Data.AttoLisp: fromLispExpr :: Lisp -> Builder
+ Data.AttoLisp: instance (FromLisp a, FromLisp b) => FromLisp (Either a b)
+ Data.AttoLisp: instance (FromLisp a, FromLisp b) => FromLisp (a, b)
+ Data.AttoLisp: instance (FromLisp a, FromLisp b, FromLisp c) => FromLisp (a, b, c)
+ Data.AttoLisp: instance (Ord a, FromLisp a, FromLisp b) => FromLisp (Map a b)
+ Data.AttoLisp: instance (ToLisp a, ToLisp b) => ToLisp (Either a b)
+ Data.AttoLisp: instance (ToLisp a, ToLisp b) => ToLisp (Map a b)
+ Data.AttoLisp: instance (ToLisp a, ToLisp b) => ToLisp (a, b)
+ Data.AttoLisp: instance (ToLisp a, ToLisp b, ToLisp c) => ToLisp (a, b, c)
+ Data.AttoLisp: instance FromLisp (Ratio Integer)
+ Data.AttoLisp: instance FromLisp Bool
+ Data.AttoLisp: instance FromLisp Char
+ Data.AttoLisp: instance FromLisp Double
+ Data.AttoLisp: instance FromLisp Float
+ Data.AttoLisp: instance FromLisp Int16
+ Data.AttoLisp: instance FromLisp Int32
+ Data.AttoLisp: instance FromLisp Int64
+ Data.AttoLisp: instance FromLisp Int8
+ Data.AttoLisp: instance FromLisp Lisp
+ Data.AttoLisp: instance FromLisp Number
+ Data.AttoLisp: instance FromLisp Word
+ Data.AttoLisp: instance FromLisp Word16
+ Data.AttoLisp: instance FromLisp Word32
+ Data.AttoLisp: instance FromLisp Word64
+ Data.AttoLisp: instance FromLisp Word8
+ Data.AttoLisp: instance FromLisp [Char]
+ Data.AttoLisp: instance FromLisp a => FromLisp [a]
+ Data.AttoLisp: instance ToLisp (Ratio Integer)
+ Data.AttoLisp: instance ToLisp Bool
+ Data.AttoLisp: instance ToLisp Char
+ Data.AttoLisp: instance ToLisp Double
+ Data.AttoLisp: instance ToLisp Float
+ Data.AttoLisp: instance ToLisp Int16
+ Data.AttoLisp: instance ToLisp Int32
+ Data.AttoLisp: instance ToLisp Int64
+ Data.AttoLisp: instance ToLisp Int8
+ Data.AttoLisp: instance ToLisp Lisp
+ Data.AttoLisp: instance ToLisp Number
+ Data.AttoLisp: instance ToLisp Word
+ Data.AttoLisp: instance ToLisp Word16
+ Data.AttoLisp: instance ToLisp Word32
+ Data.AttoLisp: instance ToLisp Word64
+ Data.AttoLisp: instance ToLisp Word8
+ Data.AttoLisp: instance ToLisp [Char]
+ Data.AttoLisp: instance ToLisp a => ToLisp [a]
- Data.AttoLisp: fromLisp :: Lisp -> Builder
+ Data.AttoLisp: fromLisp :: FromLisp a => Lisp -> Result a

Files

Data/AttoLisp.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE OverloadedStrings, Rank2Types, DeriveDataTypeable, BangPatterns,-             MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,+{-# LANGUAGE OverloadedStrings, Rank2Types, DeriveDataTypeable, BangPatterns #-}+-- The following is for the ParseList stuff+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,              UndecidableInstances #-} -- | Efficient parsing and serialisation of S-Expressions (as used by Lisp). --@@ -11,7 +12,7 @@   ( -- * Core Lisp Types     Lisp(..), nil, isNull,     -- * Type Conversion-    FromLisp(..), Result(..),+    FromLisp(..), Result(..), fromLisp,     Failure, Success, Parser,     parse, parseMaybe, parseEither, typeMismatch, @@ -21,7 +22,7 @@     mkStruct,  struct,      -- * Encoding and parsing-    encode, fromLisp,+    encode, fromLispExpr,          lisp, atom,   )@@ -35,10 +36,12 @@ import Control.Monad import Data.Attoparsec.Char8 hiding ( Parser, Result, parse, string, double ) import Data.Data+import Data.Int  ( Int8, Int16, Int32, Int64 ) import Data.List ( foldl' )+import Data.Ratio ( Ratio ) import Data.Monoid import Data.String-import Data.Word ( Word8 )+import Data.Word ( Word, Word8, Word16, Word32, Word64 ) import Numeric (showHex) import qualified Data.Attoparsec as A import qualified Data.Text as T@@ -49,6 +52,7 @@ import qualified Data.Attoparsec.Zepto as Z import qualified Blaze.ByteString.Builder as Blaze import qualified Blaze.ByteString.Builder.Char.Utf8 as Blaze+import qualified Data.Map as M -- | A Lisp expression (S-expression). -- -- Symbols are case-sensitive.@@ -273,6 +277,9 @@ class FromLisp a where   parseLisp :: Lisp -> Parser a +fromLisp :: FromLisp a => Lisp -> Result a+fromLisp = parse parseLisp+ parseIntegral :: Integral a => Lisp -> Parser a parseIntegral (Number n) = pure (floor n) parseIntegral v          = typeMismatch "Integral" v@@ -328,18 +335,47 @@   parseList (T.unpack tag) f rest struct tag _ e = typeMismatch (T.unpack tag ++ " object") e +instance ToLisp Lisp where+  toLisp = id+  {-# INLINE toLisp #-} +instance FromLisp Lisp where+  parseLisp = pure+  {-# INLINE parseLisp #-}++instance ToLisp Bool where+  toLisp b = if b then Symbol "t" else nil+  {-# INLINE toLisp #-}++instance FromLisp Bool where+  parseLisp e = if isNull e then pure False else pure True+  {-# INLINE parseLisp #-}++instance ToLisp Char where+  toLisp c = String (T.singleton c)+  {-# INLINE toLisp #-}++instance FromLisp Char where+  parseLisp (String t)+    | T.compareLength t 1 == EQ = pure (T.head t)+  parseLisp e = typeMismatch "String" e+  {-# INLINE parseLisp #-}+ instance ToLisp Integer where   toLisp n = Number (fromInteger n)+  {-# INLINE toLisp #-}  instance FromLisp Integer where   parseLisp = parseIntegral+  {-# INLINE parseLisp #-}  instance ToLisp Int where   toLisp n = Number (fromIntegral n)+  {-# INLINE toLisp #-}  instance FromLisp Int where   parseLisp = parseIntegral+  {-# INLINE parseLisp #-}  instance ToLisp T.Text where   toLisp = String@@ -347,53 +383,210 @@ instance FromLisp T.Text where   parseLisp (String t) = pure t   parseLisp e = typeMismatch "Text" e+  {-# INLINE parseLisp #-}  instance ToLisp () where   toLisp () = List []+  {-# INLINE toLisp #-}  instance FromLisp () where   parseLisp e | isNull e = pure ()               | otherwise = typeMismatch "()" e+  {-# INLINE parseLisp #-}  instance ToLisp a => ToLisp (Maybe a) where   toLisp Nothing = nil   toLisp (Just a) = toLisp a+  {-# INLINE toLisp #-}  instance FromLisp a => FromLisp (Maybe a) where   parseLisp e | isNull e = pure Nothing   parseLisp e = Just <$> parseLisp e+  {-# INLINE parseLisp #-} -{- --- TESTS -----------------------------------------------------data Msg = Msg T.Text Integer-  deriving (Eq, Show)+-- | No tag is used, hence type @a@ and @b@ must be different.+instance (ToLisp a, ToLisp b) => ToLisp (Either a b) where+  toLisp (Left a) = toLisp a+  toLisp (Right b) = toLisp b+  {-# INLINE toLisp #-} -instance ToLisp Msg where-  toLisp (Msg t n) = mkStruct "msg" [toLisp t, toLisp n]+-- | Tries to parse @a@ or, if that fails, parses a @b@.+instance (FromLisp a, FromLisp b) => FromLisp (Either a b) where+  parseLisp e = Left <$> parseLisp e <|> Right <$> parseLisp e+  {-# INLINE parseLisp #-} -instance FromLisp Msg where-  parseLisp e = struct "msg" Msg e+instance ToLisp [Char] where+  toLisp s = String (T.pack s)+  {-# INLINE toLisp #-} +instance FromLisp [Char] where+  parseLisp (String t) = pure (T.unpack t)+  parseLisp e = typeMismatch "String" e+  {-# INLINE parseLisp #-} -test_sexp1 = -  show (List [Number 42.2, Symbol "foo", "blah"]) == "(42.2 foo \"blah\")"+instance ToLisp Double where+  toLisp = Number . D+  {-# INLINE toLisp #-} -test_msg1 = toLisp (Msg "foo" 42)-test_msg2 = List [Symbol "msg"]-test_msg3 = List [Symbol "msg", "bar", "baz"]+instance FromLisp Double where+  parseLisp (Number n) =+    case n of+      D d -> pure d+      I i -> pure (fromIntegral i)+  parseLisp e | isNull e = pure (0/0)  -- useful?+  parseLisp e = typeMismatch "Double" e+  {-# INLINE parseLisp #-} -test_parse :: IO ()-test_parse = do-  mapM_ (\inp ->-           putStrLn $ show inp ++ " => " ++ show (A.parseOnly (lisp <* A.endOfInput) inp))-    inputs- where-  inputs = ["()", "42", "(4 5 6)", "(3 (4))", "(3(4))",-            "\"foo\"", "foo", "(foo \"bar\" 23)"]+instance ToLisp Float where+  toLisp = Number . fromRational . toRational+  {-# INLINE toLisp #-} +instance FromLisp Float where+  parseLisp (Number n) =+    case n of+      D d -> pure (fromRational (toRational d))+      I i -> pure (fromIntegral i)+  parseLisp e | isNull e = pure (0/0)  -- useful?+  parseLisp e = typeMismatch "Float" e+  {-# INLINE parseLisp #-} +instance ToLisp Number where+  toLisp = Number+  {-# INLINE toLisp #-} --- -}+instance FromLisp Number where+  parseLisp (Number n) = pure n+  parseLisp e | isNull e = pure (D (0/0))  -- useful?+  parseLisp e = typeMismatch "Number" e+  {-# INLINE parseLisp #-} +instance ToLisp (Ratio Integer) where+  toLisp = Number . fromRational+  {-# INLINE toLisp #-}++instance FromLisp (Ratio Integer) where+  parseLisp (Number n) =+    case n of+      D d -> pure (toRational d)+      I i -> pure (fromIntegral i)+  parseLisp e = typeMismatch "Ratio Integer" e+  {-# INLINE parseLisp #-}++instance ToLisp Int8 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Int8 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Int16 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Int16 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Int32 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Int32 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Int64 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Int64 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Word where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Word where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Word8 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Word8 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Word16 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Word16 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Word32 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Word32 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp Word64 where+  toLisp = Number . fromIntegral+  {-# INLINE toLisp #-}++instance FromLisp Word64 where+  parseLisp = parseIntegral+  {-# INLINE parseLisp #-}++instance ToLisp a => ToLisp [a] where+  toLisp l = List (map toLisp l)+  {-# INLINE toLisp #-}++instance FromLisp a => FromLisp [a] where+  parseLisp (List l) = mapM parseLisp l+  parseLisp e = typeMismatch "list" e+  {-# INLINE parseLisp #-}++instance (ToLisp a, ToLisp b) => ToLisp (a, b) where+  toLisp (a, b) = List [toLisp a, toLisp b]  -- TODO: could use dotted list+  {-# INLINE toLisp #-}++instance (FromLisp a, FromLisp b) => FromLisp (a, b) where+  parseLisp (List l) =+    case l of+      [a, b] -> (,) <$> parseLisp a <*> parseLisp b+      _ -> fail $ "Cannot unpack list into a pair"+  parseLisp (DotList hds b) =+    case hds of+      [a] -> (,) <$> parseLisp a <*> parseLisp b+      _ -> fail $ "Cannot unpack dotted list into a pair"+  parseLisp e = typeMismatch "pair" e+  {-# INLINE parseLisp #-}++instance (ToLisp a, ToLisp b, ToLisp c) => ToLisp (a, b, c) where+  toLisp (a, b, c) = List [toLisp a, toLisp b, toLisp c]+  {-# INLINE toLisp #-}++instance (FromLisp a, FromLisp b, FromLisp c) => FromLisp (a, b, c) where+  parseLisp (List l) =+    case l of+      [a, b, c] -> (,,) <$> parseLisp a <*> parseLisp b <*> parseLisp c+      _ -> fail $ "Cannot unpack list into a 3-tuple"+  parseLisp e = typeMismatch "3-tuple" e+  {-# INLINE parseLisp #-}++instance (ToLisp a, ToLisp b) => ToLisp (M.Map a b) where+  toLisp mp = toLisp [ (toLisp k, toLisp v) | (k,v) <- M.toList mp ]++instance (Ord a, FromLisp a, FromLisp b) => FromLisp (M.Map a b) where+  parseLisp e = M.fromList <$> parseLisp e+ {-  We are using the standard Common Lisp read table.@@ -413,10 +606,13 @@  -- | Parse an arbitrary lisp expression. lisp :: A.Parser Lisp-lisp = skipSpace *>+lisp = skipLispSpace *>   (char '(' *> list_ <|>+   quoted <$> (char '\'' *> char '(' *> list_) <|>    String <$> (char '"' *> lstring_) <|>    atom)+ where+  quoted l = List [Symbol "quote", l]  -- | Parse a symbol or a number.  Symbols are expected to be utf8. --@@ -441,8 +637,8 @@  list_ :: A.Parser Lisp list_ = do-  skipSpace-  elems <- (lisp `sepBy` skipSpace) <* char ')'+  skipLispSpace+  elems <- (lisp `sepBy` skipLispSpace) <* skipLispSpace <* char ')'   return (List elems)  doubleQuote :: Word8@@ -453,7 +649,15 @@ backslash = 92 {-# INLINE backslash #-} +skipLispSpace :: A.Parser ()+skipLispSpace = skipSpace >> optional comment >> skipSpace +comment :: A.Parser ()+comment = do+  _ <- char ';' >> A.many (notChar '\n')+  end <- atEnd+  if end then char '\n' >> return () else return ()+ -- | Parse a string without a leading quote. lstring_ :: A.Parser T.Text lstring_ = {-# SCC "jstring_" #-} do@@ -494,8 +698,8 @@       else rest   mapping = "\"\\/\n\t\b\r\f" -fromLisp :: Lisp -> Blaze.Builder-fromLisp (String str) = string str+fromLispExpr :: Lisp -> Blaze.Builder+fromLispExpr (String str) = string str  where    string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'    quote q =@@ -513,22 +717,22 @@         | c < '\x20' = Blaze.fromString $ "\\x" ++ replicate (2 - length h) '0' ++ h         | otherwise  = fromChar c         where h = showHex (fromEnum c) "" -fromLisp (Symbol t) = Blaze.fromText t-fromLisp (Number n) = fromNumber n-fromLisp (List []) = Blaze.fromByteString "nil"-fromLisp (List l) = enc_list l (fromChar ')')-fromLisp (DotList l t) =-  enc_list l (Blaze.fromByteString " . " `mappend` fromLisp t `mappend` fromChar ')')+fromLispExpr (Symbol t) = Blaze.fromText t+fromLispExpr (Number n) = fromNumber n+fromLispExpr (List []) = Blaze.fromByteString "nil"+fromLispExpr (List l) = enc_list l (fromChar ')')+fromLispExpr (DotList l t) =+  enc_list l (Blaze.fromByteString " . " `mappend` fromLispExpr t `mappend` fromChar ')')  enc_list :: [Lisp] -> Blaze.Builder -> Blaze.Builder enc_list [] tl = fromChar '(' `mappend` tl-enc_list (x:xs) tl = fromChar '(' `mappend` fromLisp x `mappend` foldr f tl xs- where f e t = fromChar ' ' `mappend` fromLisp e `mappend` t+enc_list (x:xs) tl = fromChar '(' `mappend` fromLispExpr x `mappend` foldr f tl xs+ where f e t = fromChar ' ' `mappend` fromLispExpr e `mappend` t  fromNumber :: Number -> Blaze.Builder fromNumber (I i) = integral i fromNumber (D d) = double d  encode :: ToLisp a => a -> Lazy.ByteString-encode = Blaze.toLazyByteString . fromLisp . toLisp+encode = Blaze.toLazyByteString . fromLispExpr . toLisp {-# INLINE encode #-}
atto-lisp.cabal view
@@ -1,5 +1,5 @@ name:                   atto-lisp-version:                0.1+version:                0.2 license:                BSD3 license-file:           LICENSE author:                 Thomas Schilling <nominolo@googlemail.com>@@ -15,11 +15,12 @@  library   build-depends:-    attoparsec    >= 0.8.5.1 && < 0.9,+    attoparsec    >= 0.8.5.1 && < 0.10,     base          >= 4.2     && < 5,     blaze-builder >= 0.3     && < 0.4,-    blaze-textual >= 0.1     && < 0.2,+    blaze-textual >= 0.1     && < 0.3,     bytestring    >= 0.9     && < 0.10,+    containers    >= 0.3     && < 0.5,     deepseq       >= 1.1     && < 1.2,     text          >= 0.10    && < 0.12