packages feed

sexpr 0.1.1 → 0.1.2

raw patch · 4 files changed

+234/−55 lines, 4 filesdep +binarydep +bytestringPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: binary, bytestring

API changes (from Hackage documentation)

- Codec.Sexpr: instance Eq Sexpr
- Codec.Sexpr.Parser: instance Read Sexpr
- Codec.Sexpr.Printer: instance Show Sexpr
+ Codec.Sexpr: instance (Eq s) => Eq (Sexpr s)
+ Codec.Sexpr: instance Foldable Sexpr
+ Codec.Sexpr: instance Functor Sexpr
+ Codec.Sexpr: instance Traversable Sexpr
+ Codec.Sexpr.Parser: instance (Read s) => Read (Sexpr s)
+ Codec.Sexpr.Parser: readSexprString :: String -> Sexpr String
+ Codec.Sexpr.Printer: instance [overlap ok] (Show s) => Show (Sexpr s)
+ Codec.Sexpr.Printer: instance [overlap ok] Show (Sexpr String)
+ Codec.Sexpr.Printer: putCanonical :: Sexpr String -> Put
+ Codec.Sexpr.Printer: putCanonicalBS :: Sexpr ByteString -> Put
- Codec.Sexpr: atom :: String -> Sexpr
+ Codec.Sexpr: atom :: a -> Sexpr a
- Codec.Sexpr: data Sexpr
+ Codec.Sexpr: data Sexpr s
- Codec.Sexpr: defaultHint :: [Char]
+ Codec.Sexpr: defaultHint :: String
- Codec.Sexpr: hint :: Sexpr -> Maybe [Char]
+ Codec.Sexpr: hint :: Sexpr a -> Maybe String
- Codec.Sexpr: hintedAtom :: [Char] -> String -> Sexpr
+ Codec.Sexpr: hintedAtom :: String -> a -> Sexpr a
- Codec.Sexpr: isAtom :: Sexpr -> Bool
+ Codec.Sexpr: isAtom :: Sexpr a -> Bool
- Codec.Sexpr: isList :: Sexpr -> Bool
+ Codec.Sexpr: isList :: Sexpr a -> Bool
- Codec.Sexpr: list :: [Sexpr] -> Sexpr
+ Codec.Sexpr: list :: [Sexpr a] -> Sexpr a
- Codec.Sexpr: unAtom :: Sexpr -> String
+ Codec.Sexpr: unAtom :: Sexpr s -> s
- Codec.Sexpr: unList :: Sexpr -> [Sexpr]
+ Codec.Sexpr: unList :: Sexpr s -> [Sexpr s]
- Codec.Sexpr.Parser: canonicalSexpr :: ReadP Sexpr
+ Codec.Sexpr.Parser: canonicalSexpr :: ReadP (Sexpr String)
- Codec.Sexpr.Parser: readSexpr :: String -> Sexpr
+ Codec.Sexpr.Parser: readSexpr :: (Read a) => String -> Sexpr a
- Codec.Sexpr.Parser: sexpr :: ReadP Sexpr
+ Codec.Sexpr.Parser: sexpr :: ReadP (Sexpr String)
- Codec.Sexpr.Printer: advanced :: Sexpr -> Doc
+ Codec.Sexpr.Printer: advanced :: Sexpr String -> Doc
- Codec.Sexpr.Printer: advancedString :: Sexpr -> String
+ Codec.Sexpr.Printer: advancedString :: Sexpr String -> String
- Codec.Sexpr.Printer: basic :: Sexpr -> Doc
+ Codec.Sexpr.Printer: basic :: Sexpr String -> Doc
- Codec.Sexpr.Printer: basicString :: Sexpr -> String
+ Codec.Sexpr.Printer: basicString :: Sexpr String -> String
- Codec.Sexpr.Printer: canonical :: Sexpr -> ShowS
+ Codec.Sexpr.Printer: canonical :: Sexpr String -> ShowS
- Codec.Sexpr.Printer: canonicalString :: Sexpr -> String
+ Codec.Sexpr.Printer: canonicalString :: Sexpr String -> String

Files

