{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Instruction
-}

{-|
Module : Compiler.Instruction
Description : Defines the bytecode instructions shared between Compiler and VM.
Stability : stable
-}
module Compiler.Instruction
    ( Instruction(..)
    , Immediate(..)
    , immediateToTypeID
    , immediateSize
    , getInstCode
    , instructionSize
    ) where

import Data.Word (Word8)
import Common.Type.Integer (IntValue(..))

-- | Immediate values pushed by PUSH.
--
-- @details
--   Immediate represents the different immediate payloads that can be encoded
--   after a PUSH opcode. The TypeID encoding follows ASM_SPEC.md (version 0x02).
--
-- @return
--   N/A (this documents the type)
--
data Immediate
    = ImmBool Bool      -- ^ TypeID 0x00
    | ImmInt IntValue   -- ^ TypeID 0x01, 0x03, 0x05, 0x07 (i8..i64)
    deriving (Int -> Immediate -> ShowS
[Immediate] -> ShowS
Immediate -> String
(Int -> Immediate -> ShowS)
-> (Immediate -> String)
-> ([Immediate] -> ShowS)
-> Show Immediate
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Immediate -> ShowS
showsPrec :: Int -> Immediate -> ShowS
$cshow :: Immediate -> String
show :: Immediate -> String
$cshowList :: [Immediate] -> ShowS
showList :: [Immediate] -> ShowS
Show, Immediate -> Immediate -> Bool
(Immediate -> Immediate -> Bool)
-> (Immediate -> Immediate -> Bool) -> Eq Immediate
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Immediate -> Immediate -> Bool
== :: Immediate -> Immediate -> Bool
$c/= :: Immediate -> Immediate -> Bool
/= :: Immediate -> Immediate -> Bool
Eq, Eq Immediate
Eq Immediate =>
(Immediate -> Immediate -> Ordering)
-> (Immediate -> Immediate -> Bool)
-> (Immediate -> Immediate -> Bool)
-> (Immediate -> Immediate -> Bool)
-> (Immediate -> Immediate -> Bool)
-> (Immediate -> Immediate -> Immediate)
-> (Immediate -> Immediate -> Immediate)
-> Ord Immediate
Immediate -> Immediate -> Bool
Immediate -> Immediate -> Ordering
Immediate -> Immediate -> Immediate
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Immediate -> Immediate -> Ordering
compare :: Immediate -> Immediate -> Ordering
$c< :: Immediate -> Immediate -> Bool
< :: Immediate -> Immediate -> Bool
$c<= :: Immediate -> Immediate -> Bool
<= :: Immediate -> Immediate -> Bool
$c> :: Immediate -> Immediate -> Bool
> :: Immediate -> Immediate -> Bool
$c>= :: Immediate -> Immediate -> Bool
>= :: Immediate -> Immediate -> Bool
$cmax :: Immediate -> Immediate -> Immediate
max :: Immediate -> Immediate -> Immediate
$cmin :: Immediate -> Immediate -> Immediate
min :: Immediate -> Immediate -> Immediate
Ord)

-- | Map an Immediate to the TypeID byte used by PUSH.
--
-- @args
--   - imm: the Immediate value whose TypeID is requested
--
-- @details
--   Returns the one-byte TypeID used by the binary format to encode immediates
--   as specified in the PUSH Type Table of ASM_SPEC.md.
--
-- @return
--   The TypeID as a 'Word8'.
--
immediateToTypeID :: Immediate -> Word8
immediateToTypeID :: Immediate -> Word8
immediateToTypeID (ImmBool Bool
_)         = Word8
0x00
immediateToTypeID (ImmInt (I8 Int8
_))     = Word8
0x01
immediateToTypeID (ImmInt (UI8 Word8
_))    = Word8
0x02
immediateToTypeID (ImmInt (IChar Int8
_))  = Word8
0x09
immediateToTypeID (ImmInt (UIChar Word8
_)) = Word8
0x10
immediateToTypeID (ImmInt (I16 Int16
_))    = Word8
0x03
immediateToTypeID (ImmInt (UI16 Word16
_))   = Word8
0x04
immediateToTypeID (ImmInt (I32 Int32
_))    = Word8
0x05
immediateToTypeID (ImmInt (UI32 Word32
_))   = Word8
0x06
immediateToTypeID (ImmInt (I64 Int64
_))    = Word8
0x07
immediateToTypeID (ImmInt (UI64 Word64
_))   = Word8
0x08

