happy-arbitrary-0.0.1: test/Language/Happy/ArbitrarySpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Strict #-}
module Language.Happy.ArbitrarySpec where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Language.Happy.Arbitrary (genTokens)
import Language.Happy.Ast (Node)
import Language.Happy.Lexer (Lexeme, runAlex)
import Language.Happy.Parser (parseGrammar)
import qualified Language.Happy.Parser as Parser
import Language.Happy.Tokens (LexemeClass (..))
import Test.Hspec (Spec, describe, expectationFailure,
it)
import Test.QuickCheck (forAll)
sampleToken :: LexemeClass -> Text
sampleToken c = case c of
IdName -> "name"
KwErrorhandlertype -> "%errorhandlertype"
KwError -> "%error"
KwExpect -> "%expect"
KwLeft -> "%left"
KwLexer -> "%lexer"
KwMonad -> "%monad"
KwName -> "%name"
KwRight -> "%right"
KwToken -> "%token"
KwTokentype -> "%tokentype"
PctColon -> ":"
PctColonColon -> "::"
PctPercentPercent -> "%%"
PctPipe -> "|"
LitCode -> "{ code }"
LitInteger -> "0"
LitString -> "'token'"
-- These should never be generated by genTokens.
ErrorToken -> "!!!ERROR!!!"
Eof -> "!!!EOF!!!"
parseToken :: Text -> LexemeClass
parseToken = read . Text.unpack . (!! 2) . concatMap (filter (not . Text.null) . Text.splitOn "\t") . Text.splitOn " "
tryParseGrammar :: Monad m => (Node (Lexeme Text) -> m ()) -> m ()
tryParseGrammar _ | BS.null Parser.source = return ()
tryParseGrammar f =
case runAlex (LBS.fromStrict Parser.source) parseGrammar of
Left err -> error err
Right ok -> f ok
spec :: Spec
spec = tryParseGrammar $ \g -> do
describe "genTokens" $ do
it "generates sequences that can be parsed again using the same grammar" $
forAll (Text.intercalate " " . map (sampleToken . parseToken) <$> genTokens "Grammar" g) $ \code -> do
case runAlex (LBS.fromStrict . Text.encodeUtf8 $ code) parseGrammar of
Left err -> expectationFailure err
Right ok -> print ok