{-# LANGUAGE FlexibleInstances #-}
-- | This module contians the DSL for writing @CExpr@s.
-- It doesn't export the orphan instance for @IsString CExpr@ which
-- can be found in "Language.C.DSL.StringLike".
-- 
-- This module also contains an orphan instance for 'Num' for @CExpr@.
-- This orphan instances contains inline implementations of 'abs' and
-- 'signum' in C rather than relying on something from @math.h@.
module Language.C.DSL.Exp where
import Language.C
import Data.String
import Language.C.DSL.StringLike

-- | Lift a Haskell string into a literal C string.
str :: String -> CExpr
str = CConst . flip CStrConst undefNode . cString

cOp :: CBinaryOp -> CExpr -> CExpr -> CExpr
cOp op a b = CBinary op a b undefNode

-- | Equality test, @a ==: b@ is equivalent to @a == b@
(==:) :: CExpr -> CExpr -> CExpr
(==:) = cOp CEqOp

-- | Inequality test, @a /=: b@ is equivalent to @a != b@
(/=:) :: CExpr -> CExpr -> CExpr
(/=:) = cOp CNeqOp

-- | Less-than test, @a <: b@ is equivalent to @a < b@
(<:) :: CExpr -> CExpr -> CExpr
(<:)  = cOp CLeOp

-- | Greater-than test, @a >: b@ is equivalent to @a > b@
(>:) :: CExpr -> CExpr -> CExpr
(>:)  = cOp CGrOp

-- | Less than or equal to, @a <=: b@ is equivalent to @a <= b@
(<=:) :: CExpr -> CExpr -> CExpr
(<=:) = cOp CLeqOp

-- | Greater than or equal to, @a >=: b@ is equivalent to @a >= b@
(>=:) :: CExpr -> CExpr -> CExpr
(>=:) = cOp CGeqOp

-- | The ternary operator in C. @ternary a b c@ will turn into @a ? b : c@.
ternary :: CExpr -> CExpr -> CExpr -> CExpr
ternary i t e = CCond i (Just t) e undefNode

-- | An orphan @Num@ instance for @CExpr@. It provides inline implementations
-- of @abs@ and @signum@ that don't rely on @math.h@.
instance Num CExpr where
  fromInteger = CConst . flip CIntConst undefNode . cInteger
  (*)         = cOp CMulOp
  (+)         = cOp CAddOp
  (-)         = cOp CSubOp
  abs a       = ternary (a >=: 0) a (negate a)
  signum a    = ternary (a >=: 0) (ternary (a ==: 0) a 1) (-1)
instance Fractional CExpr where
  (/)          = cOp CDivOp
  fromRational = CConst . flip CFloatConst undefNode . cFloat . fromRational

-- | A function mapping identifier in C to be used as variables. Normally this can be
-- avoided since "Language.C.DSL.StringLike" provides an 'IsString' instance.
var :: Ident -> CExpr
var = flip CVar undefNode

-- | Function calls, @f#[a, b, c]@ will become @f(a, b, c)@. Note
-- that @f@ is also an expression.
(#) :: CExpr -> [CExpr] -> CExpr
f # args = CCall f args undefNode

-- | The assignment operator. @var <-- value@ will become @var = value;@ in C.
(<--) :: CExpr -> CExpr -> CExpr
var <-- val = CAssign CAssignOp var val undefNode
infixl 3 <--

-- | This is the more generalized version of '(<--)'. It allows
-- any 'CAssignOp' to be passed in to facilitate writing @a += b@ and
-- similar.
assign :: CAssignOp -> CExpr -> CExpr -> CExpr
assign mode var val = CAssign mode var val undefNode


-- | A simplified unary operator type. It
-- can be converted to 'Language.C's version using
-- 'toCUnaryOp'.
data UnOp = PlusPlus
          | MinusMinus
          | Minus
          | Plus
          | Not
          | Addr -- ^ The address of operator @&@.
          | Ind -- ^ The dereferencing operator in C @*@.
          deriving(Eq, Show)

-- | Convert a 'UnOp' to the corresponding 'CUnaryOp'.
toCUnaryOp :: UnOp -> CUnaryOp
toCUnaryOp Minus = CMinOp
toCUnaryOp Plus  = CPlusOp
toCUnaryOp Not   = CNegOp
toCUnaryOp Addr  = CAdrOp
toCUnaryOp Ind   = CIndOp

-- | Apply a unary operator prefix, @op `pre` exp@ will transform into something like @op exp@
-- in C. This only matters for 'PlusPlus' and 'MinusMinus'.
pre   :: UnOp -> CExpr -> CExpr
PlusPlus   `pre` exp = CUnary CPreIncOp exp undefNode
MinusMinus `pre` exp = CUnary CPreDecOp exp undefNode
op         `pre` exp = CUnary (toCUnaryOp op) exp undefNode

-- | The postfix equivalent of 'pre'.
post  :: CExpr -> UnOp -> CExpr
exp `post` PlusPlus   = CUnary CPostIncOp exp undefNode
exp `post` MinusMinus = CUnary CPostDecOp exp undefNode
exp `post` op         = CUnary (toCUnaryOp op) exp undefNode

-- | A quick wrapper of @pre Ind exp@ since it's so common.
star :: CExpr -> CExpr
star = pre Ind

-- | The C comma operator, @comma [a, b, c]@ is equivalent to @a, b, c@ in C.
comma :: [CExpr] -> CExpr
comma = flip CComma undefNode

-- | Implements C style casts for expressions.
castTo :: CExpr -> CDecl -> CExpr
exp `castTo` ty = CCast ty exp undefNode

-- | @size of@ for types. 
sizeOfDecl :: CDecl -> CExpr
sizeOfDecl = flip CSizeofType undefNode

-- | @size of@ for expressions. Carefully note that @sizeOf "someType"@ will
-- incorrectly treat @someType@ as a variable, not a type.
sizeOf :: CExpr -> CExpr
sizeOf = flip CSizeofExpr undefNode

-- | Access a field of a struct, this C's @.@ operator.
(&) :: CExpr -> String -> CExpr
struct & field = CMember struct (fromString field) False undefNode
infixl 8 &

-- | The automatic dereferencing @->@ in C.
(&*) :: CExpr -> String -> CExpr
struct &* field = CMember struct (fromString field) True undefNode
infixl 8 &*

-- | This is the indexing operator in C, @a ! i@ is @a[i]@.
(!) :: CExpr -> CExpr -> CExpr
arr ! ind = CIndex arr ind undefNode
infixl 8 !
