packages feed

ideas-0.6: src/Common/Strategy/Abstract.hs

{-# OPTIONS -XFlexibleInstances #-}
-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Common.Strategy.Abstract 
   ( Strategy, IsStrategy(..)
   , LabeledStrategy, label, unlabel
   , fullDerivationTree, derivationTree, rulesInStrategy
   , mapRules, mapRulesS, cleanUpStrategy
     -- Accessors to the underlying representation
   , toCore, fromCore, liftCore, liftCore2, fixCore, makeLabeledStrategy
   , toLabeledStrategy
   , LabelInfo, strategyName, processLabelInfo, changeInfo, makeInfo
   , removed, collapsed, hidden, labelName, IsLabeled(..)
   ) where

import Common.Utils (commaList)
import Common.Strategy.Core
import Common.Strategy.BiasedChoice
import Common.Apply
import Common.Rewriting (RewriteRule(..))
import Common.Transformation
import Common.Derivation
import Common.Uniplate

-----------------------------------------------------------
--- Strategy data-type

-- | Abstract data type for strategies
newtype Strategy a = S { toCore :: Core LabelInfo a }

instance Show (Strategy a) where
   show = show . toCore

instance Apply Strategy where
   applyAll s = results . fullDerivationTree s

-----------------------------------------------------------
--- The information used as label in a strategy

data LabelInfo = Info 
   { labelName :: String 
   , removed   :: Bool
   , collapsed :: Bool
   , hidden    :: Bool
   }

instance Show LabelInfo where
   show info = 
      let ps = ["removed"   | removed   info] ++ 
               ["collapsed" | collapsed info] ++
               ["hidden"    | hidden    info]
          extra = " (" ++ commaList ps ++ ")"
      in show (labelName info) ++ if null ps then "" else extra

makeInfo :: String -> LabelInfo
makeInfo s = Info s False False False

-----------------------------------------------------------
--- Type class

-- | Type class to turn values into strategies
class Apply f => IsStrategy f where
   toStrategy :: f a -> Strategy a

instance IsStrategy (Core LabelInfo) where
   toStrategy = S

instance IsStrategy Strategy where
   toStrategy = id

instance IsStrategy (LabeledStrategy) where
  toStrategy (LS info (S core)) = 
     case core of
        Rule Nothing r | name r == labelName info -> 
             S (Rule (Just info) r)
        _ -> S (Label info core)

instance IsStrategy Rule where -- Major rules receive a label
   toStrategy r
      | isMajorRule r = toStrategy (toLabeled r)
      | otherwise     = S (Rule Nothing r)

instance IsStrategy RewriteRule where
   toStrategy r = 
      toStrategy (makeRule (ruleName r) (RewriteRule r))

-----------------------------------------------------------
--- Labeled Strategy data-type

-- | A strategy which is labeled with a string
data LabeledStrategy a = LS 
   { labelInfo :: LabelInfo  -- ^ Returns information associated with this label
   , unlabel   :: Strategy a -- ^ Removes the label from a strategy
   }

makeLabeledStrategy :: IsStrategy f => LabelInfo -> f a -> LabeledStrategy a
makeLabeledStrategy info = LS info . toStrategy

toLabeledStrategy :: Monad m => Strategy a -> m (LabeledStrategy a)
toLabeledStrategy s = 
   case toCore s of
      Label l c -> return (makeLabeledStrategy l (fromCore c))
      _         -> fail "Strategy without label"

strategyName :: LabeledStrategy a -> String
strategyName = getLabel

instance Show (LabeledStrategy a) where
   show s = show (labelInfo s) ++ ": " ++ show (unlabel s)

instance Apply LabeledStrategy where
   applyAll = applyAll . toStrategy

class IsLabeled f where
   toLabeled :: f a -> LabeledStrategy a
   
instance IsLabeled LabeledStrategy where
   toLabeled = id

instance IsLabeled Rule where
   toLabeled r = LS (makeInfo (name r)) (S (Rule Nothing r))

instance IsLabeled RewriteRule where
   toLabeled r = toLabeled (makeRule (ruleName r) (RewriteRule r))

-- | Labels a strategy with a string
label :: IsStrategy f => String -> f a -> LabeledStrategy a
label l = LS (makeInfo l) . toStrategy

getLabel :: IsLabeled f => f a -> String
getLabel = labelName . labelInfo . toLabeled

changeInfo :: IsLabeled f => (LabelInfo -> LabelInfo) -> f a -> LabeledStrategy a
changeInfo f a = LS (f info) s
 where LS info s = toLabeled a

-----------------------------------------------------------
--- Process Label Information

processLabelInfo :: (l -> LabelInfo) -> Core l a -> Core l a
processLabelInfo getInfo = mapCore forLabel forRule
 where
   forLabel l c 
      | removed info   = Fail
      | collapsed info = Rule (Just l) asRule
      | otherwise      = new
    where 
      new | hidden info = mapRule minorRule (Label l c)
          | otherwise   = Label l c
      info   = getInfo l
      asRule = makeSimpleRuleList (labelName info ++ " (collapsed)") (applyAll new)
   forRule (Just l) r 
      | removed info = Fail
      | hidden info  = Rule (Just l) (minorRule r)
      | otherwise    = Rule (Just l) r
    where
      info = getInfo l
   forRule _ r = Rule Nothing r

-----------------------------------------------------------
--- Remaining functions

-- | Returns the derivation tree for a strategy and a term, including all
-- minor rules
fullDerivationTree :: IsStrategy f => f a -> a -> DerivationTree (Rule a) a
fullDerivationTree = makeBiasedTree p . processLabelInfo id . toCore . toStrategy 
 where 
   p t = endpoint t || any isMajorRule (annotations t) || any p (subtrees t)

-- | Returns the derivation tree for a strategy and a term with only major rules
derivationTree :: IsStrategy f => f a -> a -> DerivationTree (Rule a) a
derivationTree s = mergeSteps isMajorRule . fullDerivationTree s

-- | Returns a list of all major rules that are part of a labeled strategy
rulesInStrategy :: IsStrategy f => f a -> [Rule a]
rulesInStrategy f = [ r | Rule _ r <- universe (toCore (toStrategy f)), isMajorRule r ]
                    
-- | Apply a function to all the rules that make up a labeled strategy
mapRules :: (Rule a -> Rule b) -> LabeledStrategy a -> LabeledStrategy b
mapRules f (LS n s) = LS n (mapRulesS f s)

mapRulesS :: (Rule a -> Rule b) -> Strategy a -> Strategy b
mapRulesS f = S . mapRule f . toCore

-- | Use a function as do-after hook for all rules in a labeled strategy
cleanUpStrategy :: (a -> a) -> LabeledStrategy a -> LabeledStrategy a
cleanUpStrategy f (LS n s) = mapRules g (LS n (S core))
 where
   core = Rule Nothing (doAfter f idRule) :*: toCore s
   g r | isMajorRule r = doAfter f r  
       | otherwise     = r
       
-----------------------------------------------------------
--- Functions to lift the core combinators

fromCore :: Core LabelInfo a -> Strategy a
fromCore = toStrategy

liftCore :: IsStrategy f => (Core LabelInfo a -> Core LabelInfo a) -> f a -> Strategy a
liftCore f = fromCore . f . toCore . toStrategy

liftCore2 :: (IsStrategy f, IsStrategy g) => (Core LabelInfo a -> Core LabelInfo a -> Core LabelInfo a) -> f a -> g a -> Strategy a
liftCore2 f = liftCore . f . toCore . toStrategy

fixCore :: (Core l a -> Core l a) -> Core l a
fixCore f = Rec i (f (Var i)) -- disadvantage: function f is applied twice
 where
    s = coreVars (f (Rule Nothing idRule))
    i = if null s then 0 else maximum s + 1