lambdabot-4.0: scripts/hoogle/src/Hoogle/MatchType.hs
{-
This file is part of Hoogle, (c) Neil Mitchell 2004-2005
http://www.cs.york.ac.uk/~ndm/hoogle/
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.0/
or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
-}
module Hoogle.MatchType(
TypeTable,
buildType,
lookupType,
compareTypes
) where
import Hoogle.TypeSig
import Hoogle.Result
import Hoogle.Parser
import Hoogle.Lexer
import Hoogle.MatchClass
import Hoogle.General
import Data.Maybe
import Data.List
data TypeTable = TypeTable [(ModuleName, Item, TypeCode)]
deriving Show
data TypeCode = TypeCode Int [Rule]
deriving Show
data Rule = FreeSet [Var]
| DataBind String Var
| ClassBind String [Var]
data Var = Var Int [Int]
instance Show Var where
show (Var n xs) = "@" ++ show n ++ concatMap (\x -> '.':show x) xs
instance Show Rule where
show (FreeSet x) = "{" ++ (concat $ intersperse " " $ map show x) ++ "}"
show (DataBind x y) = x ++ "=" ++ show y
show (ClassBind x y) = x ++ "=>" ++ show y
buildType :: [(ModuleName, Item)] -> TypeTable
buildType x = TypeTable $ map f x
where f (a, b) = (a, b, buildTypeCode $ typ b)
unpackSpine :: ConType -> (Constraint, [Type])
unpackSpine (c, TList (TLit "->":xs)) = (c, xs)
unpackSpine (c, x) = (c, [x])
buildTypeCode :: ConType -> TypeCode
buildTypeCode (con,typ) = TypeCode (length args) $ map asBound bound ++ concatMap asFree frees
where
frees = groupBy eqVar $ sortBy cmpVar free
(free, bound) = partition (isTVar . snd) binds
binds = concat $ zipWith f (map (\x -> Var x []) [0..]) (last args : init args)
asBound (var, TLit x) = DataBind x var
-- TODO: Multiparameter type classes
asFree x = FreeSet items : [ClassBind c [i] | i <- items, c <- classes]
where
items = map fst x
((_,TVar var):_) = x
classes = [r | TList [TLit r,TVar v] <- con, v == var]
f :: Var -> Type -> [(Var, Type)]
f var@(Var v vs) (TList (x:xs)) = (var, x) : (concat $ zipWith g [1..] xs)
where g n x = f (Var v (vs ++ [n])) x
f var x = [(var, x)]
args = snd $ unpackSpine (con,typ)
eqVar (_, TVar a) (_, TVar b) = a == b
cmpVar (_, TVar a) (_, TVar b) = compare a b
compareTypes :: ClassTable -> ConType -> ConType -> [[Reason]]
compareTypes classes left right = [a ++ b | a <- as, b <- bs]
where
as = map (map ReasonLeft . thd3) $ checkType classes cleft right
bs = map (map ReasonRight . thd3) $ checkType classes cright left
thd3 (_,_,x) = x
cleft = buildTypeCode left
cright = buildTypeCode right
lookupType :: ClassTable -> TypeTable -> ConType -> [Result]
lookupType classes (TypeTable types) find = mapMaybe f types
where
findCode = buildTypeCode find
f (modu, item, code) = do (order, a) <- checkTypeOne classes code find
(_, b) <- checkTypeOne classes findCode (typ item)
let reasons = map ReasonLeft a ++ map ReasonRight b
return $ Result
(Str $ showModuleName modu)
(Str $ name item)
(showTypeTags (typ item) order)
"func"
reasons
(score reasons)
(0 - length modu)
checkTypeOne :: ClassTable -> TypeCode -> ConType -> Maybe ([Int], [MatchAmount])
checkTypeOne ct tc typ = if null res then Nothing else Just (b, c)
where
(a,b,c) = maximumBy f res
res = checkType ct tc typ
f (a,_,_) (b,_,_) = compare a b
checkType :: ClassTable -> TypeCode -> ConType -> [(Score, [Int], [MatchAmount])]
checkType classes code@(TypeCode n xs) typ = if abs posExtraArgs > 2 then [] else res
where
extraArgs = n - length types
posExtraArgs = abs extraArgs
badArgs = replicate extraArgs ArgExtra
f (x:xs) = xs ++ [x]
res = [(score reasons, f $ map fst arg, badArgs ++ reasons) | arg <- args,
Just reasons <- [applyType classes code cons (map snd arg)]]
(cons, types) = unpackSpine typ
types2 = zip [1..] types
lastType = last types2
initType = init types2
addTypes = initType ++ replicate extraArgs (0, TVar "_")
args = map (lastType:) $ concatMap permute $ selection (n-1) addTypes
applyType :: ClassTable -> TypeCode -> Constraint -> [Type] -> Maybe [MatchAmount]
applyType classes (TypeCode n xs) cons types = if any isNothing res then Nothing
else Just (concatMap fromJust res)
where
res = map f xs
f :: Rule -> Maybe [MatchAmount]
f (ClassBind x y) = do items <- mapMaybeAll getElement y
lookupClass classes cons x items
f (DataBind x y) = case rootCtor y of
Nothing -> Nothing
Just "" -> Just [DataTooFree]
Just z -> if x == z then Just [] else Nothing
f (FreeSet x) = if Nothing `elem` roots || length roots > 2
then Nothing
else Just $ concat [tooSpecific, tooDifferent]
where
tooSpecific = if length roots == 2 then [DataTooSpecific] else []
tooDifferent = replicate (length items - 1) FreeDifferent
roots = nub $ Just "" : map rootCtor x
items = nub $ mapMaybe f x
f x = case fromJust (getElement x) of
TVar "_" -> Nothing
TVar x -> Just x
_ -> Nothing
-- empty string means a free variable, i.e no explicit root
-- Nothing means impossible
rootCtor :: Var -> Maybe String
rootCtor var = case getElement var of
Nothing -> Nothing
Just (TLit x) -> Just x
Just (TList (TLit x:xs)) -> Just x
x -> Just ""
getElement :: Var -> Maybe Type
getElement (Var n xs) = f xs (types !! n)
where
f [] x = Just x
f (n:ns) (TList xs) | n < length xs = f ns (xs !! n)
f (n:ns) (TVar x) = Just $ TVar "_"
f _ _ = Nothing