packages feed

ideas-0.5.8: src/Common/Rewriting/RewriteRule.hs

{-# LANGUAGE ExistentialQuantification, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
-----------------------------------------------------------------------------
-- Copyright 2009, 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.Rewriting.RewriteRule 
   ( RuleSpec(..), lhs, rhs
   , RewriteRule, Builder, rewriteRule, BuilderList, rewriteRules
   , ruleName, nrOfMetaVars, rulePair
   , inverse, bothWays, checkScope
   , rewrite, rewriteM, rewriteWith
   , normalForm, normalFormWith
   , smartGenerator, showRewriteRule, showRuleSpec
   ) where

import Common.Apply
import Common.Rewriting.AC
import Common.Rewriting.MetaVar
import Common.Rewriting.Substitution
import Common.Rewriting.Unification
import Common.Uniplate (transform)
import Common.Utils
import Control.Monad
import Data.List
import Data.Maybe
import Test.QuickCheck
import qualified Data.IntSet as IS

-----------------------------------------------------------
-- Rewrite rules

infixl 1 :~>

data RuleSpec a = a :~> a
   deriving Show

lhs, rhs :: RuleSpec a -> a
lhs (x :~> _) = x
rhs (_ :~> y) = y

data RewriteRule a = Rewrite a => R { ruleName :: String, nrOfMetaVars :: Int, rulePair :: Int -> RuleSpec a }

instance Show (RewriteRule a) where
   show r = "[" ++ ruleName r ++ "]" 

instance Eq (RewriteRule a) where
   r1 == r2 = ruleName r1 == ruleName r2

class Rewrite a => Builder t a | t -> a where
   buildSpec :: t -> Int -> RuleSpec a
   countVars :: t -> Int

instance Rewrite a => Builder (RewriteRule a) a where
   buildSpec = rulePair
   countVars = nrOfMetaVars

instance Rewrite a => Builder (RuleSpec a) a where
   buildSpec rp _ = rp
   countVars _    = 0

instance (Rewrite a, Builder b a) => Builder (a -> b) a where
   buildSpec f i = buildSpec (f (metaVar i)) (i+1)
   countVars f   = countVars (f $ error "countVars") + 1

class Rewrite a => BuilderList t a | t -> a where
   getSpecNr   :: t -> Int -> Int -> RuleSpec a
   countSpecsL :: t -> Int
   countVarsL  :: t -> Int
   
instance Rewrite a => BuilderList (RewriteRule a) a where
   getSpecNr r n = if n==0 then rulePair r else error "getSpecNr"
   countSpecsL _ = 1
   countVarsL    = nrOfMetaVars
  
instance Rewrite a => BuilderList [RuleSpec a] a where
   getSpecNr rs = buildSpec . (rs !!)
   countSpecsL  = length
   countVarsL _ = 0

instance BuilderList b a => BuilderList (a -> b) a where 
   getSpecNr f n i = getSpecNr (f (metaVar i)) n (i+1)
   countSpecsL f   = countSpecsL (f $ error "countSpecsL")
   countVarsL f    = countVarsL (f $ error "countSpecsL") + 1
   
rewriteRule :: Builder f a => String -> f -> RewriteRule a
rewriteRule s f = R s (countVars f) (buildSpec f)

rewriteRules :: BuilderList f a => String -> f -> [RewriteRule a]
rewriteRules s f = map (R s (countVarsL f) . getSpecNr f) [0 .. countSpecsL f-1]

inverse :: RewriteRule a -> Maybe (RewriteRule a)
inverse r@(R _ _ _) = if checkScope new then Just new else Nothing
 where 
   swap (x :~> y) = y :~> x
   new = R (ruleName r) (nrOfMetaVars r) (swap . rulePair r)

bothWays :: Rewrite a => [RewriteRule a] -> [RewriteRule a]
bothWays rs = rs ++ mapMaybe inverse rs

checkScope :: Rewrite a => RewriteRule a -> Bool
checkScope r = IS.null (getMetaVars rhs IS.\\ getMetaVars lhs)
 where lhs :~> rhs = rulePair r 0

-----------------------------------------------------------
-- Applying rewrite rules

instance Apply RewriteRule where
   applyAll = rewriteM
 
rewrite :: RewriteRule a -> a -> [a]
rewrite r@(R _ _ _) = rewriteWith operators r

rewriteM :: MonadPlus m => RewriteRule a -> a -> m a
rewriteM r@(R _ _ _) = msum . map return . rewriteWith operators r
      
rewriteWith :: Operators a -> RewriteRule a -> a -> [a]
rewriteWith ops r0@(R _ _ _) e = do
   r <- extendContext ops r0
   let lhs :~> rhs = rulePair r (nextMetaVar e)
   s <- matchWith ops lhs e
   return (s |-> rhs)

-- Bug fix 4/3/2009: for associative operators, we need to extend rewrite
-- rules to take "contexts" into account. In addition to a left and a right
-- context, we also should consider a context on both sides. If not, we 
-- might miss some locations, as pointed out by Josje's bug report.
extendContext :: Operators a -> RewriteRule a -> [RewriteRule a]
extendContext ops r =
   case findOperator ops (lhs $ rulePair r 0) of
      Just op | isAssociative op -> 
         [r, extend (leftContext op) r, extend (rightContext op) r 
         , extend (rightContext op) (extend (leftContext op) r) ]
      _ -> [r]
 where
   leftContext op a (x :~> y) =
      constructor op a x :~> constructor op a y
   
   rightContext op a (x :~> y) =
      constructor op x a :~> constructor op y a

extend :: (a -> RuleSpec a -> RuleSpec a) -> RewriteRule a -> RewriteRule a
extend f (R s n g) = R s (n+1) (\i -> f (metaVar (i+n)) (g i))
      
-----------------------------------------------------------
-- Normal forms

normalFormWith :: (Rewrite a, Ord a) => [Operator a] -> [RewriteRule a] -> a -> a
normalFormWith ops rs = fixpoint $ transform $ \a ->
   case [ b | r <- rs, b <- rewriteWith ops r a ] of
      hd:_ -> normalizeWith ops hd
      _    -> a
      
normalForm :: (Rewrite a, Ord a) => [RewriteRule a] -> a -> a
normalForm = normalFormWith operators

-----------------------------------------------------------
-- Smart generator that creates instantiations of the left-hand side

smartGenerator :: RewriteRule a -> Gen a
smartGenerator r@(R _ _ _) = do 
   let a :~> _ = rulePair r 0
   let vs      = getMetaVars a
   list <- vector (IS.size vs) 
   let sub = listToSubst $ zip (IS.toList vs) list
   return (sub |-> a)
   
-----------------------------------------------------------
-- Showing a rewrite-rule

showRewriteRule :: Bool -> RewriteRule a -> String
showRewriteRule sound r@(R _ _ _) = ruleName r ++ ": " ++ showRuleSpec sound r 
   
showRuleSpec :: Bool -> RewriteRule a -> String
showRuleSpec sound r@(R _ _ _) = show lhs ++ " " ++ leadsto ++ " " ++ show rhs
 where
   leadsto = if sound then ":~>" else ":/~>" 
   lhs :~> rhs = rulePair r 0