-- Please, see the file LICENSE for copyright and license information.

module HFusion.Internal.HsSyn where

import Char(isDigit)
import List(union,(\\),nub)

-- | Abstract syntax tree for programs that "HFusion" can handle.
data Prog = Prog [Def]

-- | Representation for function definitions.
data Def = Defvalue Variable Term

getDefName :: Def -> Variable
getDefName (Defvalue v _) = v

getDefTerm :: Def -> Term
getDefTerm (Defvalue _ t) = t


-- | Representation of variables.
data Variable = Vuserdef String -- ^ Name found in the original program.
              | Vgen String Int -- ^ Generated identifier containing a prefix and an index.
  deriving Ord

instance Show Variable where
    show (Vuserdef name) = name
    show (Vgen p num) = p ++ show num

instance Eq Variable where
  Vuserdef v == Vuserdef v' = v==v'
  Vgen p i == Vgen p' i' = p==p' && i==i'
  v1 == v2 = show v1 == show v2


str2var [] = Vuserdef ""
str2var s | null ds = Vuserdef s
          | otherwise = Vgen (reverse p) ((read (reverse ds))::Int)
  where (ds,p) = break (not . isDigit) (reverse s)

varPrefix :: Variable -> String
varPrefix (Vgen p _) = p
varPrefix (Vuserdef s) = reverse . dropWhile isDigit . dropWhile (=='_'). reverse$ s

-- | Representation for Literals.
data Literal = Lstring String -- ^ String literals
               | Lint String  -- ^ Integer literals
               | Lchar Char   -- ^ Character literals
               | Lrat String  -- ^ Rational literals
    deriving (Eq)

-- | Representation for constructors.
type Constructor = String

-- | Representation for terms in programs handled by "HFusion".
data Term = Tvar Variable   -- ^ Variables
            | Tlit Literal  -- ^ Literals
            | Ttuple Bool [Term] -- ^ Tuples. The boolean argument tells if the tuple must be flattened
                                 --   when nested with others under an hylo application.
            | Tlamb Boundvar Term -- ^ Lambda expressions
            | Tlet Variable Term Term -- ^ Let expressions
            | Tcase Term [Pattern] [Term] -- ^ Case expressions
            | Tfapp Variable [Term] -- ^ Function application (saturated)
            | Tcapp Constructor [Term] -- ^ Constructor application
            | Tapp Term Term -- ^ General term application
            | Tbottom  -- ^ Undefined computation

            | Tif Term Term Term -- ^ If expressions, only used for pretty printing
            | Tpar Term -- Parenthesized expressions, to better handle associativity of infix operators
            | Thyloapp Variable Int [Term] (Maybe [Int]) Term -- ^ Hylo application, only used for inlining
                                                              -- Thyloapp name recargsCount non-recargs recarg recarg may be a tuple 
    deriving (Eq)


-- | Calculates arguments of a Thyloapp

thyloArgs :: Int -> [Term] -> Maybe [Int] -> Term -> [Term]
thyloArgs 1 ts pos t = insertElems (zip ts$ maybe [0..] id pos) [t]
thyloArgs 0 ts pos t = insertElems (zip ts [0..]) []
thyloArgs i ts pos t = insertElems (zip ts$ maybe [0..] id pos) (flatten t)
 where flatten (Ttuple True ts) = concat (map flatten ts)
       flatten t = [t]


insertElems :: [(a,Int)] -> [a] -> [a]
insertElems = insert 0
  where insert i xs [] = map fst xs 
        insert i [] as = as
        insert i xs@((x,ix):xss) as@(a:ass)
                 | ix<=i = x : insert (i+1) xss as
                 | otherwise = a : insert (i+1) xs ass

applyFst f (a,b) = (f a,b)


bv2pat (Bvar v)=Pvar v
bv2pat (Bvtuple _ vs)=Ptuple (map bv2pat vs)

