-- Copyright (c) 2006-2012
--         The President and Fellows of Harvard College.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the University nor the names of its contributors
--    may be used to endorse or promote products derived from this software
--    without specific prior written permission.

-- THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.

--------------------------------------------------------------------------------
-- |
-- Module      :  Language.C.Syntax
-- Copyright   :  (c) Harvard University 2006-2012
-- License     :  BSD-style
-- Maintainer  :  mainland@eecs.harvard.edu
--
--------------------------------------------------------------------------------

{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}

module Language.C.Syntax where

import Data.Generics

import Data.Loc

data Extensions = Antiquotation
                | Gcc
                | CUDA
                | OpenCL
  deriving (Eq, Ord, Enum, Show)

data Id = Id String !SrcLoc
        | AntiId String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Storage = Tauto !SrcLoc
             | Tregister !SrcLoc
             | Tstatic !SrcLoc
             | Textern !SrcLoc
             | TexternL String !SrcLoc
             | Ttypedef !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data TypeQual = Tconst !SrcLoc
              | Tvolatile !SrcLoc
              | Tinline !SrcLoc

              -- C99
              | Trestrict !SrcLoc

              -- CUDA
              | TCUDAdevice !SrcLoc
              | TCUDAglobal !SrcLoc
              | TCUDAhost !SrcLoc
              | TCUDAconstant !SrcLoc
              | TCUDAshared !SrcLoc
              | TCUDAnoinline !SrcLoc

              -- OpenCL
              | TCLprivate !SrcLoc
              | TCLlocal !SrcLoc
              | TCLglobal !SrcLoc
              | TCLconstant !SrcLoc
              | TCLreadonly !SrcLoc
              | TCLwriteonly !SrcLoc
              | TCLkernel !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Sign = Tsigned !SrcLoc
          | Tunsigned !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data TypeSpec = Tvoid !SrcLoc
              | Tchar (Maybe Sign) !SrcLoc
              | Tshort (Maybe Sign) !SrcLoc
              | Tint (Maybe Sign) !SrcLoc
              | Tlong (Maybe Sign) !SrcLoc
              | Tlong_long (Maybe Sign) !SrcLoc
              | Tfloat !SrcLoc
              | Tdouble !SrcLoc
              | Tlong_double !SrcLoc
              | Tstruct (Maybe Id) (Maybe [FieldGroup]) [Attr] !SrcLoc
              | Tunion (Maybe Id) (Maybe [FieldGroup]) [Attr] !SrcLoc
              | Tenum (Maybe Id) [CEnum] [Attr] !SrcLoc
              | Tnamed Id !SrcLoc
              | TtypeofExp Exp !SrcLoc
              | TtypeofType Type !SrcLoc
              | Tva_list !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data DeclSpec = DeclSpec [Storage] [TypeQual] TypeSpec !SrcLoc
              | AntiDeclSpec String !SrcLoc
              | AntiTypeDeclSpec [Storage] [TypeQual] String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

-- | There are two types of declarators in C, regular declarators and abstract
-- declarators. The former is for declaring variables, function parameters,
-- typedefs, etc. and the latter for abstract types---@typedef int
-- ({*}foo)(void)@ vs. @\tt int ({*})(void)@. The difference between the two is
-- just whether or not an identifier is attached to the declarator. We therefore
-- only define one 'Decl' type and use it for both cases.

data ArraySize = ArraySize Bool Exp !SrcLoc
               | VariableArraySize !SrcLoc
               | NoArraySize !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Decl = DeclRoot !SrcLoc
          | Ptr [TypeQual] Decl !SrcLoc
          | Array [TypeQual] ArraySize Decl !SrcLoc
          | Proto Decl Params !SrcLoc
          | OldProto Decl [Id] !SrcLoc
          | AntiTypeDecl String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Type = Type DeclSpec Decl !SrcLoc
          | AntiType String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Designator = IndexDesignator Exp !SrcLoc
                | MemberDesignator Id !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Designation = Designation [Designator] !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Initializer = ExpInitializer Exp !SrcLoc
                 | CompoundInitializer [(Maybe Designation, Initializer)] !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

type AsmLabel = String