-- | Size in bytes of the immediate payload (not counting the TypeID byte).
--
-- @args
--   - imm: the Immediate whose payload size is requested
--
-- @details
--   This helper returns the number of bytes that follow the TypeID for the
--   immediate payload. It is useful for computing instruction sizes and label
--   offsets during assembly.
--
-- @return
--   The payload size in bytes as 'Int'.
--
immediateSize :: Immediate -> Int
immediateSize :: Immediate -> Int
immediateSize (ImmBool Bool
_)         = Int
1
immediateSize (ImmInt (I8 Int8
_))     = Int
1
immediateSize (ImmInt (UI8 Word8
_))    = Int
1
immediateSize (ImmInt (IChar Int8
_))  = Int
1
immediateSize (ImmInt (UIChar Word8
_)) = Int
1
immediateSize (ImmInt (I16 Int16
_))    = Int
2
immediateSize (ImmInt (UI16 Word16
_))   = Int
2
immediateSize (ImmInt (I32 Int32
_))    = Int
4
immediateSize (ImmInt (UI32 Word32
_))   = Int
4
immediateSize (ImmInt (I64 Int64
_))    = Int
8
immediateSize (ImmInt (UI64 Word64
_))   = Int
8

-- | The Instruction set used by compiler/VM
--
-- @details
--   Each constructor represents a single concrete virtual machine instruction.
--   Numeric opcodes and layout are defined in ASM_SPEC.md (version 0x02).
--
-- @return
--   N/A (this documents the type)
--
data Instruction
    -- 1. Stack Operations
    = Push Immediate          -- ^ 0x01 PUSH [Type] [Value]
    | Pop                     -- ^ 0x02 POP
    | Dup                     -- ^ 0x03 DUP
    | Swap                    -- ^ 0x04 SWAP

    -- 2. Arithmetic
    | Add                     -- ^ 0x10 ADD
    | Sub                     -- ^ 0x11 SUB
    | Mul                     -- ^ 0x12 MUL
    | Div                     -- ^ 0x13 DIV
    | Mod                     -- ^ 0x14 MOD

    -- 3. Logic & Comparison
    | Eq                      -- ^ 0x20 EQ
    | TEq                     -- ^ 0x26 TEQ
    | Lt                      -- ^ 0x21 LT
    | Le                      -- ^ 0x25 LE
    | Not                     -- ^ 0x22 NOT
    | And                     -- ^ 0x23 AND
    | Or                      -- ^ 0x24 OR

    -- 4. Flow Control
    | Jump Int                -- ^ 0x30 JUMP [off]
    | JumpIfFalse Int         -- ^ 0x31 JUMP_IF_FALSE [off]
    | JumpIfTrue Int          -- ^ 0x32 JUMP_IF_TRUE [off]

    -- 5. Functions & Calls
    | Call Int                -- ^ 0x40 CALL [off]
    | TailCall Int            -- ^ 0x41 TAILCALL [off]
    | CallIndirect            -- ^ 0x42 CALL_INDIRECT [address on stack]
    | Ret Int                 -- ^ 0x43 RET [stack size]

    -- 6. Memory (Variables)
    | LoadLocal Int           -- ^ 0x50 LOAD_LOCAL [idx]
    | StoreLocal Int          -- ^ 0x51 STORE_LOCAL [idx]
    | LoadGlobal Int          -- ^ 0x52 LOAD_GLOBAL [idx]
    | StoreGlobal Int         -- ^ 0x53 STORE_GLOBAL [idx]
    | LoadCapture Int         -- ^ 0x54 LOAD_CAPTURE [idx]
    | StoreCapture Int        -- ^ 0x55 STORE_CAPTURE [idx]

    -- 7. Closures & Types
    | MakeClosure Int Int     -- ^ 0x60 MAKE_CLOSURE [addr] [n_captures]
    | GetFuncAddr Int         -- ^ 0x61 GET_FUNC_ADDR [id]
    | BuildStruct Int         -- ^ 0x62 BUILD_STRUCT [n_fields]
    | GetStructField Int      -- ^ 0x63 GET_STRUCT_FIELD [idx]
    | AttrUpdate              -- ^ 0x64 ATTR_UPDATE [struct] [idx] [value]
    | Cast Word8              -- ^ 0x80 CAST [TypeID]
    | TypeOf                  -- ^ 0x81 TYPEOF
    | SizeOf                  -- ^ 0x82 SIZEOF

    -- 8. List Operations
    | Cons                    -- ^ 0x90 CONS
    | Head                    -- ^ 0x91 HEAD [list]
    | Tail                    -- ^ 0x92 TAIL [list]
    | Nth                     -- ^ 0x93 NTH [list] [idx]
    | NthUpdate               -- ^ 0x95 NTH_UPDATE [list] [idx] [value]
    | BuildList Int           -- ^ 0x94 BUILD_LIST [n_elements]

    -- 8. I/O
    | Open                    -- ^ 0xA0 OPEN
    | Read                    -- ^ 0xA1 READ
    | Write                   -- ^ 0xA2 WRITE
    | Close                   -- ^ 0xA3 CLOSE
    | Input                   -- ^ 0xA4 INPUT

    -- 9. System / Debug
    | Print                   -- ^ 0x70 PRINT
    | Halt                    -- ^ 0x71 HALT
    | Exit                    -- ^ 0x72 EXIT
    | CheckStack Int          -- ^ 0xFE CHECK_STACK [N]
    | Nop                     -- ^ 0xFF NOP
    deriving (Int -> Instruction -> ShowS
[Instruction] -> ShowS
Instruction -> String
(Int -> Instruction -> ShowS)
-> (Instruction -> String)
-> ([Instruction] -> ShowS)
-> Show Instruction
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Instruction -> ShowS
showsPrec :: Int -> Instruction -> ShowS
$cshow :: Instruction -> String
show :: Instruction -> String
$cshowList :: [Instruction] -> ShowS
showList :: [Instruction] -> ShowS
Show, Instruction -> Instruction -> Bool
(Instruction -> Instruction -> Bool)
-> (Instruction -> Instruction -> Bool) -> Eq Instruction
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Instruction -> Instruction -> Bool
== :: Instruction -> Instruction -> Bool
$c/= :: Instruction -> Instruction -> Bool
/= :: Instruction -> Instruction -> Bool
Eq, Eq Instruction
Eq Instruction =>
(Instruction -> Instruction -> Ordering)
-> (Instruction -> Instruction -> Bool)
-> (Instruction -> Instruction -> Bool)
-> (Instruction -> Instruction -> Bool)
-> (Instruction -> Instruction -> Bool)
-> (Instruction -> Instruction -> Instruction)
-> (Instruction -> Instruction -> Instruction)
-> Ord Instruction
Instruction -> Instruction -> Bool
Instruction -> Instruction -> Ordering
Instruction -> Instruction -> Instruction
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Instruction -> Instruction -> Ordering
compare :: Instruction -> Instruction -> Ordering
$c< :: Instruction -> Instruction -> Bool
< :: Instruction -> Instruction -> Bool
$c<= :: Instruction -> Instruction -> Bool
<= :: Instruction -> Instruction -> Bool
$c> :: Instruction -> Instruction -> Bool
> :: Instruction -> Instruction -> Bool
$c>= :: Instruction -> Instruction -> Bool
>= :: Instruction -> Instruction -> Bool
$cmax :: Instruction -> Instruction -> Instruction
max :: Instruction -> Instruction -> Instruction
$cmin :: Instruction -> Instruction -> Instruction
min :: Instruction -> Instruction -> Instruction
Ord)

