packages feed

KiCS-debugger-0.1.0: biosphere/src/Curry/Debugger/Bubble.hs

module Curry.Debugger.Bubble 
  (bubble,bubbleWithStore,
   cleanTerm,cleanTermWithStore,
   value) where

import Prelude  as P
import Data.Map as M
import Data.Maybe 

import Curry.Debugger.DebugInfo
import Curry.Debugger.Logic

--------------------------------------------------------
-- remove all unreachable sub branches,
-- e.g., eliminate ?1 below ?1
--------------------------------------------------------

cleanTerm :: Term -> Term
cleanTerm = cleanTermWithStore emptyConstraintStore

-- perhapse cleanTerm should also report on ors included
cleanTermWithStore :: ConstraintStore -> Term -> Term
cleanTermWithStore refMap (Term n si ts) = 
  Term n si (P.map (cleanTermWithStore refMap) ts)
cleanTermWithStore refMap (TermOr si r ts) =   
  case lookupRef refMap r of
   Just i  -> cleanTermWithStore refMap (ts !! i)
   Nothing -> TermOr si r (cleanSequence (insertBind refMap r) ts 0)
cleanTermWithStore _ t = t


cleanSequence :: (Int -> ConstraintStore) -> [Term] -> Int -> [Term]
cleanSequence _ [] _     = []
cleanSequence adjMap (t:ts) i = 
  cleanTermWithStore  (adjMap i) t : 
  (cleanSequence adjMap ts $! i+1)

--------------------------------------------------------
-- remove failures (in bubbled terms)
--------------------------------------------------------

value :: Term -> Maybe Term
value (Term n si ts) = do
  ts' <- mapM value ts
  return (Term n si ts')
value (TermOr si r ts) = do
  case catMaybes $ P.map value ts of
    []  -> Nothing
    [t] -> Just t
    ts' -> Just (TermOr si r ts')
value (TermFail _) = Nothing
value t = Just t

--------------------------------------------------------
-- extend terms with information about references below
--------------------------------------------------------

type OrCollect = Map OrRef Int

data Bubble = BubbleTerm {static::StaticInfo, 
                          refsBelow::OrCollect,
                          refsToBubble::OrCollect,
                          toBubbleBelow::Bool,
                          name::String,
                          args::[Bubble]}
            | BubbleOr   {static::StaticInfo,
                          orRef::OrRef,
                          refsBelow::OrCollect,
                          toBubbleBelow::Bool,
                          args::[Bubble]}
            | Bubbled Term
            deriving (Show,Eq)

allRefsBelow :: Bubble -> OrCollect
allRefsBelow (Bubbled _) = empty
allRefsBelow BubbleOr{orRef=r,refsBelow=refs} = insert r 1 refs
allRefsBelow BubbleTerm{refsBelow=refs} = refs

unionsOC :: [OrCollect] -> OrCollect
unionsOC = unionsWith (\_ _ -> 1)

-- collect all OrRefs below
addRefsToParent :: Term -> (Bubble,OrCollect,Bool)
addRefsToParent t@(Term n si ts) = 
  case unzip3 (P.map addRefsToParent ts) of
    (bs,maps,bubbleBelows) -> 
      let map=unionsWith (+) maps 
          toBubble = M.filter (>1) map 
          set = M.map (const 1) map 
          bubbleBelow = or ((toBubble /= empty) : bubbleBelows)
      in
      if map==empty 
      then (Bubbled t,map,False)
      else (BubbleTerm{static=si,
                       refsBelow=set,
                       refsToBubble=toBubble,
                       toBubbleBelow=bubbleBelow,
                       name=n,
                       args=bs},set,bubbleBelow)
addRefsToParent t@(TermOr si r ts) = 
  case unzip3 (P.map addRefsToParent ts) of
    (bs,maps,bubb) -> 
      let set=unionsOC maps
          bubbleBelow = or bubb
      in
      (BubbleOr{static=si,
                orRef=r,
                refsBelow=set, 
                toBubbleBelow=bubbleBelow,
                args=bs},insert r 1 set,bubbleBelow)
