{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, DeriveGeneric #-}

-- | Lua 5.3 syntax tree, as specified in <http://www.lua.org/manual/5.3/manual.html#9>.
-- Annotation implementation is inspired by haskell-src-exts.
module Language.Lua.Annotated.Syntax where

import           Control.DeepSeq (NFData)
import           Data.Data       (Data, Typeable)
import           GHC.Generics    (Generic)
import           Prelude         hiding (EQ, GT, LT)

data Name a = Name a String deriving (Show, Eq, Functor, Data, Typeable, Generic)

data Stat a
    = Assign a [Var a] [Exp a] -- ^var1, var2 .. = exp1, exp2 ..
    | FunCall a (FunCall a) -- ^function call
    | Label a (Name a) -- ^label for goto
    | Break a -- ^break
    | Goto a (Name a) -- ^goto label
    | Do a (Block a) -- ^do .. end
    | While a (Exp a) (Block a) -- ^while .. do .. end
    | Repeat a (Block a) (Exp a) -- ^repeat .. until ..
    | If a [(Exp a, Block a)] (Maybe (Block a)) -- ^if .. then .. [elseif ..] [else ..] end
    | ForRange a (Name a) (Exp a) (Exp a) (Maybe (Exp a)) (Block a) -- ^for x=start, end [, step] do .. end
    | ForIn a [Name a] [Exp a] (Block a) -- ^for x in .. do .. end
    | FunAssign a (FunName a) (FunBody a) -- ^function \<var\> (..) .. end
    | LocalFunAssign a (Name a) (FunBody a) -- ^local function \<var\> (..) .. end
    | LocalAssign a [Name a] (Maybe [Exp a]) -- ^local var1, var2 .. = exp1, exp2 ..
    | EmptyStat a -- ^/;/
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data Exp a
    = Nil a
    | Bool a Bool
    | Number a String
    | String a String
    | Vararg a -- ^/.../
    | EFunDef a (FunDef a) -- ^/function (..) .. end/
    | PrefixExp a (PrefixExp a)
    | TableConst a (Table a) -- ^table constructor
    | Binop a (Binop a) (Exp a) (Exp a) -- ^binary operators, /+ - * ^ % .. < <= > >= == ~= and or/
    | Unop a (Unop a) (Exp a) -- ^unary operators, /- not #/
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data Var a
    = VarName a (Name a) -- ^variable
    | Select a (PrefixExp a) (Exp a) -- ^/table[exp]/
    | SelectName a (PrefixExp a) (Name a) -- ^/table.variable/
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data Binop a = Add a | Sub a | Mul a | Div a | Exp a | Mod a | Concat a
    | LT a | LTE a | GT a | GTE a | EQ a | NEQ a | And a | Or a
    | IDiv a | ShiftL a | ShiftR a | BAnd a | BOr a | BXor a
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data Unop a = Neg a | Not a | Len a | Complement a
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data PrefixExp a
    = PEVar a (Var a)
    | PEFunCall a (FunCall a)
    | Paren a (Exp a)
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data Table a = Table a [TableField a] -- ^list of table fields
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data TableField a
    = ExpField a (Exp a) (Exp a) -- ^/[exp] = exp/
    | NamedField a (Name a) (Exp a) -- ^/name = exp/
    | Field a (Exp a)
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

-- | A block is list of statements with optional return statement.
data Block a = Block a [Stat a] (Maybe [Exp a])
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data FunName a = FunName a (Name a) [Name a] (Maybe (Name a))
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data FunDef a = FunDef a (FunBody a)
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data FunBody a = FunBody a [Name a] (Maybe a) (Block a) -- ^(args, vararg, block)
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data FunCall a
    = NormalFunCall a (PrefixExp a) (FunArg a) -- ^/prefixexp ( funarg )/
    | MethodCall a (PrefixExp a) (Name a) (FunArg a) -- ^/prefixexp : name ( funarg )/
    deriving (Show, Eq, Functor, Data, Typeable, Generic)

data FunArg a
    = Args a [Exp a] -- ^list of args
    | TableArg a (Table a) -- ^table constructor
    | StringArg a String -- ^string
    deriving (Show, Eq, Functor, Data, Typeable, Generic)


class Functor ast => Annotated ast where
    -- | Retrieve the annotation of an AST node.
    ann :: ast l -> l
    -- | Change the annotation of an AST node. Note that only the annotation of
    --   the node itself is affected, and not the annotations of any child nodes.
    --   if all nodes in the AST tree are to be affected, use 'fmap'.
    amap :: (l -> l) -> ast l -> ast l

instance Annotated Stat where
    ann (Assign a _ _) = a
    ann (FunCall a _) = a
    ann (Label a _) = a
    ann (Break a) = a
    ann (Goto a _) = a
    ann (Do a _) = a
    ann (While a _ _) = a
    ann (Repeat a _ _) = a
    ann (If a _ _) = a
    ann (ForRange a _ _ _ _ _) = a
    ann (ForIn a _ _ _) = a
    ann (FunAssign a _ _) = a
    ann (LocalFunAssign a _ _) = a
    ann (LocalAssign a _ _) = a
    ann (EmptyStat a) = a

    amap f (Assign a x1 x2) = Assign (f a) x1 x2
    amap f (FunCall a x1) = FunCall (f a) x1
    amap f (Label a x1) = Label (f a) x1
    amap f (Break a) = Break (f a)
    amap f (Goto a x1) = Goto (f a) x1
    amap f (Do a x1) = Do (f a) x1
    amap f (While a x1 x2) = While (f a) x1 x2
    amap f (Repeat a x1 x2) = Repeat (f a) x1 x2
    amap f (If a x1 x2) = If (f a) x1 x2
    amap f (ForRange a x1 x2 x3 x4 x5) = ForRange (f a) x1 x2 x3 x4 x5
    amap f (ForIn a x1 x2 x3) = ForIn (f a) x1 x2 x3
    amap f (FunAssign a x1 x2) = FunAssign (f a) x1 x2
    amap f (LocalFunAssign a x1 x2) = LocalFunAssign (f a) x1 x2
    amap f (LocalAssign a x1 x2) = LocalAssign (f a) x1 x2
    amap f (EmptyStat a) = EmptyStat (f a)

instance Annotated Exp where
    ann (Nil a) = a
    ann (Bool a _) = a
    ann (Number a _) = a
    ann (String a _) = a
    ann (Vararg a) = a
    ann (EFunDef a _) = a
    ann (PrefixExp a _) = a
    ann (TableConst a _) = a
    ann (Binop a _ _ _) = a
    ann (Unop a _ _) = a

    amap f (Nil a) = Nil (f a)
    amap f (Bool a x1) = Bool (f a) x1
    amap f (Number a x1) = Number (f a) x1
    amap f (String a x1) = String (f a) x1
    amap f (Vararg a) = Vararg (f a)
    amap f (EFunDef a x1) = EFunDef (f a) x1
    amap f (PrefixExp a x1) = PrefixExp (f a) x1
    amap f (TableConst a x1) = TableConst (f a) x1
    amap f (Binop a x1 x2 x3) = Binop (f a) x1 x2 x3
    amap f (Unop a x1 x2) = Unop (f a) x1 x2

instance Annotated Var where
    ann (VarName a _) = a
    ann (Select a _ _) = a
    ann (SelectName a _ _) = a

    amap f (VarName a x1) = VarName (f a) x1
    amap f (Select a x1 x2) = Select (f a) x1 x2
    amap f (SelectName a x1 x2) = SelectName (f a) x1 x2

instance Annotated Binop where
    ann (Add a) = a
    ann (Sub a) = a
    ann (Mul a) = a
    ann (Div a) = a
    ann (Exp a) = a
    ann (Mod a) = a
    ann (Concat a) = a
    ann (LT a) = a
    ann (LTE a) = a
    ann (GT a) = a
    ann (GTE a) = a
    ann (EQ a) = a
    ann (NEQ a) = a
    ann (And a) = a
    ann (Or a) = a
    ann (BAnd a) = a
    ann (BOr a) = a
    ann (BXor a) = a
    ann (ShiftL a) = a
    ann (ShiftR a) = a
    ann (IDiv a) = a

    amap f (Add a) = Add (f a)
    amap f (Sub a) = Sub (f a)
    amap f (Mul a) = Mul (f a)
    amap f (Div a) = Div (f a)
    amap f (Exp a) = Exp (f a)
    amap f (Mod a) = Mod (f a)
    amap f (Concat a) = Concat (f a)
    amap f (LT a) = LT (f a)
    amap f (LTE a) = LTE (f a)
    amap f (GT a) = GT (f a)
    amap f (GTE a) = GTE (f a)
    amap f (EQ a) = EQ (f a)
    amap f (NEQ a) = NEQ (f a)
    amap f (And a) = And (f a)
    amap f (Or a) = Or (f a)
    amap f (BAnd a) = BAnd (f a)
    amap f (BOr a) = BOr (f a)
    amap f (BXor a) = BXor (f a)
    amap f (ShiftL a) = ShiftL (f a)
    amap f (ShiftR a) = ShiftR (f a)
    amap f (IDiv a) = IDiv (f a)

instance Annotated Unop where
    ann (Neg a) = a
    ann (Not a) = a
    ann (Len a) = a
    ann (Complement a) = a

    amap f (Neg a) = Neg (f a)
    amap f (Not a) = Not (f a)
    amap f (Len a) = Len (f a)
    amap f (Complement a) = Complement (f a)

instance Annotated PrefixExp where
    ann (PEVar a _) = a
    ann (PEFunCall a _) = a
    ann (Paren a _) = a

    amap f (PEVar a x1) = PEVar (f a) x1
    amap f (PEFunCall a x1) = PEFunCall (f a) x1
    amap f (Paren a x1) = Paren (f a) x1

instance Annotated Table where
    ann (Table a _) = a
    amap f (Table a x1) = Table (f a) x1

instance Annotated TableField where
    ann (ExpField a _ _) = a
    ann (NamedField a _ _) = a
    ann (Field a _) = a

    amap f (ExpField a x1 x2) = ExpField (f a) x1 x2
    amap f (NamedField a x1 x2) = NamedField (f a) x1 x2
    amap f (Field a x1) = Field (f a) x1

instance Annotated Block where
    ann (Block a _ _) = a
    amap f (Block a x1 x2) = Block (f a) x1 x2

instance Annotated FunName where
    ann (FunName a _ _ _) = a
    amap f (FunName a x1 x2 x3) = FunName (f a) x1 x2 x3

instance Annotated FunDef where
    ann (FunDef a _) = a
    amap f (FunDef a x1) = FunDef (f a) x1

instance Annotated FunBody where
    ann (FunBody a _ _ _) = a
    amap f (FunBody a x1 x2 x3) = FunBody (f a) x1 x2 x3

instance Annotated FunCall where
    ann (NormalFunCall a _ _) = a
    ann (MethodCall a _ _ _) = a

    amap f (NormalFunCall a x1 x2) = NormalFunCall (f a) x1 x2
    amap f (MethodCall a x1 x2 x3) = MethodCall (f a) x1 x2 x3

instance Annotated FunArg where
    ann (Args a _) = a
    ann (TableArg a _) = a
    ann (StringArg a _) = a

    amap f (Args a x1) = Args (f a) x1
    amap f (TableArg a x1) = TableArg (f a) x1
    amap f (StringArg a x1) = StringArg (f a) x1

instance Annotated Name where
    ann (Name a _) = a
    amap f (Name a x1) = Name (f a) x1

instance NFData a => NFData (Name a)
instance NFData a => NFData (Stat a)
instance NFData a => NFData (Exp a)
instance NFData a => NFData (Var a)
instance NFData a => NFData (Binop a)
instance NFData a => NFData (Unop a)
instance NFData a => NFData (PrefixExp a)
instance NFData a => NFData (Table a)
instance NFData a => NFData (TableField a)
instance NFData a => NFData (Block a)
instance NFData a => NFData (FunName a)
instance NFData a => NFData (FunDef a)
instance NFData a => NFData (FunBody a)
instance NFData a => NFData (FunCall a)
instance NFData a => NFData (FunArg a)
