packages feed

morloc-0.33.0: library/Morloc/Frontend/Internal.hs

{-|
Module      : Morloc.Frontend.Internal
Description : Utilities for type checking
Copyright   : (c) Zebulun Arendsee, 2020
License     : GPL-3
Maintainer  : zbwrnz@gmail.com
Stability   : experimental
-}

module Morloc.Frontend.Internal
  ( (+>)
  , (++>)
  , Renameable(..)
  , Applicable(..)
  , Typed(..) 
  , access1
  , access2
  , accessWith1
  , ann
  , anns
  , cut
  , generalize
  , generalizeE
  , generalizeTypeSet
  , index
  , lookupE
  , lookupU
  , mapU
  , mapU'
  , newqul
  , newvar
  , newvarRich
  , throwError
  , serialConstraint
  , incDepth
  , decDepth
  , getDepth
  , langsOf
  ) where

import Control.Monad.Except (throwError)
import Morloc.Frontend.Namespace
import qualified Control.Monad.State as CMS
import qualified Data.Set as Set
import qualified Morloc.Data.Text as MT
import qualified Morloc.Frontend.PartialOrder as P

class HasManyLanguages a where
  langsOf :: Gamma -> a -> [Maybe Lang]

instance HasManyLanguages TypeSet where
  langsOf _ (TypeSet Nothing es) = map langOf es
  langsOf _ (TypeSet (Just e) es) = langOf e : map langOf es

instance HasManyLanguages Expr where
  langsOf g0 e0 = unique $ Nothing : langsOf' g0 e0 where
    langsOf' _ (SrcE srcs) = map (Just . srcLang) srcs
    langsOf' _ (Signature _ t) = [langOf t] 
    langsOf' g (Declaration _ e) = langsOf' g e
    langsOf' _ UniE = [] 
    langsOf' g (VarE v) = case lookupE v g of  
      (Just (_, ts)) -> langsOf g ts
      Nothing -> []
    langsOf' g (AccE e _) = langsOf' g e
    langsOf' g (ListE es) = concat . map (langsOf' g) $ es
    langsOf' g (TupleE es) = concat . map (langsOf' g) $ es
    langsOf' g (LamE _ e) = langsOf' g e 
    langsOf' g (AppE e1 e2) = langsOf' g e1 ++ langsOf' g e2 
    langsOf' _ (AnnE _ ts) = map langOf ts
    langsOf' _ (NumE _) = []
    langsOf' _ (LogE _) = [] 
    langsOf' _ (StrE _) = []
    langsOf' g (RecE entries) = concat . map (langsOf' g . snd) $ entries

class Renameable a where
  rename :: a -> Stack a
  unrename :: a -> a

instance Renameable Expr where
  rename = mapU' rename
  unrename = mapU unrename

instance Renameable UnresolvedType where
  rename t@(VarU _) = return t
  rename (ExistU v ts ds) = ExistU <$> pure v <*> (mapM rename ts) <*> (mapM rename ds)
  rename (ForallU v t) = do
    v' <- rename v
    t' <- rename (P.substitute v (VarU v') t)
    return $ ForallU v' t'
  rename (FunU t1 t2) = FunU <$> rename t1 <*> rename t2
  rename (ArrU v ts) = ArrU <$> pure v <*> mapM rename ts
  rename (NamU r v ts rs) =
    NamU r <$> pure v <*> mapM rename ts <*> mapM (\(x, t) -> (,) <$> pure x <*> rename t) rs

  unrename (VarU v) = VarU (unrename v)
  unrename (ExistU v ts ds) = ExistU v (map unrename ts) (map unrename ds)
  unrename (ForallU v t) = ForallU (unrename v) (unrename t)
  unrename (FunU t1 t2) = FunU (unrename t1) (unrename t2)
  unrename (ArrU v ts) = ArrU v (map unrename ts)
  unrename (NamU r v ts rs) = NamU r v (map unrename ts) [(x, unrename t) | (x, t) <- rs]

