module Checks.DeriveCheck (deriveCheck) where
import Curry.Base.Ident
import Curry.Base.Position
import Curry.Base.Pretty
import Curry.Syntax
import Base.Messages (Message, posMessage)
import Env.TypeConstructor
deriveCheck :: TCEnv -> Module a -> [Message]
deriveCheck tcEnv (Module _ _ m _ _ ds) = concatMap (checkDecl m tcEnv) ds
checkDecl :: ModuleIdent -> TCEnv -> Decl a -> [Message]
checkDecl m tcEnv (DataDecl _ tc _ cs clss)
| null clss = []
| null cs = [errNoAbstractDerive tc]
| otherwise = concatMap (checkDerivable m tcEnv cs) clss
checkDecl m tcEnv (NewtypeDecl _ _ _ nc clss) =
concatMap (checkDerivable m tcEnv [toConstrDecl nc]) clss
checkDecl _ _ _ = []
checkDerivable :: ModuleIdent -> TCEnv -> [ConstrDecl] -> QualIdent -> [Message]
checkDerivable m tcEnv cs cls
| ocls == qEnumId && not (isEnum cs) = [errNotEnum cls]
| ocls == qBoundedId && not (isBounded cs) = [errNotBounded cls]
| ocls `notElem` derivableClasses = [errNotDerivable ocls]
| otherwise = []
where ocls = getOrigName m cls tcEnv
derivableClasses :: [QualIdent]
derivableClasses = [qEqId, qOrdId, qEnumId, qBoundedId, qReadId, qShowId]
isEnum :: [ConstrDecl] -> Bool
isEnum cs = all ((0 ==) . constrArity) cs
isBounded :: [ConstrDecl] -> Bool
isBounded cs = length cs == 1 || isEnum cs
toConstrDecl :: NewConstrDecl -> ConstrDecl
toConstrDecl (NewConstrDecl p c ty) = ConstrDecl p c [ty]
toConstrDecl (NewRecordDecl p c (l, ty)) =
RecordDecl p c [FieldDecl p [l] ty]
constrArity :: ConstrDecl -> Int
constrArity (ConstrDecl _ _ tys) = length tys
constrArity (ConOpDecl _ _ _ _) = 2
constrArity c@(RecordDecl _ _ _) = length $ recordLabels c
errNoAbstractDerive :: HasPosition a => a -> Message
errNoAbstractDerive p = posMessage p $
text "Instances can only be derived for data types with" <+>
text "at least one constructor"
errNotDerivable :: QualIdent -> Message
errNotDerivable cls = posMessage cls $ hsep $ map text
["Instances of type class", escQualName cls, "cannot be derived"]
errNotEnum :: HasPosition a => a -> Message
errNotEnum p = posMessage p $
text "Instances for Enum can be derived only for enumeration types"
errNotBounded :: HasPosition a => a -> Message
errNotBounded p = posMessage p $
text "Instances of Bounded can be derived only for enumeration" <+>
text "and single constructor types"