data Init = Init Id Decl (Maybe AsmLabel) (Maybe Initializer) [Attr] !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Typedef = Typedef Id Decl [Attr] !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data InitGroup = InitGroup DeclSpec [Attr] [Init] !SrcLoc
               | TypedefGroup DeclSpec [Attr] [Typedef] !SrcLoc
               | AntiDecl String !SrcLoc
               | AntiDecls String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Field = Field (Maybe Id) (Maybe Decl) (Maybe Exp) !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data FieldGroup  =  FieldGroup DeclSpec [Field] !SrcLoc
                 |  AntiSdecl String !SrcLoc
                 |  AntiSdecls String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data CEnum  =  CEnum Id (Maybe Exp) !SrcLoc
            |  AntiEnum String !SrcLoc
            |  AntiEnums String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Attr  =  Attr Id [Exp] !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Param  =  Param (Maybe Id) DeclSpec Decl !SrcLoc
            |  AntiParam String !SrcLoc
            |  AntiParams String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Params = Params [Param] Bool !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Func  =  Func DeclSpec Id Decl Params [BlockItem] !SrcLoc
           |  OldFunc DeclSpec Id Decl [Id] (Maybe [InitGroup]) [BlockItem] !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Definition  =  FuncDef Func !SrcLoc
                 |  DecDef InitGroup !SrcLoc
                 |  EscDef String !SrcLoc
                 |  AntiFunc String !SrcLoc
                 |  AntiEsc String !SrcLoc
                 |  AntiEdecl String !SrcLoc
                 |  AntiEdecls String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data Stm  = Label Id Stm !SrcLoc
          | Case Exp Stm !SrcLoc
          | Default Stm !SrcLoc
          | Exp (Maybe Exp) !SrcLoc
          | Block [BlockItem] !SrcLoc
          | If Exp Stm (Maybe Stm) !SrcLoc
          | Switch Exp Stm !SrcLoc
          | While Exp Stm !SrcLoc
          | DoWhile Stm Exp !SrcLoc
          | For  (Either InitGroup (Maybe Exp)) (Maybe Exp) (Maybe Exp) Stm
                 !SrcLoc
          | Goto Id !SrcLoc
          | Continue !SrcLoc
          | Break !SrcLoc
          | Return (Maybe Exp) !SrcLoc
          | Pragma String !SrcLoc
          | Asm Bool [Attr] [String]
                     [(String, Exp)] [(String, Exp)]
                     [String] !SrcLoc
          | AntiPragma String !SrcLoc
          | AntiStm String !SrcLoc
          | AntiStms String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data BlockItem = BlockDecl InitGroup
               | BlockStm Stm
               | AntiBlockItem String !SrcLoc
               | AntiBlockItems String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

funcProto :: Func -> InitGroup
funcProto f@(Func decl_spec id decl params _ _) =
    InitGroup decl_spec []
      [Init id (Proto decl params l) Nothing Nothing [] l] l
  where
    l = srclocOf f

funcProto f@(OldFunc decl_spec id decl params _ _ _) =
    InitGroup decl_spec []
      [Init id (OldProto decl params l) Nothing Nothing [] l] l
  where
    l = srclocOf f

isPtr :: Type -> Bool
isPtr  (Type _ decl _)  = go decl
  where
    go  (DeclRoot _)        = False
    go  (Ptr _ _ _)         = True
    go  (Array _ _ _ _)     = True
    go  (Proto _ _ _)       = False
    go  (OldProto _ _ _)    = False
    go  (AntiTypeDecl _ _)  = error "isPtr: encountered antiquoted type declaration"
isPtr  (AntiType _ _)       = error "isPtr: encountered antiquoted type"

data Signed = Signed
            | Unsigned
    deriving (Eq, Ord, Data, Typeable)

data Const = IntConst String Signed Integer !SrcLoc
           | LongIntConst String Signed Integer !SrcLoc
           | LongLongIntConst String Signed Integer !SrcLoc
           | FloatConst String Rational !SrcLoc
           | DoubleConst String Rational !SrcLoc
           | LongDoubleConst String Rational !SrcLoc
           | CharConst String Char !SrcLoc
           | StringConst [String] String !SrcLoc
           | AntiInt String !SrcLoc
           | AntiUInt String !SrcLoc
           | AntiLInt String !SrcLoc
           | AntiULInt String !SrcLoc
           | AntiFloat String !SrcLoc
           | AntiDouble String !SrcLoc
           | AntiLongDouble String !SrcLoc
           | AntiChar String !SrcLoc
           | AntiString String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data ExeConfig = ExeConfig
    {  exeGridDim    :: Exp
    ,  exeBlockDim   :: Exp
    ,  exeSharedSize :: Maybe Exp
    ,  exeStream     :: Maybe Exp
    ,  exeLoc        :: !SrcLoc
    }
    deriving (Eq, Ord, Data, Typeable)

