-- | Binary tree, useful to build /Abstract Syntax Tree/ (AST)
-- made of applications of tokens.
module Language.Symantic.Grammar.BinTree where

import Control.Applicative (Applicative(..))
import Control.Monad (Monad(..))
import Data.Eq (Eq)
import Data.Foldable (Foldable(..))
import Data.Functor (Functor(..), (<$>))
import Data.Monoid (Monoid(..))
import Data.Semigroup (Semigroup(..))
import Data.Traversable (Traversable(..))
import Text.Show (Show)

-- * Type 'BinTree'
-- | /Binary Tree/.
data BinTree a
 =   BinTree0 a
 |   BinTree2 (BinTree a) (BinTree a)
 deriving (Eq, Show)

instance Semigroup (BinTree a) where
        (<>) = BinTree2
instance Functor BinTree where
        fmap f (BinTree0 a)   = BinTree0 (f a)
        fmap f (BinTree2 x y) = BinTree2 (fmap f x) (fmap f y)
instance Applicative BinTree where
        pure = BinTree0
        BinTree0 f     <*> BinTree0 a   = BinTree0 (f a)
        BinTree0 f     <*> BinTree2 x y = BinTree2 (f  <$> x) (f  <$> y)
        BinTree2 fx fy <*> a            = BinTree2 (fx <*> a) (fy <*> a)
instance Monad BinTree where
        return = BinTree0
        BinTree0 a   >>= f = f a
        BinTree2 x y >>= f = BinTree2 (x >>= f) (y >>= f)
instance Foldable BinTree where
        foldMap f (BinTree0 a)   = f a
        foldMap f (BinTree2 x y) = foldMap f x `mappend` foldMap f y
        foldr f acc (BinTree0 a)   = f a acc
        foldr f acc (BinTree2 x y) = foldr f (foldr f acc y) x
        foldl f acc (BinTree0 a)   = f acc a
        foldl f acc (BinTree2 x y) = foldl f (foldl f acc x) y
instance Traversable BinTree where
        traverse f (BinTree0 a)   = BinTree0 <$> f a
        traverse f (BinTree2 x y) = BinTree2 <$> traverse f x <*> traverse f y

-- | Collapse depth-first given 'BinTree' with given function.
--
-- Useful to apply all arguments.
collapseBT :: (a -> a -> a) -> BinTree a -> a
collapseBT _f (BinTree0 x) = x
collapseBT f (BinTree2 x y) = collapseBT f x `f` collapseBT f y