tock-mirror/pass/Traversal.hs
Adam Sampson 7a11d0b2c3 Include TypeSet in OpsM.
This means the TypeSet is only rebuilt when the ops are extended, not
each time the operation is applied (curse the unpredictability of
Haskell CAF optimisation).
2008-06-11 12:04:06 +00:00

206 lines
7.1 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2008 University of Kent
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
-}
-- | Traversal strategies over the AST and other data types.
module Traversal (
OpsM, Ops
, TransformM, Transform
, CheckM, Check
, baseOp, extOp, extOpS
, makeDepth, extOpD, extOpSD
, makeCheck, extOpC
, RecurseM, Recurse, makeRecurse
, DescendM, Descend, makeDescend
, applyDepthM, applyDepthSM, applyDepthM2
, checkDepthM, checkDepthM2
) where
import Data.Generics
import qualified AST as A
import GenericUtils
import Pass
-- | A set of generic operations.
type OpsM m = ([TypeKey], TypeSet, DescendM m -> RecurseM m)
-- | As 'OpsM', but specialised for 'PassM'.
type Ops = OpsM PassM
-- | A transformation for a single 'Data' type.
type TransformM m t = t -> m t
-- | As 'TransformM', but specialised for 'PassM'.
type Transform t = TransformM PassM t
-- | A check for a single 'Data' type.
-- This is like 'Transform', but it doesn't change the value; it may fail or
-- modify the state, though.
type CheckM m t = t -> m ()
-- | As 'CheckM', but specialised for 'PassM'.
type Check t = CheckM PassM t
-- | An empty set of operations.
baseOp :: forall m. Monad m => OpsM m
baseOp = ([], makeTypeSet [], id)
-- | Add a 'TransformM' to a set, to be applied with explicit descent
-- (that is, the transform will be responsible for recursing into child
-- elements itself).
extOp :: forall m t. (Monad m, Data t) => OpsM m -> TransformM m t -> OpsM m
extOp (tks, _, g) f
= (tks',
makeTypeSet tks',
(\descend -> g descend `extM` f))
where
tks' = typeKey (undefined :: t) : tks
-- | As 'extOp', but for transformations that work on all 'A.Structured' types.
extOpS :: forall m. Monad m =>
OpsM m
-> (forall t. Data t => TransformM m (A.Structured t))
-> OpsM m
extOpS ops f
= ops
`extOp` (f :: TransformM m (A.Structured A.Variant))
`extOp` (f :: TransformM m (A.Structured A.Process))
`extOp` (f :: TransformM m (A.Structured A.Option))
`extOp` (f :: TransformM m (A.Structured A.ExpressionList))
`extOp` (f :: TransformM m (A.Structured A.Choice))
`extOp` (f :: TransformM m (A.Structured A.Alternative))
`extOp` (f :: TransformM m (A.Structured ()))
-- | Generate an operation that applies a 'TransformM' with automatic
-- depth-first descent.
makeDepth :: forall m t. (Monad m, Data t) =>
OpsM m -> TransformM m t -> TransformM m t
makeDepth ops f v = descend v >>= f
where
descend :: DescendM m
descend = makeDescend ops
-- | Add a 'TransformM' to a set, to be applied with automatic depth-first
-- descent.
extOpD :: forall m t. (Monad m, Data t) => OpsM m -> OpsM m -> TransformM m t -> OpsM m
extOpD ops ops0 f = ops `extOp` (makeDepth ops0 f)
-- | As 'extOpD', but for transformations that work on all 'A.Structured' types.
extOpSD :: forall m. Monad m =>
OpsM m
-> OpsM m
-> (forall t. Data t => TransformM m (A.Structured t))
-> OpsM m
extOpSD ops ops0 f = ops `extOpS` (makeDepth ops0 f)
-- | Generate an operation that applies a 'CheckM' with automatic
-- depth-first descent.
makeCheck :: forall m t. (Monad m, Data t) =>
OpsM m -> CheckM m t -> TransformM m t
makeCheck ops f v = descend v >> f v >> return v
where
descend :: DescendM m
descend = makeDescend ops
-- | Add a 'CheckM' to a set, to be applied with automatic depth-first descent.
extOpC :: forall m t. (Monad m, Data t) => OpsM m -> OpsM m -> CheckM m t -> OpsM m
extOpC ops ops0 f = ops `extOp` (makeCheck ops0 f)
-- | A function that applies a generic operation.
-- This applies the operations in the set to the provided value.
--
-- This is the type of function that you want to use to apply a generic
-- operation; a pass in Tock is usually the application of a 'RecurseM' to the
-- AST. It's also what you should use when you're writing a pass that uses
-- explicit descent, and you want to explicitly recurse into one of the
-- children of a value that one of your transformations has been applied to.
type RecurseM m = (forall t. Data t => t -> m t)
-- | As 'RecurseM', but specialised for 'PassM'.
type Recurse = RecurseM PassM
-- | Build a 'RecurseM' function from a set of operations.
makeRecurse :: forall m. Monad m => OpsM m -> RecurseM m
makeRecurse ops@(_, _, f) = f descend
where
descend :: DescendM m
descend = makeDescend ops
-- | A function that applies a generic operation.
-- This applies the operations in the set to the immediate children of the
-- provided value, but not to the value itself.
--
-- You should use this type of operation when you're writing a traversal with
-- explicit descent, and you want to descend into all the children of a value
-- that one of your transformations has been applied to.
type DescendM m = (forall t. Data t => t -> m t)
-- | As 'DescendM', but specialised for 'PassM'.
type Descend = DescendM PassM
-- | Build a 'DescendM' function from a set of operations.
makeDescend :: forall m. Monad m => OpsM m -> DescendM m
makeDescend ops@(_, ts, _) = gmapMFor ts recurse
where
recurse :: RecurseM m
recurse = makeRecurse ops
-- | Apply a transformation, recursing depth-first.
applyDepthM :: forall m t1. (Monad m, Data t1) =>
TransformM m t1 -> (forall s. Data s => s -> m s)
applyDepthM f1 = makeRecurse ops
where
ops :: OpsM m
ops = baseOp `extOp` makeDepth ops f1
-- | As 'applyDepthM', but for transformations that work on all 'A.Structured'
-- types.
applyDepthSM :: forall m s. (Monad m, Data s) =>
(forall t. Data t => TransformM m (A.Structured t)) -> s -> m s
applyDepthSM f1 = makeRecurse ops
where
ops :: OpsM m
ops = extOpSD baseOp ops f1
-- | Apply two transformations, recursing depth-first.
applyDepthM2 :: forall m t1 t2 s. (Monad m, Data t1, Data t2, Data s) =>
TransformM m t1 -> TransformM m t2 -> s -> m s
applyDepthM2 f1 f2 = makeRecurse ops
where
ops :: OpsM m
ops = baseOp `extOp` makeDepth ops f1
`extOp` makeDepth ops f2
-- | Apply a check, recursing depth-first.
checkDepthM :: forall m t1 s. (Monad m, Data t1, Data s) =>
CheckM m t1 -> s -> m s
checkDepthM f1 = makeRecurse ops
where
ops :: OpsM m
ops = baseOp `extOp` makeCheck ops f1
-- | Apply two checks, recursing depth-first.
checkDepthM2 :: forall m t1 t2 s. (Monad m, Data t1, Data t2, Data s) =>
CheckM m t1 -> CheckM m t2 -> s -> m s
checkDepthM2 f1 f2 = makeRecurse ops
where
ops :: OpsM m
ops = baseOp `extOp` makeCheck ops f1
`extOp` makeCheck ops f2