data Exp = Var Id !SrcLoc
         | Const Const !SrcLoc
         | BinOp BinOp Exp Exp !SrcLoc
         | Assign Exp AssignOp Exp !SrcLoc
         | PreInc Exp !SrcLoc
         | PostInc Exp !SrcLoc
         | PreDec Exp !SrcLoc
         | PostDec Exp !SrcLoc
         | UnOp UnOp Exp !SrcLoc
         | SizeofExp Exp !SrcLoc
         | SizeofType Type !SrcLoc
         | Cast Type Exp !SrcLoc
         | Cond Exp Exp Exp !SrcLoc
         | Member Exp Id !SrcLoc
         | PtrMember Exp Id !SrcLoc
         | Index Exp Exp !SrcLoc
         | FnCall Exp [Exp] !SrcLoc
         | CudaCall Exp ExeConfig [Exp] !SrcLoc
         | Seq Exp Exp !SrcLoc
         | CompoundLit Type [(Maybe Designation, Initializer)] !SrcLoc
         | StmExpr [BlockItem] !SrcLoc
         | BuiltinVaArg Exp Type !SrcLoc
         | AntiExp String !SrcLoc
         | AntiArgs String !SrcLoc
    deriving (Eq, Ord, Data, Typeable)

data BinOp = Add
           | Sub
           | Mul
           | Div
           | Mod
           | Eq
           | Ne
           | Lt
           | Gt
           | Le
           | Ge
           | Land
           | Lor
           | And
           | Or
           | Xor
           | Lsh
           | Rsh
    deriving (Eq, Ord, Data, Typeable)

data AssignOp = JustAssign
              | AddAssign
              | SubAssign
              | MulAssign
              | DivAssign
              | ModAssign
              | LshAssign
              | RshAssign
              | AndAssign
              | XorAssign
              | OrAssign
    deriving (Eq, Ord, Data, Typeable)

data UnOp = AddrOf
          | Deref
          | Positive
          | Negate
          | Not
          | Lnot
    deriving (Eq, Ord, Data, Typeable)

instance Located Id where
    locOf (Id _ loc)      = locOf loc
    locOf (AntiId _ loc)  = locOf loc

instance Located Storage where
    locOf (Tauto loc)      = locOf loc
    locOf (Tregister loc)  = locOf loc
    locOf (Tstatic loc)    = locOf loc
    locOf (Textern loc)    = locOf loc
    locOf (TexternL _ loc) = locOf loc
    locOf (Ttypedef loc)   = locOf loc

instance Located TypeQual where
    locOf (Tconst loc)     = locOf loc
    locOf (Tvolatile loc)  = locOf loc
    locOf (Tinline loc)    = locOf loc

    locOf (Trestrict loc)  = locOf loc

    locOf (TCUDAdevice loc)    = locOf loc
    locOf (TCUDAglobal loc)    = locOf loc
    locOf (TCUDAhost loc)      = locOf loc
    locOf (TCUDAconstant loc)  = locOf loc
    locOf (TCUDAshared loc)    = locOf loc
    locOf (TCUDAnoinline loc)  = locOf loc

    locOf (TCLprivate loc)   = locOf loc
    locOf (TCLlocal loc)     = locOf loc
    locOf (TCLglobal loc)    = locOf loc
    locOf (TCLconstant loc)  = locOf loc
    locOf (TCLreadonly loc)  = locOf loc
    locOf (TCLwriteonly loc) = locOf loc
    locOf (TCLkernel loc)    = locOf loc

instance Located Sign where
    locOf (Tsigned loc)    = locOf loc
    locOf (Tunsigned loc)  = locOf loc