addRefsToParent t = (Bubbled t,empty,False)

addRefs :: Term -> Bubble
addRefs t = case addRefsToParent t of (t',_,_) -> t'

--------------------------------------------------------
-- bubble up a set of references 
--------------------------------------------------------

bubbleUp :: OrCollect -> Bubble -> Bubble
bubbleUp _ t@(Bubbled _) = t
bubbleUp set t@(BubbleTerm{refsBelow=refs,refsToBubble=bset}) 
  | toBubbleBelow t || intersection set refs /= empty
  = let bubbleSet = union bset set
     in merge (\ bs -> 
                  let below = unionsOC (P.map allRefsBelow bs) in
                  if below==empty
                  then Bubbled (Term (name t) (static t) 
                                     (P.map (\ (Bubbled t') -> t') bs))
                  else t{refsBelow=below,
                         refsToBubble=empty,
                         toBubbleBelow=False,
                         args=bs})
              Nothing
              bubbleSet
              (P.map (bubbleUp bubbleSet) (args t))
  | otherwise = t
bubbleUp set t@(BubbleOr{refsBelow=refs,orRef=r})
  | toBubbleBelow t || bubbleSet /= empty
  = let bubbledArgs = P.map (bubbleUp bubbleSet) (args t)
     in if   bubbleSet == empty || member r set
        then t{toBubbleBelow=False,args=bubbledArgs}
        else merge (\ bs -> 
                        t{refsBelow=unionsOC (P.map allRefsBelow bs),
                          toBubbleBelow=False,
                          args=bs})
                   (Just r)
                   bubbleSet
                   bubbledArgs
  | otherwise = t
  where
    bubbleSet = intersection set refs


merge :: ([Bubble] -> Bubble) -> Maybe OrRef
      -> OrCollect -> [Bubble] -> Bubble
merge parent parentRef set args = case nextOr set args of
  Nothing -> parent args 
  Just (si,r,size)  -> 
    let bubbledArgs = P.map (merge parent parentRef (delete r set)) 
                            (makeBranches r size args)
       in
       BubbleOr{static=si,
                orRef=r,
                refsBelow=unionsOC (P.map allRefsBelow bubbledArgs),
                toBubbleBelow=False,
                args=bubbledArgs}


nextOr :: OrCollect -> [Bubble] -> Maybe (StaticInfo,OrRef,Int)
nextOr _   [] = Nothing
nextOr set (o@(BubbleOr{orRef=r,refsBelow=below}):xs) 
  | member r set = Just (static o,r,length (args o))
  | hiddenRefs /= empty = nextOr set (bubbleUp hiddenRefs o:xs)
  | otherwise    = nextOr set xs
  where
    hiddenRefs = intersection below set
nextOr set (_:xs) = nextOr set xs

makeBranches :: OrRef -> Int -> [Bubble] -> [[Bubble]]
makeBranches _ i [] = replicate i []
makeBranches r i (t@(BubbleOr{orRef=r',refsBelow=refs,args=bs}):args) 
  | r==r' = zipWith (:) bs (makeBranches r i args)
  | member r refs 
  = makeBranches r i (bubbleUp (singleton r 1) t:args) 
makeBranches r i (t:args) = P.map (t:) (makeBranches r i args)


bubble :: Term -> Term
bubble = bubbleWithStore emptyConstraintStore

bubbleWithStore :: ConstraintStore -> Term -> Term
bubbleWithStore store t = 
  deBubble $ case addRefsToParent (cleanTermWithStore store t) of
               (t',_,True)  -> bubbleUp empty t'
               (t',_,False) -> t'


deBubble :: Bubble -> Term
deBubble BubbleTerm{static=si,name=n,args=ts} = Term n si (P.map deBubble ts)
deBubble BubbleOr{static=si,orRef=r,args=ts}  = TermOr si r (P.map deBubble ts)
deBubble (Bubbled t) = t