packages feed

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