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