tnet (empty) → 0.0.1
raw patch · 4 files changed
+328/−0 lines, 4 filesdep +attoparsecdep +basedep +bytestringsetup-changed
Dependencies added: attoparsec, base, bytestring, utf8-string
Files
- LICENSE +13/−0
- Setup.hs +2/−0
- TNET.hs +296/−0
- tnet.cabal +17/−0
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright 2012 Google Inc.++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ TNET.hs view
@@ -0,0 +1,296 @@+{-# LANGUAGE OverloadedStrings, FlexibleInstances, TypeSynonymInstances,+ OverlappingInstances #-}+-- | 'TNET' is a library that implements the TNET serialization protocol+-- to be used for PGI+-- (<http://code.google.com/p/polyweb/source/browse/doc/PGI.txt>)+-- applications. The TNET protocol+-- (<http://tnetstrings.org>) is designed to be simple to implement in+-- any language, please look at the README for the changes to the+-- original tnetstrings spec.+module TNET+ (+ -- * TNET Parser+ tnetParser+ -- * Classes+ , TNET(..)+ -- * Types+ , TValue+ -- * TNET serialization functions+ , decode+ , encode+ -- * Helpers for defining TNET datatypes+ , dict+ , (.=)+ , (.:)+ ) where++import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.UTF8 as U++import Data.Attoparsec as P+import Data.Attoparsec.Char8 (char8, endOfLine)+import qualified Data.Attoparsec.Char8 as P8++import Control.Applicative+import Data.Word (Word8)+import Data.Char+import Control.Arrow (second)++-- | A 'TValue' represents a raw TNET object. TNET values+-- are one of the following types:+--+-- * a string of bytes+--+-- * a UTF-8 encoded string+--+-- * an integer+--+-- * a floating point number+--+-- * a boolean+--+-- * null+--+-- * a dictionary type+--+-- * a list of TValues+data TValue = TBytes ByteString+ | TString String+ | TInteger Integer+ | TFloat Double+ | TBool Bool+ | TNull+ | TDict [(String, TValue)]+ | TList [TValue]+ deriving (Eq, Show)++-- | Used to create a TNET dictionary from+-- TNET values. Meant to be used with the+-- '.=' operator as in the following example:+--+-- > myDict = dict [ "a" .= 5+-- > , "is_dict" .= True+-- > ]+dict :: [(String, TValue)] -> TValue+dict = TDict++(.=) :: (TNET a) => String -> a -> (String, TValue)+key .= val = (key, toTNET val)++-- | Helper function to extract TNET values from+-- a TNET dictionary. Meant to be used as in the+-- following example:+--+-- > data Person = Person {+-- > name :: String+-- > , age :: Integer+-- > }+-- > personFromDict :: TValue -> Maybe Person+-- > personFromDict tdict = do+-- > name <- tdict .: "name"+-- > age <- tdict .: "age"+-- > return $ Person name age+(.:) :: TNET a => TValue -> String -> Maybe a+(TDict xs) .: key = let lkup = lookup key xs in+ case lkup of+ Nothing -> fromTNET TNull+ Just v -> fromTNET v+_ .: _ = Nothing++-- | The 'TNET' typeclass represents types that+-- can be encoded and decoded in the TNET format.+-- An example instance:+--+-- > data Person = Person {+-- > name :: String+-- > , age :: Integer+-- > }+-- > instance TNET Person where+-- > toTNET (Person n a) = dict [ "name" .= n+-- > , "age" .= a+-- > ]+-- > fromTNET tval = do+-- > n <- tval .: "name"+-- > a <- tval .: "age"+-- > return $ Person n a+class TNET a where+ toTNET :: a -> TValue+ fromTNET :: TValue -> Maybe a++instance TNET () where+ toTNET = const TNull+ fromTNET TNull = Just ()+ fromTNET _ = Nothing++instance TNET TValue where+ toTNET = id+ fromTNET = Just++instance TNET Integer where+ toTNET = TInteger+ fromTNET (TInteger n) = Just n+ fromTNET _ = Nothing++instance TNET Double where+ toTNET = TFloat+ fromTNET (TFloat n) = Just n+ fromTNET _ = Nothing++instance TNET Bool where+ toTNET = TBool+ fromTNET (TBool b) = Just b+ fromTNET _ = Nothing++instance TNET a => TNET [(String, a)] where+ toTNET = TDict . map (second toTNET)+ fromTNET (TDict d) = mapM (f . second fromTNET) d+ where f (s, Just x) = Just (s, x)+ f _ = Nothing+ fromTNET _ = Nothing++instance TNET String where+ toTNET = TString+ fromTNET (TString s) = Just s+ fromTNET _ = Nothing++instance TNET Char where+ toTNET = TString . (:[])+ fromTNET (TString [c]) = Just c+ fromTNET _ = Nothing++instance TNET ByteString where+ toTNET = TBytes+ fromTNET (TBytes b) = Just b+ fromTNET _ = Nothing++instance TNET a => TNET (Maybe a) where+ toTNET = maybe TNull toTNET+ fromTNET TNull = Just Nothing+ fromTNET x = case fromTNET x of+ Just val -> Just $ Just val+ Nothing -> Nothing++instance TNET a => TNET [a] where+ toTNET = TList . map toTNET+ fromTNET (TList xs) = mapM fromTNET xs+ fromTNET _ = Nothing++dataFromTValue :: TValue -> ByteString+dataFromTValue (TBytes bs) = bs+dataFromTValue (TString s) = U.fromString s+dataFromTValue (TBool True) = "true"+dataFromTValue (TBool False) = "false"+dataFromTValue TNull = ""+dataFromTValue (TInteger n) = (B8.pack . show) n+dataFromTValue (TFloat n) = (B8.pack . show) n+dataFromTValue (TList l) = B8.concat $ map dumpTNET l+dataFromTValue (TDict m) = B8.concat $ map dumpPair m++typeFromTValue :: TValue -> ByteString+typeFromTValue (TBytes _) = ","+typeFromTValue (TString _) = "$"+typeFromTValue (TBool _) = "!"+typeFromTValue TNull = "~"+typeFromTValue (TInteger _) = "#"+typeFromTValue (TFloat _) = "^"+typeFromTValue (TList _) = "]"+typeFromTValue (TDict _) = "}"++dumpPair :: (String, TValue) -> ByteString+dumpPair (key, value) =+ dumpTNET (TString key) `B8.append` dumpTNET value++dumpTNET :: TValue -> ByteString+dumpTNET tval = let t_data = dataFromTValue tval+ t_size = B8.pack . show $ B8.length t_data+ t_type = typeFromTValue tval+ in B8.concat [t_size, ":", t_data, t_type]++-- | Encode a Haskell value into the TNET format.+-- Some examples:+--+-- >>> encode 5+-- "1:5#"+--+-- >>> encode "Hello"+-- "5:Hello$"+--+-- >>> encode (-12.3)+-- "5:-12.3^"+--+-- >>> encode ()+-- "0:~"+encode :: TNET a => a -> ByteString+encode = dumpTNET . toTNET++-- | Decode a TNET format bytestring into a Haskell+-- value. An explicit type annotation may be needed+-- if the type of the decoded value can not be+-- determined:+--+-- >>> decode "0:~" :: Maybe ()+-- Just ()+--+-- >>> decode "0:~" :: Maybe (Maybe String)+-- Just Nothing+--+-- >>> decode "1:5#" :: Maybe Integer+-- Just 5+--+-- > let x = decode "4:true!" in+-- > case x of+-- > Just True -> putStrLn "got true!"+-- > Just False -> putStrLn "got false!"+-- > Nothing -> putStrLn "error decoding"+decode :: TNET a => ByteString -> Maybe a+decode s = case parse tnetParser s of+ Done _ v -> fromTNET v+ _ -> Nothing++tnetParser :: Parser TValue+tnetParser = do+ size <- P8.decimal <* char8 ':'+ t_data <- P.take size+ t_type <- P8.satisfy $ P8.inClass ",$#^!~}]?"+ case t_type of+ ',' -> return $ TBytes t_data+ '$' -> return $ TString $ U.toString t_data+ '~' -> if size == 0 then return TNull+ else empty+ '!' -> case parseOnly p_bool t_data of+ Left err -> fail "failed to parse bool"+ Right b -> return b+ '?' -> case parseOnly p_smallbool t_data of+ Left err -> fail "failed to parse bool"+ Right b -> return b+ '#' -> case parseOnly (P8.signed P8.decimal) t_data of+ Left err -> fail "failed to parse integer"+ Right n -> return $ TInteger n+ '^' -> case parseOnly (P8.signed P8.double) t_data of+ Left err -> fail "failed to parse float"+ Right n -> return $ TFloat n+ '}' -> case parseOnly (many p_pair) t_data of+ Left err -> fail "failed to parse dict"+ Right m -> return $ TDict m+ ']' -> case parseOnly (many tnetParser) t_data of+ Left err -> fail "failed to parse list"+ Right m -> return $ TList m+ _ -> empty++p_bool :: Parser TValue+p_bool = (return (TBool True) <* P8.string "true")+ <|> return (TBool False)++p_smallbool :: Parser TValue+p_smallbool = (return (TBool True) <* P8.string "t")+ <|> return (TBool False)++p_pair :: Parser (String, TValue)+p_pair = do+ key <- tnetParser+ value <- tnetParser+ case key of+ TString s -> return (s, value)+ TBytes s -> return (U.toString s, value)+ _ -> fail "key must be string"
+ tnet.cabal view
@@ -0,0 +1,17 @@+Name: tnet+Version: 0.0.1+Synopsis: Library for encoding/decoding TNET strings for PGI+Description:+ Provides an API to encode/decode Haskell datatypes into the+ TNET protocol.+Author: Harry Terkelsen+Maintainer: het32@cornell.edu+Cabal-Version: >= 1.2+Build-Type: Simple+Category: Web+License: OtherLicense+License-File: LICENSE+library+ Exposed-Modules: TNET+ Build-Depends: base >= 4.0 && < 5, attoparsec >= 0.10, bytestring >= 0.9,+ utf8-string >= 0.3