{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-} -- | -- Module: Language.KURE.Pathfinder -- Copyright: (c) 2012--2013 The University of Kansas -- License: BSD3 -- -- Maintainer: Neil Sculthorpe -- Stability: beta -- Portability: ghc -- -- This module provides combinators to find 'LocalPath's sub-nodes specified by a predicate. module Language.KURE.Pathfinder ( -- * Finding Local Paths -- ** Context Transformers -- | To find a 'LocalPath' to a node that satisfies a predicate, use @'withLocalPathT' (tt ('acceptLocalPathT' q))@, -- where @q@ is a translation returning 'Bool', and @tt@ is a traversal strategy, such as 'collectT' or 'onetdT'. -- This will handle the tracking of the local path. -- See the example pathfinders below. WithLocalPath , withLocalPathT , exposeLocalPathT , acceptLocalPathT -- ** Example Pathfinders , pathsToT , onePathToT , oneNonEmptyPathToT , prunePathsToT , uniquePathToT , uniquePrunePathToT ) where import Control.Category hiding ((.)) import Control.Arrow import Data.Monoid (mempty) import Language.KURE.MonadCatch import Language.KURE.Translate import Language.KURE.Combinators.Translate import Language.KURE.Path import Language.KURE.Walker import Language.KURE.ExtendableContext ------------------------------------------------------------------------------- -- | A context transformer that adds a 'LocalPath' (from the current node) to the context. type WithLocalPath c crumb = ExtendContext c (LocalPath crumb) -- | Apply a translation that stores a 'LocalPath' in the context (starting at the current node). withLocalPathT :: Translate (WithLocalPath c crumb) m a b -> Translate c m a b withLocalPathT = liftContext (extendContext mempty) {-# INLINE withLocalPathT #-} -- | Extract the current 'LocalPath' from the context. exposeLocalPathT :: Monad m => Translate (WithLocalPath c crumb) m a (LocalPath crumb) exposeLocalPathT = contextT >>^ extraContext {-# INLINE exposeLocalPathT #-} -- | Return the current 'LocalPath' if the predicate translation succeeds. acceptLocalPathT :: Monad m => Translate c m g Bool -> Translate (WithLocalPath c crumb) m g (LocalPath crumb) acceptLocalPathT q = accepterR (liftContext baseContext q) >>> exposeLocalPathT {-# INLINE acceptLocalPathT #-} ------------------------------------------------------------------------------- -- | Find the 'LocalPath's to every node that satisfies the predicate. pathsToT :: (Walker (WithLocalPath c crumb) g, MonadCatch m) => Translate c m g Bool -> Translate c m g [LocalPath crumb] pathsToT q = withLocalPathT (collectT $ acceptLocalPathT q) {-# INLINE pathsToT #-} -- | Find the 'LocalPath's to every node that satisfies the predicate, ignoring nodes below successes. prunePathsToT :: (Walker (WithLocalPath c crumb) g, MonadCatch m) => Translate c m g Bool -> Translate c m g [LocalPath crumb] prunePathsToT q = withLocalPathT (collectPruneT $ acceptLocalPathT q) {-# INLINE prunePathsToT #-} -- | Find the 'LocalPath' to the first node that satisfies the predicate (in a pre-order traversal). onePathToT :: forall c crumb g m. (Walker (WithLocalPath c crumb) g, MonadCatch m) => Translate c m g Bool -> Translate c m g (LocalPath crumb) onePathToT q = setFailMsg "No matching nodes found." $ withLocalPathT (onetdT $ acceptLocalPathT q) {-# INLINE onePathToT #-} -- | Find the 'LocalPath' to the first descendent node that satisfies the predicate (in a pre-order traversal). oneNonEmptyPathToT :: (Walker (WithLocalPath c crumb) g, MonadCatch m) => Translate c m g Bool -> Translate c m g (LocalPath crumb) oneNonEmptyPathToT q = setFailMsg "No matching nodes found." $ withLocalPathT (oneT $ onetdT $ acceptLocalPathT q) {-# INLINE oneNonEmptyPathToT #-} -- local function used by uniquePathToT and uniquePrunePathToT requireUniquePath :: Monad m => Translate c m [LocalPath crumb] (LocalPath crumb) requireUniquePath = contextfreeT $ \ ps -> case ps of [] -> fail "No matching nodes found." [p] -> return p _ -> fail $ "Ambiguous: " ++ show (length ps) ++ " matching nodes found." {-# INLINE requireUniquePath #-} -- | Find the 'LocalPath' to the node that satisfies the predicate, failing if that does not uniquely identify a node. uniquePathToT :: (Walker (WithLocalPath c crumb) g, MonadCatch m) => Translate c m g Bool -> Translate c m g (LocalPath crumb) uniquePathToT q = pathsToT q >>> requireUniquePath {-# INLINE uniquePathToT #-} -- | Build a 'LocalPath' to the node that satisfies the predicate, failing if that does not uniquely identify a node (ignoring nodes below successes). uniquePrunePathToT :: (Walker (WithLocalPath c crumb) g, MonadCatch m) => Translate c m g Bool -> Translate c m g (LocalPath crumb) uniquePrunePathToT q = prunePathsToT q >>> requireUniquePath {-# INLINE uniquePrunePathToT #-} -------------------------------------------------------------------------------