aterm-utils-0.1.0.0: src/ATerm/Utilities.hs
{-# LANGUAGE PatternGuards #-}
-- | Functions for working with the ATerms that ROSE produces.
module ATerm.Utilities
( -- * Utility functions
app -- (ATermTable -> a) -> ATermTable -> Int -> a
, foldr -- (ATermTable -> a -> a) -> a -> ATermTable -> a
, foldl -- (a -> ATermTable -> a) -> a -> ATermTable -> a
, foldl' -- (a -> ATermTable -> a) -> a -> ATermTable -> a
, foldM -- (a -> ATermTable -> m a) -> a -> ATermTable -> m a
, mapM -- (ATermTable -> m b) -> ATermTable -> m [b]
, mapM_ -- (ATermTable -> m b) -> ATermTable -> m ()
, map -- (ATermTable -> a) -> ATermTable -> [a]
, concatMap -- (ATermTable -> [a]) -> ATermTable -> [a]
-- * Check Monad
, CheckM
, appM -- (ATermTable -> a) -> Int -> CheckM log state m a
, currentTerm -- CheckM log state m ATermTable
, withCurrentTerm -- ATermTable -> CheckM log state m a -> CheckM log state m a
, childrenM -- CheckM log state m [Int]
, satisfy -- (ATermTable -> Bool) -> CheckM log st m a -> CheckM log st m (Maybe a)
, inSubtree -- CheckM log state m a -> CheckM log state m [[a]]
, inSubtree_ -- CheckM log state m a -> CheckM log state m ()
, everywhere -- CheckM log state m a -> CheckM log state m [a]
, everywhere_ -- CheckM log state m a -> CheckM log state m ()
-- * Extractions
, extractString -- ATermTable -> Maybe String
, extractInteger -- ATermTable -> Maybe Integer
, extractFileInfo -- ATermTable -> Maybe (String, Integer, Integer)
, isNamed -- String -> ATermTable -> Bool
, showATerm -- ATermTable -> String
, children -- ATermTable -> [Int]
-- * Read and write
, readATerm
, writeSharedATerm
-- * Misc
, getATermFromTable
) where
import ATerm.ReadWrite
import ATerm.SimpPretty
import ATerm.AbstractSyntax
import Control.Monad.Trans.RWS.Strict
import Control.Monad ( liftM )
import qualified Control.Monad as M
import Data.Monoid
import qualified Data.List as L ( foldl' )
import Prelude hiding (foldr, foldl, map, mapM_, mapM, concatMap)
import qualified Prelude as P
---------------------------------------------------------------------
-- Working with ATerms (eg., ATermTable)
---------------------------------------------------------------------
-- | Turns a normal function of ATermTable into a function that
-- works on the hash value instead.
app :: (ATermTable -> a) -> ATermTable -> Int -> a
app f t i = f (getATermByIndex1 i t)
-- | Standard foldr, but for ATermTables
foldr :: (ATermTable -> a -> a) -> a -> ATermTable -> a
foldr k z at = go at
where
go t = t `k` P.foldr k' z (children t)
k' i acc = app (foldr k acc) at i
-- | Standard foldl, but for ATermTables
foldl :: (a -> ATermTable -> a) -> a -> ATermTable -> a
foldl k z at = go at
where
go t = k (P.foldl k' z (children t)) t
k' acc i = app (foldl k acc) at i
-- | Standard foldl', but for ATermTables
foldl' :: (a -> ATermTable -> a) -> a -> ATermTable -> a
foldl' k z at = go at
where
go t = let z' = L.foldl' k' z (children t) in z' `seq` k z' t
k' acc i = app (foldl' k acc) at i
-- | Standard foldM, but for ATermTables
foldM :: (Monad m) => (a -> ATermTable -> m a) -> a -> ATermTable -> m a
foldM k z at = go at
where
go t = k z t >>= \a -> M.foldM k' a (children t)
k' acc i = app (foldM k acc) at i
-- | Standard mapM, but for ATermTables
mapM :: (Monad m) => (ATermTable -> m b) -> ATermTable -> m [b]
mapM f = foldM action []
where
action acc x = do
x' <- f x
return (x' : acc)
-- | Standard mapM_, but for ATermTables
mapM_ :: (Monad m) => (ATermTable -> m b) -> ATermTable -> m ()
mapM_ f = foldM action ()
where
action _ x = do
_ <- f x
return ()
-- | Standard map, but for ATermTables
map :: (ATermTable -> a) -> ATermTable -> [a]
map f at = foldr ((:) . f) [] at
-- | Standard concatMap, but for ATermTables
concatMap :: (ATermTable -> [a]) -> ATermTable -> [a]
concatMap f at = foldr ((++) . f) [] at
---------------------------------------------------------------------
-- Checker Monad
---------------------------------------------------------------------
-- | The checker monad. For now the environment is the current ATerm, in the
-- future we may also store the path from the root to the current ATerm, so use
-- 'currentTerm' instead of ask to get the current ATerm
type CheckM log state m a = RWST ATermTable log state m a
-- | Like 'app' but lifts the result into the CheckM monad
appM :: (Monoid log, Monad m) => (ATermTable -> a) -> Int -> CheckM log state m a
appM f i = do
t <- currentTerm
return (f (getATermByIndex1 i t))
-- | Use this instead of 'ask' so that we can refactor
-- the environment later without impacting existing code.
currentTerm :: (Monoid log, Monad m) => CheckM log state m ATermTable
currentTerm = ask
-- | Use this instead of 'local' so that we can refactor
-- the environment later without impacting existing code.
withCurrentTerm :: (Monoid log, Monad m)
=> ATermTable -> CheckM log state m a -> CheckM log state m a
withCurrentTerm t = local (const t)
-- | Return the hashes of the current term's children into the monad
childrenM :: (Monad m, Monoid log) => CheckM log state m [Int]
childrenM = children `liftM` currentTerm
-- | Use this when the current node must satisfy a specific property.
-- Note: Using Maybe here is a bit of a hack. Refactor to support MonadPlus style guards?
satisfy :: (Monad m, Monoid log)
=> (ATermTable -> Bool) -> CheckM log st m a -> CheckM log st m (Maybe a)
satisfy p m = do
t <- currentTerm
case p t of
True -> Just `liftM` m
False -> return Nothing
-- | Applies a traversal in a subtree of the current ATerm. Differs from 'everywhere'
-- in that it does not apply the traversal to the current term (only its children and
-- their children).
inSubtree :: (Monad m, Monoid log) => CheckM log state m a -> CheckM log state m [[a]]
inSubtree c = do
ks <- childrenM
at <- currentTerm
let kids = P.map (`getATermByIndex1` at) ks
P.mapM (`withCurrentTerm` (everywhere c)) kids
-- Like 'inSubtree_' but throws away the result.
inSubtree_ :: (Monad m, Monoid log) => CheckM log state m a -> CheckM log state m ()
inSubtree_ c = do
ks <- childrenM
at <- currentTerm
let kids = P.map (`getATermByIndex1` at) ks
P.mapM_ (`withCurrentTerm` (everywhere c)) kids
-- | Applies a traversal over the tree defined by the current node (including root node).
everywhere :: (Monad m, Monoid log) => CheckM log state m a -> CheckM log state m [a]
everywhere c = currentTerm >>= mapM (\at -> withCurrentTerm at c)
-- | Like 'everywhere' but throws away the result.
everywhere_ :: (Monad m, Monoid log) => CheckM log state m a -> CheckM log state m ()
everywhere_ c = currentTerm >>= mapM_ (\at -> withCurrentTerm at c)
{-
-- Left here as a lesson: This doesn't seem to do the intended thing
everywhere' :: (Monad m, Monoid log) => CheckM log state m a -> CheckM log state m [a]
everywhere' c = currentTerm >>= mapM (const c)
-}
---------------------------------------------------------------------
-- Turn ATerms into normal values, we make assumptions
-- about the format that ROSE emits ATerms in.
---------------------------------------------------------------------
-- | Extracts the label of Application nodes
extractString :: ATermTable -> Maybe String
extractString at =
case getATerm at of
ShAAppl s _ _ -> Just s
_ -> Nothing
-- | Extract the integer of Int nodes
extractInteger :: ATermTable -> Maybe Integer
extractInteger at =
case getATerm at of
ShAInt i _ -> Just i
_ -> Nothing
-- | Extracts the filename, line number, and colunm number from
-- file_info nodes.
extractFileInfo :: ATermTable -> Maybe (String, Integer, Integer)
extractFileInfo at =
case getATerm at of
ShAAppl s [fp,line,col] _
| s == "file_info"
, Just f <- extractString (getATermByIndex1 fp at)
, Just l <- extractInteger (getATermByIndex1 line at)
, Just c <- extractInteger (getATermByIndex1 col at) -> Just (f,l,c)
_ -> Nothing
-- | Equality test on the label of an Application node
isNamed :: String -> ATermTable -> Bool
isNamed name t =
case getATerm t of
ShAAppl s _ _ -> s == name
_ -> False
-- | It's not acually pretty, but that's not our fault.
showATerm :: ATermTable -> String
showATerm = render . writeSharedATermSDoc
-- | This pattern comes up in most traversals. Simply return the stable names
-- so we don't break sharing.
children :: ATermTable -> [Int]
children t =
case getATerm t of
ShAAppl _ l _ -> l
ShAList l _ -> l
ShAInt _ _ -> []
---------------------------------------------------------------------
-- Misc
---------------------------------------------------------------------
getATermFromTable :: ATermTable -> Int -> ATermTable
getATermFromTable = flip getATermByIndex1