packages feed

indentation-0.2.1: tests/ParensTrifecta.hs

module ParensTrifecta where

import Data.Monoid (Monoid(..))
import Control.Applicative

import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, assertEqual, assertFailure, Assertion)

import Text.Trifecta
import Text.Trifecta.Indentation

data A
  = Par A   -- '(' A ')'
  | Bra A   -- '[' A ']'
  | Seq A A -- A A
  | Nil     -- epsilon
  deriving (Show, Eq)

-- a :: (Monad m, Stream s m (Char, Indentation)) => ParsecT (IndentStream s) () m A
a :: (Applicative m, TokenParsing m, IndentationParsing m) => m A
a = choice [ Seq <$> a' <*> a, a', pure Nil ]

-- a' :: (Monad m, Stream s m (Char, Indentation)) => ParsecT (IndentStream s) () m A
a' :: (TokenParsing m, IndentationParsing m) => m A
a' = choice
    [ Par <$>
        between (localTokenMode (const Eq) $ symbolic '(')
                (localTokenMode (const Eq) $ symbolic ')')
                (localIndentation Gt a)
    , Bra <$>
        between (localTokenMode (const Ge) $ symbolic '[')
                (localTokenMode (const Ge) $ symbolic ']')
                (localIndentation Gt a)
    ]


evalCharIndentationParserT :: Monad m => IndentationParserT Char m a -> IndentationState -> m a
evalCharIndentationParserT = evalIndentationParserT

evalTokenIndentationParserT :: Monad m => IndentationParserT Token m a -> IndentationState -> m a
evalTokenIndentationParserT = evalIndentationParserT

runParse ev input
 = let indA = ev a $ mkIndentationState 0 infIndentation True Gt
   in case parseString indA mempty input of
    Failure err -> Left (show err)
    Success a -> Right a

runCharParse = runParse evalCharIndentationParserT
runTokenParse = runParse evalTokenIndentationParserT

-- conveniences for tests
parL = Par . listToSeq
braL = Bra . listToSeq

listToSeq [] = Nil
listToSeq (x:xs) = Seq x $ listToSeq xs

input1 = unlines [ "("
                 , "   [("
                 , "    )"
                 , "      ]"
                 , ")"
                 ]
output1c = runCharParse input1
output1t = runTokenParse input1
expected1 = listToSeq [ parL [braL [parL []]]
                      ]

input2 = unlines [ "("
                 , "       ["
                 , "      ("
                 , "      )"
                 , "        []"
                 , "    ]"
                 , "   ("
                 , "   )"
                 , ")"
                 ]
output2c = runCharParse input2
output2t = runTokenParse input2
expected2 = listToSeq [ parL [ braL [ parL []
                                    , braL []
                                    ]
                             , parL []
                             ]
                      ]


assertParsedOk :: (Show err, Show a, Eq a) => Either err a -> a -> Assertion
assertParsedOk actual expected =
  case actual of
   Right ok -> assertEqual "parsing succeeded, but " expected ok
   Left err -> assertFailure ("parse failed with " ++ show err
                              ++ ", expected " ++ show expected)

allTests :: TestTree
allTests =
  testGroup "parens (trifecta)"
  [
    testGroup "char parsing"
    [ testCase "1" $ assertParsedOk output1c expected1
    , testCase "2" $ assertParsedOk output2c expected2
    ]
  , testGroup "token parsing"
    [ testCase "1" $ assertParsedOk output1t expected1
    , testCase "2" $ assertParsedOk output2t expected2
    ]
  ]