{-# OPTIONS -w #-}
module Lambdabot.Plugin.Haskell.Free.Type where
import Control.Monad
import Lambdabot.Plugin.Haskell.Free.Parse
import Data.List
import Lambdabot.Plugin.Haskell.Free.Util
import Prelude hiding ((<>))
type TyVar = String
type TyName = String
data Type
= TyForall TyVar Type
| TyArr Type Type
| TyTuple [Type]
| TyCons TyName [Type]
| TyVar TyVar
deriving (Eq, Show)
precTYAPP, precARROW :: Int
precTYAPP = 11
precARROW = 10
instance Pretty Type where
prettyP p (TyForall v t)
= prettyParen (p > 0) (
text "forall" <+> text v <> text "." <+> prettyP 0 t
)
prettyP p (TyArr t1 t2)
= prettyParen (p > precARROW) (
prettyP (precARROW+1) t1 <+> text "->" <+> prettyP precARROW t2
)
prettyP _ (TyTuple [])
= parens empty
prettyP _ (TyTuple (t:ts))
= parens (prettyP 0 t <> prettyTs 0 (text ",") ts)
prettyP _ (TyCons "[]" [t])
= lbrack <> prettyP 0 t <> rbrack
prettyP p (TyCons cons ts)
= prettyParen (p > precTYAPP) (
text cons <> prettyTs (precTYAPP+1) empty ts
)
prettyP _ (TyVar v)
= text v
prettyTs :: Int -> Doc -> [Type] -> Doc
prettyTs p c [] = empty
prettyTs p c (t:ts) = c <+> prettyP p t <> prettyTs p c ts
parseType :: ParseS Type
parseType
= parseType' >>= return . normaliseType
parseType' :: ParseS Type
parseType'
= do
t <- peekToken
case t of
Just IdForall -> getToken >> parseForall
_ -> parseArrType
where
parseForall
= do
t <- getToken
case t of
Just (QVarId v)
-> parseForall >>= \t -> return (TyForall v t)
Just (QVarSym ".")
-> parseType'
_ -> fail "Expected variable or '.'"
parseArrType
= do
t1 <- parseBType
t <- peekToken
case t of
Just OpArrow
-> getToken >> parseType' >>= \t2 ->
return (TyArr t1 t2)
_ -> return t1
parseBType
= do
t1 <- parseAType
case t1 of
TyCons c ts
-> do
ts' <- parseBTypes
return (TyCons c (ts++ts'))
_ -> return t1
parseBTypes
= (parseBType >>= \t -> parseBTypes >>= \ts -> return (t:ts))
`mplus` return []
parseAType
= parseQTyCon `mplus` parseOtherAType
parseQTyCon
= do
t <- getToken
case t of
Just OpenParen
-> do
t <- getToken
case t of
Just CloseParen
-> return (TyCons "()" [])
Just OpArrow
-> match CloseParen
>> return (TyCons "->" [])
Just Comma
-> parseQTyConTuple 1
_ -> fail "Badly formed type constructor"
Just OpenBracket
-> match CloseBracket >> return (TyCons "[]" [])
Just (QConId v)
-> return (TyCons v [])
_ -> fail "Badly formed type constructor"
parseQTyConTuple :: Int -> ParseS Type
parseQTyConTuple i
= do
t <- getToken
case t of
Just Comma
-> parseQTyConTuple (i+1)
Just CloseParen
-> return (TyCons ("(" ++ take i (repeat ',') ++ ")") [])
_ -> fail "Badly formed type constructor"
parseOtherAType
= do
t1 <- getToken
case t1 of
Just OpenParen
-> do
t <- parseType'
parseTuple [t]
Just OpenBracket
-> parseType' >>= \t -> match CloseBracket
>> return (TyCons "[]" [t])
Just (QVarId v)
-> return (TyVar v)
_ -> fail "Badly formed type"
parseTuple ts
= do
t1 <- getToken
case t1 of
Just CloseParen
-> case ts of
[t] -> return t
_ -> return (TyTuple (reverse ts))
Just Comma
-> do
t <- parseType'
parseTuple (t:ts)
normaliseType :: Type -> Type
normaliseType t
= let (fvs,nt) = normaliseType' t
in foldr TyForall nt (nub fvs)
where
normaliseType' t@(TyVar v)
= ([v],t)
normaliseType' (TyForall v t')
= let (fvs,t) = normaliseType' t'
in (filter (/=v) fvs, TyForall v t)
normaliseType' (TyArr t1 t2)
= let
(fvs1,t1') = normaliseType' t1
(fvs2,t2') = normaliseType' t2
in
(fvs1++fvs2, TyArr t1' t2')
normaliseType' (TyTuple ts)
= let
fvsts = map normaliseType' ts
fvs = concat (map fst fvsts)
ts' = map snd fvsts
in (fvs, TyTuple ts')
normaliseType' (TyCons c ts)
= let
fvsts = map normaliseType' ts
fvs = concat (map fst fvsts)
ts' = map snd fvsts
in case c of
"->" -> case ts' of
[t1,t2] -> (fvs, TyArr t1 t2)
_ -> error "Arrow type should have 2 arguments"
_ -> case checkTuple c of
Just i
-> if i == length ts'
then (fvs, TyTuple ts')
else error "Tuple type has the wrong number of arguments"
Nothing
-> (fvs, TyCons c ts')
checkTuple ('(':')':cs)
= Just 0
checkTuple ('(':cs)
= checkTuple' 1 cs
checkTuple _
= Nothing
checkTuple' k ")"
= Just k
checkTuple' k (',':cs)
= checkTuple' (k+1) cs
checkTuple' _ _
= Nothing
readType :: String -> Type
readType s
= case parse parseType (lexer s) of
ParseSuccess t [] -> t
ParseSuccess t _ -> error "Extra stuff at end of type"
ParseError msg -> error msg