packages feed

s-cargot 0.1.1.1 → 0.1.2.0

raw patch · 9 files changed

+234/−48 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.SCargot.Atom: atom :: (t -> atom) -> Parser t -> Parser atom
+ Data.SCargot.Atom: mkAtomParser :: [Parser atom] -> SExprParser atom (SExpr atom)
+ Data.SCargot.Common: commonLispNumberAnyBase :: Parser Integer
+ Data.SCargot.Common: gnuM4NumberAnyBase :: Parser Integer
+ Data.SCargot.Language.HaskLike: parseHaskellFloat :: Parser Double
+ Data.SCargot.Language.HaskLike: parseHaskellInt :: Parser Integer
+ Data.SCargot.Language.HaskLike: parseHaskellString :: Parser Text
+ Data.SCargot.Repr.Basic: infixr 5 :::

Files

+ Data/SCargot/Atom.hs view
@@ -0,0 +1,40 @@+module Data.SCargot.Atom+  ( -- $intro++    atom+  , mkAtomParser+  ) where++import Data.SCargot.Parse (SExprParser, mkParser)+import Data.SCargot.Repr (SExpr)+import Text.Parsec (choice)+import Text.Parsec.Text (Parser)++-- | A convenience function for defining an atom parser from a wrapper+--   function and a parser. This is identical to 'fmap' specialized to+--   operate over 'Parser' values, and is provided as sugar.+atom :: (t -> atom) -> Parser t -> Parser atom+atom = fmap++-- | A convenience function for defining a 'SExprSpec' from a list of+--   possible atom parsers, which will be tried in sequence before failing.+mkAtomParser :: [Parser atom] -> SExprParser atom (SExpr atom)+mkAtomParser = mkParser . choice++{- $intro++This module defines small convenience functions for building an atom+type from several individual parsers. This is easy to do without these+functions, but these functions communicate intent more directly:++> data Atom+>   = Ident Text+>   | Num Integer+>+> myParser :: SExprParser Atom (SExpr Atom)+> myParser = mkAtomParser+>   [ atom Ident parseR7RSIdent+>   , atom Num   signedDecNumber+>   ]++-}
Data/SCargot/Comments.hs view
@@ -48,8 +48,8 @@ lineComment :: String -> Comment lineComment s = string s >> skipMany (noneOf "\n") >> return () --- | Given two strings, a begin and an end delimeter, produce a---   parser that matches the beginning delimeter and then ignores+-- | Given two strings, a begin and an end delimiter, produce a+--   parser that matches the beginning delimiter and then ignores --   everything until it finds the end delimiter. This does not --   consider nesting, so, for example, a comment created with --
Data/SCargot/Common.hs view
@@ -22,11 +22,15 @@                            , signedDozNumber                            , hexNumber                            , signedHexNumber+                             -- ** Numeric Literals for Arbitrary Bases+                           , commonLispNumberAnyBase+                           , gnuM4NumberAnyBase                            ) where  #if !MIN_VERSION_base(4,8,0) import Control.Applicative hiding ((<|>), many) #endif+import           Control.Monad (guard) import           Data.Char import           Data.Text (Text) import qualified Data.Text as T@@ -215,16 +219,44 @@ number base digits = foldl go 0 <$> many1 digits   where go x d = base * x + toInteger (value d)         value c-          | c == 'a' || c == 'A' = 0xa-          | c == 'b' || c == 'B' = 0xb-          | c == 'c' || c == 'C' = 0xc-          | c == 'd' || c == 'D' = 0xd-          | c == 'e' || c == 'E' = 0xe-          | c == 'f' || c == 'F' = 0xf+          | c >= 'a' && c <= 'z' = 0xa + (fromEnum c - fromEnum 'a')+          | c >= 'A' && c <= 'Z' = 0xa + (fromEnum c - fromEnum 'A')           | c >= '0' && c <= '9' = fromEnum c - fromEnum '0'           | c == '\x218a' = 0xa           | c == '\x218b' = 0xb           | otherwise = error ("Unknown letter in number: " ++ show c)++digitsFor :: Int -> [Char]+digitsFor n+  | n <= 10   = take n ['0'..'9']+  | n <= 36   = take (n-10) ['A'..'Z'] ++ take (n-10) ['a'..'z'] ++ ['0'..'9']+  | otherwise = error ("Invalid base for parser: " ++ show n)++anyBase :: Integer -> Parser Integer+anyBase n = number n (oneOf (digitsFor (fromIntegral n)))++-- | A parser for Common Lisp's arbitrary-base number syntax, of+--   the form @#[base]r[number]@, where the base is given in+--   decimal. Note that this syntax begins with a @#@, which+--   means it might conflict with defined reader macros.+commonLispNumberAnyBase :: Parser Integer+commonLispNumberAnyBase = do+  _ <- char '#'+  n <- decNumber+  guard (n >= 2 && n <= 36)+  _ <- char 'r'+  signed (anyBase n)++-- | A parser for GNU m4's arbitrary-base number syntax, of+--   the form @0r[base]:[number]@, where the base is given in+--   decimal.+gnuM4NumberAnyBase :: Parser Integer+gnuM4NumberAnyBase = do+  _ <- string "0r"+  n <- decNumber+  guard (n >= 2 && n <= 36)+  _ <- char ':'+  signed (anyBase n)  sign :: Num a => Parser (a -> a) sign =  (pure id     <* char '+')
Data/SCargot/Language/HaskLike.hs view
@@ -5,6 +5,10 @@     HaskLikeAtom(..)   , haskLikeParser   , haskLikePrinter+    -- * Individual Parsers+  , parseHaskellString+  , parseHaskellFloat+  , parseHaskellInt   ) where  #if !MIN_VERSION_base(4,8,0)@@ -24,12 +28,13 @@  {- $info -This module is intended for simple, ad-hoc configuration or data formats-that might not need their on rich structure but might benefit from a few-various kinds of literals. The 'haskLikeParser' understands identifiers as-defined by R5RS, as well as string, integer, and floating-point literals-as defined by the Haskell spec. It does __not__ natively understand other-data types, such as booleans, vectors, bitstrings.+This module is intended for simple, ad-hoc configuration or data+formats that might not need their on rich structure but might benefit+from a few various kinds of literals. The 'haskLikeParser' understands+identifiers as defined by R5RS, as well as string, integer, and+floating-point literals as defined by the Haskell 2010 spec. It does+__not__ natively understand other data types, such as booleans,+vectors, bitstrings.  -} @@ -51,8 +56,10 @@ instance IsString HaskLikeAtom where   fromString = HSIdent . fromString -pString :: Parser Text-pString = pack . catMaybes <$> between (char '"') (char '"') (many (val <|> esc))+-- | Parse a Haskell string literal as defined by the Haskell 2010+-- language specification.+parseHaskellString :: Parser Text+parseHaskellString = pack . catMaybes <$> between (char '"') (char '"') (many (val <|> esc))   where val = Just <$> satisfy (\ c -> c /= '"' && c /= '\\' && c > '\026')         esc = do _ <- char '\\'                  Nothing <$ (gap <|> char '&') <|>@@ -81,8 +88,10 @@    "\STX\ETX\EOT\ENQ\ACK\BEL\DLE\DC1\DC2\DC3\DC4\NAK" ++    "\SYN\ETB\CAN\SUB\ESC\DEL") -pFloat :: Parser Double-pFloat = do+-- | Parse a Haskell floating-point number as defined by the Haskell+-- 2010 language specification.+parseHaskellFloat :: Parser Double+parseHaskellFloat = do   n <- decNumber   withDot n <|> noDot n   where withDot n = do@@ -105,8 +114,10 @@ power :: Num a => Parser (a -> a) power = negate <$ char '-' <|> id <$ char '+' <|> return id -pInt :: Parser Integer-pInt = do+-- | Parse a Haskell integer literal as defined by the Haskell 2010+-- language specification.+parseHaskellInt :: Parser Integer+parseHaskellInt = do   s <- power   n <- pZeroNum <|> decNumber   return (fromIntegral (s n))@@ -121,9 +132,9 @@  pHaskLikeAtom :: Parser HaskLikeAtom pHaskLikeAtom-   =  HSFloat   <$> (try pFloat     <?> "float")-  <|> HSInt     <$> (try pInt       <?> "integer")-  <|> HSString  <$> (pString        <?> "string literal")+   =  HSFloat   <$> (try parseHaskellFloat <?> "float")+  <|> HSInt     <$> (try parseHaskellInt   <?> "integer")+  <|> HSString  <$> (parseHaskellString    <?> "string literal")   <|> HSIdent   <$> (parseR5RSIdent <?> "token")  sHaskLikeAtom :: HaskLikeAtom -> Text
Data/SCargot/Repr/Basic.hs view
@@ -88,28 +88,36 @@ -- -- >>> A "pachy" ::: A "derm" -- SCons (SAtom "pachy") (SAtom "derm")+#if MIN_VERSION_base(4,8,0) pattern (:::) :: SExpr a -> SExpr a -> SExpr a+#endif pattern x ::: xs = SCons x xs  -- | A shorter alias for `SAtom` -- -- >>> A "elephant" -- SAtom "elephant"+#if MIN_VERSION_base(4,8,0) pattern A :: a -> SExpr a+#endif pattern A x = SAtom x  -- | A (slightly) shorter alias for `SNil` -- -- >>> Nil -- SNil+#if MIN_VERSION_base(4,8,0) pattern Nil :: SExpr a+#endif pattern Nil = SNil  -- | An alias for matching a proper list. -- -- >>> L [A "pachy", A "derm"] -- SExpr (SAtom "pachy") (SExpr (SAtom "derm") SNil)+#if MIN_VERSION_base(4,8,0) pattern L :: [SExpr a] -> SExpr a+#endif pattern L xs <- (gatherList -> Right xs) #if MIN_VERSION_base(4,8,0)   where L []     = SNil@@ -121,7 +129,9 @@ -- -- >>> DL [A "pachy"] A "derm" -- SExpr (SAtom "pachy") (SAtom "derm")+#if MIN_VERSION_base(4,8,0) pattern DL :: [SExpr a] -> a -> SExpr a+#endif pattern DL xs x <- (gatherDList -> Just (xs, x)) #if MIN_VERSION_base(4,8,0)   where DL []     a = SAtom a
Data/SCargot/Repr/Rich.hs view
@@ -107,7 +107,9 @@ -- -- >>> A "one" ::: L [A "two", A "three"] -- RSList [RSAtom "one",RSAtom "two",RSAtom "three"]+#if MIN_VERSION_base(4,8,0) pattern (:::) :: RichSExpr a -> RichSExpr a -> RichSExpr a+#endif pattern x ::: xs <- (uncons -> Just (x, xs)) #if MIN_VERSION_base(4,8,0)   where x ::: xs = cons x xs@@ -117,28 +119,36 @@ -- -- >>> A "elephant" -- RSAtom "elephant"+#if MIN_VERSION_base(4,8,0) pattern A :: a -> RichSExpr a+#endif pattern A a = R.RSAtom a  -- | A shorter alias for `RSList` -- -- >>> L [A "pachy", A "derm"] -- RSList [RSAtom "pachy",RSAtom "derm"]+#if MIN_VERSION_base(4,8,0) pattern L :: [RichSExpr a] -> RichSExpr a+#endif pattern L xs = R.RSList xs  -- | A shorter alias for `RSDotted` -- -- >>> DL [A "pachy"] "derm" -- RSDotted [RSAtom "pachy"] "derm"+#if MIN_VERSION_base(4,8,0) pattern DL :: [RichSExpr a] -> a -> RichSExpr a+#endif pattern DL xs x = R.RSDotted xs x  -- | A shorter alias for `RSList` @[]@ -- -- >>> Nil -- RSList []+#if MIN_VERSION_base(4,8,0) pattern Nil :: RichSExpr a+#endif pattern Nil = R.RSList []  -- | Utility function for parsing a pair of things: this parses a two-element list,
Data/SCargot/Repr/WellFormed.hs view
@@ -59,28 +59,36 @@ --   instead. -- -- >>> let sum (x ::: xs) = x + sum xs; sum Nil = 0+#if MIN_VERSION_base(4,8,0) pattern (:::) :: WellFormedSExpr a -> WellFormedSExpr a -> WellFormedSExpr a+#endif pattern x ::: xs <- (uncons -> Just (x, xs))  -- | A shorter alias for `WFSList` -- -- >>> L [A "pachy", A "derm"] -- WFSList [WFSAtom "pachy",WFSAtom "derm"]+#if MIN_VERSION_base(4,8,0) pattern L :: [WellFormedSExpr t] -> WellFormedSExpr t+#endif pattern L xs = R.WFSList xs  -- | A shorter alias for `WFSAtom` -- -- >>> A "elephant" -- WFSAtom "elephant"+#if MIN_VERSION_base(4,8,0) pattern A :: t -> WellFormedSExpr t+#endif pattern A a  = R.WFSAtom a  -- | A shorter alias for `WFSList` @[]@ -- -- >>> Nil -- WFSList []+#if MIN_VERSION_base(4,8,0) pattern Nil :: WellFormedSExpr t+#endif pattern Nil = R.WFSList []  getShape :: WellFormedSExpr a -> String
s-cargot.cabal view
@@ -1,5 +1,5 @@ name:                s-cargot-version:             0.1.1.1+version:             0.1.2.0 synopsis:            A flexible, extensible s-expression library. homepage:            https://github.com/aisamanra/s-cargot description:         S-Cargot is a library for working with s-expressions in@@ -35,6 +35,7 @@                        Data.SCargot.Repr.WellFormed,                        Data.SCargot.Parse,                        Data.SCargot.Print,+                       Data.SCargot.Atom,                        Data.SCargot.Comments,                        Data.SCargot.Common,                        Data.SCargot.Language.Basic,
test/SCargotQC.hs view
@@ -1,29 +1,20 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-} -module Data.SCargot.ReprQC (reprQC) where+module Main where -import Data.SCargot ( SExprParser-                    , SExprPrinter-                    , mkParser-                    , flatPrint-                    , encodeOne-                    , decodeOne-                    , asRich-                    , asWellFormed-                    )-import Data.SCargot.Repr ( SExpr(..)-                         , RichSExpr-                         , fromRich-                         , toRich-                         , WellFormedSExpr(..)-                         , fromWellFormed-                         , toWellFormed-                         )-import Test.QuickCheck-import Test.QuickCheck.Arbitrary-import Text.Parsec (char)-import Text.Parsec.Text (Parser)+import Data.SCargot+import Data.SCargot.Comments+import Data.SCargot.Repr +import           Data.Monoid ((<>))+import           Data.Text (Text)+import qualified Data.Text as T+import           Test.QuickCheck+import           Test.QuickCheck.Arbitrary+import           Text.Parsec (char)+import           Text.Parsec.Text (Parser)+ instance Arbitrary a => Arbitrary (SExpr a) where   arbitrary = sized $ \n ->     if n <= 0@@ -54,12 +45,35 @@                 ]           ] +data EncodedSExpr = EncodedSExpr+  { encoding :: Text+  , original :: SExpr ()+  } deriving (Eq, Show)++instance Arbitrary EncodedSExpr where+  arbitrary = do+    sexpr :: SExpr () <- arbitrary+    let chunks = T.words (encodeOne printer sexpr)+    whitespace <- sequence [ mkWs | _ <- chunks ]+    pure (EncodedSExpr { encoding = T.concat (zipWith (<>) chunks whitespace)+                       , original = sexpr+                       })+    where mkWs = do+            n :: Int <- choose (1, 10)+            T.pack <$> sequence [ elements " \t\r\n"+                                | _ <- [0..n]+                                ]+ parser :: SExprParser () (SExpr ()) parser = mkParser (() <$ char 'X')  printer :: SExprPrinter () (SExpr ()) printer = flatPrint (const "X") +prettyPrinter :: SExprPrinter () (SExpr ())+prettyPrinter = basicPrint (const "X")++ richIso :: SExpr () -> Bool richIso s = fromRich (toRich s) == s @@ -79,23 +93,83 @@ encDec :: SExpr () -> Bool encDec s = decodeOne parser (encodeOne printer s) == Right s +encDecPretty :: SExpr () -> Bool+encDecPretty s = decodeOne parser (encodeOne prettyPrinter s) == Right s++decEnc :: EncodedSExpr -> Bool+decEnc s = decodeOne parser (encoding s) == Right (original s)++ encDecRich :: RichSExpr () -> Bool encDecRich s = decodeOne (asRich parser) (encodeOne printer (fromRich s))                 == Right s +encDecRichPretty :: RichSExpr () -> Bool+encDecRichPretty s = decodeOne (asRich parser)+                               (encodeOne prettyPrinter (fromRich s))+                       == Right s++decEncRich :: EncodedSExpr -> Bool+decEncRich s = decodeOne (asRich parser) (encoding s) == Right (toRich (original s))++ encDecWF :: WellFormedSExpr () -> Bool encDecWF s = decodeOne (asWellFormed parser) (encodeOne printer (fromWellFormed s))                == Right s -reprQC :: IO ()-reprQC = do+encDecWFPretty :: WellFormedSExpr () -> Bool+encDecWFPretty s =+  decodeOne (asWellFormed parser) (encodeOne prettyPrinter (fromWellFormed s))+    == Right s++decEncWF :: EncodedSExpr -> Bool+decEncWF s = decodeOne (asWellFormed parser) (encoding s) == toWellFormed (original s)+++insertComments :: Text -> Text -> Text -> Text+insertComments lc rc sexpr =+  T.replace " " (" " <> lc <> "blahblahblah" <> rc <> " ") sexpr++encDecLineComments :: SExpr () -> Bool+encDecLineComments s =+  decodeOne (withLispComments parser)+            (insertComments ";" "\n" (encodeOne printer s)) == Right s++encDecBlockComments :: SExpr () -> Bool+encDecBlockComments s =+  decodeOne (withHaskellBlockComments parser)+            (insertComments "{-" "-}" (encodeOne printer s)) == Right s++-- Sometimes we generate really huge test cases, which can take a really+-- long time to process---especially when we're modifying the whitespace+-- to produce weird anomalous S-expressions. So, we make the size parameter+-- a bit smaller for good measure.+reallyQuickCheck :: Testable prop => prop -> IO ()+reallyQuickCheck = quickCheckWith stdArgs { maxSize = 25 }++main :: IO ()+main = do   putStrLn "The SExpr <--> Rich translation should be isomorphic"   quickCheck richIso   quickCheck richIsoBk+   putStrLn "The SExpr <--> WF translation should be near-isomorphic"   quickCheck wfIso   quickCheck wfIsoBk+   putStrLn "This should be true when parsing, as well"   quickCheck encDec+  reallyQuickCheck decEnc   quickCheck encDecRich+  reallyQuickCheck decEncRich   quickCheck encDecWF+  reallyQuickCheck decEncWF++  putStrLn "And it should be true if pretty-printed"+  reallyQuickCheck encDecPretty+  reallyQuickCheck encDecRichPretty+  reallyQuickCheck encDecWFPretty++  putStrLn "Comments should not affect parsing"+  reallyQuickCheck encDecLineComments+  reallyQuickCheck encDecBlockComments