implementation module Sil.Types

import StdBool
from StdFunc import const, o
import StdList
import StdMisc
import StdOverloaded
import StdString

import GenEq

import Control.Applicative
import Control.Monad
import Data.Error
from Data.Func import $
import Data.Functor
import Data.Maybe
from Text import <+

from ABC.Assembler import :: BasicType(..)

import Sil.Error
import Sil.Syntax
import Sil.Util.Printer

derive gEq Type
instance == Type where == a b = gEq{|*|} a b

instance toString Type
where
	toString TBool         = "Bool"
	toString TInt          = "Int"
	toString TVoid         = "Void"
	toString (at --> rt)   = "(" <+ at <+ " -> " <+ rt <+ ")"
	toString (TTuple _ ts) = "(" <+ printersperse ", " ts <+ ")"
	toString (TList t)     = "[" <+ t <+ "]"

instance zero TypeSize where zero = {asize=0, bsize=0, btypes=[]}

typeSize :: Type -> TypeSize
typeSize TVoid        = zero
typeSize TBool        = {zero & bsize=1, btypes=[BT_Bool]}
typeSize TInt         = {zero & bsize=1, btypes=[BT_Int]}
typeSize (TTuple _ _) = {zero & asize=1}
typeSize (TList _)    = {zero & asize=1}

(+~) infixl 6 :: TypeSize TypeSize -> TypeSize
(+~) a b =
	{ asize  = a.asize  +  b.asize
	, bsize  = a.bsize  +  b.bsize
	, btypes = a.btypes ++ b.btypes
	}

(-~) infixl 6 :: TypeSize TypeSize -> TypeSize
(-~) a b =
	{ asize  = a.asize  -  b.asize
	, bsize  = a.bsize  -  b.bsize
	, btypes = abort "btypes after -~\r\n"
	}

instance zero TypeResolver where zero = const Nothing

instance type Function
where
	type res f = Just $ Ok $ foldr (-->) f.f_type [a.arg_type \\ a <- f.f_args]

instance type Expression
where
	type res (Name n) = type res n
	type res (Literal lit) = case lit of
		BLit _ -> Just $ Ok TBool
		ILit _ -> Just $ Ok TInt
	type res (App n args) =
		mapM (type res) args >>= \ats ->
		res n >>= \ft -> pure
		( sequence ats >>= \ats ->
		  ft           >>= \ft  -> foldM tryApply ft ats)
	type res (BuiltinApp op e) =
		type res e  >>= \te  ->
		type res op >>= \top -> pure
		( top >>= \top ->
		  te  >>= \te  -> tryApply top te)
	type res (BuiltinApp2 e1 Cons e2) =
		type res e1 >>= \te1 ->
		type res e2 >>= \te2 -> pure
		( te1 >>= \te1 ->
		  te2 >>= \te2 ->
		  let top = te1 --> TList te1 --> TList te1 in
		  foldM tryApply top [te1,te2])
	type res (BuiltinApp2 e1 op e2) =
		type res e1 >>= \te1 ->
		type res e2 >>= \te2 ->
		type res op >>= \top -> pure
		( top >>= \top ->
		  te1 >>= \te1 ->
		  te2 >>= \te2 -> foldM tryApply top [te1,te2])
	type res e=:(List (Just t) es) =
		mapM (type res) es >>= \tes -> pure
		(sequence tes >>= \tes -> case [(e,t`) \\ e <- es & t` <- tes | t <> t`] of
			[(e`,t`):_] -> Error $ C_TypeMisMatch t e` t`
			[]          -> Ok $ TList t)
	type res (List Nothing []) = Nothing
	type res e=:(List Nothing es) =
		mapM (type res) es >>= \tes -> pure
		(sequence tes >>= \tes -> case removeDup tes of
			[t]    -> Ok $ TList t
			[_:_]  -> Error $ C_CouldNotDeduceType e)
	type res (Tuple n es)
	| n > 32 = Just $ Error $ T_TooHighTupleArity n
	| otherwise =
		mapM (type res) es >>= \ats -> pure (sequence ats >>= pure o TTuple n)
	type res (Field f e)
	| isTuple = type res e >>= \te -> pure (te >>= \te -> case te of
		TTuple arity es -> if (0 < tupleEl && tupleEl <= arity)
			(Ok $ es!!(tupleEl - 1))
			(Error $ T_IllegalField f te)
		_ -> Error $ T_IllegalField f te)
	| f == "hd" = type res e >>= \te -> pure (te >>= \te -> case te of
		TList t -> Ok t
		_       -> Error $ T_IllegalField f te)
	| f == "tl" = type res e >>= \te -> pure (te >>= \te -> case te of
		t=:(TList _) -> Ok t
		_            -> Error $ T_IllegalField f te)
	| f == "nil" = type res e >>= \te -> pure (te >>= \te -> case te of
		(TList _) -> Ok TBool
		_         -> Error $ T_IllegalField f te)
	| otherwise = type res e >>= \te -> pure (te >>= Error o T_IllegalField f)
	where
		f` = fromString f

		isTuple = length f` >= 2 && hd f` == '_' && all isDigit (tl f`)
		tupleEl = toInt $ toString $ tl f`

tryApply :: Type Type -> MaybeError Error Type
tryApply ft=:(at --> rt) et
| et == at     = Ok rt
| otherwise    = Error $ T_IllegalApplication ft et
tryApply ft et = Error $ T_IllegalApplication ft et

instance type Name where type res n = res n

instance type Op1
where
	type _ Neg = Just $ Ok $ TInt  --> TInt
	type _ Not = Just $ Ok $ TBool --> TBool

instance type Op2
where
	type _ Add      = Just $ Ok $ TInt  --> TInt  --> TInt
	type _ Sub      = Just $ Ok $ TInt  --> TInt  --> TInt
	type _ Mul      = Just $ Ok $ TInt  --> TInt  --> TInt
	type _ Div      = Just $ Ok $ TInt  --> TInt  --> TInt
	type _ Rem      = Just $ Ok $ TInt  --> TInt  --> TInt
	type _ Equals   = Just $ Ok $ TInt  --> TInt  --> TBool
	type _ Unequals = Just $ Ok $ TInt  --> TInt  --> TBool
	type _ CmpLe    = Just $ Ok $ TInt  --> TInt  --> TBool
	type _ CmpGe    = Just $ Ok $ TInt  --> TInt  --> TBool
	type _ CmpLt    = Just $ Ok $ TInt  --> TInt  --> TBool
	type _ CmpGt    = Just $ Ok $ TInt  --> TInt  --> TBool
	type _ LogOr    = Just $ Ok $ TBool --> TBool --> TBool
	type _ LogAnd   = Just $ Ok $ TBool --> TBool --> TBool