packages feed

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

{-|
Module      : Morloc.Frontend.Treeify
Description : Translate from the frontend DAG to the backend SAnno AST forest
Copyright   : (c) Zebulun Arendsee, 2020
License     : GPL-3
Maintainer  : zbwrnz@gmail.com
Stability   : experimental
-}

module Morloc.Frontend.Treeify (treeify) where

import Morloc.Frontend.Namespace
import Morloc.Data.Doc
import Morloc.Frontend.PartialOrder ()
import qualified Morloc.Data.Text as MT
import qualified Morloc.Monad as MM
import qualified Morloc.Data.DAG as MDD
import qualified Data.Map as Map
import qualified Data.Set as Set
import Morloc.Frontend.Pretty ()

data TermOrigin = Declared Expr | Sourced Source
  deriving(Show, Ord, Eq)

treeify
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> MorlocMonad [SAnno GMeta Many [CType]]
treeify d
  | Map.size d == 0 = return []
  | otherwise = case MDD.roots d of
    [] -> MM.throwError CyclicDependency
    [k] -> case MDD.lookupNode k d of
      Nothing -> MM.throwError . DagMissingKey . render $ pretty k
      (Just n) -> do
        -- initialize state counter to 0, used to index manifolds
        MM.startCounter
        mapM (collect d n) (Set.toList (typedNodeExports n))
    _ -> MM.throwError . CallTheMonkeys $ "How did you end up with so many roots?"


-- -- | Build the call tree for a single nexus command. The result is ambiguous,
-- -- with 1 or more possible tree topologies, each with one or more possible for
-- -- each function.
collect
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> TypedNode
  -> EVar
  -> MorlocMonad (SAnno GMeta Many [CType])
collect d n v = do
  trees <- collectSExprs d n v

  -- Just look at one x, since any should emit the same GMeta (if not, then
  -- something is broken upstream of GMeta is not general enough)
  gmeta <- makeGMeta (Just v) n Nothing

  return $ SAnno (Many trees) gmeta

collectSExprs
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> TypedNode
  -> EVar
  -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]
collectSExprs d n v = do
  -- DAG MVar None (EVar, (TypedNode, [TermOrigin]))
  let termTree = MDD.lookupAliasedTerm v (typedNodeModuleName n) (makeTermOrigin v) d

  -- DAG MVar None [(SExpr GMeta Many [CType], [CType])]
  sexprTree <- MDD.mapNodeM (\(v',(n',ts)) -> collectTerms d v' n' ts) termTree

  -- [(SExpr GMeta Many [CType], [CType])]
  let trees = concat . MDD.nodes $ sexprTree

  return trees


-- | Find info common across realizations of a given term in a given module
makeGMeta
  :: Maybe EVar
  -> TypedNode
  -> Maybe GType
  -> MorlocMonad GMeta
makeGMeta name n gtype = do
  i <- MM.getCounter
  case name >>= (flip Map.lookup) (typedNodeTypeMap n) of
    (Just (TypeSet (Just e) _)) -> do
      let g = (Just . GType) $ resolve (etype e)
      return $ GMeta
        { metaId = i
        , metaGType = maybe g Just gtype
        , metaName = name
        , metaProperties = eprop e
        , metaConstraints = econs e
        , metaPackers = typedNodePackers n
        , metaConstructors = typedNodeConstructors n
        , metaTypedefs = typedNodeTypedefs n
        }
    _ -> do
      return $ GMeta
        { metaId = i
        , metaGType = gtype
        , metaName = name
        , metaProperties = Set.empty
        , metaConstraints = Set.empty
        , metaPackers = typedNodePackers n
        , metaConstructors = typedNodeConstructors n
        , metaTypedefs = typedNodeTypedefs n
        }

makeTermOrigin
  :: EVar
  -> TypedNode
  -> (TypedNode, [TermOrigin])
makeTermOrigin v n = (n, declared ++ sourced) where
  declared = [Declared e | (Declaration v' e) <- typedNodeBody n, v' == v]
  sourced = map Sourced
          $ filter (\s -> srcAlias s == v) (Map.elems $ typedNodeSourceMap n)


collectTerms
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> EVar
  -> TypedNode
  -> [TermOrigin]
  -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]
collectTerms d v n ts = mapM (collectTerm d v n) ts


-- Notice that `args` is NOT an input to collectTerm. Morloc uses lexical
-- scoping, and the input to collectTerm is the origin of a term, so the
-- definition of the term is outside the scope of the parent expression.
collectTerm
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> EVar
  -> TypedNode
  -> TermOrigin
  -> MorlocMonad (SExpr GMeta Many [CType], [CType])