-- | Opcode mapping for each instruction as defined in doc/ASM_SPEC.md.
--
-- @args
--   - instr: the 'Instruction' to map to its opcode byte
--
-- @details
--   This function centralizes the numeric opcode mapping, ensuring the
--   compiler and assembler use the same byte values when serializing to the
--   .gla format. It returns the opcode byte for a given concrete instruction.
--
-- @return
--   The opcode as a 'Word8'.
--
getInstCode :: Instruction -> Word8
getInstCode :: Instruction -> Word8
getInstCode (Push Immediate
_)             = Word8
0x01
getInstCode Instruction
Pop                  = Word8
0x02
getInstCode Instruction
Dup                  = Word8
0x03
getInstCode Instruction
Swap                 = Word8
0x04

getInstCode Instruction
Add                  = Word8
0x10
getInstCode Instruction
Sub                  = Word8
0x11
getInstCode Instruction
Mul                  = Word8
0x12
getInstCode Instruction
Div                  = Word8
0x13
getInstCode Instruction
Mod                  = Word8
0x14

getInstCode Instruction
Eq                   = Word8
0x20
getInstCode Instruction
Lt                   = Word8
0x21
getInstCode Instruction
Not                  = Word8
0x22
getInstCode Instruction
And                  = Word8
0x23
getInstCode Instruction
Or                   = Word8
0x24
getInstCode Instruction
Le                   = Word8
0x25
getInstCode Instruction
TEq                  = Word8
0x26

getInstCode (Jump Int
_)             = Word8
0x30
getInstCode (JumpIfFalse Int
_)      = Word8
0x31
getInstCode (JumpIfTrue Int
_)       = Word8
0x32

getInstCode (Call Int
_)             = Word8
0x40
getInstCode (TailCall Int
_)         = Word8
0x41
getInstCode Instruction
CallIndirect         = Word8
0x42
getInstCode (Ret Int
_)              = Word8
0x43

getInstCode (LoadLocal Int
_)        = Word8
0x50
getInstCode (StoreLocal Int
_)       = Word8
0x51
getInstCode (LoadGlobal Int
_)       = Word8
0x52
getInstCode (StoreGlobal Int
_)      = Word8
0x53
getInstCode (LoadCapture Int
_)      = Word8
0x54
getInstCode (StoreCapture Int
_)     = Word8
0x55

