{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Language.Cimple.DescribeAstSpec where

import           Test.Hspec          (Spec, describe, it, shouldBe,
                                      shouldNotContain)

import qualified Data.List.Extra     as List
import           Data.Text           (Text)
import qualified Data.Text           as Text
import           Language.Cimple.IO  (parseExpr, parseStmt, parseText)
import           Language.CimpleSpec (sampleToken)
import           Test.QuickCheck     (Testable (property))


expected :: (Text -> Either String a) -> Text -> String
expected parse code =
    case parse code of
        Left err -> snd $ List.breakOn "expected " err
        Right _  -> ""


spec :: Spec
spec = do
    describe "error messages" $ do
        it "has useful suggestions" $ do
            parseText "int a() {}" `shouldBe` Left
                ":1:10: Parse error near right brace: \"}\"; expected statement or declaration"

            expected parseText "Beep Boop" `shouldBe`
                "expected variable name"

            expected parseText "const *a() {}" `shouldBe`
                "expected type specifier"

            expected parseText "int a() { int }" `shouldBe`
                "expected variable name"

            expected parseStmt "(int){" `shouldBe`
                "expected constant or literal"

        it "has suggestions for any sequence of tokens in top level" $ do
            property $ \tokens ->
                expected parseText (Text.intercalate " " (map sampleToken tokens)) `shouldNotContain`
                    "expected one of"

        it "has suggestions for any sequence of tokens in expressions" $ do
            property $ \tokens ->
                expected parseExpr (Text.intercalate " " (map sampleToken tokens)) `shouldNotContain`
                    "expected one of"

        it "has suggestions for any sequence of tokens in statements" $ do
            property $ \tokens ->
                expected parseStmt (Text.intercalate " " (map sampleToken tokens)) `shouldNotContain`
                    "expected one of"

        it "does not support multiple declarators per declaration" $ do
            let ast = parseText "int main() { int a, b; }"
            ast `shouldBe` Left
                ":1:19: Parse error near comma: \",\"; expected '=' or ';'"