-- | -- Module: Language.KURE.Combinators -- Copyright: (c) 2006-2008 Andy Gill -- License: BSD3 -- -- Maintainer: Andy Gill -- Stability: unstable -- Portability: ghc -- -- This module contains various combinators that use 'Translate' and 'Rewrite'. The convension is that -- 'Translate' based combinators end with @T@, and 'Rewrite' based combinators end with @R@. Of course, -- because 'Rewrite' is a type synomim of 'Translate', the 'Rewrite' functions also operate with on 'Translate', -- and the 'Translate' functions operate with 'Rewrite'. module Language.KURE.Combinators ( -- * The 'Translate' combinators (<+) , (>->) , failT , readerT , readEnvT , mapEnvT , writeEnvT , pureT , constT , concatT , -- * The 'Rewrite' combinators (.+) , (!->) , tryR , changedR , repeatR , acceptR , idR , failR , -- * The Prelude combinators tuple2R , listR , maybeR , tuple2U , listU , maybeU , -- * Generic failure, over both 'Monad's and 'Translate's. (?) , Failable(..) ) where import Language.KURE.RewriteMonad import Language.KURE.Translate import Language.KURE.Rewrite import Data.Monoid import Control.Monad infixl 3 <+, >->, .+, !-> infixr 3 ? -- Note: We use < for catching fail, . for catching id. -------------------------------------------------------------------------------- -- The Translate combinators. -- | like a catch, '<+' does the first translate , and if it fails, then does the second translate. (<+) :: (Monoid dec, Monad m) => Translate m dec a b -> Translate m dec a b -> Translate m dec a b (<+) rr1 rr2 = transparently $ translate $ \ e -> apply rr1 e `catchM` (\ _ -> apply rr2 e) -- | like a @;@ If the first translate succeeds, then do to the second translate after the first translate. (>->) :: (Monoid dec, Monad m) => Translate m dec a b -> Translate m dec b c -> Translate m dec a c (>->) rr1 rr2 = transparently $ translate $ \ e -> chainM (apply rr1 e) ( \ _i e2 -> apply rr2 e2) -- | failing translation. failT :: (Monad m, Monoid dec) => String -> Translate m dec a b failT msg = translate $ \ _ -> failM msg -- | look at the argument for the translation before choosing which translation to perform. readerT :: (Monoid dec, Monad m) => (a -> Translate m dec a b) -> Translate m dec a b readerT fn = transparently $ translate $ \ expA -> apply (fn expA) expA -- | look at the @dec@ before choosing which translation to do. readEnvT :: (Monad m, Monoid dec) => (dec -> Translate m dec a b) -> Translate m dec a b readEnvT f = transparently $ translate $ \ e -> do dec <- readEnvM apply (f dec) e -- | add to the context 'dec', which is propogated using a writer monad. writeEnvT :: (Monad m, Monoid dec) => dec -> Rewrite m dec a writeEnvT dec = translate $ \ e -> do writeEnvM dec ; return e -- | change the @dec@'s for a scoped translation. mapEnvT :: (Monoid dec,Monad m) => (dec -> dec) -> Translate m dec a r -> Translate m dec a r mapEnvT f_env rr = transparently $ translate $ \ e -> mapEnvM f_env (apply rr e) -- | 'pureT' promotes a function into an unfailable, non-identity 'Translate'. pureT :: (Monad m,Monoid dec) => (a -> b) -> Translate m dec a b pureT f = translate $ \ a -> return (f a) -- | 'constT' always translates into an unfailable 'Translate' that returns the first argument. constT :: (Monad m,Monoid dec) => b -> Translate m dec a b constT = pureT . const -- | 'concatT' composes a list of 'Translate' into a single 'Translate' which 'mconcat's its result. concatT :: (Monad m,Monoid dec,Monoid r) => [Translate m dec a r] -> Translate m dec a r concatT ts = translate $ \ e -> do rs <- sequence [ apply t e | t <- ts ] return (mconcat rs) -------------------------------------------------------------------------------- -- The 'Rewrite' combinators. -- | if the first rewrite is an identity, then do the second rewrite. (.+) :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec a -> Rewrite m dec a (.+) a b = a `countTrans` (\ i -> if i == 0 then b else idR) -- | if the first rewrite was /not/ an identity, then also do the second rewrite. (!->) :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec a -> Rewrite m dec a (!->) a b = a `countTrans` (\ i -> if i == 0 then idR else b) -- | catch a failing 'Rewrite', making it into an identity. tryR :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec a tryR s = s <+ idR -- | if this is an identity rewrite, make it fail. To succeed, something must have changed. changedR :: (Monoid dec,Monad m) => Rewrite m dec a -> Rewrite m dec a changedR rr = rr .+ failR "unchanged" -- | repeat a rewrite until it fails, then return the result before the failure. repeatR :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec a repeatR s = tryR (s >-> repeatR s) -- | look at the argument to a rewrite, and choose to be either a failure of trivial success. acceptR :: (Monoid dec, Monad m) => (a -> Bool) -> Rewrite m dec a acceptR fn = transparently $ translate $ \ expA -> if fn expA then return expA else fail "accept failed" -- | identity rewrite. idR :: (Monad m, Monoid dec) => Rewrite m dec exp idR = transparently $ rewrite $ \ e -> return e -- | failing rewrite. failR :: (Monad m, Monoid dec) => String -> Rewrite m dec a failR = failT -------------------------------------------------------------------------------- -- Prelude structures tuple2R :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec b -> Rewrite m dec (a,b) tuple2R rra rrb = transparently $ rewrite $ \ (a,b) -> liftM2 (,) (apply rra a) (apply rrb b) listR :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec [a] listR rr = transparently $ rewrite $ mapM (apply rr) maybeR :: (Monoid dec, Monad m) => Rewrite m dec a -> Rewrite m dec (Maybe a) maybeR rr = transparently $ rewrite $ \ e -> case e of Just e' -> liftM Just (apply rr e') Nothing -> return $ Nothing tuple2U :: (Monoid dec, Monad m, Monoid r) => Translate m dec a r -> Translate m dec b r -> Translate m dec (a,b) r tuple2U rra rrb = translate $ \ (a,b) -> liftM2 mappend (apply rra a) (apply rrb b) listU :: (Monoid dec, Monad m, Monoid r) => Translate m dec a r -> Translate m dec [a] r listU rr = translate $ liftM mconcat . mapM (apply rr) maybeU :: (Monoid dec, Monad m, Monoid r) => Translate m dec a r -> Translate m dec (Maybe a) r maybeU rr = translate $ \ e -> case e of Just e' -> apply rr e' Nothing -> return $ mempty -------------------------------------------------------------------------------- -- | Failable structure. class Failable f where failure :: String -> f a instance (Monad m, Monoid dec) => Failable (Translate m dec a) where failure msg = failT msg instance (Monad m, Monoid dec) => Failable (RewriteM m dec) where failure msg = fail msg -- | Guarded translate or monadic action. (?) :: (Failable f) => Bool -> f a -> f a (?) False _rr = failure "(False ?)" (?) True rr = rr -------------------------------------------------------------------------------- -- internal to this module. countTrans :: (Monoid dec, Monad m) => Rewrite m dec a -> (Int -> Rewrite m dec a) -> Rewrite m dec a countTrans rr fn = transparently $ translate $ \ e -> chainM (apply rr e) (\ i e' -> apply (fn i) e')