-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor tests into separate modules
- Loading branch information
Showing
12 changed files
with
432 additions
and
334 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
module Empty where | ||
import Control.Applicative | ||
import Test.Tasty | ||
import Test.Tasty.QuickCheck as QC | ||
|
||
import Text.Earley | ||
|
||
tests :: TestTree | ||
tests = testGroup "Empty productions" | ||
[ QC.testProperty "The empty production doesn't parse anything" $ | ||
\(input :: String) -> | ||
allParses (parser (return empty :: forall r. Grammar r (Prod r () Char ()))) input | ||
== (,) [] Report { position = 0 | ||
, expected = [] | ||
, unconsumed = input | ||
} | ||
, QC.testProperty "Many empty productions parse very little" $ | ||
\(input :: String) -> | ||
allParses (parser (return $ many empty <* pure "blah" :: forall r. Grammar r (Prod r () Char [()]))) input | ||
== (,) [([], 0)] Report { position = 0 | ||
, expected = [] | ||
, unconsumed = input | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
{-# LANGUAGE RecursiveDo #-} | ||
module Expr where | ||
import Control.Applicative | ||
import Data.Char | ||
import Test.Tasty | ||
import Test.Tasty.QuickCheck as QC | ||
|
||
import Text.Earley | ||
|
||
tests :: TestTree | ||
tests = testGroup "Expr" | ||
[ QC.testProperty "Expr: parse . pretty = id" $ | ||
\e -> [e] === parseExpr (prettyExpr 0 e) | ||
, QC.testProperty "Ambiguous Expr: parse . pretty ≈ id" $ | ||
\e -> e `elem` parseAmbiguousExpr (prettyExpr 0 e) | ||
] | ||
|
||
parseExpr :: String -> [Expr] | ||
parseExpr input = fst (fullParses (parser expr) (lexExpr input)) -- We need to annotate types for point-free version | ||
|
||
parseAmbiguousExpr :: String -> [Expr] | ||
parseAmbiguousExpr input = fst (fullParses (parser ambiguousExpr) (lexExpr input)) | ||
|
||
data Expr | ||
= Add Expr Expr | ||
| Mul Expr Expr | ||
| Var String | ||
deriving (Eq, Ord, Show) | ||
|
||
instance Arbitrary Expr where | ||
arbitrary = sized arbExpr | ||
where arbIdent = Var <$> elements ["a", "b", "c", "x", "y", "z"] | ||
arbExpr n | n > 0 = oneof [ arbIdent | ||
, Add <$> arbExpr1 <*> arbExpr1 | ||
, Mul <$> arbExpr1 <*> arbExpr1 | ||
] | ||
where arbExpr1 = arbExpr (n `div` 2) | ||
arbExpr _ = arbIdent | ||
|
||
shrink (Var _) = [] | ||
shrink (Add a b) = a : b : [ Add a' b | a' <- shrink a ] ++ [ Add a b' | b' <- shrink b ] | ||
shrink (Mul a b) = a : b : [ Mul a' b | a' <- shrink a ] ++ [ Mul a b' | b' <- shrink b ] | ||
|
||
expr :: Grammar r (Prod r String String Expr) | ||
expr = mdo | ||
x1 <- rule $ Add <$> x1 <* namedSymbol "+" <*> x2 | ||
<|> x2 | ||
<?> "sum" | ||
x2 <- rule $ Mul <$> x2 <* namedSymbol "*" <*> x3 | ||
<|> x3 | ||
<?> "product" | ||
x3 <- rule $ Var <$> (satisfy ident <?> "identifier") | ||
<|> namedSymbol "(" *> x1 <* namedSymbol ")" | ||
return x1 | ||
where | ||
ident (x:_) = isAlpha x | ||
ident _ = False | ||
|
||
ambiguousExpr :: Grammar r (Prod r String String Expr) | ||
ambiguousExpr = mdo | ||
x1 <- rule $ Add <$> x1 <* namedSymbol "+" <*> x1 | ||
<|> x2 | ||
<?> "sum" | ||
x2 <- rule $ Mul <$> x2 <* namedSymbol "*" <*> x2 | ||
<|> x3 | ||
<?> "product" | ||
x3 <- rule $ Var <$> (satisfy ident <?> "identifier") | ||
<|> namedSymbol "(" *> x1 <* namedSymbol ")" | ||
return x1 | ||
where | ||
ident (x:_) = isAlpha x | ||
ident _ = False | ||
|
||
prettyParens :: Bool -> String -> String | ||
prettyParens True s = "(" ++ s ++ ")" | ||
prettyParens False s = s | ||
|
||
prettyExpr :: Int -> Expr -> String | ||
prettyExpr _ (Var s) = s | ||
prettyExpr d (Add a b) = prettyParens (d > 0) $ prettyExpr 0 a ++ " + " ++ prettyExpr 1 b | ||
prettyExpr d (Mul a b) = prettyParens (d > 1) $ prettyExpr 1 a ++ " * " ++ prettyExpr 2 b | ||
|
||
-- @words@ like lexer, but consider parentheses as separate tokens | ||
lexExpr :: String -> [String] | ||
lexExpr "" = [] | ||
lexExpr ('(' : s) = "(" : lexExpr s | ||
lexExpr (')' : s) = ")" : lexExpr s | ||
lexExpr (c : s) | ||
| isSpace c = lexExpr s | ||
| otherwise = let (tok, rest) = span p (c : s) | ||
in tok : lexExpr rest | ||
where p x = not (x == '(' || x == ')' || isSpace x) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{-# LANGUAGE RecursiveDo, ScopedTypeVariables #-} | ||
module InlineAlts where | ||
import Control.Applicative | ||
import Test.Tasty | ||
import Test.Tasty.HUnit as HU | ||
|
||
import Text.Earley | ||
|
||
tests :: TestTree | ||
tests = testGroup "Inline alternatives" | ||
[ HU.testCase "They work" $ | ||
let input = "ababbbaaabaa" in | ||
allParses (parser inlineAlts) input @?= allParses (parser nonInlineAlts) input | ||
] | ||
|
||
inlineAlts :: Grammar r (Prod r Char Char String) | ||
inlineAlts = mdo | ||
p <- rule $ pure [] | ||
<|> (:) <$> (namedSymbol 'a' <|> namedSymbol 'b') <*> p | ||
return p | ||
|
||
nonInlineAlts :: Grammar r (Prod r Char Char String) | ||
nonInlineAlts = mdo | ||
ab <- rule $ namedSymbol 'a' <|> namedSymbol 'b' | ||
p <- rule $ pure [] <|> (:) <$> ab <*> p | ||
return p |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
{-# LANGUAGE RecursiveDo #-} | ||
module Issue11 where | ||
import Control.Applicative | ||
import Test.Tasty | ||
import Test.Tasty.HUnit as HU | ||
|
||
import Text.Earley | ||
import Text.Earley.Mixfix | ||
|
||
tests :: TestTree | ||
tests = testGroup "Issue 11" | ||
[ let x = words "+ + 5 6 7" in | ||
HU.testCase "1" $ | ||
fullParses (parser $ grammar LeftAssoc) x | ||
@?= (,) [] Report {position = 1, expected = [], unconsumed = drop 1 x} | ||
, let x = words "+ 5 + 6 7" in | ||
HU.testCase "2" $ | ||
fullParses (parser $ grammar LeftAssoc) x | ||
@?= (,) [] Report {position = 2, expected = [], unconsumed = drop 2 x} | ||
, let x = words "+ 5 6" in | ||
HU.testCase "3" $ | ||
fullParses (parser $ grammar LeftAssoc) x | ||
@?= (,) [Plus (Var "5") (Var "6")] | ||
Report {position = 3, expected = [], unconsumed = []} | ||
, let x = words "+ + 5 6 7" in | ||
HU.testCase "4" $ | ||
fullParses (parser $ grammar RightAssoc) x | ||
@?= (,) [Plus (Plus (Var "5") (Var "6")) (Var "7")] | ||
Report {position = 5, expected = [], unconsumed = []} | ||
, let x = words "+ 5 + 6 7" in | ||
HU.testCase "5" $ | ||
fullParses (parser $ grammar RightAssoc) x | ||
@?= (,) [Plus (Var "5") (Plus (Var "6") (Var "7"))] | ||
Report {position = 5, expected = [], unconsumed = []} | ||
, let x = words "+ 5 6" in | ||
HU.testCase "6" $ | ||
fullParses (parser $ grammar RightAssoc) x | ||
@?= (,) [Plus (Var "5") (Var "6")] | ||
Report {position = 3, expected = [], unconsumed = []} | ||
, let x = words "+ + 5 6 7" in | ||
HU.testCase "7" $ | ||
fullParses (parser $ grammar NonAssoc) x | ||
@?= (,) [Plus (Plus (Var "5") (Var "6")) (Var "7")] | ||
Report {position = 5, expected = [], unconsumed = []} | ||
, let x = words "+ 5 + 6 7" in | ||
HU.testCase "8" $ | ||
fullParses (parser $ grammar NonAssoc) x | ||
@?= (,) [Plus (Var "5") (Plus (Var "6") (Var "7"))] | ||
Report {position = 5, expected = [], unconsumed = []} | ||
, let x = words "+ 5 6" in | ||
HU.testCase "9" $ | ||
fullParses (parser $ grammar NonAssoc) x | ||
@?= (,) [Plus (Var "5") (Var "6")] | ||
Report {position = 3, expected = [], unconsumed = []} | ||
] | ||
|
||
data AST | ||
= Var String | ||
| Plus AST AST | ||
deriving (Eq, Ord, Show) | ||
|
||
grammar :: Associativity -> Grammar r (Prod r String String AST) | ||
grammar a = mdo | ||
atomicExpr <- rule $ Var <$> satisfy (/= "+") | ||
|
||
expr <- mixfixExpression | ||
[[([Just (symbol "+"), Nothing, Nothing], a)]] | ||
atomicExpr | ||
(\x y -> case (x,y) of | ||
([Just "+", Nothing, Nothing], [e1,e2]) -> Plus e1 e2 | ||
_ -> undefined) | ||
|
||
return expr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
module Issue14 where | ||
import Control.Applicative | ||
import Test.Tasty | ||
import Test.Tasty.QuickCheck as QC | ||
|
||
import Text.Earley | ||
|
||
tests :: TestTree | ||
tests = testGroup "Issue 14" | ||
[ QC.testProperty "The same rule in alternatives gives many results" $ | ||
\x -> fullParses (parser (issue14 x)) "" | ||
== (,) (replicate (issue14Length x) ()) | ||
Report { position = 0, expected = [], unconsumed = [] } | ||
] | ||
|
||
data Issue14 a | ||
= Pure a | ||
| Alt (Issue14 a) (Issue14 a) | ||
| Ap (Issue14 a) (Issue14 a) | ||
deriving (Eq, Ord, Show) | ||
|
||
instance Arbitrary a => Arbitrary (Issue14 a) where | ||
arbitrary = sized arbTree | ||
where arbTree n | n > 0 = oneof [ Pure <$> arbitrary | ||
, Alt <$> arbTree1 <*> arbTree1 | ||
, Ap <$> arbTree1 <*> arbTree1 | ||
] | ||
where arbTree1 = arbTree (n `div` 2) | ||
arbTree _ = Pure <$> arbitrary | ||
|
||
shrink (Pure a) = Pure <$> shrink a | ||
shrink (Alt a b) = a : b : [Alt a' b | a' <- shrink a] ++ [Alt a b' | b' <- shrink b] | ||
shrink (Ap a b) = a : b : [Ap a' b | a' <- shrink a] ++ [Ap a b' | b' <- shrink b] | ||
|
||
issue14Length :: Issue14 () -> Int | ||
issue14Length (Pure ()) = 1 | ||
issue14Length (Alt a b) = issue14Length a + issue14Length b | ||
issue14Length (Ap a b) = issue14Length a * issue14Length b | ||
|
||
issue14 :: Issue14 () -> Grammar r (Prod r () Char ()) | ||
issue14 tree = do | ||
emptyRule <- rule $ pure () | ||
let x = go emptyRule tree | ||
return x | ||
where | ||
go x (Pure ()) = x | ||
go x (Alt b1 b2) = go x b1 <|> go x b2 | ||
go x (Ap b1 b2) = go x b1 <* go x b2 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module Main where | ||
import Test.Tasty | ||
|
||
import qualified Empty | ||
import qualified Expr | ||
import qualified InlineAlts | ||
import qualified Issue11 | ||
import qualified Issue14 | ||
import qualified Mixfix | ||
import qualified Optional | ||
import qualified ReversedWords | ||
import qualified VeryAmbiguous | ||
|
||
main :: IO () | ||
main = defaultMain $ testGroup "Tests" | ||
[ Empty.tests | ||
, Expr.tests | ||
, InlineAlts.tests | ||
, Issue11.tests | ||
, Issue14.tests | ||
, Mixfix.tests | ||
, Optional.tests | ||
, ReversedWords.tests | ||
, VeryAmbiguous.tests | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
module Mixfix where | ||
import Control.Applicative | ||
import Test.Tasty | ||
import Test.Tasty.HUnit as HU | ||
|
||
import Text.Earley | ||
import Text.Earley.Mixfix | ||
|
||
tests :: TestTree | ||
tests = testGroup "Mixfix" | ||
[ HU.testCase "1" $ | ||
let x = Ident [Just "x"] in | ||
fullParses (parser mixfixGrammar) (words "if x then x else x") | ||
@?= (,) [App ifthenelse [x, x, x]] Report {position = 6, expected = [], unconsumed = []} | ||
, HU.testCase "2" $ | ||
let x = Ident [Just "x"] in | ||
fullParses (parser mixfixGrammar) (words "prefix x postfix") | ||
@?= (,) [App prefix [App postfix [x]]] Report {position = 3, expected = [], unconsumed = []} | ||
, HU.testCase "3" $ | ||
let x = Ident [Just "x"] in | ||
fullParses (parser mixfixGrammar) (words "x infix1 x infix2 x") | ||
@?= (,) [App infix1 [x, App infix2 [x, x]]] Report {position = 5, expected = [], unconsumed = []} | ||
, HU.testCase "4" $ | ||
let x = Ident [Just "x"] in | ||
fullParses (parser mixfixGrammar) (words "[ x ]") | ||
@?= (,) [App closed [x]] Report {position = 3, expected = [], unconsumed = []} | ||
] | ||
|
||
data MixfixExpr = Ident (Holey String) | App (Holey String) [MixfixExpr] | ||
deriving (Eq, Show) | ||
|
||
mixfixGrammar :: Grammar r (Prod r String String MixfixExpr) | ||
mixfixGrammar = mixfixExpression table | ||
(Ident . pure . Just <$> namedSymbol "x") | ||
App | ||
where | ||
hident = map (fmap symbol) | ||
table = | ||
[ [(hident ifthenelse, RightAssoc)] | ||
, [(hident prefix, RightAssoc)] | ||
, [(hident postfix, LeftAssoc)] | ||
, [(hident infix1, LeftAssoc)] | ||
, [(hident infix2, RightAssoc)] | ||
, [(hident closed, NonAssoc)] | ||
] | ||
|
||
ifthenelse, prefix, postfix, infix1, infix2, closed :: Holey String | ||
ifthenelse = [Just "if", Nothing, Just "then", Nothing, Just "else", Nothing] | ||
prefix = [Just "prefix", Nothing] | ||
postfix = [Nothing, Just "postfix"] | ||
infix1 = [Nothing, Just "infix1", Nothing] | ||
infix2 = [Nothing, Just "infix2", Nothing] | ||
closed = [Just "[", Nothing, Just "]"] |
Oops, something went wrong.