-- | Annotations for syntax trees

module Language.Syntactic.Features.Annotate where



import Language.Syntactic.Syntax
import Language.Syntactic.Analysis.Equality
import Language.Syntactic.Analysis.Render
import Language.Syntactic.Analysis.Evaluation
import Language.Syntactic.Analysis.Hash



-- | Annotating an expression with arbitrary information.
--
-- This can be used to annotate every node of a syntax tree, which is done by
-- changing
--
-- > AST dom a
--
-- to
--
-- > AST (Ann info dom) a
--
-- Injection/projection of an annotated tree is done using
-- 'injectAnn' / 'projectAnn'.
data Ann info expr a
  where
    Ann :: info (EvalResult a) -> expr a -> Ann info expr a



instance ExprEq expr => ExprEq (Ann info expr)
  where
    Ann _ a `exprEq` Ann _ b = exprEq a b

instance Render expr => Render (Ann info expr)
  where
    render (Ann _ a) = render a

instance ToTree expr => ToTree (Ann info expr)
  where
    toTreePart args (Ann _ a) = toTreePart args a

instance Eval expr => Eval (Ann info expr)
  where
    evaluate (Ann _ a) = evaluate a

instance ExprHash expr => ExprHash (Ann info expr)
  where
    exprHash (Ann _ a) = exprHash a



injectAnn :: (sub :<: sup, ConsType a) =>
    info (EvalResult a) -> sub a -> AST (Ann info sup) a
injectAnn info = Symbol . Ann info . inject

projectAnn :: (sub :<: sup) =>
    AST (Ann info sup) a -> Maybe (info (EvalResult a), sub a)
projectAnn a = do
    Symbol (Ann info b) <- return a
    c                   <- project b
    return (info, c)

getInfo :: AST (Ann info sup) a -> info (EvalResult a)
getInfo (Symbol (Ann info _)) = info
getInfo (f :$: _)             = getInfo f