Earley-0.13.0.1: tests/Lambda.hs
{-# LANGUAGE RecursiveDo #-}
module Lambda where
import Control.Applicative
import Data.List as List
import Data.Foldable
import Test.Tasty
import Test.Tasty.HUnit as HU
import Test.Tasty.QuickCheck as QC
import Text.Earley
import qualified Arbitrary
tests :: TestTree
tests = testGroup "Lambda"
[ HU.testCase "Generate exactly 0" $
exactly 0 gen @?= []
, HU.testCase "Generate upTo 0" $
upTo 0 gen @?= []
, HU.testCase "Generate exactly 4" $
sort (snd <$> exactly 4 gen)
@?=
["(a)a","(a)b","(aa)","(ab)","(b)a","(b)b","(ba)","(bb)"
,"\\a.a","\\a.b","\\b.a","\\b.b","a(a)","a(b)","a+aa","a+ab"
,"a+ba","a+bb","aa+a","aa+b","aaaa","aaab","aaba","aabb"
,"ab+a","ab+b","abaa","abab","abba","abbb","b(a)","b(b)"
,"b+aa","b+ab","b+ba","b+bb","ba+a","ba+b","baaa","baab"
,"baba","babb","bb+a","bb+b","bbaa","bbab","bbba","bbbb"
]
, HU.testCase "upTo contains exactly" $ List.and (do
m <- [0..5]
let ys = snd <$> upTo m gen
n <- [0..m]
(_, x) <- upTo n gen
return $ x `List.elem` ys)
@? "exactly contains upTo"
, HU.testCase "language contains upTo" $ do
let ys = snd <$> language gen
List.and (do
n <- [0..5]
(_, x) <- upTo n gen
return $ x `List.elem` ys)
@? "exactly contains upTo"
, QC.testProperty "Arbitrary" $ do
let p = parser grammar
(e, s) <- Arbitrary.arbitrary $ generator grammar tokens
return
$ [e] === fst (fullParses p $ prettyExpr 0 e)
.&&. [e] === fst (fullParses p s)
]
where
gen = generator grammar tokens
data Expr
= Var Char
| Lam String Expr
| App Expr Expr
| Add Expr Expr
deriving (Eq, Ord, Show)
prettyExpr :: Int -> Expr -> String
prettyExpr _ (Var c) = [c]
prettyExpr d (Lam xs e) = prettyParens (d > 0) $ "\\" ++ xs ++ "." ++ prettyExpr d e
prettyExpr d (Add a b) = prettyParens (d > 1) $ prettyExpr 2 a ++ "+" ++ prettyExpr 1 b
prettyExpr d (App a b) = prettyParens (d > 3) $ prettyExpr 3 a ++ prettyExpr 4 b
prettyParens :: Bool -> String -> String
prettyParens True s = "(" ++ s ++ ")"
prettyParens False s = s
tokens :: String
tokens = "(\\ab.+*)"
instance Arbitrary Expr where
arbitrary = sized go
where
var = elements "ab"
go 0 = Var <$> var
go n = oneof
[ Var <$> var
, Lam <$> (take 2 <$> listOf1 var) <*> go'
, App <$> go' <*> go'
, Add <$> go' <*> go'
]
where
go' = go (n `div` 10)
shrink (Var _) = []
shrink (Lam xs e) = e : [Lam xs' e' | xs' <- shrink xs, not (null xs), e' <- shrink e]
shrink (App a b) = a : b : [App a' b' | a' <- shrink a, b' <- shrink b]
shrink (Add a b) = a : b : [Add a' b' | a' <- shrink a, b' <- shrink b]
grammar :: Grammar r (Prod r String Char Expr)
grammar = mdo
let v = asum (token <$> "ab")
<?> "variable"
x1 <- rule
$ Lam <$ token '\\' <*> some v <* token '.' <*> x1
<|> x2
<?> "lambda"
x2 <- rule
$ Add <$> x3 <* token '+' <*> x2
<|> x3
<?> "sum"
x3 <- rule
$ App <$> x3 <*> x4
<|> x4
<?> "application"
let x4 = Var <$> v
<|> token '(' *> x1 <* token ')'
return x1