2020-08-11 12:32:04 +04:00

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