instance Renameable TVar where
  unrename (TV l t) = TV l . head $ MT.splitOn "." t
  rename = newqul


class Applicable a where
  apply :: Gamma -> a -> a

-- | Apply a context to a type (See Dunfield Figure 8).
instance Applicable UnresolvedType where
  -- [G]a = a
  apply _ a@(VarU _) = a
  -- [G](A->B) = ([G]A -> [G]B)
  apply g (FunU a b) = FunU (apply g a) (apply g b)
  -- [G]ForallU a.a = forall a. [G]a
  apply g (ForallU x a) = ForallU x (apply g a)
  -- [G[a=t]]a = [G[a=t]]t
  apply g (ExistU v ts ds) =
    case lookupU v g of
      -- FIXME: this seems problematic - do I keep the previous parameters or the new ones?
      (Just t') -> apply g t' -- reduce an existential; strictly smaller term
      Nothing -> ExistU v (map (apply g) ts) (map (apply g) ds)
  apply g (ArrU v ts) = ArrU v (map (apply g) ts)
  apply g (NamU r v ts rs) = NamU r v (map (apply g) ts) (map (\(n, t) -> (n, apply g t)) rs)

instance Applicable Expr where
  apply g e = mapU (apply g) e

instance Applicable EType where
  apply g e = e { etype = apply g (etype e) }


class Typed a where
  toType :: Maybe Lang -> a -> Maybe UnresolvedType
  fromType :: Maybe Lang -> UnresolvedType -> a

instance Typed EType where
  toType lang e
    | (langOf . etype) e == lang = Just (etype e)
    | otherwise = Nothing
  fromType _ t =
    EType
      { etype = t
      , eprop = Set.empty
      , econs = Set.empty
      }


instance Typed TypeSet where
  toType Nothing (TypeSet e _) = e >>= toType Nothing
  toType lang (TypeSet _ ts) = case filter (\e -> (langOf . etype) e == lang) ts of 
    [ ] -> Nothing
    [e] -> Just (etype e)
    _ -> error "a typeset can contain only one instance of each language"

  fromType Nothing t = TypeSet (Just (fromType Nothing t)) []
  fromType lang t = TypeSet Nothing [fromType lang t]

serialConstraint :: UnresolvedType -> UnresolvedType -> Stack ()
serialConstraint t1 t2 = do
  s <- CMS.get
  CMS.put (s {stateSer = (t1, t2):stateSer s})

incDepth :: Stack Int
incDepth = do
  s <- CMS.get 
  let depth = stateDepth s + 1
  CMS.put (s {stateDepth = depth})
  return depth

decDepth :: Stack Int
decDepth = do
  s <- CMS.get 
  let depth = stateDepth s - 1
  CMS.put (s {stateDepth = depth})
  return depth

getDepth :: Stack Int
getDepth = CMS.gets stateDepth

mapU :: (UnresolvedType -> UnresolvedType) -> Expr -> Expr
mapU f (LamE v e) = LamE v (mapU f e)
mapU f (ListE es) = ListE (map (mapU f) es)
mapU f (TupleE es) = TupleE (map (mapU f) es)
mapU f (RecE rs) = RecE (zip (map fst rs) (map (mapU f . snd) rs))
mapU f (AppE e1 e2) = AppE (mapU f e1) (mapU f e2)
mapU f (AnnE e ts) = AnnE (mapU f e) (map f ts)
mapU f (Declaration v e) = Declaration v (mapU f e)
mapU f (Signature v e) = Signature v $ e {etype = f (etype e)}
mapU _ e = e

mapU' :: Monad m => (UnresolvedType -> m UnresolvedType) -> Expr -> m Expr
mapU' f (LamE v e) = LamE <$> pure v <*> mapU' f e
mapU' f (ListE es) = ListE <$> mapM (mapU' f) es
mapU' f (RecE rs) = do
  es' <- mapM (mapU' f . snd) rs
  return $ RecE (zip (map fst rs) es')
mapU' f (TupleE es) = TupleE <$> mapM (mapU' f) es
mapU' f (AppE e1 e2) = AppE <$> mapU' f e1 <*> mapU' f e2
mapU' f (AnnE e ts) = AnnE <$> mapU' f e <*> mapM f ts
mapU' f (Declaration v e) = Declaration <$> pure v <*> mapU' f e
mapU' f (Signature v e) = do
  t' <- f (etype e)
  return $ Signature v (e {etype = t'})
mapU' _ e = return e

(+>) :: Indexable a => Gamma -> a -> Gamma
(+>) xs x = (index x) : xs

(++>) :: Indexable a => Gamma -> [a] -> Gamma
(++>) g xs = map index (reverse xs) ++ g 

-- | remove context up to a marker
cut :: GammaIndex -> Gamma -> Stack Gamma
cut _ [] = throwError EmptyCut
cut i (x:xs)
  | i == x = return xs
  | otherwise = cut i xs

-- | Look up a type annotated expression
lookupE :: EVar -> Gamma -> Maybe (Expr, TypeSet)
lookupE _ [] = Nothing
lookupE v ((AnnG (Declaration v' e) t):gs)
  | v == v' = Just (e, t)
  | otherwise = lookupE v gs
lookupE v ((AnnG e@(VarE v') t):gs)
  | v == v' = Just (e, t)
  | otherwise = lookupE v gs
lookupE v (_:gs) = lookupE v gs

-- | Look up a solved existential type variable
lookupU :: TVar -> Gamma -> Maybe UnresolvedType
lookupU _ [] = Nothing
lookupU v ((SolvedG v' t):gs)
  | v == v' = Just t
  | otherwise = lookupU v gs
lookupU v (_:gs) = lookupU v gs

access1 :: TVar -> Gamma -> Maybe (Gamma, GammaIndex, Gamma)
access1 v gs =
  case findIndex (exists v) gs of
    (Just 0) -> Just ([], head gs, tail gs)
    (Just i) -> Just (take i gs, gs !! i, drop (i + 1) gs)
    _ -> Nothing
  where
    exists :: TVar -> GammaIndex -> Bool
    exists v1 (ExistG v2 _ _) = v1 == v2
    exists _ _ = False

accessWith1 :: Monad m =>
     (GammaIndex -> Bool) -- ^ method for finding the index
  -> (GammaIndex -> m GammaIndex) -- ^ alter GammaIndex
  -> (Gamma -> m Gamma) -- ^ default action if the index is not found
  -> Gamma -- ^ context that is searched
  -> m Gamma
accessWith1 select make def g =
  case findIndex select g of
    (Just i) ->
      case (i, g !! i) of
        (0, x) -> make x >>= (\y -> return ([] <> (y : tail g)))
        (_, x) -> make x >>= (\y -> return (take i g <> (y : drop (i + 1) g)))
    Nothing -> def g

access2 ::
     TVar -> TVar
  -> Gamma -> Maybe (Gamma, GammaIndex, Gamma, GammaIndex, Gamma)
access2 lv rv gs =
  case access1 lv gs of
    Just (ls, x, rs) ->
      case access1 rv rs of
        Just (ls', y, rs') -> Just (ls, x, ls', y, rs')
        _ -> Nothing
    _ -> Nothing

ann :: Expr -> UnresolvedType -> Expr
ann (AnnE e _) t = AnnE e [t] 
ann e@(Declaration _ _) _ = e
ann e@(Signature _ _) _ = e
ann e t = AnnE e [t]

anns :: Expr -> [UnresolvedType] -> Expr
anns (AnnE e _) ts = AnnE e ts 
anns e@(Declaration _ _) _ = e
anns e@(Signature _ _) _ = e
anns e ts = AnnE e ts

-- | Deal with existentials.
-- This function is used to resolve remaining existentials when no further
-- inferences about their type can be made. If the existentials have a default
-- type, then that type can be used to replace the existential. Otherwise, the
-- existential can be cast as generic (ForallU).
generalize :: UnresolvedType -> UnresolvedType
generalize = (\t -> generalize' (existentialMap t) t) . setDefaults where
  generalize' :: [(TVar, Name)] -> UnresolvedType -> UnresolvedType
  generalize' [] t = t
  generalize' ((e, r):xs) t = generalize' xs (generalizeOne e r t)

  setDefaults :: UnresolvedType -> UnresolvedType
  setDefaults (ExistU v ps []) = ExistU v (map setDefaults ps) []
  setDefaults (ExistU _ _ (d:_)) = setDefaults d
  setDefaults t@(VarU _) = t
  setDefaults (ForallU v t) = ForallU v (setDefaults t)
  setDefaults (FunU t1 t2) = FunU (setDefaults t1) (setDefaults t2)
  setDefaults (ArrU v ts) = ArrU v (map setDefaults ts)
  setDefaults (NamU r v ts es)
    = NamU r v (map setDefaults ts) (zip (map fst es) (map (setDefaults . snd) es))

  variables = [1 ..] >>= flip replicateM ['a' .. 'z']

  existentialMap t =
    zip (Set.toList (findExistentials t)) (map (Name . MT.pack) variables)

  findExistentials :: UnresolvedType -> Set.Set TVar
  findExistentials (VarU _) = Set.empty
  findExistentials (ExistU v ts ds) =
    Set.unions
      $ [Set.singleton v]
      ++ map findExistentials ts
      ++ map findExistentials ds
  findExistentials (ForallU v t) = Set.delete v (findExistentials t)
  findExistentials (FunU t1 t2) =
    Set.union (findExistentials t1) (findExistentials t2)
  findExistentials (ArrU _ ts) = Set.unions (map findExistentials ts)
  findExistentials (NamU _ _ ts rs)
    = Set.unions (map findExistentials ts ++ map (findExistentials . snd) rs)

  generalizeOne :: TVar -> Name -> UnresolvedType -> UnresolvedType
  generalizeOne v0@(TV lang0 _) r0 t0 = ForallU (TV lang0 (unName r0)) (f v0 t0)
    where
      f :: TVar -> UnresolvedType -> UnresolvedType
      f v t1@(ExistU v' [] _)
        | v == v' = VarU (TV lang0 (unName r0))
        | otherwise = t1
      f v (ExistU v' ts _)
        | v == v' = ArrU (TV lang0 (unName r0)) (map (f v) ts)
        | otherwise = ArrU v (map (f v) ts)
      f v (FunU t1 t2) = FunU (f v t1) (f v t2)
      f v t1@(ForallU x t2)
        | v /= x = ForallU x (f v t2)
        | otherwise = t1
      f v (ArrU v' xs) = ArrU v' (map (f v) xs)
      f v (NamU r v' ts xs) = NamU r v' (map (f v) ts) [(k, f v t) | (k, t) <- xs]
      f _ t1 = t1

generalizeE :: Expr -> Expr
generalizeE = mapU generalize

generalizeEType :: EType -> EType
generalizeEType e = e {etype = generalize (etype e)}

generalizeTypeSet :: TypeSet -> TypeSet
generalizeTypeSet (TypeSet t ts) =
  TypeSet (fmap generalizeEType t) (map generalizeEType ts)

newvar :: Maybe Lang -> Stack UnresolvedType
newvar = newvarRich [] []

newvarRich
  :: [UnresolvedType]
  -> [UnresolvedType] -- ^ default types
  -> Maybe Lang
  -> Stack UnresolvedType
newvarRich ps ds lang = do
  s <- CMS.get
  let v = newvars !! stateVar s
  CMS.put $ s {stateVar = stateVar s + 1}
  return (ExistU (TV lang v) ps ds)
  where
    newvars =
      zipWith (\x y -> MT.pack (x ++ show y)) (repeat "t") ([0 ..] :: [Integer])

newqul :: TVar -> Stack TVar
newqul (TV l v) = do
  s <- CMS.get
  let v' = TV l (v <> "." <> (MT.pack . show $ stateQul s)) -- create a new variable such as "a.0"
  CMS.put $ s {stateQul = stateQul s + 1}
  return v'