distributors-0.6.0.0: test/Properties/Kleene.hs
{-# OPTIONS_GHC -Wno-orphans #-}
module Properties.Kleene (kleeneProperties) where
import Control.Lens.Grammar
import Data.Foldable (for_)
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck
instance Arbitrary GeneralCategory where
arbitrary = arbitraryBoundedEnum
shrink = shrinkBoundedEnum
instance Arbitrary (TokenClass Char) where
arbitrary = sized go
where
go 0 = frequency
[ (1, pure falseB)
, (1, pure trueB)
, (4, oneOf <$> (arbitrary :: Gen [Char]))
, (4, notOneOf <$> (arbitrary :: Gen [Char]))
, (3, asIn <$> arbitrary)
, (3, notAsIn <$> arbitrary)
]
go n = frequency
[ (2, go 0)
, (2, (>||<) <$> go (n `div` 2) <*> go (n `div` 2))
, (2, (>&&<) <$> go (n `div` 2) <*> go (n `div` 2))
, (1, notB <$> go (n - 1))
]
instance Arbitrary (RegEx Char) where
arbitrary = sized go
where
go 0 = frequency
[ (1, pure (zeroK :: RegEx Char))
, (1, pure (mempty :: RegEx Char))
, (6, tokenClass <$> (arbitrary :: Gen (TokenClass Char)))
]
go n = frequency
[ (2, go 0)
, (2, (<>) <$> go (n `div` 2) <*> go (n `div` 2))
, (2, (>|<) <$> go (n `div` 2) <*> go (n `div` 2))
, (1, starK <$> go (n - 1))
, (1, plusK <$> go (n - 1))
, (1, optK <$> go (n - 1))
]
kleeneProperties :: Spec
kleeneProperties = do
describe "KleeneStarAlgebra" $ do
prop "starK x = optK (plusK x)" $ \(x :: RegEx Char) ->
starK x == optK (plusK x)
prop "plusK x = x <> starK x" $ \(x :: RegEx Char) ->
plusK x == x <> starK x
prop "optK x = mempty >|< x" $ \(x :: RegEx Char) ->
optK x == (mempty >|< x)
prop "x >|< x = x" $ \(x :: RegEx Char) ->
(x >|< x) == x
prop "zeroK >|< x = x" $ \(x :: RegEx Char) ->
(zeroK >|< x) == x
prop "x >|< zeroK = x" $ \(x :: RegEx Char) ->
(x >|< zeroK) == x
prop "x >|< mempty = optK x" $ \(x :: RegEx Char) ->
(x >|< mempty) == optK x
prop "zeroK <> x = zeroK" $ \(x :: RegEx Char) ->
(zeroK <> x) == zeroK
prop "x <> zeroK = zeroK" $ \(x :: RegEx Char) ->
(x <> zeroK) == zeroK
prop "mempty <> x = x" $ \(x :: RegEx Char) ->
(mempty <> x) == x
prop "x <> mempty = x" $ \(x :: RegEx Char) ->
(x <> mempty) == x
describe "TokenAlgebra" $ do
it "zeroK = tokenClass falseB" $
(zeroK :: RegEx Char) `shouldBe` tokenClass falseB
prop "tokenClass x >|< tokenClass y = tokenClass (x >||< y)" $
\(x :: TokenClass Char) (y :: TokenClass Char) ->
((tokenClass x :: RegEx Char) >|< tokenClass y)
== tokenClass (x >||< y)
describe "TokenAlgebra RegEx" $ do
it "anyToken = tokenClass anyToken" $
(anyToken :: RegEx Char) `shouldBe` tokenClass anyToken
prop "token c = tokenClass (token c)" $
\(c :: Char) ->
(token c :: RegEx Char) == tokenClass (token c)
prop "oneOf cs = tokenClass (oneOf cs)" $
\(cs :: [Char]) ->
(oneOf cs :: RegEx Char) == tokenClass (oneOf cs)
prop "notOneOf cs = tokenClass (notOneOf cs)" $
\(cs :: [Char]) ->
(notOneOf cs :: RegEx Char) == tokenClass (notOneOf cs)
prop "asIn cat = tokenClass (asIn cat)" $
\(cat :: GeneralCategory) ->
(asIn cat :: RegEx Char) == tokenClass (asIn cat)
prop "notAsIn cat = tokenClass (notAsIn cat)" $
\(cat :: GeneralCategory) ->
(notAsIn cat :: RegEx Char) == tokenClass (notAsIn cat)
it "matching agrees with lifted Bnf on examples" $ do
let
cases =
[ ("", mempty :: RegEx Char)
, ("a", token 'a')
, ("b", token 'a')
, ("ab", token 'a' <> token 'b')
, ("a", token 'a' >|< token 'b')
, ("bbb", starK (token 'b'))
, ("bbb", plusK (token 'b'))
, ("", optK (token 'b'))
, ("x", oneOf "xyz")
, ("x", notOneOf "abc")
, ("A", asIn UppercaseLetter)
, ("a", notAsIn UppercaseLetter)
, ("abbb", token 'a' <> starK (token 'b'))
, ("cat", terminal "cat" >|< terminal "dog")
]
for_ cases $ \(word, rex) ->
(word =~ rex) `shouldBe` (word =~ liftBnf0 rex)
describe "BooleanAlgebra TokenClass" $ do
it "trueB = anyToken" $
(trueB :: TokenClass Char) `shouldBe` anyToken
it "trueB = notOneOf []" $
(trueB :: TokenClass Char) `shouldBe` notOneOf []
it "falseB = oneOf []" $
(falseB :: TokenClass Char) `shouldBe` oneOf []
prop "notB . oneOf = notOneOf" $
\(cs :: [Char]) ->
notB (oneOf cs :: TokenClass Char) == notOneOf cs
prop "notB . notOneOf = oneOf" $
\(cs :: [Char]) ->
notB (notOneOf cs :: TokenClass Char) == oneOf cs
prop "notB . asIn = notAsIn" $
\(cat :: GeneralCategory) ->
notB (asIn cat :: TokenClass Char) == notAsIn cat
prop "notB . notAsIn = asIn" $
\(cat :: GeneralCategory) ->
notB (notAsIn cat :: TokenClass Char) == asIn cat
prop "x >||< x = x" $ \(x :: TokenClass Char) ->
(x >||< x) == x
prop "x >&&< x = x" $ \(x :: TokenClass Char) ->
(x >&&< x) == x