tapp (Tvar v) t = Tfapp v [t]
tapp (Tfapp v []) t = Tfapp v [t]
tapp t1 t2 = Tapp t1 t2

ttuple [a] = a
ttuple as = Ttuple False as

ptuple [a] = a
ptuple as = Ptuple as

bvtuple [a] = a
bvtuple as = Bvtuple False as


-- | Representation of bound variables in lambda expressions.
data Boundvar = Bvar Variable -- Variables
              | Bvtuple Bool [Boundvar] -- ^ Bound variable tuples. Uses the boolean value like in 'Ttuple'.
                                        --   but when bounding input variables of hylomorphisms.
    deriving (Eq)

-- | Representation of patterns
data Pattern = Pvar Variable  -- ^ Variables
               | Ptuple [Pattern] -- ^ Tuple patterns
               | Pcons Constructor [Pattern] -- ^ Constructor application patterns
               | Plit Literal -- ^ Literals
               | Pas Variable Pattern -- ^ \@\-pattern
    deriving (Eq)

pany = Pvar (Vuserdef "_")


-- Representación de functores
-- Fue discutida, pero aún podría sufrir cambios.
newtype Func = Fsum [Fprod]
          deriving (Show,Eq)
type Fprod = (Int,Int)


-- | Operation for collecting free variables without repetitions.

class Vars a where
  vars :: a -> [Variable]

instance Vars Variable where
  vars a = [a]

instance Vars Boundvar where
  vars (Bvar v) = [v]
  vars (Bvtuple _ vs) = vars vs

instance Vars Pattern where
  vars p@(Pvar v) | p/=pany = [v]
                  | otherwise = []
  vars (Ptuple ps) = concat (map vars ps)
  vars (Pcons _ ps) = concat (map vars ps)
  vars (Plit _) = []
  vars (Pas v p) = v:vars p
 -- vars p = error "vars Pattern: not defined" -- ++ (not_Defined_For_Applied_Pattern p)

instance Vars a => Vars [a] where
  vars = concat.map vars

instance (Vars a, Vars b) => Vars (Either a b) where
  vars = either vars vars

instance Vars Term where
  vars (Tvar v) = [v]
  vars (Ttuple _ ps) = foldr union [] (map vars ps)
  vars (Tcapp _ ps) = foldr union [] (map vars ps)
  vars (Tlit _) = []
  vars (Tfapp fn ps) = foldr union [fn] (map vars ps)
  vars (Tapp t1 t2) = union (vars t1) (vars t2)
  vars (Tlamb bv t) = vars t \\ vars bv
  vars (Tlet v t0 t1) = union (vars t0) (vars t1 \\ [v])
  vars (Tpar t) = vars t
  vars (Tif t0 t1 t2) = union (vars t0) (union (vars t1) (vars t2))
  vars (Tcase t ps ts) = foldr union (vars t) (zipWith (\pi ti ->vars ti \\ vars pi) ps ts)
  vars Tbottom = []
  vars (Thyloapp v i ts _ t) = nub (v:(vars ts++vars t))



-- | Operations for obtaining bound variables.

class VarsB a where
  varsB :: a -> [Variable]

instance (VarsB a) => (VarsB [a]) where
 varsB x = concat$ map varsB x

instance VarsB Term where
  varsB (Tvar _) = []
  varsB (Ttuple _ ps) = concat (map varsB ps)
  varsB (Tcapp _ ps) = concat (map varsB ps)
  varsB (Tlit _) = []
  varsB (Tfapp _ ps) = concat (map varsB ps)
  varsB (Tapp t1 t2) = varsB t1 ++ varsB t2
  varsB (Tlamb bv t) = varsB t ++ vars bv
  varsB (Tlet v t0 t1) = v : (varsB t0 ++ varsB t1)
  varsB (Tif t0 t1 t2) = varsB t0 ++ varsB t1 ++ varsB t2
  varsB (Tpar t) = varsB t
  varsB (Tcase t ps ts) = varsB t ++ concat (zipWith (\pi ti ->varsB ti ++ vars pi) ps ts)
  varsB Tbottom = []
  varsB (Thyloapp _ i ts _ t) = varsB ts++varsB t

