{-# LANGUAGE
    FlexibleContexts
  #-}

module LText.Eval where

import LText.Expr

import           Data.HashSet        (HashSet)
import qualified Data.HashSet        as HS
import Data.Monoid


evaluate :: Expr -> Expr
evaluate e =
  case e of
    Var n    -> Var n
    Abs n e' -> Abs n (evaluate e')
    Lit t    -> Lit t
    Concat e1 e2 ->
      case (evaluate e1, evaluate e2) of
        (Lit t1, Lit t2) -> Lit $! t1 ++ t2
        (e1'   , e2'   ) -> Concat e1' e2'
    App e1 e2 ->
      case evaluate e1 of
        Abs n e1' -> substitute n (evaluate e2) (evaluate e1')
        e1'       -> App e1' (evaluate e2)


substitute :: String -> Expr -> Expr -> Expr
substitute n x e =
  case e of
    Lit t                 -> Lit t
    Concat e1 e2          -> Concat (substitute n x e1) (substitute n x e2)
    Var n'    | n == n'   -> x
              | otherwise -> Var n'
    App e1 e2             -> App (substitute n x e1) (substitute n x e2)
    Abs n' e' | n == n'   -> Abs n' e'
              | otherwise -> Abs n' $ substitute n x e'


freeVars :: Expr -> HashSet String
freeVars e =
  case e of
    Abs n e'     -> HS.delete n $ freeVars e'
    App e1 e2    -> freeVars e1 <> freeVars e2
    Var n        -> HS.singleton n
    Lit _        -> HS.empty
    Concat e1 e2 -> freeVars e1 <> freeVars e2