packages feed

ddc-core-flow-0.4.3.1: DDC/Core/Flow/Transform/Slurp/Context.hs

module DDC.Core.Flow.Transform.Slurp.Context
    ( insertContext
    , mergeContexts
    , resizeContext )
where
import DDC.Core.Flow.Context
import DDC.Core.Flow.Transform.Slurp.Error
import DDC.Core.Flow.Transform.Slurp.Resize
import DDC.Core.Flow.Prim
import DDC.Core.Flow.Exp.Simple.Compounds
import DDC.Core.Flow.Exp.Simple.Exp
import Data.List     (nub)


-- "embed" is to be pushed inside "into"
-- only one of "embed" or "into" can contain inner contexts;
-- otherwise, no promises are made about merging these
insertContext
        :: Context
        -> Context
        -> Either Error Context
insertContext embed into
 = case into of
    ContextRate{}
     -> case embed of
         ContextRate{}
          | contextRate into == contextRate embed
          -> dropops
          | otherwise
          -> descend

         ContextSelect{}
          -> descendorpush (contextRate into == contextOuterRate embed)

         ContextSegment{}
          -> descendorpush (contextRate into == contextOuterRate embed)

         ContextAppend{}
          -> app into embed


    ContextSelect{}
     -> case embed of
         ContextRate{}
          | contextInnerRate into == contextRate embed
          -> dropops
          | otherwise
          -> descend

         ContextSelect{}
          | contextOuterRate into == contextOuterRate embed
          , contextInnerRate into == contextInnerRate embed
          , contextFlags     into == contextFlags     embed
          , contextSelector  into == contextSelector  embed
          -> dropops
          | otherwise
          -> descendorpush (contextInnerRate into == contextOuterRate embed)

         ContextSegment{}
          -> descendorpush (contextInnerRate into == contextOuterRate embed)

         ContextAppend{}
          -> app into embed


    ContextSegment{}
     -> case embed of
         ContextRate{}
          | contextInnerRate into == contextRate embed
          -> dropops
          | otherwise
          -> descend

         ContextSelect{}
          -> descendorpush (contextInnerRate into == contextOuterRate embed)

         ContextSegment{}
          | contextOuterRate into == contextOuterRate embed
          , contextInnerRate into == contextInnerRate embed
          , contextLens      into == contextLens      embed
          , contextSegd      into == contextSegd      embed
          -> dropops
          | otherwise
          -> descendorpush (contextInnerRate into == contextOuterRate embed)

         ContextAppend{}
          -> app into embed


    ContextAppend{}
     -> app embed into

 where
  descend =
   case tryInserts embed (contextInner into) of
    Nothing -> Left (ErrorCannotMergeContext embed into)
    Just cs -> return into { contextInner = cs }

  dropops =
   let ops' = contextOps   into ++ contextOps   embed
   in  return
         into { contextOps   = nub ops'
              , contextInner = mergeLists (contextInner into) (contextInner embed) }

  pushinner =
   return
       into { contextInner = mergeLists (contextInner into) [embed] }

  descendorpush p =
   case descend of
    Right v
     -> return v
    Left e
     | p
     -> pushinner
     | otherwise
     -> Left e

  app splittee injectee
   = do (ls, rs) <- splitContextIntoApps splittee
        ls'      <- insertContext ls (contextInner1 injectee)
        rs'      <- insertContext rs (contextInner2 injectee)
        return $ injectee
                 { contextInner1 = ls'
                 , contextInner2 = rs' }

mergeLists :: [Context] -> [Context] -> [Context]
mergeLists lefts []
 = lefts
mergeLists lefts (right:rest)
 = case mergeAny [] lefts right of
    Nothing  -> mergeLists (lefts ++ [right]) rest
    Just ls' -> mergeLists ls' rest
 where
  mergeAny _ [] _
   = Nothing
  mergeAny pres (l:ls) r
   = case insertContext l r of
      Right l' -> Just (pres ++ [l'] ++ ls)
      Left _   -> mergeAny (pres ++ [l]) ls r 