instance Located TypeSpec where
    locOf (Tvoid loc)          = locOf loc
    locOf (Tchar _ loc)        = locOf loc
    locOf (Tshort _ loc)       = locOf loc
    locOf (Tint _ loc)         = locOf loc
    locOf (Tlong _ loc)        = locOf loc
    locOf (Tlong_long _ loc)   = locOf loc
    locOf (Tfloat loc)         = locOf loc
    locOf (Tdouble loc)        = locOf loc
    locOf (Tlong_double loc)   = locOf loc
    locOf (Tstruct _ _ _ loc)  = locOf loc
    locOf (Tunion _ _ _ loc)   = locOf loc
    locOf (Tenum _ _ _ loc)    = locOf loc
    locOf (Tnamed _ loc)       = locOf loc
    locOf (TtypeofExp _ loc)   = locOf loc
    locOf (TtypeofType _ loc)  = locOf loc
    locOf (Tva_list loc)       = locOf loc

instance Located DeclSpec where
    locOf (DeclSpec _ _ _ loc)          = locOf loc
    locOf (AntiDeclSpec _ loc)          = locOf loc
    locOf (AntiTypeDeclSpec _ _ _ loc)  = locOf loc

instance Located ArraySize where
    locOf (ArraySize _ _ loc)     = locOf loc
    locOf (VariableArraySize loc) = locOf loc
    locOf (NoArraySize loc)       = locOf loc

instance Located Decl where
    locOf (DeclRoot loc)        = locOf loc
    locOf (Ptr _ _ loc)         = locOf loc
    locOf (Array _ _ _ loc)     = locOf loc
    locOf (Proto _ _ loc)       = locOf loc
    locOf (OldProto _ _ loc)    = locOf loc
    locOf (AntiTypeDecl _ loc)  = locOf loc

instance Located Type where
    locOf (Type _ _ loc)    = locOf loc
    locOf (AntiType _ loc)  = locOf loc

instance Located Designator where
    locOf (IndexDesignator _ loc)   = locOf loc
    locOf (MemberDesignator _ loc)  = locOf loc

instance Located Designation where
    locOf (Designation _ loc)   = locOf loc

instance Located Initializer where
    locOf (ExpInitializer _ loc)       = locOf loc
    locOf (CompoundInitializer _ loc)  = locOf loc

instance Located Init where
    locOf (Init _ _ _ _ _ loc) = locOf loc

instance Located Typedef where
    locOf (Typedef _ _ _ loc) = locOf loc

instance Located InitGroup where
    locOf (InitGroup _ _ _ loc)     = locOf loc
    locOf (TypedefGroup _ _ _ loc)  = locOf loc
    locOf (AntiDecl _ loc)          = locOf loc
    locOf (AntiDecls _ loc)         = locOf loc

instance Located Field where
    locOf (Field _ _ _ loc) = locOf loc

instance Located FieldGroup where
    locOf (FieldGroup _ _ loc)  = locOf loc
    locOf (AntiSdecl _ loc)     = locOf loc
    locOf (AntiSdecls _ loc)    = locOf loc

instance Located CEnum where
    locOf (CEnum _ _ loc)    = locOf loc
    locOf (AntiEnum _ loc)   = locOf loc
    locOf (AntiEnums _ loc)  = locOf loc

instance Located Attr where
    locOf (Attr _ _ loc) = locOf loc

instance Located Param where
    locOf (Param _ _ _ loc)   = locOf loc
    locOf (AntiParam _ loc)   = locOf loc
    locOf (AntiParams _ loc)  = locOf loc

instance Located Params where
    locOf (Params _ _ loc) = locOf loc

instance Located Func where
    locOf (Func _ _ _ _ _ loc)      = locOf loc
    locOf (OldFunc _ _ _ _ _ _ loc) = locOf loc

instance Located Definition where
    locOf (FuncDef _ loc)     = locOf loc
    locOf (DecDef _ loc)      = locOf loc
    locOf (EscDef _ loc)      = locOf loc
    locOf (AntiFunc _ loc)    = locOf loc
    locOf (AntiEsc _ loc)     = locOf loc
    locOf (AntiEdecl _ loc)   = locOf loc
    locOf (AntiEdecls _ loc)  = locOf loc

