module Test.Angle.Parse.Parser
    ( tests
    ) where

import Angle.Parse.Parser.Internal
import qualified Test.QuickCheck.Property as P
import Angle.Types.Lang
import TestHelper


testShowSynStmt :: Stmt -> Bool
testShowSynStmt x = showSynTest x stmt


testShowSynSingStmt :: SingStmt -> Bool
testShowSynSingStmt x = showSynTest x singStmt


testShowSynLangStruct :: LangStruct -> P.Result
testShowSynLangStruct x = prettySyn x langStruct


testShowSynExpr :: Expr -> P.Result
testShowSynExpr x = prettySyn x expr


testShowSynLambda :: Lambda -> P.Result
testShowSynLambda x = prettySyn x lambda


showSynTest :: (ShowSyn a, Eq a) => a -> Parser a -> Bool
showSynTest x sc = evalParse (showSyn x) sc == Right x


prettySyn :: (ShowSyn a, Eq a) => a -> Parser a -> P.Result
prettySyn = withPretty f p
    where p x sc = either (const $ "Could not parse: \n" ++ showSyn x)
                     showSyn (evalParse (showSyn x) sc)
          f = showSynTest


withPretty :: (a -> Parser b -> Bool) -- ^ Test function to apply
           -> (a -> Parser b -> String) -- ^ Prettify function
           -> a -> Parser b -> P.Result
withPretty f pretty x sc = P.result
                             { P.ok = Just b
                             , P.reason = if b
                                          then ""
                                          else pretty x sc
                             }
    where b = f x sc


escapedStr :: String -> String
escapedStr xs = "\"" ++ xs ++ "\""



newtype NoQuoteString = NoQuoteString String
  deriving (Show, Eq)

instance Arbitrary NoQuoteString where
  arbitrary = liftM NoQuoteString $ suchThat arbitrary ("\"" `noneElem`)


testLitStrEmpty :: Bool
testLitStrEmpty = evalParse "\"\"" litStr == Right (LitStr "")


testLitStr :: String -> Property
testLitStr x = "\"\\" `noneElem` x ==> litStrShow x litStr


noneElem :: (Eq a) => [a] -> [a] -> Bool
noneElem xs ys = all (`notElem` ys) xs


litStrShow :: String -> Parser LangLit -> P.Result
litStrShow = withPretty f p
    where f x sc = evalParse (escapedStr x) sc == Right (LitStr x)
          p x sc = either (const "got an error!\n") showSyn (evalParse (escapedStr x) sc)


testLitInt :: Int -> Bool
testLitInt x = evalParse (show x) litInt == Right (LitInt x)


testLitBool :: Bool
testLitBool = evalParse "true" litBool == Right (LitBool True)
              && evalParse "false" litBool == Right (LitBool False)


testEmptyCall :: Bool
testEmptyCall = evalParse "foo()" exprFunCall
 == Right (ExprFunCall (LangIdent "foo") False [])


testOpAdd :: Bool
testOpAdd = evalParse "(+ 1 2)" langOp
 == Right (MultiOp OpAdd [ExprLit (LitInt 1), ExprLit (LitInt 2)])


testOpNeg :: Bool
testOpNeg = evalParse "-x" langOp ==  Right (SpecOp OpNeg (ExprIdent (LangIdent "x")))


tests :: [TestTree]
tests = [ testGroup "literals"
          [ testProperty "boolean" testLitBool
          , testProperty "integer" testLitInt
          , testProperty "string" testLitStr
          , testProperty "empty string" testLitStrEmpty
          ]
        , testGroup "functions"
          [ testProperty "no args from empty call" $ once testEmptyCall
          ]
        , testGroup "operators"
          [ testProperty "addition operator" $ once testOpAdd
          , testProperty "negation operator" $ once testOpNeg
          ]
        , localOption (QuickCheckMaxSize 10) $
          testGroup "show syntax"
          [ localOption (QuickCheckMaxSize 9) $
            testProperty "Stmt" testShowSynStmt
          , testProperty "SingStmt" testShowSynSingStmt
          , localOption (QuickCheckMaxSize 9) $
            testProperty "LangStruct" testShowSynLangStruct
          , testProperty "Expr" testShowSynExpr
          , localOption (QuickCheckMaxSize 10) $ testProperty "Lambda" testShowSynLambda
          ]
        ]