tryInserts :: Context -> [Context] -> Maybe [Context]
tryInserts embed intos
 = go intos []
 where
  go [] _
   = Nothing
  go (i:is) rs
    = case insertContext embed i of
       Right c' -> Just  (rs ++ [c'] ++ is)
       Left _   -> go is (rs ++ [i])


-- cannot split appends.
-- but only called by insertContext, which does not take appends anyway.
splitContextIntoApps :: Context -> Either Error (Context,Context)
splitContextIntoApps ctx
 = case ctx of
    ContextRate{}
     | Just (tl, tr) <- takeAppend $ contextRate ctx
     -> do      (lis, ris) <- unzip <$> mapM splitContextIntoApps (contextInner ctx)
                return ( ctx { contextRate      = tl
                             , contextInner     = lis }
                       , ctx { contextRate      = tr
                             , contextInner     = ris } )

    ContextSelect{}
     | Just (tl, tr) <- takeAppend $ contextOuterRate ctx
     -> do      (lis, ris) <- unzip <$> mapM splitContextIntoApps (contextInner ctx)
                return ( ctx { contextOuterRate  = tl
                             , contextInner      = lis }
                       , ctx { contextOuterRate  = tr
                             , contextInner      = ris } )

    ContextSegment{}
     | Just (tl, tr) <- takeAppend $ contextOuterRate ctx
     -> do      (lis, ris) <- unzip <$> mapM splitContextIntoApps (contextInner ctx)
                return ( ctx { contextOuterRate  = tl
                             , contextInner      = lis }
                       , ctx { contextOuterRate  = tr
                             , contextInner      = ris } )

    ContextAppend{}
     ->         return ( contextInner1 ctx
                       , contextInner2 ctx )

    _
     -> -- Left (ErrorCannotSplitContext ctx) 
        return (ctx, ctx)


 where


mergeContexts :: Context -> Context -> Either Error Context
mergeContexts a b
 = insertContext b a

resizeContext :: Resize -> Context -> Either Error Context
resizeContext resize ctx
 = case resize of
    Id _    
     -> return ctx
    AppL a b
     -> return
         ContextAppend
         { contextRate1  = a
         , contextInner1 = wrapCtx b ctx
         , contextRate2  = b
         , contextInner2 = emptyCtx a
         }

    AppR a b
     -> return
         ContextAppend
         { contextRate1  = a
         , contextInner1 = emptyCtx a
         , contextRate2  = b
         , contextInner2 = wrapCtx b ctx
         }

    App _ k' _ l' ls rs
     | ContextAppend{} <- ctx
     -> do  in1 <- resizeContext ls (contextInner1 ctx)
            in2 <- resizeContext rs (contextInner2 ctx)
            return
             ContextAppend
             { contextRate1  = k'
             , contextInner1 = in1
             , contextRate2  = l'
             , contextInner2 = in2 }
     | otherwise
     -> Left (ErrorCannotResizeContext ctx)

    Sel1 _ _k _ r
     -> do  ctx' <- resizeContext r ctx
            return $ ctx'

    Segd _ _k _ r
     -> do  ctx' <- resizeContext r ctx
            return $ ctx'

    Cross _ k _ r
     -> do  ctx' <- resizeContext r ctx
            return $ wrapCtx k ctx'
            
            


emptyCtx :: Type Name -> Context
emptyCtx k
 = ContextRate
 { contextRate  = k
 , contextInner = []
 , contextOps   = [] }

wrapCtx :: Type Name -> Context -> Context
wrapCtx k ctx
 = case ctx of
   ContextRate{}
    | contextRate ctx == k
    -> ctx

   ContextAppend{}
    | Just (l,r) <- takeAppend k
    , contextRate1 ctx == l
    , contextRate2 ctx == r
    -> ctx
    
   _
    | otherwise
    -> ContextRate
       { contextRate  = k
       , contextInner = [ctx]
       , contextOps   = [] }

takeAppend ty
 | Just (NameTyConFlow TyConFlowRateAppend, [tK, tL])
          <- takePrimTyConApps ty
 = Just (tK, tL)
 | otherwise
 = Nothing