174 lines
4.3 KiB
Haskell
174 lines
4.3 KiB
Haskell
|
|
{- | The carrier type for AST.
|
|
|
|
"Untypedness" of the tree is a payoff to ablity to stop and navigate
|
|
anywhere, not just inside the expression context.
|
|
|
|
Is a `Functor` and `Foldable` over its @info@ parameter.
|
|
Is not `Traversable`, because this will definitely not preserve scope.
|
|
Use `updateTree` instead of `traverse`/`for`.
|
|
-}
|
|
|
|
module Tree
|
|
( -- * Tree type
|
|
Tree
|
|
, lookupTree
|
|
, traverseTree
|
|
, mk
|
|
, infoOf
|
|
|
|
-- * Callbacks on update
|
|
, UpdateOver (..)
|
|
, skip
|
|
)
|
|
where
|
|
|
|
import Data.Foldable
|
|
import Data.List
|
|
import Data.Sum
|
|
import Data.Monoid (First(..), getFirst)
|
|
|
|
import Lattice
|
|
import Comment
|
|
import Pretty
|
|
import Error
|
|
|
|
import Debug.Trace
|
|
|
|
-- | A tree, where each layer is one of @layers@ `Functor`s.
|
|
--
|
|
-- Is equipped with @info@.
|
|
--
|
|
-- Can contain `Error` instead of all the above.
|
|
--
|
|
newtype Tree layers info = Tree
|
|
{ unTree :: Either (Error info) (info, Sum layers (Tree layers info))
|
|
}
|
|
|
|
dumpTree
|
|
:: (Apply Functor layers, Apply Foldable layers, HasComments info, Pretty1 (Sum layers), Pretty info)
|
|
=> Tree layers info
|
|
-> Doc
|
|
dumpTree (Tree tree) =
|
|
case tree of
|
|
Left e -> "ERR"
|
|
Right (i, ls) ->
|
|
pp (Tree tree) `indent` block (dumpTree <$> toList ls)
|
|
|
|
instance Apply Functor layers => Functor (Tree layers) where
|
|
fmap f = go
|
|
where
|
|
go (Tree (Left err)) = Tree $ Left $ fmap f err
|
|
go (Tree (Right (a, rest))) = Tree $ Right (f a, fmap go rest)
|
|
|
|
instance Apply Foldable layers => Foldable (Tree layers) where
|
|
foldMap f = go
|
|
where
|
|
go (Tree (Left err)) = foldMap f err
|
|
go (Tree (Right (a, rest))) = f a <> foldMap go rest
|
|
|
|
instance
|
|
( Apply Functor layers
|
|
, HasComments info
|
|
, Pretty1 (Sum layers)
|
|
, Pretty info
|
|
)
|
|
=>
|
|
Show (Tree layers info)
|
|
where
|
|
show = show . pp
|
|
|
|
instance {-# OVERLAPS #-}
|
|
( HasComments info
|
|
, Apply Functor fs
|
|
, Pretty1 (Sum fs)
|
|
, Pretty info
|
|
)
|
|
=>
|
|
Pretty (Tree fs info)
|
|
where
|
|
pp = go
|
|
where
|
|
go (Tree (Left err)) = pp err
|
|
go (Tree (Right (info, fTree))) = c info $ pp fTree
|
|
|
|
-- | Return all subtrees that cover the range, ascending in size.
|
|
lookupTree
|
|
:: forall fs info
|
|
. ( Apply Foldable fs
|
|
, Apply Functor fs
|
|
)
|
|
=> (info -> Bool)
|
|
-> Tree fs info
|
|
-> Maybe (Tree fs info)
|
|
lookupTree rightInfo = go
|
|
where
|
|
go :: Tree fs info -> Maybe (Tree fs info)
|
|
go tree = do
|
|
if rightInfo (infoOf tree)
|
|
then getFirst $ foldMap (First . go) (layers tree) <> First (Just tree)
|
|
else Nothing
|
|
|
|
layers :: (Apply Foldable fs) => Tree fs info -> [Tree fs info]
|
|
layers (Tree (Right (_, ls))) = toList ls
|
|
-- | Traverse the tree over some monad that exports its methods.
|
|
--
|
|
-- For each tree piece, will call `before` and `after` callbacks.
|
|
--
|
|
traverseTree
|
|
:: ( UpdateOver m (Sum fs) (Tree fs a)
|
|
, Apply Foldable fs
|
|
, Apply Functor fs
|
|
, Apply Traversable fs
|
|
)
|
|
=> (a -> m b) -> Tree fs a -> m (Tree fs b)
|
|
traverseTree act = go
|
|
where
|
|
go (Tree (Right (a, union))) = do
|
|
b <- act a
|
|
before union
|
|
union' <- traverse go union
|
|
after union
|
|
return (Tree (Right (b, union')))
|
|
|
|
go (Tree (Left err)) = do
|
|
err' <- traverse act err
|
|
return (Tree (Left err'))
|
|
|
|
-- | Make a tree out of a layer and an info.
|
|
mk :: (Functor f, Element f fs) => info -> f (Tree fs info) -> Tree fs info
|
|
mk i fx = Tree $ Right (i, inject fx)
|
|
|
|
-- | Get info from the tree.
|
|
infoOf :: Tree fs info -> info
|
|
infoOf = either eInfo fst . unTree
|
|
|
|
instance Stubbed (Tree fs info) info where
|
|
stub = Tree . Left
|
|
|
|
instance Apply Foldable fs => HasErrors (Tree fs info) info where
|
|
errors = go
|
|
where
|
|
go (Tree (Left err)) = pure err
|
|
go (Tree (Right (_, rest))) = foldMap go rest
|
|
|
|
-- | Update callbacks for a @f a@ while working inside monad @m@.
|
|
class Monad m => UpdateOver m f a where
|
|
before :: f a -> m ()
|
|
after :: f a -> m ()
|
|
|
|
before _ = skip
|
|
after _ = skip
|
|
|
|
-- | Do nothing.
|
|
skip :: Monad m => m ()
|
|
skip = return ()
|
|
|
|
instance Monad m => UpdateOver m (Sum '[]) a where
|
|
before = error "Sum.empty"
|
|
after = error "Sum.empty"
|
|
|
|
instance (UpdateOver m f a, UpdateOver m (Sum fs) a) => UpdateOver m (Sum (f : fs)) a where
|
|
before = either before before . decompose
|
|
after = either after after . decompose
|