getInstCode (MakeClosure Int
_ Int
_)    = Word8
0x60
getInstCode (GetFuncAddr Int
_)      = Word8
0x61
getInstCode (BuildStruct Int
_)      = Word8
0x62
getInstCode (GetStructField Int
_)   = Word8
0x63
getInstCode Instruction
AttrUpdate           = Word8
0x64
getInstCode (Cast Word8
_)             = Word8
0x80
getInstCode Instruction
TypeOf               = Word8
0x81
getInstCode Instruction
SizeOf               = Word8
0x82

getInstCode Instruction
Cons                 = Word8
0x90
getInstCode Instruction
Head                 = Word8
0x91
getInstCode Instruction
Tail                 = Word8
0x92
getInstCode Instruction
Nth                  = Word8
0x93
getInstCode (BuildList Int
_)        = Word8
0x94
getInstCode Instruction
NthUpdate            = Word8
0x95

getInstCode Instruction
Open                 = Word8
0xA0
getInstCode Instruction
Read                 = Word8
0xA1
getInstCode Instruction
Write                = Word8
0xA2
getInstCode Instruction
Close                = Word8
0xA3
getInstCode Instruction
Input                = Word8
0xA4

getInstCode Instruction
Print                = Word8
0x70
getInstCode Instruction
Halt                 = Word8
0x71
getInstCode Instruction
Exit                 = Word8
0x72
getInstCode (CheckStack Int
_)       = Word8
0xFE
getInstCode Instruction
Nop                  = Word8
0xFF

-- | Computes the size in bytes of a given instruction, including its payload.
--
-- @args
--   - instr: the 'Instruction' whose size is to be computed
--
-- @details
--   Returns the total number of bytes required to encode the instruction,
--   including opcode and any immediate or address payloads. Used for offset
--   calculations and binary serialization.
--
-- @return
--   The instruction size in bytes as 'Int'.
--
instructionSize :: Instruction -> Int
instructionSize :: Instruction -> Int
instructionSize (Push Immediate
imm)          = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Immediate -> Int
immediateSize Immediate
imm
instructionSize Instruction
Pop                 = Int
1
instructionSize Instruction
Dup                 = Int
1
instructionSize Instruction
Swap                = Int
1

-- Arithmetic
instructionSize Instruction
Add                 = Int
1
instructionSize Instruction
Sub                 = Int
1
instructionSize Instruction
Mul                 = Int
1
instructionSize Instruction
Div                 = Int
1
instructionSize Instruction
Mod                 = Int
1

-- Logic & Comparison
instructionSize Instruction
Eq                  = Int
1
instructionSize Instruction
TEq                 = Int
1
instructionSize Instruction
Lt                  = Int
1
instructionSize Instruction
Le                  = Int
1
instructionSize Instruction
Not                 = Int
1
instructionSize Instruction
And                 = Int
1
instructionSize Instruction
Or                  = Int
1

-- Flow Control
instructionSize (Jump Int
_)            = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (JumpIfFalse Int
_)     = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (JumpIfTrue Int
_)      = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4

-- Functions & Calls
instructionSize (Call Int
_)            = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (TailCall Int
_)        = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize Instruction
CallIndirect        = Int
1
instructionSize (Ret Int
_)             = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4

-- Memory (Variables)
instructionSize (LoadLocal Int
_)       = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (StoreLocal Int
_)      = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (LoadGlobal Int
_)      = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (StoreGlobal Int
_)     = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (LoadCapture Int
_)     = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (StoreCapture Int
_)    = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4

-- Closures & Types
instructionSize (MakeClosure Int
_ Int
_)   = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (GetFuncAddr Int
_)     = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (BuildStruct Int
_)     = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize (GetStructField Int
_)  = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize Instruction
AttrUpdate          = Int
1
instructionSize (Cast Word8
_)            = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
instructionSize Instruction
TypeOf              = Int
1
instructionSize Instruction
SizeOf              = Int
1

-- List Operations
instructionSize Instruction
Cons                = Int
1
instructionSize Instruction
Head                = Int
1
instructionSize Instruction
Tail                = Int
1
instructionSize Instruction
Nth                 = Int
1
instructionSize (BuildList Int
_)       = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize Instruction
NthUpdate           = Int
1

-- I/O
instructionSize Instruction
Open                = Int
1
instructionSize Instruction
Read                = Int
1
instructionSize Instruction
Write               = Int
1
instructionSize Instruction
Close               = Int
1
instructionSize Instruction
Input               = Int
1

-- System / Debug
instructionSize Instruction
Print               = Int
1
instructionSize Instruction
Halt                = Int
1
instructionSize Instruction
Exit                = Int
1
instructionSize (CheckStack Int
_)      = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
4
instructionSize Instruction
Nop                 = Int
1