collectTerm _ v n (Sourced src)
  = case Map.lookup v (typedNodeTypeMap n) of
    Nothing -> MM.throwError . CallTheMonkeys $ "No type found for this"
    (Just (TypeSet _ es)) -> do
      let ts = [etype e | e <- es, Just (srcLang src) == langOf e]
          ts' = map resolve ts
      return (CallS src, map CType ts')
collectTerm d _ n (Declared (AnnE e ts)) = do
  ts' <- getCTypes ts
  xs <- collectExpr d Set.empty n ts' e
  case xs of
    [x] -> return x
    _ -> MM.throwError . GeneratorError $
      "Expected exactly one topology for a declared term"
collectTerm _ _ _ (Declared _) = MM.throwError . GeneratorError $
  "Invalid expression in CollectTerm Declared, expected AnnE"


collectAnno
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> Set.Set EVar
  -> TypedNode
  -> Expr
  -> MorlocMonad (SAnno GMeta Many [CType])
collectAnno d args n (AnnE e ts) = do
  gtype <- getGType ts
  gmeta <- makeGMeta (getExprName e) n gtype
  ts' <- getCTypes ts
  trees <- collectExpr d args n ts' e
  return $ SAnno (Many trees) gmeta
collectAnno _ _ _ _ = error "impossible bug - unannotated expression"

getCTypes :: [UnresolvedType] -> MorlocMonad [CType]
getCTypes ts = do
  let ts' = map resolve [t | t <- ts, isJust (langOf t)]
  return $ map CType ts'



collectExpr
  :: DAG MVar [(EVar, EVar)] TypedNode
  -> Set.Set EVar
  -> TypedNode
  -> [CType]
  -> Expr
  -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]
collectExpr _ _ _ ts (UniE)   = return [(UniS, ts)]
collectExpr _ _ _ ts (NumE x) = return [(NumS x, ts)]
collectExpr _ _ _ ts (LogE x) = return [(LogS x, ts)]
collectExpr _ _ _ ts (StrE x) = return [(StrS x, ts)]
collectExpr d args n ts (VarE v)
  | Set.member v args = return [(VarS v, ts)]
  | otherwise = do
      xs <- collectSExprs d n v
      let chosen = map (chooseTypes ts) xs
      return chosen
  where
    -- FIXME: The typesystem should handle this. It should unroll every
    -- type as far as it can be unrolled, and infer specialized types all
    -- the way down. Multiple declarations of every term within a given
    -- language should be allowed. The function below will only work in
    -- special cases where there is A) a single instance of the term in
    -- each language and B) types beneath the term (if this is a
    -- composition) do not depend on the type on top.
    chooseTypes
      :: [CType]
      -> (SExpr GMeta Many [CType], [CType])
      -> (SExpr GMeta Many [CType], [CType])
    chooseTypes ts1 (x, ts2) =
      (x, [ t
          | t <- ts1
          , t' <- ts2
          , langOf t == langOf t'])
collectExpr d args n ts (AccE e k) = do
  e' <- collectAnno d args n e
  return [(AccS e' k, ts)]
collectExpr d args n ts (ListE es) = do
  es' <- mapM (collectAnno d args n) es
  return [(ListS es', ts)]
collectExpr d args n ts (TupleE es) = do
  es' <- mapM (collectAnno d args n) es
  return [(TupleS es', ts)]
collectExpr d args n ts (RecE entries) = do
  es' <- mapM (collectAnno d args n) (map snd entries)
  let entries' = zip (map fst entries) es'
  return [(RecS entries', ts)]
collectExpr d args n ts e@(LamE _ _) = do
  case unrollLambda e of
    (args', e') -> do
      e'' <- collectAnno d (Set.union args (Set.fromList args')) n e'
      return [(LamS args' e'', ts)]
collectExpr d args n _ (AppE e1 e2) = do
  -- The topology may vary. It could be a direct binary function. Or
  -- it could be a partially applied function. So it is necessary to map
  -- over the Many.
  (SAnno (Many fs) g1) <- collectAnno d args n e1
  e2' <- collectAnno d args n e2
  mapM (app g1 e2') fs
-- None of these should occur unless there is a bug in the code
collectExpr _ _ _ _ x@(AnnE _ _) = error $ show x
collectExpr _ _ _ _ x@(SrcE _) = error $ show x
collectExpr _ _ _ _ x@(Signature _ _) = error $ show x
collectExpr _ _ _ _ x@(Declaration _ _) = error $ show x



app
  :: GMeta
  -> SAnno GMeta Many [CType]
  -> (SExpr GMeta Many [CType], [CType])
  -> MorlocMonad (SExpr GMeta Many [CType], [CType])
app _ e2 ((AppS f es), ts) = do
  ts' <- mapM partialApplyConcrete ts
  return (AppS f (es ++ [e2]), ts')
app g e2 (f, ts) = do
  ts' <- mapM partialApplyConcrete ts
  return (AppS (SAnno (Many [(f, ts)]) g) [e2], ts')

partialApplyConcrete :: CType -> MorlocMonad CType
partialApplyConcrete t =
  fmap CType $ partialApply (unCType t)

partialApply :: Type -> MorlocMonad Type
partialApply (FunT _ t) = return t
partialApply _ = MM.throwError . GeneratorError $
  "Cannot partially apply a non-function type"

getExprName :: Expr -> Maybe EVar
getExprName (VarE v) = Just v
getExprName _ = Nothing

getGType :: [UnresolvedType] -> MorlocMonad (Maybe GType)
getGType ts = do
  let ts' = map resolve [t | t <- ts, isNothing (langOf t)]
  case map GType ts' of
    [] -> return Nothing
    [x] -> return $ Just x
    xs -> MM.throwError . GeneratorError $
      "Expected 0 or 1 general types, found " <> MT.show' (length xs)

unrollLambda :: Expr -> ([EVar], Expr)
unrollLambda (LamE v e2) = case unrollLambda e2 of
  (vs, e) -> (v:vs, e)
unrollLambda (AnnE (LamE v e2) _) = case unrollLambda e2 of
  (vs, e) -> (v:vs, e)
unrollLambda e = ([], e)