1
1
mirror of https://github.com/github/semantic.git synced 2024-12-25 07:55:12 +03:00
semantic/src/Term.hs

118 lines
4.4 KiB
Haskell
Raw Normal View History

2017-09-08 18:24:11 +03:00
{-# LANGUAGE MultiParamTypeClasses, RankNTypes, TypeFamilies, TypeSynonymInstances, UndecidableInstances #-}
2017-07-28 21:37:02 +03:00
module Term
( Term
, TermF
, SyntaxTerm
, SyntaxTermF
, zipTerms
, termSize
, alignCofreeWith
, cofree
, runCofree
2017-09-08 18:24:11 +03:00
, Cofree(..)
, extract
, unwrap
, hoistCofree
, CofreeF.headF
, CofreeF.tailF
, CofreeF.CofreeF()
2017-07-28 21:37:02 +03:00
) where
2017-09-08 18:24:11 +03:00
import Control.Comonad
import Control.Comonad.Cofree.Class
2017-07-28 21:37:02 +03:00
import qualified Control.Comonad.Trans.Cofree as CofreeF
import Control.DeepSeq
import Control.Monad.Free
2016-07-11 22:13:16 +03:00
import Data.Align.Generic
import Data.Functor.Both
2017-08-23 18:49:43 +03:00
import Data.Functor.Classes.Pretty.Generic
2017-07-28 21:37:02 +03:00
import Data.Functor.Foldable
import Data.Maybe
import Data.Proxy
import Data.Record
import Data.These
import Data.Union
import Syntax
2016-10-22 01:06:31 +03:00
-- | A Term with an abstract syntax tree and an annotation.
2017-09-08 18:24:11 +03:00
type Term f = Cofree f
2017-07-28 21:37:02 +03:00
type TermF = CofreeF.CofreeF
2016-05-04 21:37:24 +03:00
2017-09-08 18:24:11 +03:00
infixr 5 :<
data Cofree f a = a :< f (Cofree f a)
2016-10-22 00:39:13 +03:00
-- | A Term with a Syntax leaf and a record of fields.
type SyntaxTerm fields = Term Syntax (Record fields)
type SyntaxTermF fields = TermF Syntax (Record fields)
2017-09-08 18:24:11 +03:00
instance (NFData (f (Cofree f a)), NFData a, Functor f) => NFData (Cofree f a) where
rnf = rnf . runCofree
2017-07-28 21:37:02 +03:00
instance (NFData a, NFData (f b)) => NFData (CofreeF.CofreeF f a b) where
rnf (a CofreeF.:< s) = rnf a `seq` rnf s `seq` ()
2016-01-13 23:35:40 +03:00
-- | Zip two terms by combining their annotations into a pair of annotations.
-- | If the structure of the two terms don't match, then Nothing will be returned.
2016-09-15 00:09:50 +03:00
zipTerms :: (Traversable f, GAlign f) => Term f annotation -> Term f annotation -> Maybe (Term f (Both annotation))
2016-07-11 22:13:16 +03:00
zipTerms t1 t2 = iter go (alignCofreeWith galign (const Nothing) both (These t1 t2))
2017-07-28 21:37:02 +03:00
where go (a CofreeF.:< s) = cofree . (a CofreeF.:<) <$> sequenceA s
2015-11-20 04:17:17 +03:00
2016-04-11 22:06:53 +03:00
-- | Return the node count of a term.
termSize :: (Foldable f, Functor f) => Term f annotation -> Int
2016-01-13 23:32:03 +03:00
termSize = cata size where
2017-07-28 21:37:02 +03:00
size (_ CofreeF.:< syntax) = 1 + sum syntax
2016-07-11 21:33:51 +03:00
-- | Aligns (zips, retaining non-overlapping portions of the structure) a pair of terms.
2016-07-11 21:41:59 +03:00
alignCofreeWith :: Functor f
=> (forall a b. f a -> f b -> Maybe (f (These a b))) -- ^ A function comparing a pair of structures, returning `Just` the combined structure if they are comparable (e.g. if they have the same constructor), and `Nothing` otherwise. The 'Data.Align.Generic.galign' function is usually what you want here.
-> (These (Term f a) (Term f b) -> contrasted) -- ^ A function mapping a 'These' of incomparable terms into 'Pure' values in the resulting tree.
2016-07-11 21:41:59 +03:00
-> (a -> b -> combined) -- ^ A function mapping the input terms annotations into annotations in the 'Free' values in the resulting tree.
-> These (Term f a) (Term f b) -- ^ The input terms.
-> Free (TermF f combined) contrasted
alignCofreeWith compare contrast combine = go
where go terms = fromMaybe (pure (contrast terms)) $ case terms of
2017-09-08 18:24:11 +03:00
These (a1 :< f1) (a2 :< f2) -> wrap . (combine a1 a2 CofreeF.:<) . fmap go <$> compare f1 f2
_ -> Nothing
2017-07-28 21:37:02 +03:00
2017-09-08 18:24:11 +03:00
cofree :: CofreeF.CofreeF f a (Cofree f a) -> Cofree f a
cofree (a CofreeF.:< f) = a :< f
2017-07-28 21:37:02 +03:00
2017-09-08 18:24:11 +03:00
runCofree :: Cofree f a -> CofreeF.CofreeF f a (Cofree f a)
runCofree (a :< f) = a CofreeF.:< f
2017-08-22 19:51:25 +03:00
2017-09-08 18:24:11 +03:00
hoistCofree :: Functor f => (forall a. f a -> g a) -> Cofree f a -> Cofree g a
hoistCofree f = go where go (a :< r) = a :< f (fmap go r)
2017-08-22 19:51:25 +03:00
2017-09-08 18:24:11 +03:00
instance Pretty1 f => Pretty1 (Cofree f) where
liftPretty p pl = go where go (a :< f) = p a <+> liftPretty go (list . map (liftPretty p pl)) f
2017-08-22 19:52:09 +03:00
2017-09-08 18:24:11 +03:00
instance (Pretty1 f, Pretty a) => Pretty (Cofree f a) where
2017-08-23 18:52:00 +03:00
pretty = liftPretty pretty prettyList
instance Apply1 Pretty1 fs => Pretty1 (Union fs) where
liftPretty p pl = apply1 (Proxy :: Proxy Pretty1) (liftPretty p pl)
2017-09-08 18:24:11 +03:00
type instance Base (Cofree f a) = CofreeF.CofreeF f a
instance Functor f => Recursive (Cofree f a) where project = runCofree
instance Functor f => Corecursive (Cofree f a) where embed = cofree
instance Functor f => Comonad (Cofree f) where
extract (a :< _) = a
duplicate w = w :< fmap duplicate (unwrap w)
extend f = go where go w = f w :< fmap go (unwrap w)
instance Functor f => Functor (Cofree f) where
fmap f = go where go (a :< r) = f a :< fmap go r
instance Functor f => ComonadCofree f (Cofree f) where
unwrap (_ :< as) = as
{-# INLINE unwrap #-}
instance (Eq (f (Cofree f a)), Eq a) => Eq (Cofree f a) where
a1 :< f1 == a2 :< f2 = a1 == a2 && f1 == f2
instance (Show (f (Cofree f a)), Show a) => Show (Cofree f a) where
showsPrec d (a :< f) = showParen (d > 5) $ showsPrec 6 a . showString " :< " . showsPrec 5 f