packages feed

megaparsec-6.4.0: tests/Control/Applicative/CombinatorsSpec.hs

{-# LANGUAGE CPP        #-}
{-# LANGUAGE MultiWayIf #-}

module Control.Applicative.CombinatorsSpec (spec) where

import Data.Char (isLetter, isDigit)
import Data.List (intersperse)
import Data.Maybe (fromMaybe, maybeToList, isNothing, fromJust)
import Data.Monoid
import Test.Hspec
import Test.Hspec.Megaparsec
import Test.Hspec.Megaparsec.AdHoc
import Test.QuickCheck
import Text.Megaparsec
import Text.Megaparsec.Char

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative hiding (many, some)
#endif

spec :: Spec
spec = do

  describe "between" $
    it "works" . property $ \pre c n' post -> do
      let p = between (string pre) (string post) (many (char c))
          n = getNonNegative n'
          b = length (takeWhile (== c) post)
          z = replicate n c
          s = pre ++ z ++ post
      if b > 0
        then prs_ p s `shouldFailWith` err (posN (length pre + n + b) s)
          ( etoks post <> etok c <>
            if length post == b
              then ueof
              else utoks (drop b post) )
        else prs_ p s `shouldParse` z

  describe "choice" $
    it "works" . property $ \cs' s' -> do
      let cs = getNonEmpty cs'
          p = choice (char <$> cs)
          s = [s']
      if s' `elem` cs
        then prs_ p s `shouldParse` s'
        else prs_ p s `shouldFailWith` err posI (utok s' <> mconcat (etok <$> cs))

  describe "count" $ do
    it "works" . property $ \n x' -> do
      let x = getNonNegative x'
          p = count n (char 'x')
          p' = count' n n (char 'x')
          s = replicate x 'x'
      prs_ p s `shouldBe` prs_ p' s
    rightOrder (count 3 letterChar) "abc" "abc"

  describe "count'" $ do
    it "works" . property $ \m n x' -> do
      let x = getNonNegative x'
          p = count' m n (char 'x')
          s = replicate x 'x'
      if | n <= 0 || m > n ->
           if x == 0
             then prs_ p s `shouldParse` ""
             else prs_ p s `shouldFailWith` err posI (utok 'x' <> eeof)
         | m <= x && x <= n ->
           prs_ p s `shouldParse` s
         | x < m ->
           prs_ p s `shouldFailWith` err (posN x s) (ueof <> etok 'x')
         | otherwise ->
           prs_ p s `shouldFailWith` err (posN n s) (utok 'x' <> eeof)
    rightOrder (count' 1 3 letterChar) "abc" "abc"

  describe "eitherP" $
    it "works" . property $ \ch -> do
      let p = eitherP letterChar digitChar
          s = pure ch
      if | isLetter ch -> prs_ p s `shouldParse` Left ch
         | isDigit  ch -> prs_ p s `shouldParse` Right ch
         | otherwise   -> prs_ p s `shouldFailWith`
           err posI (utok ch <> elabel "letter" <> elabel "digit")

  describe "endBy" $ do
    it "works" . property $ \n' c -> do
      let n = getNonNegative n'
          p = endBy (char 'a') (char '-')
          s = intersperse '-' (replicate n 'a') ++ [c]
      if | c == 'a' && n == 0 ->
           prs_ p s `shouldFailWith` err (posN (1 :: Int) s) (ueof <> etok '-')
         | c == 'a' ->
           prs_ p s `shouldFailWith` err (posN (g n) s) (utok 'a' <> etok '-')
         | c == '-' && n == 0 ->
           prs_ p s `shouldFailWith` err posI (utok '-' <> etok 'a'<> eeof)
         | c /= '-' ->
           prs_ p s `shouldFailWith` err (posN (g n) s)
             ( utok c <>
               (if n > 0 then etok '-' else eeof) <>
               (if n == 0 then etok 'a' else mempty) )
         | otherwise -> prs_ p s `shouldParse` replicate n 'a'
    rightOrder (endBy letterChar (char ',')) "a,b,c," "abc"

  describe "endBy1" $ do
    it "works" . property $ \n' c -> do
      let n = getNonNegative n'
          p = endBy1 (char 'a') (char '-')
          s = intersperse '-' (replicate n 'a') ++ [c]
      if | c == 'a' && n == 0 ->
           prs_ p s `shouldFailWith` err (posN (1 :: Int) s) (ueof <> etok '-')
         | c == 'a' ->
           prs_ p s `shouldFailWith` err (posN (g n) s) (utok 'a' <> etok '-')
         | c == '-' && n == 0 ->
           prs_ p s `shouldFailWith` err posI (utok '-' <> etok 'a')
         | c /= '-' ->
           prs_ p s `shouldFailWith` err (posN (g n) s)
             ( utok c <>
               (if n > 0 then etok '-' else mempty) <>
               (if n == 0 then etok 'a' else mempty) )
         | otherwise -> prs_ p s `shouldParse` replicate n 'a'
    rightOrder (endBy1 letterChar (char ',')) "a,b,c," "abc"

  describe "manyTill" $ do
    it "works" . property $ \a' b' c' -> do
      let [a,b,c] = getNonNegative <$> [a',b',c']
          p = (,) <$> manyTill letterChar (char 'c') <*> many letterChar
          s = abcRow a b c
      if c == 0
        then prs_ p s `shouldFailWith` err (posN (a + b) s)
             (ueof <> etok 'c' <> elabel "letter")
        else let (pre, post) = break (== 'c') s
             in prs_ p s `shouldParse` (pre, drop 1 post)
    rightOrder (manyTill letterChar (char 'd')) "abcd" "abc"

  describe "someTill" $ do
    it "works" . property $ \a' b' c' -> do
      let [a,b,c] = getNonNegative <$> [a',b',c']
          p = (,) <$> someTill letterChar (char 'c') <*> many letterChar
          s = abcRow a b c
      if | null s ->
           prs_ p s `shouldFailWith` err posI (ueof <> elabel "letter")
         | c == 0 ->
           prs_ p s `shouldFailWith` err (posN (a + b) s)
             (ueof <> etok 'c' <> elabel "letter")
         | s == "c" ->
           prs_ p s `shouldFailWith` err
             (posN (1 :: Int) s) (ueof <> etok 'c' <> elabel "letter")
         | head s == 'c' ->
           prs_ p s `shouldParse` ("c", drop 2 s)
         | otherwise ->
           let (pre, post) = break (== 'c') s
           in prs_ p s `shouldParse` (pre, drop 1 post)
    rightOrder (someTill letterChar (char 'd')) "abcd" "abc"

  describe "option" $
    it "works" . property $ \d a s -> do
      let p = option d (string a)
          p' = fromMaybe d <$> optional (string a)
      prs_ p s `shouldBe` prs_ p' s

  describe "sepBy" $ do
    it "works" . property $ \n' c' -> do
      let n = getNonNegative n'
          c = fromJust c'
          p = sepBy (char 'a') (char '-')
          s = intersperse '-' (replicate n 'a') ++ maybeToList c'
      if | isNothing c' ->
           prs_ p s `shouldParse` replicate n 'a'
         | c == 'a' && n == 0 ->
           prs_ p s `shouldParse` "a"
         | n == 0 ->
           prs_ p s `shouldFailWith` err posI
             (utok c <> etok 'a' <> eeof)
         | c == '-' ->
           prs_ p s `shouldFailWith` err (posN (length s) s)
             (ueof <> etok 'a')
         | otherwise ->
           prs_ p s `shouldFailWith` err (posN (g n) s)
             (utok c <> etok '-' <> eeof)
    rightOrder (sepBy letterChar (char ',')) "a,b,c" "abc"

  describe "sepBy1" $ do
    it "works" . property $ \n' c' -> do
      let n = getNonNegative n'
          c = fromJust c'
          p = sepBy1 (char 'a') (char '-')
          s = intersperse '-' (replicate n 'a') ++ maybeToList c'
      if | isNothing c' && n >= 1 ->
           prs_ p s `shouldParse` replicate n 'a'
         | isNothing c' ->
           prs_ p s `shouldFailWith` err posI (ueof <> etok 'a')
         | c == 'a' && n == 0 ->
           prs_ p s `shouldParse` "a"
         | n == 0 ->
           prs_ p s `shouldFailWith` err posI (utok c <> etok 'a')
         | c == '-' ->
           prs_ p s `shouldFailWith` err (posN (length s) s) (ueof <> etok 'a')
         | otherwise ->
           prs_ p s `shouldFailWith` err (posN (g n) s) (utok c <> etok '-' <> eeof)
    rightOrder (sepBy1 letterChar (char ',')) "a,b,c" "abc"

  describe "sepEndBy" $ do
    it "works" . property $ \n' c' -> do
      let n = getNonNegative n'
          c = fromJust c'
          p = sepEndBy (char 'a') (char '-')
          a = replicate n 'a'
          s = intersperse '-' (replicate n 'a') ++ maybeToList c'
      if | isNothing c' ->
           prs_ p s `shouldParse` a
         | c == 'a' && n == 0 ->
           prs_ p s `shouldParse` "a"
         | n == 0 ->
           prs_ p s `shouldFailWith` err posI (utok c <> etok 'a' <> eeof)
         | c == '-' ->
           prs_ p s `shouldParse` a
         | otherwise ->
           prs_ p s `shouldFailWith` err (posN (g n) s) (utok c <> etok '-' <> eeof)
    rightOrder (sepEndBy letterChar (char ',')) "a,b,c," "abc"

  describe "sepEndBy1" $ do
    it "works" . property $ \n' c' -> do
      let n = getNonNegative n'
          c = fromJust c'
          p = sepEndBy1 (char 'a') (char '-')
          a = replicate n 'a'
          s = intersperse '-' (replicate n 'a') ++ maybeToList c'
      if | isNothing c' && n >= 1 ->
           prs_ p s `shouldParse` a
         | isNothing c' ->
           prs_ p s `shouldFailWith` err posI (ueof <> etok 'a')
         | c == 'a' && n == 0 ->
           prs_ p s `shouldParse` "a"
         | n == 0 ->
           prs_ p s `shouldFailWith` err posI (utok c <> etok 'a')
         | c == '-' ->
           prs_ p s `shouldParse` a
         | otherwise ->
           prs_ p s `shouldFailWith` err (posN (g n) s) (utok c <> etok '-' <> eeof)
    rightOrder (sepEndBy1 letterChar (char ',')) "a,b,c," "abc"

  describe "skipMany" $
    it "works" . property $ \c n' a -> do
      let p = skipMany (char c) *> string a
          n = getNonNegative n'
          p' = many (char c) >> string a
          s = replicate n c ++ a
      prs_ p s `shouldBe` prs_ p' s

  describe "skipSome" $
    it "works" . property $ \c n' a -> do
      let p = skipSome (char c) *> string a
          n = getNonNegative n'
          p' = some (char c) >> string a
          s = replicate n c ++ a
      prs_ p s `shouldBe` prs_ p' s

  describe "skipCount" $
    it "works" . property $ \c n' a -> do
      let p = skipCount n (char c) *> string a
          n = getNonNegative n'
          p' = count n (char c) *> string a
          s = replicate n c ++ a
      prs_ p s `shouldBe` prs_ p' s

  describe "skipManyTill" $
    it "works" . property $ \c n' a -> c /= a ==> do
      let p = skipManyTill (char c) (char a)
          n = getNonNegative n'
          s = replicate n c ++ [a]
      prs_ p s `shouldParse` a

  describe "skipSomeTill" $
    it "works" . property $ \c n' a -> c /= a ==> do
      let p = skipSomeTill (char c) (char a)
          n = getNonNegative n'
          s = replicate n c ++ [a]
      if n == 0
        then prs_ p s `shouldFailWith` err posI (utok a <> etok c)
        else prs_ p s `shouldParse` a

----------------------------------------------------------------------------
-- Helpers

g :: Int -> Int
g x = x + if x > 0 then x - 1 else 0