instance Located Stm where
    locOf (Label _ _ loc)       = locOf loc
    locOf (Case _ _ loc)        = locOf loc
    locOf (Default _ loc)       = locOf loc
    locOf (Exp _ loc)           = locOf loc
    locOf (Block _ loc)         = locOf loc
    locOf (If _ _ _ loc)        = locOf loc
    locOf (Switch _ _ loc)      = locOf loc
    locOf (While _ _ loc)       = locOf loc
    locOf (DoWhile _ _ loc)     = locOf loc
    locOf (For _ _ _ _ loc)     = locOf loc
    locOf (Goto _ loc)          = locOf loc
    locOf (Continue loc)        = locOf loc
    locOf (Break loc)           = locOf loc
    locOf (Return _ loc)        = locOf loc
    locOf (Pragma _ loc)        = locOf loc
    locOf (Asm _ _ _ _ _ _ loc) = locOf loc
    locOf (AntiPragma _ loc)    = locOf loc
    locOf (AntiStm _ loc)       = locOf loc
    locOf (AntiStms _ loc)      = locOf loc

instance Located BlockItem where
    locOf (BlockDecl decl)       = locOf decl
    locOf (BlockStm stm)         = locOf stm
    locOf (AntiBlockItem _ loc)  = locOf loc
    locOf (AntiBlockItems _ loc) = locOf loc

instance Located Const where
    locOf (IntConst _ _ _ loc)          = locOf loc
    locOf (LongIntConst _ _ _ loc)      = locOf loc
    locOf (LongLongIntConst _ _ _ loc)  = locOf loc
    locOf (FloatConst _ _ loc)          = locOf loc
    locOf (DoubleConst _ _ loc)         = locOf loc
    locOf (LongDoubleConst _ _ loc)     = locOf loc
    locOf (CharConst _ _ loc)           = locOf loc
    locOf (StringConst _ _ loc)         = locOf loc
    locOf (AntiInt _ loc)               = locOf loc
    locOf (AntiUInt _ loc)              = locOf loc
    locOf (AntiLInt _ loc)              = locOf loc
    locOf (AntiULInt _ loc)             = locOf loc
    locOf (AntiFloat _ loc)             = locOf loc
    locOf (AntiDouble _ loc)            = locOf loc
    locOf (AntiLongDouble _ loc)        = locOf loc
    locOf (AntiChar _ loc)              = locOf loc
    locOf (AntiString _ loc)            = locOf loc

instance Located ExeConfig where
    locOf conf = locOf (exeLoc conf)

instance Located Exp where
    locOf (Var _ loc)             = locOf loc
    locOf (Const _ loc)           = locOf loc
    locOf (BinOp _ _ _ loc)       = locOf loc
    locOf (Assign _ _ _ loc)      = locOf loc
    locOf (PreInc _ loc)          = locOf loc
    locOf (PostInc _ loc)         = locOf loc
    locOf (PreDec _ loc)          = locOf loc
    locOf (PostDec _ loc)         = locOf loc
    locOf (UnOp _ _ loc)          = locOf loc
    locOf (SizeofExp _ loc)       = locOf loc
    locOf (SizeofType _ loc)      = locOf loc
    locOf (Cast _ _ loc)          = locOf loc
    locOf (Cond _ _ _ loc)        = locOf loc
    locOf (Member _ _ loc)        = locOf loc
    locOf (PtrMember _ _ loc)     = locOf loc
    locOf (Index _ _ loc)         = locOf loc
    locOf (FnCall _ _ loc)        = locOf loc
    locOf (CudaCall _ _ _ loc)    = locOf loc
    locOf (Seq _ _ loc)           = locOf loc
    locOf (CompoundLit _ _ loc)   = locOf loc
    locOf (StmExpr _ loc)         = locOf loc
    locOf (BuiltinVaArg _ _ loc)  = locOf loc
    locOf (AntiExp _ loc)         = locOf loc
    locOf (AntiArgs _ loc)        = locOf loc

ctypedef :: Id -> Decl -> [Attr] -> Typedef
ctypedef id decl attrs =
    Typedef id decl attrs (id `srcspan` decl `srcspan` attrs)

cdeclSpec :: [Storage] -> [TypeQual] -> TypeSpec -> DeclSpec
cdeclSpec storage quals spec =
    DeclSpec storage quals spec (storage `srcspan` quals `srcspan` spec)

cinitGroup :: DeclSpec -> [Attr] -> [Init] -> InitGroup
cinitGroup dspec attrs inis =
    InitGroup dspec attrs inis (dspec `srcspan` attrs `srcspan` inis)

ctypedefGroup :: DeclSpec -> [Attr] -> [Typedef] -> InitGroup
ctypedefGroup dspec attrs typedefs =
    TypedefGroup dspec attrs typedefs (dspec `srcspan` attrs `srcspan` typedefs)