-- |
--
-- Module:      Language.Egison.Syntax.Pattern.Parser.Combinator
-- Description: Useful combinators to manipulate Egison pattern ASTs
-- Stability:   experimental
--
-- Useful combinators to manipulate Egison pattern ASTs.

module Language.Egison.Syntax.Pattern.Combinator
  ( unAnnotate
  , foldExpr
  , mapName
  , mapVarName
  , mapValueExpr
  , variables
  )
where

import           Control.Applicative            ( Alternative(..) )
import           Data.Foldable                  ( asum )
import           Data.Functor.Foldable          ( cata
                                                , embed
                                                )
import           Control.Comonad.Cofree         ( Cofree )
import           Control.Comonad.Trans.Cofree   ( CofreeF(..) )

import           Language.Egison.Syntax.Pattern.Expr
                                                ( Expr(..) )
import           Language.Egison.Syntax.Pattern.Base
                                                ( ExprF(..) )


-- | Unwrap annotations from AST.
unAnnotate :: Cofree (ExprF n v e) a -> Expr n v e
unAnnotate = cata go where go (_ :< x) = embed x

-- | fold an v expr.
--
-- Note that this is just a type specialization of 'cata'.
foldExpr :: (ExprF n v e a -> a) -> Expr n v e -> a
foldExpr = cata

-- TODO: Implement 'mapName' and 'mapValueExpr' by adding newtype wrapper for them and making them instances of 'MonoFunctor'

-- | Map over @n@ in @Expr n v e@.
mapName :: (n -> n') -> Expr n v e -> Expr n' v e
mapName f = cata go
 where
  go (InfixF n a b ) = Infix (f n) a b
  go (PatternF n ps) = Pattern (f n) ps
  -- TODO: omit these verbose matches
  go WildcardF       = Wildcard
  go (VariableF  n)  = Variable n
  go (ValueF     e)  = Value e
  go (PredicateF e)  = Predicate e
  go (AndF p1 p2  )  = And p1 p2
  go (OrF  p1 p2  )  = Or p1 p2
  go (NotF p1     )  = Not p1

-- | Map over @v@ in @Expr n v e@.
mapVarName :: (v -> v') -> Expr n v e -> Expr n v' e
mapVarName f = cata go
 where
  go (VariableF v)   = Variable (f v)
  -- TODO: omit these verbose matches
  go WildcardF       = Wildcard
  go (ValueF     e ) = Value e
  go (PredicateF e ) = Predicate e
  go (AndF p1 p2   ) = And p1 p2
  go (OrF  p1 p2   ) = Or p1 p2
  go (NotF p1      ) = Not p1
  go (InfixF n a b ) = Infix n a b
  go (PatternF n ps) = Pattern n ps

-- | Map over @e@ in @Expr n v e@.
mapValueExpr :: (e -> e') -> Expr n v e -> Expr n v e'
mapValueExpr f = cata go
 where
  go (ValueF     e)   = Value (f e)
  go (PredicateF e)   = Predicate (f e)
  -- TODO: omit these verbose matches
  go WildcardF        = Wildcard
  go (VariableF n   ) = Variable n
  go (InfixF n p1 p2) = Infix n p1 p2
  go (PatternF n  ps) = Pattern n ps
  go (AndF     p1 p2) = And p1 p2
  go (OrF      p1 p2) = Or p1 p2
  go (NotF p1       ) = Not p1

-- | List bound pattern variables in a pattern.
variables :: Alternative f => Expr n v e -> f v
variables = cata go
 where
  go (VariableF n)   = pure n
  -- TODO: omit these verbose matches
  go WildcardF       = empty
  go (ValueF     _ ) = empty
  go (PredicateF _ ) = empty
  go (AndF p1 p2   ) = p1 <|> p2
  go (OrF  p1 p2   ) = p1 <|> p2
  go (NotF p1      ) = p1
  go (InfixF _ a b ) = a <|> b
  go (PatternF _ ps) = asum ps