Lots more of a proper Adnot repo
Getty Ritter
8 years ago
| 1 | {-# LANGUAGE OverloadedStrings #-} | |
| 2 | ||
| 3 | module Data.ADTN where | |
| 4 | ||
| 5 | import Control.Applicative((<|>)) | |
| 6 | import Data.Attoparsec.ByteString | |
| 7 | import Data.Attoparsec.ByteString.Char8 | |
| 8 | import Data.ByteString (ByteString) | |
| 9 | import qualified Data.ByteString as BS | |
| 10 | import Data.Map.Strict (Map) | |
| 11 | import qualified Data.Map as M | |
| 12 | import Data.Text (Text) | |
| 13 | import qualified Data.Text as T | |
| 14 | import Data.Vector (Vector) | |
| 15 | import qualified Data.Vector as V | |
| 16 | ||
| 17 | ||
| 18 | data Value | |
| 19 | = Sum Text Array | |
| 20 | | Product Object | |
| 21 | | List Array | |
| 22 | | Integer Integer | |
| 23 | | Double Double | |
| 24 | | Symbol Text | |
| 25 | | String Text | |
| 26 | deriving (Eq, Show) | |
| 27 | ||
| 28 | type Array = Vector Value | |
| 29 | type Object = Map Text Value | |
| 30 | ||
| 31 | decodeValue :: ByteString -> Either String Value | |
| 32 | decodeValue = parseOnly pVal | |
| 33 | where pVal :: Parser Value | |
| 34 | pVal = skipSpace *> (pSum <|> pProd <|> pList <|> pLit) | |
| 35 | pSum = Sum <$> (char '(' *> skipSpace *> pIdent) | |
| 36 | <*> (pValueList <* char ')') | |
| 37 | pProd = Product . M.fromList | |
| 38 | <$> (char '{' *> pProdBody <* skipSpace <* char '}') | |
| 39 | pProdBody = many' pPair | |
| 40 | pPair = (,) <$> (skipSpace *> pIdent) <*> pVal | |
| 41 | pList = List <$> (char '[' *> pValueList <* skipSpace <* char ']') | |
| 42 | pLit = Symbol <$> pIdent | |
| 43 | <|> String <$> pString | |
| 44 | <|> Integer <$> decimal | |
| 45 | pValueList = V.fromList <$> many' pVal | |
| 46 | pIdent = T.pack <$> many1' letter_ascii | |
| 47 | pString = T.pack <$> (char '"' *> manyTill pStrChar (char '"')) | |
| 48 | pStrChar = '\n' <$ string "\\n" | |
| 49 | <|> '\t' <$ string "\\t" | |
| 50 | <|> '\r' <$ string "\\r" | |
| 51 | <|> '\b' <$ string "\\b" | |
| 52 | <|> '\f' <$ string "\\f" | |
| 53 | <|> '\'' <$ string "\\'" | |
| 54 | <|> '\"' <$ string "\\\"" | |
| 55 | <|> '\\' <$ string "\\\\" | |
| 56 | <|> anyChar |
| 1 | {-# LANGUAGE OverloadedStrings #-} | |
| 2 | {-# LANGUAGE OverloadedLists #-} | |
| 3 | {-# LANGUAGE TypeSynonymInstances #-} | |
| 4 | {-# LANGUAGE FlexibleInstances #-} | |
| 5 | {-# LANGUAGE FlexibleContexts #-} | |
| 6 | {-# LANGUAGE TypeOperators #-} | |
| 7 | {-# LANGUAGE ScopedTypeVariables #-} | |
| 8 | {-# LANGUAGE DefaultSignatures #-} | |
| 9 | {-# LANGUAGE DataKinds #-} | |
| 10 | ||
| 11 | module Data.Adnot.Class where | |
| 12 | ||
| 13 | import Data.Adnot.Type | |
| 14 | import Data.Adnot.Emit | |
| 15 | import Data.Int | |
| 16 | import Data.Word | |
| 17 | import qualified Data.ByteString.Lazy as BSL | |
| 18 | import qualified Data.Foldable as F | |
| 19 | import qualified Data.List.NonEmpty as NE | |
| 20 | import qualified Data.Map.Lazy as ML | |
| 21 | import qualified Data.Map.Strict as MS | |
| 22 | import qualified Data.Sequence as Seq | |
| 23 | import qualified Data.Text as T | |
| 24 | import qualified Data.Text.Lazy as TL | |
| 25 | import qualified Data.Vector as V | |
| 26 | import GHC.Generics | |
| 27 | import GHC.TypeLits (KnownSymbol) | |
| 28 | ||
| 29 | encode :: ToAdnot a => a -> BSL.ByteString | |
| 30 | encode = encodeValue . toAdnot | |
| 31 | ||
| 32 | class GenToAdnot f where | |
| 33 | genToAdnot :: f p -> Value | |
| 34 | ||
| 35 | instance (GenToAdnot l, GenToAdnot r) => GenToAdnot (l :+: r) where | |
| 36 | genToAdnot (L1 x) = genToAdnot x | |
| 37 | genToAdnot (R1 y) = genToAdnot y | |
| 38 | ||
| 39 | instance ToAdnot x => GenToAdnot (K1 i x) where | |
| 40 | genToAdnot (K1 x) = toAdnot x | |
| 41 | ||
| 42 | instance (GatherSequence f, Constructor name) => GenToAdnot (C1 name f) where | |
| 43 | genToAdnot (M1 x) = Sum (T.pack (conName c)) (V.fromList (gatherSequence x)) | |
| 44 | where c :: M1 c name f () | |
| 45 | c = undefined | |
| 46 | ||
| 47 | instance (GenToAdnot f) => GenToAdnot (S1 name f) where | |
| 48 | genToAdnot (M1 x) = genToAdnot x | |
| 49 | ||
| 50 | instance (GenToAdnot f) => GenToAdnot (D1 name f) where | |
| 51 | genToAdnot (M1 x) = genToAdnot x | |
| 52 | ||
| 53 | instance (GatherSequence l, GatherSequence r) => GenToAdnot (l :*: r) where | |
| 54 | genToAdnot x = List (V.fromList (gatherSequence x)) | |
| 55 | ||
| 56 | class GatherRecord f where | |
| 57 | gatherRecord :: f p -> [(T.Text, Value)] | |
| 58 | ||
| 59 | instance (GatherRecord l, GatherRecord r) => GatherRecord (l :*: r) where | |
| 60 | gatherRecord (x :*: y) = gatherRecord x ++ gatherRecord y | |
| 61 | ||
| 62 | instance (GenToAdnot f, Selector name) => GatherRecord (S1 name f) where | |
| 63 | gatherRecord (M1 x) = [(T.pack (selName s), genToAdnot x)] | |
| 64 | where s :: S1 name f () | |
| 65 | s = undefined | |
| 66 | ||
| 67 | instance GatherRecord U1 where | |
| 68 | gatherRecord U1 = [] | |
| 69 | ||
| 70 | class GatherSequence f where | |
| 71 | gatherSequence :: f p -> [Value] | |
| 72 | ||
| 73 | instance GatherSequence U1 where | |
| 74 | gatherSequence U1 = [] | |
| 75 | ||
| 76 | instance (GatherSequence l, GatherSequence r) => GatherSequence (l :*: r) where | |
| 77 | gatherSequence (x :*: y) = gatherSequence x ++ gatherSequence y | |
| 78 | ||
| 79 | instance ToAdnot x => GatherSequence (K1 i x) where | |
| 80 | gatherSequence (K1 x) = [toAdnot x] | |
| 81 | ||
| 82 | instance GenToAdnot f => GatherSequence (S1 name f) where | |
| 83 | gatherSequence (M1 x) = [genToAdnot x] | |
| 84 | ||
| 85 | instance GenToAdnot f => GatherSequence (D1 name f) where | |
| 86 | gatherSequence (M1 x) = [genToAdnot x] | |
| 87 | ||
| 88 | instance GenToAdnot U1 where | |
| 89 | genToAdnot U1 = List [] | |
| 90 | ||
| 91 | genericToAdnot :: (GenToAdnot (Rep t), Generic t) => t -> Value | |
| 92 | genericToAdnot x = genToAdnot (from x) | |
| 93 | ||
| 94 | class ToAdnot a where | |
| 95 | toAdnot :: a -> Value | |
| 96 | ||
| 97 | default toAdnot :: (Generic a, GenToAdnot (Rep a)) => a -> Value | |
| 98 | toAdnot = genericToAdnot | |
| 99 | ||
| 100 | -- * Integral types | |
| 101 | instance ToAdnot Int where | |
| 102 | toAdnot n = Integer (fromIntegral n) | |
| 103 | ||
| 104 | instance ToAdnot Integer where | |
| 105 | toAdnot n = Integer n | |
| 106 | ||
| 107 | instance ToAdnot Int8 where | |
| 108 | toAdnot n = Integer (fromIntegral n) | |
| 109 | ||
| 110 | instance ToAdnot Int16 where | |
| 111 | toAdnot n = Integer (fromIntegral n) | |
| 112 | ||
| 113 | instance ToAdnot Int32 where | |
| 114 | toAdnot n = Integer (fromIntegral n) | |
| 115 | ||
| 116 | instance ToAdnot Int64 where | |
| 117 | toAdnot n = Integer (fromIntegral n) | |
| 118 | ||
| 119 | instance ToAdnot Word where | |
| 120 | toAdnot n = Integer (fromIntegral n) | |
| 121 | ||
| 122 | instance ToAdnot Word8 where | |
| 123 | toAdnot n = Integer (fromIntegral n) | |
| 124 | ||
| 125 | instance ToAdnot Word16 where | |
| 126 | toAdnot n = Integer (fromIntegral n) | |
| 127 | ||
| 128 | instance ToAdnot Word32 where | |
| 129 | toAdnot n = Integer (fromIntegral n) | |
| 130 | ||
| 131 | instance ToAdnot Word64 where | |
| 132 | toAdnot n = Integer (fromIntegral n) | |
| 133 | ||
| 134 | -- * Rational/Floating types | |
| 135 | instance ToAdnot Double where | |
| 136 | toAdnot d = Double d | |
| 137 | ||
| 138 | instance ToAdnot Float where | |
| 139 | toAdnot d = Double (fromRational (toRational d)) | |
| 140 | ||
| 141 | -- * String types | |
| 142 | instance {-# INCOHERENT #-} ToAdnot String where | |
| 143 | toAdnot s = String (T.pack s) | |
| 144 | ||
| 145 | instance ToAdnot T.Text where | |
| 146 | toAdnot s = String s | |
| 147 | ||
| 148 | instance ToAdnot TL.Text where | |
| 149 | toAdnot s = String (TL.toStrict s) | |
| 150 | ||
| 151 | instance ToAdnot Char where | |
| 152 | toAdnot c = String (T.singleton c) | |
| 153 | ||
| 154 | -- * List types | |
| 155 | instance ToAdnot a => ToAdnot [a] where | |
| 156 | toAdnot ls = List (fmap toAdnot (V.fromList ls)) | |
| 157 | ||
| 158 | instance ToAdnot a => ToAdnot (V.Vector a) where | |
| 159 | toAdnot ls = List (fmap toAdnot ls) | |
| 160 | ||
| 161 | instance ToAdnot a => ToAdnot (Seq.Seq a) where | |
| 162 | toAdnot ls = List (V.fromList (F.toList (fmap toAdnot ls))) | |
| 163 | ||
| 164 | instance ToAdnot a => ToAdnot (NE.NonEmpty a) where | |
| 165 | toAdnot ls = List (V.fromList (F.toList (fmap toAdnot ls))) | |
| 166 | ||
| 167 | -- * Mapping types | |
| 168 | instance ToAdnot a => ToAdnot (MS.Map T.Text a) where | |
| 169 | toAdnot ls = Product (fmap toAdnot ls) | |
| 170 | ||
| 171 | -- * Tuples | |
| 172 | instance ToAdnot () where | |
| 173 | toAdnot () = List [] | |
| 174 | ||
| 175 | instance (ToAdnot a, ToAdnot b) => ToAdnot (a, b) where | |
| 176 | toAdnot (a, b) = List [toAdnot a, toAdnot b] | |
| 177 | ||
| 178 | instance (ToAdnot a, ToAdnot b, ToAdnot c) => ToAdnot (a, b, c) where | |
| 179 | toAdnot (a, b, c) = List [toAdnot a, toAdnot b, toAdnot c] | |
| 180 | ||
| 181 | instance (ToAdnot a, ToAdnot b, ToAdnot c, ToAdnot d) | |
| 182 | => ToAdnot (a, b, c, d) where | |
| 183 | toAdnot (a, b, c, d) = List [toAdnot a, toAdnot b, toAdnot c, toAdnot d] | |
| 184 | ||
| 185 | -- Some common Haskell algebraic data types | |
| 186 | instance ToAdnot a => ToAdnot (Maybe a) where | |
| 187 | toAdnot Nothing = Sum "Nothing" [] | |
| 188 | toAdnot (Just x) = Sum "Just" [toAdnot x] | |
| 189 | ||
| 190 | instance (ToAdnot a, ToAdnot b) => ToAdnot (Either a b) where | |
| 191 | toAdnot (Left x) = Sum "Left" [toAdnot x] | |
| 192 | toAdnot (Right y) = Sum "Right" [toAdnot y] | |
| 193 | ||
| 194 | instance ToAdnot Bool where | |
| 195 | toAdnot True = Symbol "True" | |
| 196 | toAdnot False = Symbol "False" | |
| 197 | ||
| 198 | -- * Parsing | |
| 199 | ||
| 200 | type ParseError = String | |
| 201 | type Parser a = Either ParseError a | |
| 202 | ||
| 203 | withSum :: String -> (T.Text -> Array -> Parser a) -> Value -> Parser a | |
| 204 | withSum n k val = case val of | |
| 205 | Sum t as -> k t as | |
| 206 | _ -> Left ("Expected sum in " ++ n) | |
| 207 | ||
| 208 | class FromAdnot a where | |
| 209 | parseAdnot :: Value -> Parser a |
| 1 | {-# LANGUAGE OverloadedStrings #-} | |
| 2 | ||
| 3 | module Data.Adnot.Emit where | |
| 4 | ||
| 5 | import Control.Monad (sequence) | |
| 6 | import Data.ByteString.Lazy (ByteString) | |
| 7 | import Data.ByteString.Builder | |
| 8 | import Data.List (intersperse) | |
| 9 | import qualified Data.Map.Strict as M | |
| 10 | import Data.Monoid ((<>)) | |
| 11 | import Data.Text (Text) | |
| 12 | import Data.Text.Encoding (encodeUtf8) | |
| 13 | import qualified Data.Vector as V | |
| 14 | ||
| 15 | import Data.Adnot.Type | |
| 16 | ||
| 17 | encodeValue :: Value -> ByteString | |
| 18 | encodeValue = toLazyByteString . buildValue | |
| 19 | ||
| 20 | buildValue :: Value -> Builder | |
| 21 | buildValue (Sum n vs) | |
| 22 | | V.null vs = char7 '(' <> ident n <> char7 ')' | |
| 23 | | otherwise = | |
| 24 | char7 '(' <> ident n <> char7 ' ' <> spaceSepArr vs <> char7 ')' | |
| 25 | buildValue (Product ps) = | |
| 26 | char7 '{' <> buildPairs ps <> char7 '}' | |
| 27 | buildValue (List vs) = | |
| 28 | char7 '[' <> spaceSepArr vs <> char7 ']' | |
| 29 | buildValue (Integer i) = integerDec i | |
| 30 | buildValue (Double d) = doubleDec d | |
| 31 | buildValue (Symbol t) = ident t | |
| 32 | buildValue (String t) = | |
| 33 | char7 '"' <> byteString (encodeUtf8 t) <> char7 '"' | |
| 34 | ||
| 35 | spaceSep :: [Builder] -> Builder | |
| 36 | spaceSep = mconcat . intersperse (char7 ' ') | |
| 37 | ||
| 38 | spaceSepArr :: Array -> Builder | |
| 39 | spaceSepArr = spaceSep . map buildValue . V.toList | |
| 40 | ||
| 41 | ident :: Text -> Builder | |
| 42 | ident = byteString . encodeUtf8 | |
| 43 | ||
| 44 | buildPairs :: Product -> Builder | |
| 45 | buildPairs ps = spaceSep [ go k v | (k, v) <- M.toList ps ] | |
| 46 | where go k v = ident k <> char7 ' ' <> buildValue v |
| 1 | {-# LANGUAGE OverloadedStrings #-} | |
| 2 | ||
| 3 | module Data.Adnot.Parse (decodeValue) where | |
| 4 | ||
| 5 | import Control.Applicative((<|>)) | |
| 6 | import Data.Attoparsec.ByteString.Char8 | |
| 7 | import Data.ByteString (ByteString) | |
| 8 | import qualified Data.Map as M | |
| 9 | import qualified Data.Text as T | |
| 10 | import qualified Data.Vector as V | |
| 11 | ||
| 12 | import Data.Adnot.Type | |
| 13 | ||
| 14 | decodeValue :: ByteString -> Either String Value | |
| 15 | decodeValue = parseOnly pVal | |
| 16 | where pVal = ws *> (pSum <|> pProd <|> pList <|> pLit) | |
| 17 | pSum = Sum <$> (char '(' *> ws *> pIdent) | |
| 18 | <*> (pValueList <* char ')') | |
| 19 | pProd = Product . M.fromList | |
| 20 | <$> (char '{' *> pProdBody <* ws <* char '}') | |
| 21 | pProdBody = many' pPair | |
| 22 | pPair = (,) <$> (ws *> pIdent) <*> pVal | |
| 23 | pList = List <$> (char '[' *> pValueList <* ws <* char ']') | |
| 24 | pLit = Symbol <$> pIdent | |
| 25 | <|> String <$> pString | |
| 26 | <|> Integer <$> decimal | |
| 27 | pValueList = V.fromList <$> many' pVal | |
| 28 | pIdent = T.pack <$> | |
| 29 | ((:) <$> (letter_ascii <|> char '_') | |
| 30 | <*> many' (letter_ascii <|> digit <|> char '_')) | |
| 31 | pString = T.pack <$> (char '"' *> manyTill pStrChar (char '"')) | |
| 32 | pStrChar = '\n' <$ string "\\n" | |
| 33 | <|> '\t' <$ string "\\t" | |
| 34 | <|> '\r' <$ string "\\r" | |
| 35 | <|> '\b' <$ string "\\b" | |
| 36 | <|> '\f' <$ string "\\f" | |
| 37 | <|> '\'' <$ string "\\'" | |
| 38 | <|> '\"' <$ string "\\\"" | |
| 39 | <|> '\\' <$ string "\\\\" | |
| 40 | <|> anyChar | |
| 41 | ws = skipSpace *> ((comment *> ws) <|> return ()) | |
| 42 | comment = char '#' *> manyTill anyChar (char '\n') |
| 1 | {-# LANGUAGE DeriveDataTypeable #-} | |
| 2 | {-# LANGUAGE BangPatterns #-} | |
| 3 | ||
| 4 | module Data.Adnot.Type (Value(..), Array, Product) where | |
| 5 | ||
| 6 | import Control.DeepSeq (NFData(..)) | |
| 7 | import Data.Data (Data) | |
| 8 | import Data.Typeable (Typeable) | |
| 9 | import Data.Map.Strict (Map) | |
| 10 | import qualified Data.Map as M | |
| 11 | import Data.Text (Text) | |
| 12 | import Data.Vector (Vector) | |
| 13 | import GHC.Exts (IsString(..)) | |
| 14 | ||
| 15 | -- | An Adnot value represented as a Haskell value | |
| 16 | data Value | |
| 17 | = Sum !Text !Array | |
| 18 | | Product !Product | |
| 19 | | List !Array | |
| 20 | | Integer !Integer | |
| 21 | | Double !Double | |
| 22 | | Symbol !Text | |
| 23 | | String !Text | |
| 24 | deriving (Eq, Show, Read, Typeable, Data) | |
| 25 | ||
| 26 | instance NFData Value where | |
| 27 | rnf (Sum t as) = rnf t `seq` rnf as | |
| 28 | rnf (Product ls) = rnf ls | |
| 29 | rnf (List as) = rnf as | |
| 30 | rnf (Integer i) = rnf i | |
| 31 | rnf (Double d) = rnf d | |
| 32 | rnf (Symbol t) = rnf t | |
| 33 | rnf (String t) = rnf t | |
| 34 | ||
| 35 | instance IsString Value where | |
| 36 | fromString = String . fromString | |
| 37 | ||
| 38 | type Array = Vector Value | |
| 39 | type Product = Map Text Value |
| 1 | module Data.Adnot ( Value(..) | |
| 2 | , Array | |
| 3 | , Product | |
| 4 | , decodeValue | |
| 5 | , encodeValue | |
| 6 | ) where | |
| 7 | ||
| 8 | import Data.Adnot.Emit | |
| 9 | import Data.Adnot.Parse | |
| 10 | import Data.Adnot.Type |
| 1 | module Main where | |
| 2 | ||
| 3 | import Data.Adnot | |
| 4 | import qualified Data.ByteString as BS | |
| 5 | import qualified Data.ByteString.Lazy.Char8 as BSL | |
| 6 | import System.Environment (getArgs) | |
| 7 | import System.Exit (die) | |
| 8 | ||
| 9 | helpText :: String | |
| 10 | helpText = "Usage: adnot-id [file]" | |
| 11 | ||
| 12 | main = do | |
| 13 | content <- do | |
| 14 | args <- getArgs | |
| 15 | case args of | |
| 16 | [] -> BS.getContents | |
| 17 | ["-"] -> BS.getContents | |
| 18 | [file] -> BS.readFile file | |
| 19 | _ -> die helpText | |
| 20 | case decodeValue content of | |
| 21 | Right val -> BSL.putStrLn (encodeValue val) | |
| 22 | Left err -> die err |
| 1 | name: adnot | |
| 2 | version: 0.1.0.0 | |
| 3 | -- synopsis: | |
| 4 | -- description: | |
| 5 | license: BSD3 | |
| 6 | license-file: LICENSE | |
| 7 | author: Getty Ritter | |
| 8 | maintainer: gettyritter@gmail.com | |
| 9 | copyright: ©2016 Getty Ritter | |
| 10 | category: Data | |
| 11 | build-type: Simple | |
| 12 | cabal-version: >=1.12 | |
| 13 | ||
| 14 | library | |
| 15 | exposed-modules: Data.Adnot | |
| 16 | other-modules: Data.Adnot.Parse, | |
| 17 | Data.Adnot.Class, | |
| 18 | Data.Adnot.Emit, | |
| 19 | Data.Adnot.Type | |
| 20 | build-depends: base >=4.8 && <5, | |
| 21 | attoparsec, | |
| 22 | bytestring, | |
| 23 | deepseq, | |
| 24 | hashable, | |
| 25 | containers, | |
| 26 | text, | |
| 27 | vector | |
| 28 | default-language: Haskell2010 | |
| 29 | ||
| 30 | executable adnot-id | |
| 31 | default-language: Haskell2010 | |
| 32 | hs-source-dirs: adnot-id | |
| 33 | main-is: Main.hs | |
| 34 | build-depends: base >=4.8 && <5, | |
| 35 | bytestring, | |
| 36 | adnot |
| 1 | -- Initial adtn.cabal generated by cabal init. For further documentation, | |
| 2 | -- see http://haskell.org/cabal/users-guide/ | |
| 3 | ||
| 4 | name: adtn | |
| 5 | version: 0.1.0.0 | |
| 6 | -- synopsis: | |
| 7 | -- description: | |
| 8 | license: BSD3 | |
| 9 | license-file: LICENSE | |
| 10 | author: Getty Ritter | |
| 11 | maintainer: gettyritter@gmail.com | |
| 12 | -- copyright: | |
| 13 | category: Data | |
| 14 | build-type: Simple | |
| 15 | -- extra-source-files: | |
| 16 | cabal-version: >=1.10 | |
| 17 | ||
| 18 | library | |
| 19 | exposed-modules: Data.ADTN | |
| 20 | -- other-modules: | |
| 21 | build-depends: base >=4.8 && <4.9, | |
| 22 | attoparsec, | |
| 23 | bytestring, | |
| 24 | aeson, | |
| 25 | containers, | |
| 26 | text, | |
| 27 | vector | |
| 28 | default-language: Haskell2010⏎ |