Codec/Sexpr.hs view
@@ -1,50 +1,123 @@-module Codec.Sexpr (Sexpr,-                    atom,-                    hintedAtom,-                    list,+-- |A Sexpr is an S-expressionin the style of Rivest's Canonical+-- S-expressions.  Atoms may be of any type, but String and+-- ByteString have special support.  Rivest's implementation of+-- S-expressions is unusual in supporting MIME type hints for each+-- atom.  See http://people.csail.mit.edu/rivest/Sexp.txt++module Codec.Sexpr (-- * Basics+                    Sexpr,                     isAtom,                     isList,-                    hint,-                    defaultHint,+                    atom,+                    list,                     unAtom,                     unList,+                    -- * Hinted Atoms+                    hintedAtom,+                    hint,+                    defaultHint,+                    -- * Character predicates to support encoding                     isTokenChar,isInitialTokenChar,isQuoteableChar                    ) where +import Control.Applicative import Data.Char+import qualified Data.Foldable as F+import Data.Traversable -data Sexpr = Atom String-           | HintedAtom String String-           | List [Sexpr] +data Sexpr s = Atom s+             | HintedAtom String s+             | List [Sexpr s]  -instance Eq Sexpr where+instance Eq s => Eq (Sexpr s) where     (List a) == (List b) = and $ zipWith (==) a b     a == b = unAtom a == unAtom b && hint a == hint b +-- |The 'Functor' instance maps over the underlying atomic contents of+-- the S-expression.  It does not map only over the top-level list,+-- but over the fringe.+instance Functor Sexpr where+    fmap f (Atom s) = Atom (f s)+    fmap f (HintedAtom h s) = HintedAtom h (f s)+    fmap f (List ss) = List $ map (fmap f) ss++instance F.Foldable Sexpr where+    foldMap f (Atom s) = f s+    foldMap f (HintedAtom h s) = f s+    foldMap f (List ss) = F.foldMap (F.foldMap f) ss++instance Traversable Sexpr where+    traverse f (Atom s) = Atom <$> f s+    traverse f (HintedAtom h s) = HintedAtom h <$> f s+    traverse f (List ss) = List <$> traverse (traverse f) ss++-- |@fold f s@ applies f to each sub-S-expression of s, from each leaf+-- to the root.  @f@ need not preserve the shape of @s@, in contrast+-- to the shape-preserving @Traversable@ instance.+fold :: (Sexpr t -> Sexpr t) -> Sexpr t -> Sexpr t+fold f z@(Atom s) = f z+fold f z@(HintedAtom h s) = f z+fold f   (List ss) = f . List $ map (fold f) ss++-- |Any atom whose hint is not specified is assumed to be +-- "text/plain; charset=iso-8859-1".  This is that default value.+defaultHint :: String defaultHint = "text/plain; charset=iso-8859-1" +-- |Construct an atom.+atom :: a -> Sexpr a atom s = Atom s++-- |Construct a list.+list :: [Sexpr a] -> Sexpr a list xs = List xs++-- |Construct an atom with a MIME type hint.+-- @'hintedAtom' 'defaultHint' == 'atom'@+hintedAtom :: String -> a -> Sexpr a hintedAtom h s | h == defaultHint = Atom s hintedAtom h s = HintedAtom h s +-- |A predicate for recognizing lists.+isList :: Sexpr a -> Bool isList (List _) = True isList _ = False +-- |A predicate for identifying atoms, whether or not they have+-- explicit hints.+isAtom :: Sexpr a -> Bool isAtom (List _) = False isAtom _ = True +-- |Extract the hint of an atom.  Lists do not have hints, but all+-- atoms have hints.+hint :: Sexpr a -> Maybe String hint (Atom s) = Just defaultHint hint (HintedAtom h s) = Just h hint _ = Nothing +-- |Extract the content of an atom, discarding any MIME type hint.+unAtom :: Sexpr s -> s unAtom (Atom s) = s unAtom (HintedAtom h s) = s +-- |Extract the sub-S-expressions of a List.  If all you intend to do+-- is traverse or map over that list, the Functor instance of+-- S-expressions may work just fine.+unList :: Sexpr s -> [Sexpr s] unList (List xs) = xs +-- |Tokens may begin with any alphabetic character or the characters+-- in @"-./_:*+="@ ;+isInitialTokenChar :: Char -> Bool isInitialTokenChar x = isAlpha x || x `elem` "-./_:*+=" +-- |Tokens may internally contain any of the characters legitimate to+-- begin tokens, or any numeral.+isTokenChar :: Char -> Bool isTokenChar x = isAlphaNum x || x `elem` "-./_:*+=" +-- |Only token characters and spaces don't need to be escaped when+-- shown in the "quoted" syntax.+isQuoteableChar :: Char -> Bool isQuoteableChar x = isTokenChar x || isSpace x
Codec/Sexpr/Parser.hs view
@@ -1,62 +1,100 @@+-- |All present parsers work on Strings, one character at a time.  The canonical encoding is clearly susceptible to efficient parsing as a Lazy ByteString.+-- +-- This package also includes the Read instance for Sexprs.+-- +-- From Rivest's documentation:+-- +-- > <sexpr>    	:: <string> | <list>+-- > <string>   	:: <display>? <simple-string> ;+-- > <simple-string>	:: <raw> | <token> | <base-64> | <hexadecimal> | +-- > 		           <quoted-string> ;+-- > <display>  	:: "[" <simple-string> "]" ;+-- > <raw>      	:: <decimal> ":" <bytes> ;+-- > <decimal>  	:: <decimal-digit>+ ;+-- > 		-- decimal numbers should have no unnecessary leading zeros+-- > <bytes> 	-- any string of bytes, of the indicated length+-- > <token>    	:: <tokenchar>+ ;+-- > <base-64>  	:: <decimal>? "|" ( <base-64-char> | <whitespace> )* "|" ;+-- > <hexadecimal>      :: "#" ( <hex-digit> | <white-space> )* "#" ;+-- > <quoted-string>    :: <decimal>? <quoted-string-body>  +-- > <quoted-string-body> :: "\"" <bytes> "\""+-- > <list>     	:: "(" ( <sexp> | <whitespace> )* ")" ;+-- > <whitespace> 	:: <whitespace-char>* ;+-- > <token-char>  	:: <alpha> | <decimal-digit> | <simple-punc> ;+-- > <alpha>       	:: <upper-case> | <lower-case> | <digit> ;+-- > <lower-case>  	:: "a" | ... | "z" ;+-- > <upper-case>  	:: "A" | ... | "Z" ;+-- > <decimal-digit>    :: "0" | ... | "9" ;+-- > <hex-digit>        :: <decimal-digit> | "A" | ... | "F" | "a" | ... | "f" ;+-- > <simple-punc> 	:: "-" | "." | "/" | "_" | ":" | "*" | "+" | "=" ;+-- > <whitespace-char>  :: " " | "\t" | "\r" | "\n" ;+-- > <base-64-char> 	:: <alpha> | <decimal-digit> | "+" | "/" | "=" ;+-- > <null>        	:: "" ;+ module Codec.Sexpr.Parser (readSexpr,+                           readSexprString,                            sexpr,                            canonicalSexpr) where  import Codec.Sexpr +-- import Data.Binary.Get+-- import Data.ByteString+ import Data.Char import Text.ParserCombinators.ReadP import qualified Codec.Binary.Base64.String as B64 -{--From Rivest's documentation: -<sexpr>    	:: <string> | <list>-<string>   	:: <display>? <simple-string> ;-<simple-string>	:: <raw> | <token> | <base-64> | <hexadecimal> | -		           <quoted-string> ;-<display>  	:: "[" <simple-string> "]" ;-<raw>      	:: <decimal> ":" <bytes> ;-<decimal>  	:: <decimal-digit>+ ;-		-- decimal numbers should have no unnecessary leading zeros-<bytes> 	-- any string of bytes, of the indicated length-<token>    	:: <tokenchar>+ ;-<base-64>  	:: <decimal>? "|" ( <base-64-char> | <whitespace> )* "|" ;-<hexadecimal>   :: "#" ( <hex-digit> | <white-space> )* "#" ;-<quoted-string> :: <decimal>? <quoted-string-body>  -<quoted-string-body> :: "\"" <bytes> "\""-<list>     	:: "(" ( <sexp> | <whitespace> )* ")" ;-<whitespace> 	:: <whitespace-char>* ;-<token-char>  	:: <alpha> | <decimal-digit> | <simple-punc> ;-<alpha>       	:: <upper-case> | <lower-case> | <digit> ;-<lower-case>  	:: "a" | ... | "z" ;-<upper-case>  	:: "A" | ... | "Z" ;-<decimal-digit> :: "0" | ... | "9" ;-<hex-digit>     :: <decimal-digit> | "A" | ... | "F" | "a" | ... | "f" ;-<simple-punc> 	:: "-" | "." | "/" | "_" | ":" | "*" | "+" | "=" ;-<whitespace-char> :: " " | "\t" | "\r" | "\n" ;-<base-64-char> 	:: <alpha> | <decimal-digit> | "+" | "/" | "=" ;-<null>        	:: "" ;--}+instance Read s => Read (Sexpr s) where+    readsPrec n s = map (\(a,b) -> (fmap read a, b)) s'+        where +          s' = readP_to_S sexpr s :: [(Sexpr String,String)] -instance Read Sexpr where-    readsPrec n = readP_to_S sexpr+-- |Read a @'Sexpr' 'String'@ in any encoding: Canonical, Basic, or Advanced.+readSexprString :: String -> Sexpr String+readSexprString s = fst . head $ readP_to_S sexpr s -readSexpr s = fst . head $ readP_to_S sexpr s+-- |Read a @'Sexpr' a@ using the 'Read' instance for @a@.  The Sexpr+-- may be in any encoding: Canonical, Basic, or Advanced.+readSexpr :: Read a => String -> Sexpr a+readSexpr = fmap read . readSexprString +-- |Parser for @'Sexpr' 'String'@s suitable for embedding in other +-- @ReadP@ parsers.+sexpr :: ReadP (Sexpr String) sexpr = do   skipSpaces   s <- canonicalSexpr   skipSpaces   return s -canonicalSexpr :: ReadP Sexpr+{-+getCanonicalAtom :: Get (Sexpr ByteString)+getCanonicalAtom = do+  l <- getDecimal+  skip 1 -- ':'+  s <- getLazyByteString l -- FIXME doesn't handle hints+  return $ atom s++getCanonicalList :: Get S+getCanonicalList = do+  skip 1 -- '('+  -- FIXME mostly missing+-}++-- |For some applications it is wise to accept only very carefully+-- specified input.  This is useful when you know you are receiving+-- exactly a Canonical S-Expression.  It will read only a Canonical+-- S-expression (and optional terminating NUL), but not the Basic or+-- Advanced encodings.+canonicalSexpr :: ReadP (Sexpr String) canonicalSexpr = do   s <- atomR <++ listR <++ basicTransport   optional $ char '\NUL'   return s -basicTransport :: ReadP Sexpr+basicTransport :: ReadP (Sexpr String) basicTransport = do   b64Octets <- between (char '{') (char '}') $ many1 b64char   let parses = readP_to_S sexpr $ B64.decode b64Octets@@ -68,12 +106,12 @@ hexchar = satisfy isHexDigit hexchar' = skipSpaces >> hexchar -listR :: ReadP Sexpr+listR :: ReadP (Sexpr String) listR = do   l <- between (char '(') (char ')') $ many sexpr   return $ list l -atomR :: ReadP Sexpr+atomR :: ReadP (Sexpr String) atomR = unhinted +++ hinted   where      unhinted = simpleString >>= (return . atom)
Codec/Sexpr/Printer.hs view
@@ -1,23 +1,55 @@-module Codec.Sexpr.Printer (canonical, canonicalString,-                            basic, basicString,-                            advanced, advancedString+{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}++-- | Export S-expressions in any of the three ordinary forms: +-- +-- * Canonical, where a different string implies a different meaning+-- +-- * Basic, suitable for transport over 7-bit and awkward media+-- +-- * Advanced, a human-readable pretty-printed encoding.+-- +-- The @-> 'String'@ functions are probably what you want unless you+-- know you want something else.+-- +-- The 'Show' instance for Sexpr is provided here, using+-- advancedString and the underlying Show instance.  Overlapping+-- instances are used to provide a nice show for @'Sexpr' 'String'@.++module Codec.Sexpr.Printer (-- * String printers+                            canonicalString,+                            basicString,+                            advancedString,+                            -- * ShowS printers+                            canonical,+                            -- * Doc pretty printers+                            basic, +                            advanced, +                            -- * Put binary printers+                            putCanonical, putCanonicalBS                            ) where  import Codec.Sexpr +import Data.Binary.Put+import qualified Data.ByteString.Char8 as B+ import Data.Char import Data.Maybe import Text.PrettyPrint import qualified Codec.Binary.Base64.String as B64 -instance Show Sexpr where+instance Show (Sexpr String) where     show s = advancedString s +instance Show s => Show (Sexpr s) where+    show s = advancedString $ fmap show s+ raw s = shows (length s) . showString ":" . showString s +canonicalString :: Sexpr String -> String canonicalString s = canonical s "" -canonical :: Sexpr -> ShowS+canonical :: Sexpr String -> ShowS canonical s | isAtom s && hint s == Just defaultHint = raw $ unAtom s canonical s | isAtom s = showString "["                           . raw (fromJust $ hint s)@@ -27,13 +59,48 @@                  . showString (foldr (.) id (map canonical $ unList s) $ "")                  . showString ")" +putRaw :: String -> Put+putRaw s = do+  putByteString . B.pack . show $ length s+  putChar' ':'+  putByteString (B.pack s)++putRawBS :: B.ByteString -> Put+putRawBS s = do+  putByteString . B.pack . show $ B.length s+  putChar' ':'+  putByteString s++putChar' = putWord8 . fromIntegral . ord++putCanonical :: Sexpr String -> Put+putCanonical = putCanonicalHelper putRaw++putCanonicalBS :: Sexpr B.ByteString -> Put+putCanonicalBS = putCanonicalHelper putRawBS++putCanonicalHelper :: (a -> Put) -> Sexpr a -> Put+putCanonicalHelper putRaw' s | isAtom s && hint s == +                               Just defaultHint = putRaw' $ unAtom s+putCanonicalHelper putRaw' s | isAtom s = do+  putChar' '['+  putRaw (fromJust $ hint s)+  putChar' ']'+  putRaw' (unAtom s)+putCanonicalHelper putRaw' s | isList s = do+  putChar' '('+  mapM_ (putCanonicalHelper putRaw') $ unList s+  putChar' ')'++basicString :: Sexpr String -> String basicString s = render $ basic s -basic :: Sexpr -> Doc+basic :: Sexpr String -> Doc basic s = braces . hcat $ map char . B64.encode $ canonical s "" -- FIXME should basic add and encode a NUL terminator?--- FIXMIE We parse it out in canonical---should canonical encodings end with a NUL? +-- FIXME We parse it out in canonical---should canonical encodings end with a NUL?  +advancedString :: Sexpr String -> String advancedString s = render $ advanced s  @@ -59,6 +126,7 @@  base64 s = (char '|') <> hcat (map char $ B64.encode s) <> (char '|') +advanced :: Sexpr String -> Doc advanced s | isAtom s && hint s == Just defaultHint = format $ unAtom s advanced s | isAtom s = brackets (format $ fromJust $ hint s)                          <> (format $ unAtom s) 
sexpr.cabal view
@@ -1,5 +1,5 @@ Name:                sexpr-Version:             0.1.1+Version:             0.1.2 Cabal-Version:	     >= 1.2 Synopsis:            S-expression printer and parser  Description:         Parser and printer for S-expressions, including Ron @@ -14,6 +14,6 @@ Build-type: 	     Simple  Library-    Build-Depends: base, base64-string, pretty+    Build-Depends: base, base64-string, pretty, bytestring, binary     Exposed-modules: Codec.Sexpr, Codec.Sexpr.Parser, Codec.Sexpr.Printer