{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main where 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 Text.Parsec (char) instance Arbitrary a => Arbitrary (SExpr a) where arbitrary = sized $ \n -> if n <= 0 then pure SNil else oneof [ SAtom <$> arbitrary , do k <- choose (0, n) elems <- sequence [ resize (n-k) arbitrary | _ <- [0..k] ] rest <- oneof [ SAtom <$> arbitrary , pure SNil ] pure (foldr SCons rest elems) ] instance Arbitrary a => Arbitrary (RichSExpr a) where arbitrary = toRich `fmap` arbitrary instance Arbitrary a => Arbitrary (WellFormedSExpr a) where arbitrary = sized $ \n -> oneof [ WFSAtom <$> arbitrary , do k <- choose (0, n) WFSList <$> sequence [ resize (n-k) arbitrary | _ <- [0..k] ] ] 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") widePrinter :: SExprPrinter () (SExpr ()) widePrinter = unconstrainedPrint (const "X") richIso :: SExpr () -> Bool richIso s = fromRich (toRich s) == s richIsoBk :: RichSExpr () -> Bool richIsoBk s = toRich (fromRich s) == s wfIso :: SExpr () -> Bool wfIso s = case toWellFormed s of Left _ -> True Right y -> s == fromWellFormed y wfIsoBk :: WellFormedSExpr () -> Bool wfIsoBk s = toWellFormed (fromWellFormed s) == Right s encDec :: SExpr () -> Bool encDec s = decodeOne parser (encodeOne printer s) == Right s encDecPretty :: SExpr () -> Bool encDecPretty s = decodeOne parser (encodeOne prettyPrinter s) == Right s encDecWide :: SExpr () -> Bool encDecWide s = decodeOne parser (encodeOne widePrinter 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 encDecRichWide :: RichSExpr () -> Bool encDecRichWide s = decodeOne (asRich parser) (encodeOne widePrinter (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 encDecWFPretty :: WellFormedSExpr () -> Bool encDecWFPretty s = decodeOne (asWellFormed parser) (encodeOne prettyPrinter (fromWellFormed s)) == Right s encDecWFWide :: WellFormedSExpr () -> Bool encDecWFWide s = decodeOne (asWellFormed parser) (encodeOne widePrinter (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 "And it should be true if pretty-printed using the wide-format printer" reallyQuickCheck encDecWide reallyQuickCheck encDecRichWide reallyQuickCheck encDecWFWide putStrLn "Comments should not affect parsing" reallyQuickCheck encDecLineComments reallyQuickCheck encDecBlockComments