morloc-0.33.0: library/Morloc/Frontend/Desugar.hs
{-|
Module : Morloc.Frontend.Desugar
Description : Write Module objects to resolve type aliases and such
Copyright : (c) Zebulun Arendsee, 2020
License : GPL-3
Maintainer : zbwrnz@gmail.com
Stability : experimental
-}
module Morloc.Frontend.Desugar (desugar, desugarType) where
import Morloc.Frontend.Namespace
import Morloc.Pretty ()
import Morloc.Data.Doc
import qualified Morloc.Monad as MM
import qualified Morloc.Data.DAG as MDD
import qualified Morloc.Data.Text as MT
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Morloc.Frontend.PartialOrder as MTP
desugar
:: DAG MVar Import ParserNode
-> MorlocMonad (DAG MVar [(EVar, EVar)] PreparedNode)
desugar s
-- DAG MVar Import ParserNode
= resolveImports s
-- DAG MVar (Map EVar EVar) ParserNode
>>= desugarDag
-- DAG MVar (Map EVar EVar) PreparedNode
>>= simplify
-- Add packer map
>>= addPackerMap
-- | Consider export/import information to determine which terms are imported
-- into each module. This step reduces the Import edge type to an m-to-n source
-- name to alias map.
resolveImports
:: DAG MVar Import ParserNode
-> MorlocMonad (DAG MVar [(EVar, EVar)] ParserNode)
resolveImports = MDD.mapEdgeWithNodeM resolveImport where
resolveImport
:: ParserNode
-> Import
-> ParserNode
-> MorlocMonad [(EVar, EVar)]
resolveImport _ (Import _ Nothing exc _) n2
= return
. map (\x -> (x,x)) -- alias is identical
. Set.toList
$ Set.difference (parserNodeExports n2) (Set.fromList exc)
resolveImport _ (Import _ (Just inc) exc _) n2
| length contradict > 0
= MM.throwError . CallTheMonkeys
$ "Error: The following terms are both included and excluded: " <>
render (tupledNoFold $ map pretty contradict)
| length missing > 0
= MM.throwError . CallTheMonkeys
$ "Error: The following terms are not exported: " <>
render (tupledNoFold $ map pretty missing)
| otherwise = return inc
where
missing = [n | (n, _) <- inc, not $ Set.member n (parserNodeExports n2)]
contradict = [n | (n, _) <- inc, elem n exc]
desugarDag
:: DAG MVar [(EVar, EVar)] ParserNode
-> MorlocMonad (DAG MVar [(EVar, EVar)] ParserNode)
desugarDag m = do
mapM_ checkForSelfRecursion (map parserNodeTypedefs (MDD.nodes m))
MDD.mapNodeWithKeyM (desugarParserNode m) m
simplify
:: (DAG MVar [(EVar, EVar)] ParserNode)
-> MorlocMonad (DAG MVar [(EVar, EVar)] PreparedNode)
simplify = return . MDD.mapNode prepare where
prepare :: ParserNode -> PreparedNode
prepare n1 = PreparedNode
{ preparedNodePath = parserNodePath n1
, preparedNodeBody = parserNodeBody n1
, preparedNodeSourceMap = parserNodeSourceMap n1
, preparedNodeExports = parserNodeExports n1
, preparedNodePackers = Map.empty -- This will be filled in in `addPackerMap`
, preparedNodeTypedefs = parserNodeTypedefs n1
}
checkForSelfRecursion :: Map.Map TVar (UnresolvedType, [TVar]) -> MorlocMonad ()
checkForSelfRecursion h = mapM_ (uncurry f) [(v,t) | (v,(t,_)) <- Map.toList h] where
f :: TVar -> UnresolvedType -> MorlocMonad ()
f v (VarU v')
| v == v' = MM.throwError . SelfRecursiveTypeAlias $ v
| otherwise = return ()
f _ (ExistU _ _ _) = MM.throwError $ CallTheMonkeys "existential crisis"
f v (ForallU _ t) = f v t
f v (FunU t1 t2) = f v t1 >> f v t2
f v (ArrU v0 ts)
| v == v0 = MM.throwError . SelfRecursiveTypeAlias $ v
| otherwise = mapM_ (f v) ts
f v (NamU _ _ _ rs) = mapM_ (f v) (map snd rs)
desugarParserNode
:: DAG MVar [(EVar, EVar)] ParserNode
-> MVar
-> ParserNode
-> MorlocMonad ParserNode
desugarParserNode d k n = do
nodeBody <- mapM (desugarExpr d k) (parserNodeBody n)
return $ n { parserNodeBody = nodeBody }
desugarExpr
:: DAG MVar [(EVar, EVar)] ParserNode
-> MVar
-> Expr
-> MorlocMonad Expr
desugarExpr _ _ e@(SrcE _) = return e
desugarExpr d k (Signature v t) = Signature v <$> desugarEType d k t
desugarExpr d k (Declaration v e) = Declaration v <$> desugarExpr d k e
desugarExpr _ _ UniE = return UniE
desugarExpr _ _ e@(VarE _) = return e
desugarExpr d k (AccE e key) = AccE <$> desugarExpr d k e <*> pure key
desugarExpr d k (ListE xs) = ListE <$> mapM (desugarExpr d k) xs
desugarExpr d k (TupleE xs) = TupleE <$> mapM (desugarExpr d k) xs
desugarExpr d k (LamE v e) = LamE v <$> desugarExpr d k e
desugarExpr d k (AppE e1 e2) = AppE <$> desugarExpr d k e1 <*> desugarExpr d k e2
desugarExpr d k (AnnE e ts) = AnnE <$> desugarExpr d k e <*> mapM (desugarType d k) ts
desugarExpr _ _ e@(NumE _) = return e
desugarExpr _ _ e@(LogE _) = return e
desugarExpr _ _ e@(StrE _) = return e
desugarExpr d k (RecE rs) = do
es <- mapM (desugarExpr d k) (map snd rs)
return (RecE (zip (map fst rs) es))
desugarEType :: DAG MVar [(EVar, EVar)] ParserNode -> MVar -> EType -> MorlocMonad EType
desugarEType d k (EType t ps cs) = EType <$> desugarType d k t <*> pure ps <*> pure cs
desugarType
:: DAG MVar [(EVar, EVar)] ParserNode
-> MVar
-> UnresolvedType
-> MorlocMonad UnresolvedType
desugarType d k t0@(VarU v) =
case lookupTypedefs v k d of
[] -> return t0
ts'@(t':_) -> do
(t, _) <- foldlM (mergeAliases v 0) t' ts'
desugarType d k t
desugarType d k (ExistU v ts ds) = do
ts' <- mapM (desugarType d k) ts
ds' <- mapM (desugarType d k) ds
return $ ExistU v ts' ds'
desugarType d k (ForallU v t) = ForallU v <$> desugarType d k t
desugarType d k (FunU t1 t2) = FunU <$> desugarType d k t1 <*> desugarType d k t2
desugarType d k (ArrU v ts) =
case lookupTypedefs v k d of
[] -> ArrU v <$> mapM (desugarType d k) ts
(t':ts') -> do
(t, vs) <- foldlM (mergeAliases v (length ts)) t' ts'
if length ts == length vs
-- substitute parameters into alias
then desugarType d k (foldr parsub (choiceExistential t) (zip vs (map choiceExistential ts)))
else MM.throwError $ BadTypeAliasParameters v (length vs) (length ts)
desugarType d k (NamU r v ts rs) = do
let keys = map fst rs
vals <- mapM (desugarType d k) (map snd rs)
return (NamU r v ts (zip keys vals))
lookupTypedefs
:: TVar
-> MVar
-> DAG MVar [(EVar, EVar)] ParserNode
-> [(UnresolvedType, [TVar])]
lookupTypedefs (TV lang v) k h
= catMaybes
. MDD.nodes
. MDD.mapNode (\(EVar v', typemap) -> Map.lookup (TV lang v') typemap)
$ MDD.lookupAliasedTerm (EVar v) k parserNodeTypedefs h
-- When a type alias is imported from two places, this function reconciles them, if possible
mergeAliases
:: TVar
-> Int
-> (UnresolvedType, [TVar])
-> (UnresolvedType, [TVar])
-> MorlocMonad (UnresolvedType, [TVar])
mergeAliases v i t@(t1, ts1) (t2, ts2)
| i /= length ts1 = MM.throwError $ BadTypeAliasParameters v i (length ts1)
| MTP.isSubtypeOf t1' t2'
&& MTP.isSubtypeOf t2' t1'
&& length ts1 == length ts2 = return t
| otherwise = MM.throwError (ConflictingTypeAliases (unresolvedType2type t1) (unresolvedType2type t2))
where
t1' = foldl (\t' v' -> ForallU v' t') t1 ts1
t2' = foldl (\t' v' -> ForallU v' t') t2 ts2
parsub :: (TVar, UnresolvedType) -> UnresolvedType -> UnresolvedType
parsub (v, t2) t1@(VarU v0)
| v0 == v = t2 -- substitute
| otherwise = t1 -- keep the original
parsub _ (ExistU _ _ _) = error "What the bloody hell is an existential doing down here?"
parsub pair (ForallU v t1) = ForallU v (parsub pair t1)
parsub pair (FunU a b) = FunU (parsub pair a) (parsub pair b)
parsub pair (ArrU v ts) = ArrU v (map (parsub pair) ts)
parsub pair (NamU r v ts rs) = NamU r v (map (parsub pair) ts) (zip (map fst rs) (map (parsub pair . snd) rs))
addPackerMap
:: (DAG MVar [(EVar, EVar)] PreparedNode)
-> MorlocMonad (DAG MVar [(EVar, EVar)] PreparedNode)
addPackerMap d = do
maybeDAG <- MDD.synthesizeDAG gatherPackers d
case maybeDAG of
Nothing -> MM.throwError CyclicDependency
(Just d') -> return d'
gatherPackers
:: MVar -- the importing module name (currently unused)
-> PreparedNode -- data about the importing module
-> [( MVar -- the name of an imported module
, [(EVar -- the name of a term in the imported module
, EVar -- the alias in the importing module
)]
, PreparedNode -- data about the imported module
)]
-> MorlocMonad PreparedNode
gatherPackers _ n1 es = do
let packers = starpack n1 Pack
unpackers = starpack n1 Unpack
nodepackers <- makeNodePackers packers unpackers n1
let m = Map.unionsWith (<>) $ map (\(_, e, n2) -> inheritPackers e n2) es
m' = Map.unionWith (<>) nodepackers m
return $ n1 { preparedNodePackers = m' }
starpack :: PreparedNode -> Property -> [(EVar, UnresolvedType, [Source])]
starpack n pro
= [ (v, t, maybeToList $ lookupSource v t (preparedNodeSourceMap n))
| (Signature v e@(EType t p _)) <- preparedNodeBody n
, isJust (langOf e)
, Set.member pro p]
where
lookupSource :: EVar -> UnresolvedType -> Map.Map (EVar, Lang) Source -> Maybe Source
lookupSource v t m = langOf t >>= (\lang -> Map.lookup (v, lang) m)
makeNodePackers
:: [(EVar, UnresolvedType, [Source])]
-> [(EVar, UnresolvedType, [Source])]
-> PreparedNode
-> MorlocMonad (Map.Map (TVar, Int) [UnresolvedPacker])
makeNodePackers xs ys n =
let xs' = map (\(x,y,z)->(x, choiceExistential y, z)) xs
ys' = map (\(x,y,z)->(x, choiceExistential y, z)) ys
items = [ ( packerKey t2
, [UnresolvedPacker (packerTerm v2 n) (packerType t1) ss1 ss2])
| (_ , t1, ss1) <- xs'
, (v2, t2, ss2) <- ys'
, packerTypesMatch t1 t2
]
in return $ Map.fromList items
packerTerm :: EVar -> PreparedNode -> Maybe EVar
packerTerm v n = listToMaybe . catMaybes $
[ termOf e
| (Signature v' e) <- preparedNodeBody n
, v == v'
, isNothing (langOf e)
]
where
termOf :: EType -> Maybe EVar
termOf e = case splitArgs (etype e) of
(_, [VarU (TV _ term), _]) -> Just $ EVar term
(_, [ArrU (TV _ term) _, _]) -> Just $ EVar term
_ -> Nothing
choiceExistential :: UnresolvedType -> UnresolvedType
choiceExistential (VarU v) = VarU v
choiceExistential (ExistU _ _ (t:_)) = (choiceExistential t)
choiceExistential (ExistU _ _ []) = error "Existential with no default value"
choiceExistential (ForallU v t) = ForallU v (choiceExistential t)
choiceExistential (FunU t1 t2) = FunU (choiceExistential t1) (choiceExistential t2)
choiceExistential (ArrU v ts) = ArrU v (map choiceExistential ts)
choiceExistential (NamU r v ts recs) = NamU r v (map choiceExistential ts) (zip (map fst recs) (map (choiceExistential . snd) recs))
packerTypesMatch :: UnresolvedType -> UnresolvedType -> Bool
packerTypesMatch t1 t2 = case (splitArgs t1, splitArgs t2) of
((vs1@[_,_], [t11, t12]), (vs2@[_,_], [t21, t22]))
-> MTP.equivalent (qualify vs1 t11) (qualify vs2 t22)
&& MTP.equivalent (qualify vs1 t12) (qualify vs2 t21)
_ -> False
packerType :: UnresolvedType -> UnresolvedType
packerType t = case splitArgs t of
(params, [t1, _]) -> qualify params t1
_ -> error "bad packer"
packerKey :: UnresolvedType -> (TVar, Int)
packerKey t = case splitArgs t of
(params, [VarU v, _]) -> (v, length params)
(params, [ArrU v _, _]) -> (v, length params)
(params, [NamU _ v _ _, _]) -> (v, length params)
_ -> error "bad packer"
qualify :: [TVar] -> UnresolvedType -> UnresolvedType
qualify [] t = t
qualify (v:vs) t = ForallU v (qualify vs t)
splitArgs :: UnresolvedType -> ([TVar], [UnresolvedType])
splitArgs (ForallU v u) =
let (vs, ts) = splitArgs u
in (v:vs, ts)
splitArgs (FunU t1 t2) =
let (vs, ts) = splitArgs t2
in (vs, t1:ts)
splitArgs t = ([], [t])
inheritPackers
:: [( EVar -- key in THIS module descrived in the PreparedNode argument
, EVar -- alias used in the importing module
)]
-> PreparedNode
-> Map.Map (TVar, Int) [UnresolvedPacker]
inheritPackers es n =
-- names of terms exported from this module
let names = Set.fromList (map (unEVar . fst) es)
in Map.map (map toAlias)
$ Map.filter (isImported names) (preparedNodePackers n)
where
toAlias :: UnresolvedPacker -> UnresolvedPacker
toAlias n' = n' { unresolvedPackerTerm = unresolvedPackerTerm n' >>= (flip lookup) es }
isImported :: Set.Set MT.Text -> [UnresolvedPacker] -> Bool
isImported _ [] = False
isImported names' (n0:_) = case unresolvedPackerTerm n0 of
(Just (EVar v)) -> Set.member v names'
_ -> False