instance (VarsB a, VarsB b) => VarsB (Either a b) where
  varsB = either varsB varsB


-- | Alpha conversion.

class AlphaConvertible a where
  -- The [Variable] is the list of variables in scope (i.e. which can be replaced).
  alphaConvert :: [Variable] -> [(Variable, Variable)] -> a -> a

instance AlphaConvertible Variable where
  alphaConvert sc lvars v = if elem v sc then maybe v id $ lookup v lvars else v

instance AlphaConvertible Term where
  alphaConvert sc ss t@(Tvar v) = if elem v sc then maybe t Tvar $ lookup v ss else t
  alphaConvert sc ss (Tlamb bv t) = Tlamb (alphaConvert sc ss bv) (alphaConvert (sc++vars bv) ss t)
  alphaConvert sc ss (Tlet v t0 t1) = case lookup v ss of
                                      Just valor -> Tlet valor (alphaConvert (v:sc) ss t0) (alphaConvert (v:sc) ss t1)
                                      Nothing ->  Tlet v (alphaConvert sc ss t0) (alphaConvert sc ss t1)
  alphaConvert sc ss (Tcase t0 ps ts) = Tcase (alphaConvert sc ss t0) (map (alphaConvert sc ss) ps) (zipWith alphaConvert' ps ts)
    where alphaConvert' p t = alphaConvert (sc++vars p) ss t
  alphaConvert sc ss t@(Tlit _) = t
  alphaConvert sc ss (Ttuple b ts) = Ttuple b$ map (alphaConvert sc ss) ts
  alphaConvert sc ss (Tfapp v ts) = case lookup v ss of
             Just valor -> foldl Tapp (Tvar valor) (map (alphaConvert sc ss) ts)
             Nothing -> Tfapp v (map (alphaConvert sc ss) ts)
  alphaConvert sc ss (Tcapp cons ts) = Tcapp cons (map (alphaConvert sc ss) ts)
  alphaConvert sc ss (Tapp t0 t1) = Tapp (alphaConvert sc ss t0) (alphaConvert sc ss t1)
  alphaConvert sc ss (Thyloapp v i ts pos t) = case lookup v ss of
             Just valor -> foldl Tapp (Tvar valor) (map (alphaConvert sc ss) (thyloArgs i ts pos t))
             Nothing -> Thyloapp v i (map (alphaConvert sc ss) ts) pos (alphaConvert sc ss t)
  alphaConvert sc ss t = error$ "alphaConvert: unexpected term"

instance (AlphaConvertible a, AlphaConvertible b) => AlphaConvertible (Either a b) where
  alphaConvert sc lvars = either (Left . alphaConvert sc lvars) (Right . alphaConvert sc lvars)

instance (AlphaConvertible a) => (AlphaConvertible [a]) where
 alphaConvert sc lvars x = map (alphaConvert sc lvars) x


instance AlphaConvertible Pattern where
  alphaConvert sc lvars t@(Pvar v) = maybe t Pvar $ lookup v lvars
  alphaConvert sc lvars (Ptuple ps) = Ptuple (map (alphaConvert sc lvars) ps)
  alphaConvert sc lvars (Pcons c ps) = Pcons c (map (alphaConvert sc lvars) ps)
  alphaConvert sc lvars t@(Plit l) = t
  alphaConvert sc lvars (Pas v p) = Pas (maybe v id$ lookup v lvars) (alphaConvert sc lvars p)

instance AlphaConvertible Boundvar where
  alphaConvert sc lvars b@(Bvar v) = maybe b Bvar $ lookup v lvars
  alphaConvert sc lvars (Bvtuple b vs) = Bvtuple b$ alphaConvert sc lvars vs