rest-rewrite 0.2.0 → 0.3.0
raw patch · 17 files changed
+276/−144 lines, 17 filesdep ~hashable
Dependency ranges changed: hashable
Files
- rest-rewrite.cabal +4/−2
- src/Language/REST/ExploredTerms.hs +38/−28
- src/Language/REST/Internal/EquivalenceClass.hs +16/−11
- src/Language/REST/Internal/Orphans.hs +34/−0
- src/Language/REST/Internal/PartialOrder.hs +36/−1
- src/Language/REST/Internal/WQO.hs +21/−3
- src/Language/REST/Internal/WorkStrategy.hs +13/−13
- src/Language/REST/Op.hs +2/−0
- src/Language/REST/RESTDot.hs +12/−4
- src/Language/REST/Rest.hs +14/−18
- src/Language/REST/Types.hs +1/−7
- test/BagExample.hs +1/−2
- test/ExploredTerms.hs +45/−0
- test/Main.hs +20/−10
- test/Test.hs +3/−2
- testlib/DSL.hs +5/−5
- testlib/Language/REST/ConcreteOC.hs +11/−38
rest-rewrite.cabal view
@@ -1,12 +1,12 @@ name: rest-rewrite build-type: Simple-version: 0.2.0+version: 0.3.0 cabal-version: 2.0 category: Rewriting maintainer: Zack Grannan <zgrannan@cs.ubc.ca> author: Zack Grannan <zgrannan@cs.ubc.ca> license: BSD3-description: Rewriting library with online termination checking.+description: REST is a Rewriting library with online termination checking. For more details see the paper at https://arxiv.org/abs/2202.05872. synopsis: Rewriting library with online termination checking license-file: LICENSE @@ -25,6 +25,7 @@ Language.REST.Internal.MultiSet Language.REST.Internal.MultisetOrder Language.REST.Internal.OpOrdering+ Language.REST.Internal.Orphans Language.REST.Internal.PartialOrder Language.REST.Internal.Rewrite Language.REST.Internal.Util@@ -98,6 +99,7 @@ , rest-rewrite , testlib other-modules:+ ExploredTerms KBO LazyOC MultisetOrder
src/Language/REST/ExploredTerms.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-}+-- | This module implements the optimizations to prune the+-- exploration of rewrites of terms that have been already considered+-- (section 6.4 of the REST paper). module Language.REST.ExploredTerms ( ExploredTerms@@ -21,63 +24,72 @@ data ExploreStrategy = ExploreAlways | ExploreLessConstrained | ExploreWhenNeeded | ExploreOnce -data ExploreFuncs c m = EF+data ExploreFuncs term c m = EF { union :: c -> c -> c+ -- | @c0 `subsumes` c1@ if @c0@ permits all orderings permited by @c1@ , subsumes :: c -> c -> m Bool+ , exRefine :: c -> term -> term -> c } -- A mapping of terms, to the rewritten terms that need to be fully explored -- in order for this term to be fully explored data ExploredTerms term c m =- ET (M.HashMap term (c, (S.HashSet term))) (ExploreFuncs c m) ExploreStrategy--trace' :: String -> b -> b--- trace' = trace-trace' _ x = x-+ ET (M.HashMap term (c, (S.HashSet term))) (ExploreFuncs term c m) ExploreStrategy size :: ExploredTerms term c m -> Int size (ET m _ _) = M.size m -empty :: ExploreFuncs c m -> ExploreStrategy -> ExploredTerms term c m+empty :: ExploreFuncs term c m -> ExploreStrategy -> ExploredTerms term c m empty = ET M.empty visited :: (Eq term, Hashable term) => term -> ExploredTerms term c m -> Bool visited t (ET m _ _) = M.member t m insert :: (Eq term, Hashable term) => term -> c -> S.HashSet term -> ExploredTerms term c m -> ExploredTerms term c m-insert t oc s (ET etMap ef@(EF union _ ) strategy) = ET (M.insertWith go t (oc, s) etMap) ef strategy+insert t oc s (ET etMap ef@(EF union _ _) strategy) = ET (M.insertWith go t (oc, s) etMap) ef strategy where go (oc1, s1) (oc2, s2) = (union oc1 oc2, S.union s1 s2) lookup :: (Eq term, Hashable term) => term -> ExploredTerms term c m -> Maybe (c, (S.HashSet term)) lookup t (ET etMap _ _) = M.lookup t etMap -isFullyExplored :: forall term c m . (Monad m, Show term, Eq term, Hashable term, Eq c) =>+-- | @isFullyExplored t c M = not explorable(t, c)@ where @explorable@ is+-- defined as in the REST paper.+isFullyExplored :: forall term c m . (Monad m, Eq term, Hashable term, Hashable c, Eq c, Show c) => term -> c -> ExploredTerms term c m -> m Bool-isFullyExplored t0 oc0 et@(ET _ (EF{subsumes}) _) = result where+isFullyExplored t0 oc0 et@(ET _ (EF{subsumes,exRefine}) _) = result where - result = go S.empty [t0]- -- if (trace ("Check " ++ show t0) go) S.empty [t0]- -- then trace (show t0 ++ " is fully explored.") True- -- else False+ result = go S.empty [(t0, oc0)] - go :: S.HashSet term -> [term] -> m Bool+ -- Arg 1: Steps that have already been seen+ -- Arg 2: Steps to consider+ go :: S.HashSet (term, c) -> [(term, c)] -> m Bool++ -- Completed worklist, this term is fully explored at these constraints go _ [] = return True- go seen (h:t) | Just (oc, trms) <- lookup h et++ -- Term `h` has been seen before at constraints `oc`+ go seen ((h, oc'):rest) | Just (oc, trms) <- lookup h et = do- ns <- oc `subsumes` oc0- if ns- then go seen' t+ exploringPathWouldNotPermitDifferentSteps <- oc `subsumes` oc'+ if exploringPathWouldNotPermitDifferentSteps+ then go seen' rest else- let ts = (S.union trms (S.fromList t)) `S.difference` seen'- in go seen' (S.toList ts)+ let+ -- Exploring `h` at these constraints+ -- would allow exploration of each t in trms,+ -- at the constraints generated by the step from h to t+ trms' = S.map (\t -> (t, exRefine oc' h t)) trms+ ts = (S.union trms' (S.fromList rest)) `S.difference` seen'+ in+ go seen' (S.toList ts) where- seen' = S.insert h seen+ seen' = S.insert (h, oc') seen - go _ _ | otherwise = trace' "GF" $ return False -- trace ("Must check " ++ show t0 ++ " . Visited: " ++ (show $ visited t0 et)) False+ -- There exists a reachable term that has never previously been seen; not fully explored+ go _ _ | otherwise = return False -shouldExplore :: forall term c m . (Monad m, Show term, Eq term, Hashable term, Eq c, Show c) =>+shouldExplore :: forall term c m . (Monad m, Eq term, Hashable term, Eq c, Show c, Hashable c) => term -> c -> ExploredTerms term c m -> m Bool shouldExplore t oc et@(ET _ EF{subsumes} strategy) = case strategy of@@ -88,7 +100,5 @@ case lookup t et of Just (oc', _) -> do s <- oc' `subsumes` oc- return $ if s- then trace' ((show oc') ++ " subsumes " ++ (show oc)) False- else True+ return $ not s Nothing -> return True
src/Language/REST/Internal/EquivalenceClass.hs view
@@ -1,5 +1,9 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE StandaloneDeriving #-} module Language.REST.Internal.EquivalenceClass ( isMember@@ -12,7 +16,6 @@ , head , EquivalenceClass , elems- , toPairs , isSubsetOf ) where @@ -24,9 +27,20 @@ import Language.REST.Types () -- Hashable (S.Set a) +-- | Equivalent classes of the @(==)@ relation of a type @a@. newtype EquivalenceClass a =- EquivalenceClass (S.Set a) deriving (Ord, Eq, Generic, Hashable)+ -- | The set contains all of the elements of the class+ EquivalenceClass (S.Set a)+#if MIN_VERSION_hashable(1,3,5)+ deriving (Ord, Eq, Generic, Hashable)+#else+ deriving (Ord, Eq, Generic)+#endif +#if !MIN_VERSION_hashable(1,3,5)+deriving instance Hashable (S.Set a) => Hashable (EquivalenceClass a)+#endif+ instance Show a => Show (EquivalenceClass a) where show (EquivalenceClass xs) = L.intercalate " = " (map show (S.toList xs)) @@ -61,15 +75,6 @@ toList :: EquivalenceClass a -> [a] toList (EquivalenceClass s) = S.toList s--toPairs :: EquivalenceClass b -> [(b, b)]-toPairs e =- let- list = toList e- in- if length list < 2- then []- else zip list (tail list) {-# INLINE elems #-} elems :: EquivalenceClass a -> S.Set a
+ src/Language/REST/Internal/Orphans.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Language.REST.Internal.Orphans() where++#if !MIN_VERSION_hashable(1,3,4)+import Data.Hashable+import Data.Hashable.Lifted+import Data.Set as Set+import Data.Map as Map++instance Hashable1 Set where+ liftHashWithSalt h s x = Set.foldl' h (hashWithSalt s (Set.size x)) x++instance (Hashable a) => Hashable (Set a) where+ hashWithSalt = hashWithSalt1++instance Hashable2 Map.Map where+ liftHashWithSalt2 hk hv s m = Map.foldlWithKey'+ (\s' k v -> hv (hk s' k) v)+ (hashWithSalt s (Map.size m))+ m++instance Hashable k => Hashable1 (Map.Map k) where+ liftHashWithSalt h s m = Map.foldlWithKey'+ (\s' k v -> h (hashWithSalt s' k) v)+ (hashWithSalt s (Map.size m))+ m++-- | @since 1.3.4.0+instance (Hashable k, Hashable v) => Hashable (Map.Map k v) where+ hashWithSalt = hashWithSalt2++#endif
src/Language/REST/Internal/PartialOrder.hs view
@@ -23,9 +23,23 @@ import qualified Data.List as L import Language.REST.Types () -- Hashable (M.Map a b)+import Language.REST.Internal.Orphans () import Text.Printf -newtype PartialOrder a = PartialOrder (M.Map a (S.Set a))+-- | Irreflexive (strict) partial orders+newtype PartialOrder a =+ -- | @PartialOrder m@ represents the relation+ --+ -- > (>) = { (a, b) | (a, bs) <- toList m, b <- bs }+ --+ -- Transitivity implies that @m ! a == { b | a > b}@ if @a@ is in the map.+ --+ -- Asymmetry implies that @member a (m ! b)@ implies+ -- @not (member b (m ! a))@.+ --+ -- Irreflexivity means that @a@ cannot be in @m ! a@.+ --+ PartialOrder (M.Map a (S.Set a)) deriving (Ord, Eq, Generic, Hashable) instance (Show a) => Show (PartialOrder a) where@@ -40,21 +54,27 @@ isEmpty :: Eq a => PartialOrder a -> Bool isEmpty p = p == empty +-- | @canInsert (>) a b@ iff @a /= b && not (a > b) && not (b > a)@ canInsert :: (Eq a, Ord a, Hashable a) => PartialOrder a -> a -> a -> Bool canInsert o f g = f /= g && not (gt o f g) && not (gt o g f) +-- | @gt (>) a b == (a > b)@ gt :: (Eq a, Ord a, Hashable a) => PartialOrder a -> a -> a -> Bool gt po t u = S.member u $ descendents t po unionDisjointUnsafe :: Ord a => PartialOrder a -> PartialOrder a -> PartialOrder a unionDisjointUnsafe (PartialOrder m) (PartialOrder m') = PartialOrder (M.union m m') +-- | ascendants a (>) = { b | b > a } ascendants :: Ord k => k -> PartialOrder k -> S.Set k ascendants k (PartialOrder m) = M.keysSet $ M.filter (S.member k) m +-- | descendents a (>) = { b | a > b } descendents :: Ord a => a -> PartialOrder a -> S.Set a descendents k (PartialOrder m) = M.findWithDefault S.empty k m +-- | @insertUnsafe (>) a b@ is unsafe because it may not respect some+-- of its properties if @canInsert (>) a b@ doesn't hold. {-# INLINE insertUnsafe #-} insertUnsafe :: Ord a => PartialOrder a -> a -> a -> PartialOrder a insertUnsafe o@(PartialOrder m) f g = result@@ -83,6 +103,21 @@ elems :: (Eq a, Ord a, Hashable a) => PartialOrder a -> S.Set a elems (PartialOrder m) = S.union (M.keysSet m) (S.unions (M.elems m)) +-- | @replaceUnsafe olds new (>)@ replaces every element in @olds@ with+-- @new@ in the partial order @(>)@.+--+-- More formally:+--+-- > replaceUnsafe olds new (>) =+-- > { (a, b) | notElem a olds, notElem b olds }+-- > U { (new, b) | o <- olds, o > b }+-- > U { (a, new) | o <- olds, a > o }+--+-- This operation is unsafe because it only yields a partial order+-- if forall @o@ in @olds@:+-- * @o > b@ implies @not (b > new)@, and+-- * @a > o@ implies @not (new > a)@.+-- replaceUnsafe :: (Eq a, Ord a, Hashable a) => [a] -> a -> PartialOrder a -> PartialOrder a replaceUnsafe froms to po@(PartialOrder m) = result where
src/Language/REST/Internal/WQO.hs view
@@ -35,6 +35,7 @@ import qualified Language.REST.Internal.EquivalenceClass as EC import qualified Language.REST.Internal.PartialOrder as PO+import Language.REST.Internal.Orphans () import Language.REST.Op import Language.REST.SMT @@ -71,8 +72,13 @@ getECs :: WQO a -> S.Set (EquivalenceClass a) getECs (WQO ecs _) = ecs --- Invariant: the first set contains all ECs-data WQO a = WQO (S.Set (EquivalenceClass a)) (PartialOrder (EquivalenceClass a))+-- | Well-founded reflexive partial orders+data WQO a =+ -- Invariant: the first set contains all equivalence classes+ --+ -- The strict partial order describes the ordering of the+ -- equivalence classes in the first set.+ WQO (S.Set (EquivalenceClass a)) (PartialOrder (EquivalenceClass a)) deriving (Ord, Eq, Generic, Hashable) instance (Show a, Eq a, Hashable a) => Show (WQO a) where@@ -100,6 +106,10 @@ elems :: (Ord a) => WQO a -> S.Set a elems (WQO ec _) = S.unions $ map EC.elems (S.toList ec) +-- | @getEquivalenceClasses (>=) a b@ retrieves the equivanlence classes of+-- @a@ and @b@.+--+-- TODO: Why are these looked up in pairs and not individually? {-# INLINE getEquivalenceClasses #-} getEquivalenceClasses :: (Ord a, Eq a, Hashable a) => WQO a -> a -> a -> (Maybe (EquivalenceClass a), Maybe (EquivalenceClass a))@@ -109,6 +119,8 @@ u = L.find (EC.isMember target) classes' classes' = S.toList classes +-- | Like @getEquivalenceClasses@ but only yields a result+-- if classes of equivalence are found for both elements. {-# INLINE getEquivalenceClasses' #-} getEquivalenceClasses' :: (Ord a, Hashable a)@@ -125,6 +137,8 @@ where classes' = S.toList classes +-- | @getRelation (>=) a b == QEQ@ iff @a >= b@+-- @getRelation (>=) a b == QGT@ iff @a > b@ {-# INLINE getRelation #-} getRelation :: (Ord a, Eq a, Hashable a) => WQO a -> a -> a -> Maybe QORelation getRelation _ f g | f == g = Just QEQ@@ -138,6 +152,8 @@ else Nothing | otherwise = Nothing +-- | @expandEC (>=) ec x@ adds an element @x@ to the equivalence class+-- @ec@ of @(>=)@. expandEC :: (Ord a, Eq a, Hashable a) => WQO a -> EquivalenceClass a -> a -> WQO a expandEC (WQO ecs po) ec x = WQO ecs' po' where@@ -145,6 +161,8 @@ ecs' = S.insert ec' $ S.delete ec ecs po' = PO.replaceUnsafe [ec] ec' po +-- | @mergeECs (>=) ec1 ec2@ combines the equivalence classes @ec1@ and @ec2@+-- of @(>=)@. mergeECs :: (Ord a, Eq a, Hashable a) => WQO a -> EquivalenceClass a -> EquivalenceClass a -> WQO a mergeECs (WQO ecs po) ec1 ec2 = WQO ecs' po' where@@ -311,7 +329,7 @@ (Just ec1, Just ec2) -> WQO ecs (PO.insertUnsafe po ec1 ec2) -+-- | Generates all the possible orderings of the elements in the given set. orderings :: forall a. (Ord a, Eq a, Hashable a) => S.Set a -> S.Set (WQO a) orderings ops = go S.empty (S.singleton empty) where
src/Language/REST/Internal/WorkStrategy.hs view
@@ -9,30 +9,30 @@ import Data.Hashable import qualified Data.List as L -type GetWork m rule term et oc = [Path rule term oc] -> (term -> et) -> ExploredTerms et oc m -> (Path rule term oc, [Path rule term oc])+type GetWork m rule term oc = [Path rule term oc] -> ExploredTerms term oc m -> (Path rule term oc, [Path rule term oc]) -newtype WorkStrategy rule term et oc = WorkStrategy (forall m . GetWork m rule term et oc)+newtype WorkStrategy rule term oc = WorkStrategy (forall m . GetWork m rule term oc) -bfs :: WorkStrategy rule term et oc+bfs :: WorkStrategy rule term oc bfs = WorkStrategy bfs' -notVisitedFirst :: (Eq term, Eq rule, Eq oc, Eq et, Hashable et) => WorkStrategy rule term et oc+notVisitedFirst :: (Eq term, Eq rule, Eq oc, Hashable term) => WorkStrategy rule term oc notVisitedFirst = WorkStrategy notVisitedFirst' -bfs' :: [Path rule term oc] -> (term -> et) -> ExploredTerms et oc m -> (Path rule term oc, [Path rule term oc])-bfs' (h:t) _ _ = (h, t)-bfs' _ _ _ = error "empty path list"+bfs' :: [Path rule term oc] -> ExploredTerms et oc m -> (Path rule term oc, [Path rule term oc])+bfs' (h:t) _ = (h, t)+bfs' _ _ = error "empty path list" -notVisitedFirst' :: (Eq term, Eq rule, Eq oc, Eq et, Hashable et) => GetWork m rule term et oc-notVisitedFirst' paths toET et =- case L.find (\p -> not (ET.visited (toET $ runtimeTerm p) et)) paths of+notVisitedFirst' :: (Eq term, Eq rule, Eq oc, Hashable term) => GetWork m rule term oc+notVisitedFirst' paths et =+ case L.find (\p -> not (ET.visited (runtimeTerm p) et)) paths of Just p -> (p, L.delete p paths) Nothing -> (head paths, tail paths) -commutesLast :: forall term oc et . (Eq term, Eq oc, Eq et, Hashable et) => WorkStrategy Rewrite term et oc+commutesLast :: forall term oc . (Eq term, Eq oc, Hashable term) => WorkStrategy Rewrite term oc commutesLast = WorkStrategy go where- go paths toET et =- case L.find (\p -> not (ET.visited (toET $ runtimeTerm p) et || fromComm p)) paths of+ go paths et =+ case L.find (\p -> not (ET.visited (runtimeTerm p) et || fromComm p)) paths of Just p -> (p, L.delete p paths) Nothing -> (head paths, tail paths) fromComm ([], _) = False
src/Language/REST/Op.hs view
@@ -37,4 +37,6 @@ go ' ' = "_space_" go '∪' = "_cup_" go '\\' = "_bslash_"+ go '(' = "_lp_"+ go ')' = "_rp_" go c = singleton c
src/Language/REST/RESTDot.hs view
@@ -11,11 +11,15 @@ import Language.REST.Dot import Language.REST.Path +data ShowRejectsOpt =+ ShowRejectsWithRule | ShowRejectsWithoutRule | HideRejects+ deriving Eq+ data PrettyPrinter rule term ord = PrettyPrinter { printRule :: rule -> String , printTerm :: term -> String , printOrd :: ord -> String- , showRejects :: Bool+ , showRejects :: ShowRejectsOpt } rejNodeID :: (Hashable rule, Hashable term, Hashable a) => GraphType -> Path rule term a -> term -> String@@ -23,7 +27,7 @@ rejectedNodes :: forall rule term a . (Hashable rule, Hashable term, Hashable a) => GraphType -> PrettyPrinter rule term a -> Path rule term a -> S.Set Node-rejectedNodes _ pp _ | not (showRejects pp) = S.empty+rejectedNodes _ pp _ | showRejects pp == HideRejects = S.empty rejectedNodes gt pp p@(_steps, (PathTerm {rejected})) = S.fromList $ map go (HS.toList rejected) where go :: (term, rule) -> Node@@ -55,12 +59,16 @@ rejEdges :: Path rule term a -> S.Set Edge rejEdges p@(_, PathTerm _ rej) =- if showRejects pp+ if showRejects pp /= HideRejects then S.fromList $ map go (HS.toList rej) else S.empty where+ ruleText r =+ if showRejects pp == ShowRejectsWithRule+ then printRule pp r+ else "" go (rejTerm, r) =- Edge (nodeID (endNode gt pp p)) (rejNodeID gt p rejTerm) (printRule pp r) "red" " " "dotted"+ Edge (nodeID (endNode gt pp p)) (rejNodeID gt p rejTerm) (ruleText r) "red" " " "dotted" toEdge :: (Path rule term a, Path rule term a) -> Edge
src/Language/REST/Rest.hs view
@@ -57,51 +57,47 @@ data RESTState m rule term oc et rtype = RESTState { finished :: rtype rule term oc , working :: [Path rule term oc]- , explored :: ExploredTerms et oc m+ , explored :: ExploredTerms term oc m , targetPath :: Maybe (Path rule term oc) } -data RESTParams m rule term oc et rtype = RESTParams+data RESTParams m rule term oc rtype = RESTParams { re :: S.HashSet rule , ru :: S.HashSet rule- , toET :: term -> et , target :: Maybe term- , workStrategy :: WorkStrategy rule term et oc+ , workStrategy :: WorkStrategy rule term oc , ocImpl :: OCAlgebra oc term m , initRes :: rtype rule term oc , etStrategy :: ExploreStrategy } -rest :: forall m rule term oc et rtype .+rest :: forall m rule term oc rtype . ( MonadIO m , RewriteRule m rule term- , Show et , Hashable term , Eq term , Hashable rule- , Hashable et , Hashable oc , Eq rule- , Eq et , Eq oc , Show oc , RESTResult rtype)- => RESTParams m rule term oc et rtype+ => RESTParams m rule term oc rtype -> term -> m ((rtype rule term oc), Maybe (Path rule term oc))-rest RESTParams{re,ru,toET,ocImpl,workStrategy,initRes,target,etStrategy} t =+rest RESTParams{re,ru,ocImpl,workStrategy,initRes,target,etStrategy} t = rest' (RESTState initRes [([], PathTerm t S.empty)] initET Nothing) where (WorkStrategy ws) = workStrategy- initET = ET.empty (EF (AC.union ocImpl) (AC.notStrongerThan ocImpl)) etStrategy+ initET = ET.empty (EF (AC.union ocImpl) (AC.notStrongerThan ocImpl) (refine ocImpl)) etStrategy rest' (RESTState fin [] _ targetPath) = return (fin, targetPath) rest' state@(RESTState _ paths et (Just targetPath))- | ((steps, _), remaining) <- ws paths toET et+ | ((steps, _), remaining) <- ws paths et , length steps >= length (fst targetPath) = rest' state{working = remaining} rest' state@(RESTState fin paths et targetPath) = do- se <- shouldExplore (toET ptTerm) lastOrdering et+ se <- shouldExplore ptTerm lastOrdering et if se then do evalRWs <- candidates re@@ -112,7 +108,7 @@ rest' (state{ working = remaining }) where - (path@(ts, PathTerm ptTerm _), remaining) = ws paths toET et+ (path@(ts, PathTerm ptTerm _), remaining) = ws paths et lastOrdering :: oc lastOrdering = if L.null ts then top ocImpl else ordering $ last ts@@ -153,9 +149,9 @@ , finished = if null p' then includeInResult (ts, pt) fin else fin , explored = let- deps = S.map (toET . fst) (S.union evalRWs userRWs)+ deps = S.map fst (S.union evalRWs userRWs) in- ET.insert (toET ptTerm) lastOrdering deps et+ ET.insert ptTerm lastOrdering deps et , targetPath = if Just ptTerm == target then case targetPath of@@ -180,11 +176,11 @@ (t', r) <- ListT $ return (S.toList evalRWs) guard $ L.notElem t' tsTerms let ord = refine ocImpl lastOrdering ptTerm t'- lift (shouldExplore (toET t') ord et) >>= guard+ lift (shouldExplore t' ord et) >>= guard return (ts ++ [Step pt r ord True], PathTerm t' S.empty) userPaths = runListT $ do (t', r) <- liftSet userRWs ord <- ListT $ return $ Mb.maybeToList $ M.lookup t' acceptedUserRewrites- lift (shouldExplore (toET t') ord et) >>= guard+ lift (shouldExplore t' ord et) >>= guard return (ts ++ [Step pt r ord False], PathTerm t' S.empty)
src/Language/REST/Types.hs view
@@ -6,8 +6,8 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-}-{-# OPTIONS_GHC -Wno-orphans #-} + module Language.REST.Types ( prettyPrint , PPArgs(..)@@ -22,7 +22,6 @@ import qualified Data.List as L import qualified Data.HashSet as S import qualified Data.Set as OS-import qualified Data.Map as M import qualified Data.Text as T import Text.Printf @@ -69,11 +68,6 @@ show GTE = "≥" show EQ = "≅" -instance Hashable a => Hashable (OS.Set a) where- hashWithSalt i s = hashWithSalt i (OS.toList s)--instance (Hashable a, Hashable b) => Hashable (M.Map a b) where- hashWithSalt i s = hashWithSalt i (M.toList s) toOrderedSet :: (Eq a, Hashable a, Ord a) => S.HashSet a -> OS.Set a toOrderedSet = OS.fromList . S.toList
test/BagExample.hs view
@@ -105,14 +105,13 @@ RESTParams { re = S.empty , ru = rules- , toET = id , target = Nothing , workStrategy = bfs , ocImpl = impl , initRes = pathsResult , etStrategy = ExploreWhenNeeded } (bag start)- let prettyPrinter = PrettyPrinter showRule showBag show True+ let prettyPrinter = PrettyPrinter showRule showBag show ShowRejectsWithRule writeDot "example" Tree prettyPrinter (toOrderedSet paths) where impl = lift SC.strictOC $ cmapConstraints toMultiset (multisetOrder compareChar)
+ test/ExploredTerms.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveAnyClass #-}++module ExploredTerms where++import Control.Monad.Identity+import Data.Hashable+import qualified Data.HashSet as S+import GHC.Generics (Generic)++import Language.REST.ExploredTerms as ET++type Constraints = Int++-- 2nd argument is cost to explore+data Term = Term String Int+ deriving (Eq, Generic, Hashable, Show)++exploreFuncs :: ExploreFuncs Term Constraints Identity+exploreFuncs = EF undefined subsume refine where+ subsume c0 c1 = return $ c0 >= c1+ refine c _ (Term _ dest) = c - dest++t0 :: Term+t0 = Term "t0" 5++t1 :: Term+t1 = Term "t1" 0++t2 :: Term+t2 = Term "t2" 0++et0 :: ExploredTerms Term Constraints Identity+et0 = ET.empty exploreFuncs ExploreWhenNeeded++et :: ExploredTerms Term Constraints Identity+et = ET.insert t1 15 (S.fromList [t0]) $ ET.insert t0 14 (S.fromList [t2]) et0++tests :: [(String, Bool)]+tests =+ [ -- Described in https://github.com/zgrannan/rest/issues/9+ ("Explore-opt", not $ runIdentity $ shouldExplore t1 17 et)++ , ("Explore", runIdentity $ shouldExplore t1 21 et)+ ]
test/Main.hs view
@@ -8,6 +8,7 @@ import Control.Monad.Identity import Data.Time.Clock import Data.Hashable+import qualified Data.Set as DS import qualified Data.HashSet as S import Text.Printf @@ -25,6 +26,7 @@ import qualified Lists as Li import Language.REST.Core+import Language.REST.ConcreteOC import Language.REST.ExploredTerms import Language.REST.OCAlgebra import Language.REST.OCToAbstract@@ -84,12 +86,12 @@ { gShowConstraints :: Bool , gTarget :: Maybe String , gGraphType :: GraphType- , gShowRejects :: Bool+ , gShowRejects :: ShowRejectsOpt , gUseETOpt :: Bool } defaultParams :: GraphParams-defaultParams = GraphParams False Nothing Tree True True+defaultParams = GraphParams False Nothing Tree ShowRejectsWithoutRule True withTarget :: String -> GraphParams -> GraphParams withTarget target0 gp = gp{gTarget = Just target0}@@ -101,10 +103,13 @@ withNoETOpt gp = gp{gUseETOpt = False} withHideRejects :: GraphParams -> GraphParams-withHideRejects gp = gp{gShowRejects = False}+withHideRejects gp = gp{gShowRejects = HideRejects} -data SolverType = LPOStrict | LPO | RPO | KBO | Fuel Int+withShowRejectsRule :: GraphParams -> GraphParams+withShowRejectsRule gp = gp{gShowRejects = ShowRejectsWithRule} +data SolverType = LPOStrict | LPO | RPO | RPOConcrete [Op] | KBO | Fuel Int+ mkRESTGraph :: SolverType -> S.HashSet Rewrite@@ -119,6 +124,8 @@ withZ3 $ \z3 -> mkRESTGraph' (lift (AC.adtOC z3) lpo) evalRWs0 userRWs0 name term0 params mkRESTGraph RPO evalRWs0 userRWs0 name term0 params = withZ3 $ \z3 -> mkRESTGraph' (lift (AC.adtOC z3) rpo) evalRWs0 userRWs0 name term0 params+mkRESTGraph (RPOConcrete ops) evalRWs0 userRWs0 name term0 params =+ mkRESTGraph' (concreteOC $ DS.fromList ops) evalRWs0 userRWs0 name term0 params mkRESTGraph KBO evalRWs0 userRWs0 name term0 params = withZ3 $ \z3 -> mkRESTGraph' (kbo z3) evalRWs0 userRWs0 name term0 params mkRESTGraph (Fuel n) evalRWs0 userRWs0 name term0 params =@@ -142,7 +149,6 @@ RESTParams { re = evalRWs0 , ru = userRWs0- , toET = id , target = fmap parseTerm (gTarget params) , workStrategy = bfs , ocImpl = impl@@ -162,16 +168,20 @@ Nothing -> printf "TARGET %s NOT FOUND\n" (pp (parseTerm target1))) Nothing -> return () +setDistribRules :: S.HashSet Rewrite+setDistribRules = S.fromList+ [ distribL (/\) (\/)+ , distribR (/\) (\/)+ , distribL (\/) (/\)+ , distribR (\/) (/\)+ ]+ challengeRulesNoCommute :: S.HashSet Rewrite-challengeRulesNoCommute = S.fromList+challengeRulesNoCommute = S.union setDistribRules $ S.fromList [ x /\ x ~> x , x \/ x ~> x , x \/ emptyset ~> x , x /\ emptyset ~> emptyset- , distribL (/\) (\/)- , distribR (/\) (\/)- , distribL (\/) (/\)- , distribR (\/) (/\) , assocL (\/) , assocR (\/) ]
test/Test.hs view
@@ -12,6 +12,7 @@ import qualified Arith as A import qualified Data.HashMap.Strict as M+import qualified ExploredTerms as ExploredTerms import OpOrdering import DSL import WQO as WQO@@ -56,7 +57,6 @@ RESTParams { re = evalRWs , ru = userRWs- , toET = id , target = Nothing , workStrategy = notVisitedFirst , ocImpl = ?impl@@ -131,7 +131,7 @@ )) , ("Eval1", arithEQ (intToTerm 2 .+ intToTerm 3) 5) , ("Eval2", arithEQ (ack (intToTerm 3) (intToTerm 2)) 29)- , ("Subst1", return $ subst (M.fromList [("x", intToTerm 1), ("y", intToTerm 2)]) (x #+ y) == (intToTerm 1 .+ intToTerm 2))+ , ("Subst1", return $ subst (M.fromList [("X", intToTerm 1), ("Y", intToTerm 2)]) (x #+ y) == (intToTerm 1 .+ intToTerm 2)) , ("ArithTerm", termTest) , ("ArithTerm2", termTest2) , ("Arith0", eq (t1 .+ t2 .+ intToTerm 1) (t1 .+ (intToTerm 1 .+ t2)))@@ -237,6 +237,7 @@ go z3 = do putStrLn "Running REST Test Suite"+ runTestSuite "ExploredTerms" ExploredTerms.tests runTestSuite "SMT" SMT.tests runTestSuite "KBO" (KBO.tests z3) _ <- QuickCheckTests.tests
testlib/DSL.hs view
@@ -33,11 +33,11 @@ d = App (Op "d") [] x, y, v, w, z' :: MT.MetaTerm-x = MT.Var "x"-y = MT.Var "y"-v = MT.Var "v"-w = MT.Var "w"-z' = MT.Var "z"+x = MT.Var "X"+y = MT.Var "Y"+v = MT.Var "V"+w = MT.Var "W"+z' = MT.Var "Z" f, g, h :: Op f = Op "f"
testlib/Language/REST/ConcreteOC.hs view
@@ -7,53 +7,26 @@ import qualified Language.REST.Internal.WQO as WQO import Language.REST.RuntimeTerm import Language.REST.RPO-import Language.REST.Internal.OpOrdering-import Language.REST.MetaTerm+import Language.REST.Op -import Data.List as L import Data.Hashable import GHC.Generics (Generic) import qualified Data.Set as S -data ConcreteOC = ConcreteOC [RuntimeTerm] (Maybe OpOrdering)+data ConcreteOC = ConcreteOC (S.Set (WQO.WQO Op)) deriving (Eq, Ord, Generic, Hashable) instance Show ConcreteOC where- show (ConcreteOC _ (Just oo)) = show oo- show _ = "impossible"--isSat :: ConcreteOC -> Bool-isSat (ConcreteOC _ (Just _)) = True-isSat _ = False--getOrdering :: [RuntimeTerm] -> Maybe OpOrdering-getOrdering ts =- let- ops = S.unions $ map termOps ts- orderings = S.toList $ WQO.orderings ops- in- L.find (`orients` ts) orderings+ show (ConcreteOC ords) = show (S.size ords) ++ " orderings" +concreteOC :: Monad m => S.Set Op -> AOC.OCAlgebra ConcreteOC RuntimeTerm m+concreteOC ops = AOC.OCAlgebra (return . isSat) refine (ConcreteOC (WQO.orderings ops)) union notStrongerThan+ where+ union (ConcreteOC ord1) (ConcreteOC ord2) = ConcreteOC $ S.union ord1 ord2+ notStrongerThan (ConcreteOC ord1) (ConcreteOC ord2) = return $ ord1 == ord2 || ord2 `S.isSubsetOf` ord1 -orients :: OpOrdering -> [RuntimeTerm] -> Bool-orients ordering terms =- let- pairs = zip terms (tail terms)- in- all (uncurry $ synGTE ordering) pairs+ isSat :: ConcreteOC -> Bool+ isSat (ConcreteOC ords) = not $ S.null ords -concreteOC :: Monad m => AOC.OCAlgebra ConcreteOC RuntimeTerm m-concreteOC = AOC.OCAlgebra (return . isSat) refine (ConcreteOC [] (Just (WQO.empty))) constUnion notStrongerThan- where- constUnion t1 _ = t1- notStrongerThan _ _ = return False refine :: ConcreteOC -> RuntimeTerm -> RuntimeTerm -> ConcreteOC- refine (ConcreteOC ts (Just o)) _ u =- let- ts' = ts ++ [u]- in- ConcreteOC ts' $- if o `orients` ts'- then Just o- else getOrdering ts'- refine (ConcreteOC ts Nothing) _ u = ConcreteOC (ts ++ [u]) Nothing+ refine (ConcreteOC ords) t u = ConcreteOC (S.filter (\ord -> synGTE ord t u) ords)