{-|
Module      : Language.Qux.Annotated.Exception
Description : Exceptions and utility raising functions.

Copyright   : (c) Henry J. Wylde, 2015
License     : BSD3
Maintainer  : public@hjwylde.com

Exceptions and utility creation functions.
-}

module Language.Qux.Annotated.Exception (
    -- * Type exceptions
    TypeException,

    -- ** Creation functions
    duplicateFunctionName, duplicateParameterName, invalidArgumentsCount, mismatchedType,
    undefinedFunctionCall
) where

import Data.List (intercalate)

import Language.Qux.Annotated.Parser (SourcePos, sourceName, sourceLine, sourceColumn)
import Language.Qux.Annotated.PrettyPrinter
import qualified Language.Qux.Annotated.Syntax as Ann
import Language.Qux.Syntax

import Text.PrettyPrint (doubleQuotes)


-- | An exception that occurs during type checking. See "Language.Qux.TypeChecker".
data TypeException = TypeException SourcePos String

instance Show TypeException where
    show (TypeException pos message) = show pos ++ ":\n" ++ message


-- |    @duplciateFunctionName decl@ creates a 'TypeException' indicating that a duplicate function
--      declaration @decl@ was found.
duplicateFunctionName :: Ann.Decl SourcePos -> TypeException
duplicateFunctionName (Ann.FunctionDecl pos (Ann.Id _ name) _ _) = TypeException pos ("duplicate function name \"" ++ name ++ "\"")

-- |    @duplicateParameterName parameter@ creates a 'TypeException' indicating that a duplicate
--      parameter @parameter@ was found.
duplicateParameterName :: Ann.Id SourcePos -> TypeException
duplicateParameterName (Ann.Id pos name) = TypeException pos ("duplicate parameter name \"" ++ name ++ "\"")
--
-- |    @invalidArgumentsCount received expected@ creates a 'TypeException' indicating that an
--      application call (@received@) with an invalid number of arguments was passed to a function
--      expecting @expected@.
invalidArgumentsCount :: Ann.Expr SourcePos -> Int -> TypeException
invalidArgumentsCount (Ann.ApplicationExpr pos _ arguments) expected = TypeException pos $ intercalate " " [
    "invalid arguments count", show $ length arguments,
    "\nexpecting", show expected
    ]

-- |    @mismatchedType received expects@ creates a 'TypeException' indicating that one of @expects@
--      was expected.
mismatchedType :: Ann.Type SourcePos -> [Type] -> TypeException
mismatchedType received expects = TypeException (Ann.ann received) $ intercalate " " [
    "unexpected type", renderOneLine $ doubleQuotes (pPrint received),
    "\nexpecting", sentence "or" (map (renderOneLine . doubleQuotes . pPrint) expects)
    ]

-- | @undefinedFunctionCall app@ creates a 'TypeException' indicating that an application call
-- (@app@) was made to an undefined function.
undefinedFunctionCall :: Ann.Expr SourcePos -> TypeException
undefinedFunctionCall (Ann.ApplicationExpr pos (Ann.Id _ name) _) = TypeException pos ("call to undefined function \"" ++ name ++ "\"")

sentence :: String -> [String] -> String
sentence _ [x]  = x
sentence sep xs = intercalate " " [intercalate ", " (map show $ init xs), sep, show $ last xs]